/*
* PARSEENV.CMD - C. Langanke for Netlabs EPM Distribution Project 2002
*
* Syntax: PARSEENV sourcefile targetfile
* [VAR=VALUE [VAR=VALUE [...]]]
*
* This program writes a copy of the sourcefile replaces environment
* variables in a (copy of a) text file. Additional vars can be specified
* as symbols as commandline parameters. To escape a % char, it has to
* be doubled.
*
* The XML Tag includes a C-headerfile and
* loads defined symbols. Note that
* - the filename may include envvars to distinct between different
* files for e.g. different languages
* - only #defines from within these files are interpreted.
* - #ifdef #else #endif are not supported, but ignored
* - no C style comments are supported like: /* comment */
* - C++ style comments are supported like: // comment
* but are not skipped when being used within strings (should be)
* - no symbols can be used within the #define values yet
* - strings may include \r \n and \t
* - backslashes must be doubled.
*
* The XML Tag includes textfiles as part of
* the wis script. Note that
* - the filename may include envvars to distinct between different
* files for e.g. different languages.
*
*/
/* The first comment is used as online help text */
/****************************** Module Header *******************************
*
* Module Name: parseenv.cmd
*
* Batch for to replace environment variables in text files
*
* Copyright (c) Netlabs EPM Distribution Project 2002
*
* $Id$
*
* ===========================================================================
*
* This file is part of the Netlabs EPM Distribution package and is free
* software. You can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* Netlabs EPM Distribution. This library is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
****************************************************************************/
SIGNAL ON HALT
TitleLine = STRIP(SUBSTR(SourceLine(2), 3));
PARSE VAR TitleLine CmdName'.CMD 'Info;
PARSE VALUE "$Revision$" WITH . Version .;
Title = CmdName 'V'Version Info;
env = 'OS2ENVIRONMENT';
TRUE = (1 = 1);
FALSE = (0 = 1);
CrLf = '0d0a'x;
Redirection = '> NUL 2>&1';
'@ECHO OFF'
/* OS/2 Error codes */
ERROR.NO_ERROR = 0;
ERROR.INVALID_FUNCTION = 1;
ERROR.FILE_NOT_FOUND = 2;
ERROR.PATH_NOT_FOUND = 3;
ERROR.ACCESS_DENIED = 5;
ERROR.NOT_ENOUGH_MEMORY = 8;
ERROR.INVALID_FORMAT = 11;
ERROR.INVALID_DATA = 13;
ERROR.NO_MORE_FILES = 18;
ERROR.WRITE_FAULT = 29;
ERROR.READ_FAULT = 30;
ERROR.GEN_FAILURE = 31;
ERROR.INVALID_PARAMETER = 87;
ERROR.OPEN_FAILED = 110;
ERROR.ENVVAR_NOT_FOUND = 203;
GlobalVars = 'Title CmdName CrLf env TRUE FALSE Redirection ERROR.';
call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
call SysLoadFuncs
/* eventually show help */
ARG Parm .
IF ((Parm = '') | (POS('?', Parm) > 0)) THEN
DO
rc = ShowHelp();
EXIT( ERROR.INVALID_PARAMETER);
END;
/* default values */
GlobalVars = GlobalVars 'fDebug fVerbose';
fDebug = FALSE;
fVerbose = FALSE;
rc = ERROR.NO_ERROR;
SourceFile = '';
TargetFile = '';
DO UNTIL (TRUE)
/* don't modify environment of CMD.EXE */
rcx = SETLOCAL();
/* determine parameters */
PARSE ARG Parms;
DO WHILE (Parms \= '')
PARSE VAR Parms ThisParm Parms;
PARSE VAR ThisParm ThisVar'='ThisValue;
ThisVar = STRIP( ThisVar);
ThisValue = STRIP( ThisValue);
IF (ThisValue \= '') THEN
DO
/* pass symbols to environment */
rcx = VALUE( ThisVar, ThisValue, env);
ITERATE;
END;
/* all other parms must be file or dir specifications */
SELECT
WHEN (SourceFile = '') THEN
SourceFile = ThisParm;
WHEN (TargetFile = '') THEN
TargetFile = ThisParm;
OTHERWISE
DO
SAY CmdName': Error: Invalid parameter' ThisParm 'specified.';
rc = ERROR.INVALID_PARAMETER;
LEAVE;
END;
END;
END;
IF (rc \= ERROR.NO_ERROR) THEN
LEAVE;
/* source file specified? */
IF (SourceFile = '') THEN
DO
SAY CmdName': Error: No source file specified.';
rc = ERROR.INVALID_PARAMETER;
LEAVE;
END;
/* does it exist? */
IF (\FileExist( SourceFile)) THEN
DO
SAY CmdName': Error: Source file' SourceFile 'cannot be found.';
rc = ERROR.FILE_NOT_FOUND;
LEAVE;
END;
/* target file not specified? */
IF (TargetFile = '') THEN
DO
SAY CmdName': Error: No target file specified.';
rc = ERROR.INVALID_PARAMETER;
LEAVE;
END;
rcx = SysFileDelete( TargetFile);
/* open files */
IF (STREAM( SourceFile, 'C', 'OPEN READ') \= 'READY:') THEN
DO
SAY CmdName': Error: Cannot open source file' SourceFile 'for read.';
rc = ERROR.OPEN_FAILED;
LEAVE;
END;
IF (STREAM( TargetFile, 'C', 'OPEN WRITE') \= 'READY:') THEN
DO
SAY CmdName': Error: Cannot open target file' TargetFile 'for write.';
rc = ERROR.OPEN_FAILED;
LEAVE;
END;
/* now copy contents */
Linecount = 0;
fInComment = FALSE;
DO WHILE (LINES( SourceFile))
ThisLine = LINEIN( SourceFile);
Linecount = Linecount + 1;
/* check for include files */
CheckLine = TRANSLATE( ThisLine);
CommentCheckLine = CheckLine;
/* Filter out comments in CommentCheckLine, set fInComment flag */
DO FOREVER
commentStartPos = POS( '', CommentCheckLine);
SELECT
WHEN (fInComment & (commentEndPos > 0)) THEN
DO
/* Use part after comment */
CommentCheckLine = SUBSTR( CommentCheckLine, commentEndPos + 3);
fInComment = FALSE;
END;
WHEN (\fInComment & (commentStartPos > 0) & (commentEndPos > 0)) THEN
DO
/* Remove oneline comment */
CommentCheckLine = LEFT( CommentCheckLine, commentStartPos - 1) ||,
SUBSTR( CommentCheckLine, commentEndPos + 3);
END;
WHEN (\fInComment & (commentStartPos > 0)) THEN
DO
/* Use part before comment */
CommentCheckLine = LEFT( CommentCheckLine, commentStartPos - 1);
fInComment = TRUE;
END;
OTHERWISE LEAVE;
END;
END;
SELECT
WHEN fInComment THEN Tag = '';
WHEN (POS( ' 0) THEN Tag = 'CINCLUDE';
WHEN (POS( ' 0) THEN Tag = 'TINCLUDE';
OTHERWISE Tag = '';
END;
IF (Tag \= '') THEN
DO
tPos = POS( '<'Tag, CheckLine);
NewLineStart = LEFT( ThisLine, tPos - 1);
NewLineEnd = '';
TagLine = SUBSTR( ThisLine, tPos);
/* search end of tag */
ePos = POS( '>', ThisLine, tPos);
IF (ePos > 0) THEN
DO
NewLineEnd = SUBSTR( ThisLine, ePos + 1);
TagLine = TagLine LEFT( ThisLine, ePos);
END;
DO WHILE (ePos = 0)
ThisLine = LINEIN( SourceFile);
Linecount = Linecount + 1;
ePos = POS( '>', ThisLine, tPos);
IF (ePos > 0) THEN
DO
NewLineEnd = SUBSTR( ThisLine, ePos + 1);
TagLine = TagLine LEFT( ThisLine, ePos);
END;
ELSE
TagLine = ThisLine;
END;
PARSE VAR TagLine '"'IncludeFile'"';
IncludeFile = ParseLine( IncludeFile);
IF (STREAM( IncludeFile, 'C', 'OPEN READ') \= 'READY:') THEN
DO
SAY SourceFile'('Linecount') : Error: Cannot open include file 'IncludeFile
rc = ERROR.INVALID_DATA;
LEAVE;
END;
SELECT
WHEN (Tag = 'CINCLUDE') THEN
DO
rcx = ProcessCIncludeFile( IncludeFile);
ThisLine = NewLineStart''NewLineEnd;
END;
WHEN (Tag = 'TINCLUDE') THEN
DO
Content = ProcessScriptIncludeFile( IncludeFile);
ThisLine = NewLineStart''Content''NewLineEnd;
END;
OTHERWISE NOP;
END;
END;
NewLine = ParseLine( ThisLine);
rcx = LINEOUT( TargetFile, NewLine);
END;
rcx = STREAM( SourceFile, 'C', 'CLOSE');
rcx = STREAM( TargetFile, 'C', 'CLOSE');
END;
/* cleanup target file on error */
IF ((rc \= ERROR.NO_ERROR) & (TargetFile \= '')) THEN
rcx = SysFileDelete( TargetFile);
EXIT( rc);
/* ------------------------------------------------------------------------- */
HALT:
SAY 'Interrupted by user.';
EXIT( ERROR.GEN_FAILURE);
/* ------------------------------------------------------------------------- */
ShowHelp: PROCEDURE EXPOSE (GlobalVars)
/* show title */
SAY;
SAY Title;
SAY;
PARSE SOURCE . . ThisFile
/* skip header */
DO i = 1 TO 3
rc = LINEIN( ThisFile);
END;
/* show help text */
ThisLine = LINEIN( Thisfile);
DO WHILE ( ThisLine \= ' */')
SAY SUBSTR( ThisLine, 7);
ThisLine = LINEIN( Thisfile);
END;
/* close file */
rc = LINEOUT( Thisfile);
RETURN( '');
/* ------------------------------------------------------------------------- */
FileExist: PROCEDURE
PARSE ARG FileName
RETURN( STREAM( Filename, 'C', 'QUERY EXISTS') > '');
/* ------------------------------------------------------------------------- */
/* Resolve environment vars. */
/* Don't process WPS %-params. */
ParseLine: PROCEDURE EXPOSE env
PARSE ARG ThisLine
Delim = '%';
NoDelim = '%*';
pStart = 1;
DO WHILE 1 > 0
p1 = pos( Delim, ThisLine, pStart);
IF p1 = 0 THEN
LEAVE;
IF pos( NoDelim, ThisLine, p1) = p1 THEN
DO
pStart = pStart + LENGTH( NoDelim);
ITERATE;
END;
p2 = pos( Delim, ThisLine, p1 + 1);
IF (p2 = 0) THEN
LEAVE;
/* Process %% strings to esacape % chars */
IF (p2 = p1 + 1) THEN
DO
ThisLine = DELSTR( ThisLine, p2, 1);
pStart = p1 + 1;
ITERATE;
END;
LeftPart = SUBSTR( ThisLine, 1, p1 - 1);
RightPart = SUBSTR( ThisLine, p2 + 1);
ThisVar = SUBSTR( ThisLine, p1 + 1, p2 - p1 - 1);
Resolved = VALUE( ThisVar, , env);
pStart = LENGTH( LeftPart) + LENGTH( Resolved) + 1;
ThisLine = LeftPart''Resolved''RightPart;
END;
RETURN( ThisLine);
/* ========================================================================= */
/* Syntax: Str = StrReplace( StrSearch, StrReplace, Src [,StrExclude]) */
StrReplace: PROCEDURE
PARSE ARG StrSearch, StrReplace, Str, StrExclude;
pStart = 1;
DO FOREVER
p1 = POS( StrSearch, Str, pStart);
IF (p1 = 0) THEN
LEAVE;
IF StrExclude <> '' THEN
DO
/* Check if StrSearch is a substring of StrExclude */
p2 = POS( StrExclude, Str, pStart);
IF (p2 > 0 & p2 <= p1) THEN
DO
/* Start after found StrExclude */
pStart = p2 + LENGTH( StrExclude);
ITERATE;
END;
END;
Str = INSERT( StrReplace,,
DELSTR( Str, p1, LENGTH( StrSearch)),,
p1 - 1);
/* Advance the next start position by the length of the replace string */
pStart = p1 + LENGTH( StrReplace);
END;
RETURN( Str);
/* ========================================================================= */
ReplaceSpecialChars: PROCEDURE
PARSE ARG Str;
Str = StrReplace( '\n', '0a'x, Str, '\\');
Str = StrReplace( '\r', '0d'x, Str, '\\');
Str = StrReplace( '\t', '09'x, Str, '\\');
/* Remove backslashes of undefined escape strings */
Str = StrReplace( '\', '', Str, '\\');
/* Replace exclude string at last one */
Str = StrReplace( '\\', '\', Str);
RETURN( Str);
/* ========================================================================= */
/* Count quotes in LeftLine, e.g. in part of line before a position. */
/* If odd, then char at position must belong to a string. */
/* Syntax: fString = IsString( [, ]) */
/* fString is 0 | 1. */
/* is a space-separated list of quote chars. */
/* Default value is " '. */
IsString: PROCEDURE
PARSE ARG LeftLine, Quotes
IF Quotes = '' THEN
Quotes = '"' || " '"
fString = 0
DO w = 1 TO WORDS( Quotes)
Quote = WORD( Quotes, w)
numQ = 0
pStartQ = 1
DO FOREVER
pQ = POS( Quote, LeftLine, pStartQ)
IF pQ = 0 THEN
LEAVE
numQ = numQ + 1
pStartQ = pQ + 1
END
IF (numQ // 2 <> 0) THEN
DO
fString = 1
LEAVE
END
END
RETURN( fString)
/* ========================================================================= */
ProcessCIncludeFile: PROCEDURE EXPOSE (GlobalVars)
PARSE ARG File;
ErrorMsg = '';
rc = ERROR.NO_ERROR;
Linecount = 0;
DefinePending = FALSE;
Linecount = 0;
VarName = '';
VarValue = '';
IF (fVerbose) THEN
SAY '- processing C include file' File
DO UNTIL (TRUE)
DO WHILE (LINES( File))
ThisLine = LINEIN( File);
Linecount = Linecount + 1;
/* strip comments */
CommentPos = POS('//', ThisLine);
IF (CommentPos > 0) THEN
DO
LeftLine = LEFT( ThisLine, CommentPos - 1);
IF \(IsString( LeftLine)) THEN
ThisLine = LeftLine;
END
/* search for #defines */
Tag = TRANSLATE( WORD( ThisLine, 1));
SELECT
WHEN (Tag = '#DEFINE') THEN
DO
/* store old value first */
IF (VarName \= '') THEN
DO
VarValue = ReplaceSpecialChars( VarValue);
rcx = VALUE( VarName, VarValue, env);
IF (fDebug) THEN
SAY '1 storing' VarName '->' VarValue;
END;
/* read new value */
fNextLine = FALSE;
VarValue = '';
IF (RIGHT( STRIP( ThisLine), 1) = '\') THEN
DO
ThisLine = STRIP( ThisLine);
ThisLine = LEFT( ThisLine, LENGTH( ThisLine) - 1);
fNextLine = TRUE;
END;
PARSE VAR ThisLine . VarName ThisLine;
DO WHILE (TRUE)
/* store this value */
VarAddValue = STRIP( ThisLine);
VarAddValue = STRIP( VarAddValue);
IF (LEFT( VarAddValue, 1) = '"') THEN
PARSE VAR VarAddValue '"'VarAddValue'"';
VarValue = VarValue''VarAddValue;
/* scan nextline */
IF (fNextLine) THEN
DO
ThisLine = LINEIN( File);
Linecount = Linecount + 1;
fNextLine = FALSE;
IF (RIGHT( STRIP( ThisLine), 1) = '\') THEN
DO
PARSE VAR ThisLine ThisLine'\'.;
fNextLine = TRUE;
END;
END;
ELSE
LEAVE;
END;
DefinePending = TRUE;
END;
WHEN ((LEFT( Tag, 1) = '#') | (Tage = '')) THEN
DO
IF (VarName \= '') THEN
DO
VarValue = ReplaceSpecialChars( VarValue);
rcx = VALUE( VarName, VarValue, env);
IF (fDebug) THEN
SAY '2 storing' VarName '->' VarValue;
END;
DefinePending = FALSE;
END;
OTHERWISE NOP;
END;
END;
/* store any pending var */
IF ((DefinePending) & (VarName \= '')) THEN
DO
VarValue = ReplaceSpecialChars( VarValue);
rcx = VALUE( VarName, VarValue, env);
IF (fDebug) THEN
SAY 'storing' VarName '->' VarValue;
END;
rcx = STREAM( File, 'C', 'CLOSE');
END;
RETURN( STRIP( rc ErrorMsg));
/* ========================================================================= */
ProcessScriptIncludeFile: PROCEDURE EXPOSE (GlobalVars)
PARSE ARG File;
ErrorMsg = '';
rc = ERROR.NO_ERROR;
fCommentPending = FALSE;
CommentStart = '';
Content = '';
IF (fVerbose) THEN
SAY '- processing script includefile' File
DO UNTIL (TRUE)
DO WHILE (LINES( File))
ThisLine = LINEIN( File);
IF (fCommentPending) THEN
DO
/* check for end of comment */
tPos = POS( CommentEnd, ThisLine);
IF (tPos > 0) THEN
DO
fCommentPending = FALSE;
ThisLine = SUBSTR( ThisLine, tPos + LENGTH( CommentEnd));
IF (STRIP( ThisLine) = '') THEN
ITERATE;
END;
ELSE
/* skip lines while comment is pending */
ITERATE;
END;
ELSE
DO
/* check for start of comment */
tPos = POS( CommentStart, ThisLine);
IF (tPos > 0) THEN
DO
fCommentPending = TRUE;
ThisLine = LEFT( ThisLine, tPos - 1);
IF (STRIP( ThisLine) = '') THEN
ITERATE;
END;
END;
/* save as content */
IF (Content = '') THEN
Content = ThisLine;
ELSE
Content = Content''CrLf''ThisLine;
END;
rcx = STREAM( File, 'C', 'CLOSE');
END;
RETURN( Content);