mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
763 lines
23 KiB
763 lines
23 KiB
/*** IFEXPR.C -- routines to handle directives *********************************
|
|
*
|
|
* Copyright (c) 1988-1989, Microsoft Corporation. All rights reserved.
|
|
*
|
|
* Purpose:
|
|
* Module contains routines to handle !directives. This module is transparent to
|
|
* rest of NMAKE. It also contains lgetc() used by lexer.c
|
|
*
|
|
* Revision History:
|
|
* 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
|
|
* 01-Jun-1993 HV Created UngetTxtChr()
|
|
* 01-Jun-1993 HV Change #ifdef KANJI to _MBCS.
|
|
* Eliminate #include <jctype.h>
|
|
* 10-May-1993 HV Add include file mbstring.h
|
|
* Change the str* functions to STR*
|
|
* 30-Jul-1990 SB Freeing ptr in the middle of a string for 'undef foo' case
|
|
* 01-Dec-1989 SB Changed realloc() to REALLOC()
|
|
* 22-Nov-1989 SB Changed free() to FREE()
|
|
* 05-Apr-1989 SB made all funcs NEAR; Reqd to make all function calls NEAR
|
|
* 19-Sep-1988 RB Remove ESCH processing from readInOneLine().
|
|
* 15-Sep-1988 RB Move chBuf to GLOBALS.
|
|
* 17-Aug-1988 RB Clean up.
|
|
* 29-Jun-1988 rj Added support for cmdswitches e,q,p,t,b,c in tools.ini.
|
|
* 23-Jun-1988 rj Fixed GP fault when doing directives in tools.ini.
|
|
* 23-Jun-1988 rj Add support for ESCH to readInOneLine().
|
|
* 25-May-1988 rb Add missing argument to makeError() call.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include "nmake.h"
|
|
#include "nmmsg.h"
|
|
#include "proto.h"
|
|
#include "globals.h"
|
|
#include "grammar.h"
|
|
/*
|
|
* function prototypes
|
|
*/
|
|
|
|
LOCAL void NEAR skipToNextDirective(void);
|
|
LOCAL void NEAR processIfs(char*, UCHAR);
|
|
LOCAL UCHAR NEAR ifsPresent(char*, unsigned, char**);
|
|
LOCAL void NEAR processCmdSwitches(char*);
|
|
LOCAL char * NEAR readInOneLine(void);
|
|
LOCAL char * NEAR getDirType(char*, UCHAR*);
|
|
|
|
|
|
|
|
/*
|
|
* macros that deal w/ the if/else directives' stack
|
|
*/
|
|
|
|
#define ifStkTop() (ifStack[ifTop])
|
|
#define popIfStk() (ifStack[ifTop--])
|
|
#define pushIfStk(A) (ifStack[++ifTop] = A)
|
|
|
|
#define INCLUDE 0x09
|
|
#define CMDSWITCHES 0x0A
|
|
#define ERROR 0x0B
|
|
#define MESSAGE 0x0C
|
|
#define UNDEF 0x0D
|
|
|
|
|
|
#ifdef _MBCS
|
|
/*
|
|
* GetTxtChr : get the next character from a text file stream
|
|
*
|
|
* This routine handles mixed DBCS and ASCII characters as
|
|
* follows:
|
|
*
|
|
* 1. The second byte of a DBCS character is returned in a
|
|
* word with the high byte set to the lead byte of the character.
|
|
* Thus the return value can be used in comparisions with
|
|
* ASCII constants without being mistakenly matched.
|
|
*
|
|
* 2. A DBCS space character (0x8140) is returned as two
|
|
* ASCII spaces (0x20). I.e. return a space the 1st and 2nd
|
|
* times we're called.
|
|
*
|
|
* 3. ASCII characters and lead bytes of DBCS characters
|
|
* are returned in the low byte of a word with the high byte
|
|
* set to 0.
|
|
*/
|
|
|
|
int NEAR
|
|
GetTxtChr (bs)
|
|
FILE *bs;
|
|
{
|
|
extern int NEAR chBuf; /* Character buffer */
|
|
int next; /* The next byte */
|
|
int next2; /* The one after that */
|
|
|
|
/* -1 in chBuf means it doesn't contain a valid character */
|
|
|
|
/* If we're not in the middle of a double-byte character,
|
|
* get the next byte and process it.
|
|
*/
|
|
if(chBuf == -1)
|
|
{
|
|
next = getc(bs);
|
|
/* If this byte is a lead byte, get the following byte
|
|
* and store both as a word in chBuf.
|
|
*/
|
|
if (_ismbblead(next))
|
|
{
|
|
next2 = getc(bs);
|
|
chBuf = (next << 8) | next2;
|
|
/* If the pair matches a DBCS space, set the return value
|
|
* to ASCII space.
|
|
*/
|
|
if(chBuf == 0x8140)
|
|
next = 0x20;
|
|
}
|
|
}
|
|
/* Else we're in the middle of a double-byte character. */
|
|
else
|
|
{
|
|
/* If this is the 2nd byte of a DBCS space, set the return
|
|
* value to ASCII space.
|
|
*/
|
|
if(chBuf == 0x8140)
|
|
next = 0x20;
|
|
/* Else set the return value to the whole DBCS character */
|
|
else
|
|
next = chBuf;
|
|
/* Reset the character buffer */
|
|
chBuf = -1;
|
|
}
|
|
/* Return the next character */
|
|
return(next);
|
|
}
|
|
#endif // _MBCS
|
|
|
|
#ifdef _MBCS
|
|
/*** UngetTxtChr -- Unget character fetched by GetTxtChr ***********************
|
|
*
|
|
* Scope:
|
|
* Global.
|
|
*
|
|
* Purpose:
|
|
* Since GetTxtChr() sometimes reads ahead one character and saves it in chBuf,
|
|
* ungetc() will sometimes put back characters in incorrect sequence.
|
|
* UngetTxtChr, on the other hand, understands how GetTxtChr works and will
|
|
* correctly put those characers back.
|
|
*
|
|
* Input:
|
|
* c -- The character read by GetTxtChr()
|
|
* bs -- The file buffer which c was read from.
|
|
*
|
|
* Output:
|
|
* Returns c if c is put back OK, otherwise returns EOF
|
|
*
|
|
* Errors/Warnings:
|
|
*
|
|
* Assumes:
|
|
* Assumes that characters are read only by GetTxtChr(), not by getc, etc.
|
|
*
|
|
* Modifies Globals:
|
|
* chBuf -- The composite character, read ahead by GetTxtChr()
|
|
*
|
|
* Uses Globals:
|
|
* chBuf -- The composite character, read ahead by GetTxtChr()
|
|
*
|
|
* Notes:
|
|
* There are three cases to consider:
|
|
* 1. Normal character (chBuf == -1 && c == 0x00XX)
|
|
* In this case, just put back c is sufficient.
|
|
* 2. Trail byte character (chBuf == -1 && c = LB|TB)
|
|
* chBuf = c;
|
|
* 3. Lead byte character (chBuf == LB|TB && c == LB)
|
|
* put back TB
|
|
* put back LB
|
|
* chBuf = -1
|
|
*
|
|
* History:
|
|
* 01-Jun-1993 HV Created.
|
|
*
|
|
*******************************************************************************/
|
|
int NEAR
|
|
UngetTxtChr (int c, FILE *bs)
|
|
{
|
|
extern int NEAR chBuf; // Character buffer
|
|
int nTrailByte; // The trail byte to put back
|
|
|
|
if (-1 == chBuf) // We're not in the middle of a DB character
|
|
{
|
|
if (0 == (c >> 8)) // CASE 1: normal character
|
|
c = ungetc(c, bs); // putback normal char
|
|
else // CASE 2: at trail byte (c=LBTB)
|
|
chBuf = c; // change chBuf is sufficient
|
|
}
|
|
else // CASE 3: at lead byte (c=LB, chBuf=LBTB)
|
|
{
|
|
nTrailByte = chBuf & (int)0xff; // Figure out the trail byte to putback
|
|
ungetc(nTrailByte, bs); // putback trail byte
|
|
c = ungetc(c, bs); // putback lead byte
|
|
chBuf = -1;
|
|
}
|
|
return (c);
|
|
}
|
|
#endif // _MBCS
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* lgetc() local getc - handles directives and returns char
|
|
*
|
|
* arguments: init global boolean value -- TRUE if tools.ini
|
|
* is the file being parsed
|
|
* colZero global boolean value -- TRUE if at first column
|
|
*
|
|
* actions:
|
|
* gets a character from the currently open file.
|
|
* loop
|
|
* if it is column zero and the char is '!' or
|
|
* there is a previous directive to be processed do
|
|
* read in one line into buffer.
|
|
* find directive type and get a pointer to rest of
|
|
* text.
|
|
* case directive of:
|
|
*
|
|
* CMDSWITCHES : set/reset global flags
|
|
* ERROR : set up global error message
|
|
* printed by error routine on
|
|
* termination. (not implemented yet )
|
|
* INCLUDE : calls processInclude
|
|
* continues with new file...
|
|
* UNDEF : undef the macro in the table
|
|
* IF
|
|
* IFDEF
|
|
* IFNDEF
|
|
* ELSE
|
|
* ENDIF : change the state information
|
|
* on the ifStack
|
|
* evaluate expression if required
|
|
* skip text if required (and look
|
|
* for the next directive)
|
|
* ( look at processIfs() )
|
|
* free extra buffers used (only one buffer need be
|
|
* maintained )
|
|
* increment lexer's line count
|
|
* we 're now back at column zero
|
|
* get next char from current file
|
|
* end if
|
|
* end loop
|
|
* return a char
|
|
*
|
|
* returns : a character (that is not part of any directive...)
|
|
*
|
|
* modifies: ifStack if directives' stack, static to this module
|
|
* ifTop index of current element at top of stack
|
|
* line lexer's line count...
|
|
*
|
|
* file current file, if !include is found...
|
|
* fName if !include is processed...
|
|
*/
|
|
|
|
int NEAR
|
|
lgetc()
|
|
{
|
|
UCHAR dirType;
|
|
int c;
|
|
char *s, *t;
|
|
MACRODEF *m;
|
|
|
|
for (c = GetTxtChr(file); prevDirPtr || (colZero && (c == '!'));
|
|
++line, c = GetTxtChr(file)) {
|
|
colZero = FALSE; /* we saw a '!' incolZero */
|
|
if (!prevDirPtr)
|
|
s = readInOneLine(); /* might modify lbufPtr -
|
|
if input text causes realloc */
|
|
else {
|
|
UngetTxtChr(c, file);
|
|
s = prevDirPtr;
|
|
prevDirPtr = NULL;
|
|
}
|
|
t = getDirType(s, &dirType);
|
|
|
|
if (dirType == INCLUDE) {
|
|
if (init)
|
|
makeError(line, SYNTAX_UNEXPECTED_TOKEN, s);
|
|
/* processInclude eats up first char in new file
|
|
* if it is space char. we check for that and break out.
|
|
*/
|
|
if (processIncludeFile(t) == (UCHAR) NEWLINESPACE) {
|
|
c = ' '; /* space character is returned */
|
|
break; /* colZero is now FALSE */
|
|
}
|
|
}
|
|
else if (dirType == CMDSWITCHES)
|
|
processCmdSwitches(t);
|
|
else if (dirType == ERROR)
|
|
makeError(line, USER_CONTROLLED, t);
|
|
else if (dirType == MESSAGE) {
|
|
if (!_ftcsnicmp(t, "\\t", 2)) {
|
|
printf("\t");
|
|
t+=2;
|
|
}
|
|
makeMessage(USER_MESSAGE, t);
|
|
}
|
|
else if (dirType == UNDEF) {
|
|
char *tmp;
|
|
tmp = _ftcstok(t, " \t");
|
|
if (_ftcstok(NULL, " \t"))
|
|
makeError(line, SYNTAX_UNEXPECTED_TOKEN, tmp);
|
|
if (NULL != (m = findMacro(tmp)))
|
|
SET(m->flags, M_UNDEFINED);
|
|
/**CONSIDER: why not remove symbol from table? [RB] */
|
|
|
|
}
|
|
else processIfs(t, dirType);
|
|
colZero = TRUE; /* finished with this directive */
|
|
if (s != lbufPtr) /* free buffer if it had expanded macros */
|
|
FREE(s);
|
|
} /* for */
|
|
return(c); /* return a character to the lexer */
|
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* readInOneLine()
|
|
*
|
|
* arguments: lbufPtr pointer(static/global to this module) to buffer that
|
|
* will hold text of line being read in
|
|
* lbufSize size of buffer(static/global to this module), updated
|
|
* if buffer is realloc'd
|
|
* actions : skip spaces/tabs and look for the directive.
|
|
* line continuations allowed in usual way
|
|
* if space-backslash-nl keep looking...
|
|
* if colZero of next line has comment char
|
|
* (#, or ; in tools.ini), look at next line...
|
|
* if first non-space char is '\n' or EOF report
|
|
* fatal-error and stop.
|
|
*
|
|
* keep reading in chars and storing in the buffer until
|
|
* a newline, EOF or a '#' which is NOT in column
|
|
* zero is seen
|
|
* if comment char in column zero ('#' or ';' in tools.ini)
|
|
* skip the line, continue with text on next line.
|
|
* if buffer needs to be realloc'd increase size by
|
|
* MAXBUF, a global constant.
|
|
* if newline was found, eat up newline.
|
|
* null terminate string for return.
|
|
* if '#' was found discard chars till the a newline or EOF.
|
|
* if EOF was found, push it back on stream for return
|
|
* to the lexer the next time.
|
|
*
|
|
* now expand macros. get a different buffer with clean
|
|
* text after expansion of macros.
|
|
*
|
|
* modifies : colZero global boolean value ( thru' call to
|
|
* skipBackSlash())
|
|
* lbufPtr buffer pointer, in case of reallocs.
|
|
* lbufSize size of buffer, increased if buffer is realloc'd
|
|
* Note: the buffer size will grow to be just greater than the size
|
|
* of the longest directive in any of the files processed,
|
|
* if it calls for any realloc's
|
|
* Do NOT process ESCH here. It is processed at a higher level.
|
|
*
|
|
* returns : pointer to buffer.
|
|
*
|
|
*/
|
|
|
|
|
|
LOCAL char * NEAR
|
|
readInOneLine()
|
|
{
|
|
extern STRINGLIST *eMacros;
|
|
int c;
|
|
unsigned index = 0;
|
|
register char *s;
|
|
|
|
if (((c = skipWhiteSpace(FROMSTREAM)) == '\n') || (c == EOF))
|
|
makeError(line, SYNTAX_MISSING_DIRECTIVE);
|
|
|
|
UngetTxtChr(c, file);
|
|
|
|
for (;;) {
|
|
c = GetTxtChr(file);
|
|
c = skipBackSlash(c, FROMSTREAM);
|
|
if (c == '#' || c == '\n' || c == EOF) break;
|
|
if ((index+2) > lbufSize) {
|
|
lbufSize += MAXBUF;
|
|
if (!lbufPtr)
|
|
lbufPtr = allocate(lbufSize+1); /* +1 for NULL byte */
|
|
else {
|
|
lbufPtr = REALLOC(lbufPtr, lbufSize+1);
|
|
if (!lbufPtr)
|
|
makeError(line, MACRO_TOO_LONG);
|
|
}
|
|
}
|
|
*(lbufPtr + (index++)) = (char) c;
|
|
}
|
|
*(lbufPtr + index) = '\0'; /* null terminate the string */
|
|
if (c == '#')
|
|
for(c = GetTxtChr(file); (c != '\n') && (c != EOF);c = GetTxtChr(file));
|
|
/* newline at end is eaten up */
|
|
if (c == EOF)
|
|
UngetTxtChr(c, file); /* this directive is to be processed */
|
|
|
|
s = lbufPtr; /* start expanding macros here */
|
|
s = removeMacros(s); /* remove and expand macros in string s */
|
|
return(s);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------
|
|
* getDirType()
|
|
*
|
|
* arguments: s - pointer to buffer that has directive text.
|
|
* dirType - pointer to unsigned char that gets set
|
|
* with directive type.
|
|
*
|
|
* actions : goes past directive keyword, sets the type code and
|
|
* returns a pointer to rest of test.
|
|
*
|
|
*/
|
|
|
|
|
|
LOCAL char * NEAR
|
|
getDirType(s, dirType)
|
|
char *s;
|
|
UCHAR *dirType;
|
|
{
|
|
char *t;
|
|
int len;
|
|
|
|
*dirType = 0;
|
|
for (t = s; *t && !WHITESPACE(*t); ++t);
|
|
len = (t - s); /* store len of directive */
|
|
while (*t && WHITESPACE(*t)) ++t; /*go past directive keyword */
|
|
if (!_ftcsnicmp(s, "INCLUDE", 7) && (len == 7))
|
|
*dirType = INCLUDE;
|
|
else if (!_ftcsnicmp(s, "CMDSWITCHES", 11) && (len == 11))
|
|
*dirType = CMDSWITCHES;
|
|
else if (!_ftcsnicmp(s, "ERROR", 5) && (len == 5))
|
|
*dirType = ERROR;
|
|
else if (!_ftcsnicmp(s, "MESSAGE", 7) && (len == 7))
|
|
*dirType = MESSAGE;
|
|
else if (!_ftcsnicmp(s, "UNDEF", 5) && (len == 5))
|
|
*dirType = UNDEF;
|
|
else *dirType = ifsPresent(s, len, &t) ; /* directive one of "if"s? */
|
|
|
|
if (!*dirType)
|
|
makeError(line, SYNTAX_BAD_DIRECTIVE, lbufPtr);
|
|
return(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* processCmdSwitches() -- processes command line switches in makefiles
|
|
*
|
|
* arguments: t pointer to flag settings specified.
|
|
*
|
|
* actions : sets or resets global flags as specified in the directive.
|
|
* The allowed flags are:
|
|
* s - silent mode, d - debug output (dates printed)
|
|
* n - no execute mode, i - ignore error returns from commands
|
|
* If parsing tools.ini, can also handle epqtbc
|
|
* reports a bad directive error for any other flags
|
|
* specified
|
|
*
|
|
* modifies : nothing
|
|
*
|
|
* returns : nothing
|
|
*/
|
|
|
|
LOCAL void NEAR
|
|
processCmdSwitches(t)
|
|
register char *t; /* pointer to switch values */
|
|
{
|
|
for (; *t; ++t) { /*ignore errors in flags specified*/
|
|
switch (*t) {
|
|
case '+': while (*++t && *t != '-') {
|
|
if (_ftcschr("DINS", (unsigned short)_totupper(*t)))
|
|
setFlags(*t, TRUE);
|
|
else if (init &&
|
|
_ftcschr("ABCEKLMPQRTV", (unsigned short)_totupper(*t)))
|
|
setFlags(*t, TRUE);
|
|
else makeError(line, SYNTAX_BAD_CMDSWITCHES);
|
|
}
|
|
if (!*t) break;
|
|
case '-': while (*++t && *t != '+') {
|
|
if (_ftcschr("DINS", (unsigned short)_totupper(*t)))
|
|
setFlags(*t, FALSE);
|
|
else if (init &&
|
|
_ftcschr("ABCEKLMPQRTV", (unsigned short)_totupper(*t)))
|
|
setFlags(*t, FALSE);
|
|
else makeError(line, SYNTAX_BAD_CMDSWITCHES);
|
|
}
|
|
break;
|
|
default: if (!WHITESPACE(*t))
|
|
makeError(line, SYNTAX_BAD_CMDSWITCHES);
|
|
break;
|
|
}
|
|
if (!*t)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* ifsPresent() -- checks if current directive is one of the "if"s
|
|
*
|
|
* arguments: s pointer to buffer with directive name in it
|
|
* len length of the directive that was seen
|
|
* t pointer to address upto which processed
|
|
*
|
|
* actions : does a string compare in the buffer for one of the
|
|
* directive keywords. If string matches true, it
|
|
* returns a non-zero value, the code for the specific
|
|
* directive
|
|
*
|
|
* modifies : nothing
|
|
*
|
|
* returns : a zero if no match, or the code for directive found.
|
|
*
|
|
*/
|
|
|
|
LOCAL UCHAR NEAR
|
|
ifsPresent(
|
|
char *s,
|
|
unsigned len,
|
|
char **t
|
|
) {
|
|
UCHAR ifFlags = 0; /* takes non-zero value when one of
|
|
if/else etc is to be processed */
|
|
|
|
if (!_ftcsnicmp(s, "IF", 2) && (len == 2))
|
|
ifFlags = IF_TYPE;
|
|
else if (!_ftcsnicmp(s, "IFDEF", 5) && (len == 5))
|
|
ifFlags = IFDEF_TYPE;
|
|
else if (!_ftcsnicmp(s, "IFNDEF", 6) && (len == 6))
|
|
ifFlags = IFNDEF_TYPE;
|
|
else if (!_ftcsnicmp(s, "ELSE", 4) && (len == 4)) {
|
|
//'else' or 'else if' or 'else ifdef' or 'else ifndef'
|
|
char *p = *t;
|
|
|
|
if (!*p)
|
|
ifFlags = ELSE_TYPE;
|
|
else {
|
|
for (s = p; *p && !WHITESPACE(*p); p++)
|
|
;
|
|
len = (p - s);
|
|
while (*p && WHITESPACE(*p))
|
|
p++;
|
|
*t = p;
|
|
if (!_ftcsnicmp(s, "IF", 2) && (len == 2))
|
|
ifFlags = ELSE_IF_TYPE;
|
|
else if (!_ftcsnicmp(s, "IFDEF", 5) && (len == 5))
|
|
ifFlags = ELSE_IFDEF_TYPE;
|
|
else if (!_ftcsnicmp(s, "IFNDEF", 6) && (len == 6))
|
|
ifFlags = ELSE_IFNDEF_TYPE;
|
|
}
|
|
}
|
|
else if (!_ftcsnicmp(s, "ELSEIF", 6) && (len == 6))
|
|
ifFlags = ELSE_IF_TYPE;
|
|
else if (!_ftcsnicmp(s, "ELSEIFDEF", 9) && (len == 9))
|
|
ifFlags = ELSE_IFDEF_TYPE;
|
|
else if (!_ftcsnicmp(s, "ELSEIFNDEF", 10) && (len == 10))
|
|
ifFlags = ELSE_IFNDEF_TYPE;
|
|
else if (!_ftcsnicmp(s, "ENDIF", 5) && (len == 5))
|
|
ifFlags = ENDIF_TYPE;
|
|
|
|
return(ifFlags);
|
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* processIfs() -- sets up / changes state information on "if"s
|
|
*
|
|
* arguments: s pointer to "if" expression ( don't care
|
|
* for "endif" )
|
|
*
|
|
* kind code indicating if processing if/else/ifdef etc.
|
|
* actions : modifies a stack (ifStack) by pushing/popping or
|
|
* sets/resets bits in the top element on the
|
|
* stack(examining the previous element pushed if
|
|
* required).
|
|
* case (kind) of
|
|
* IF
|
|
* IFDEF
|
|
* IFNDEF
|
|
* IF defined() : if no more space on ifStack
|
|
* (too many nesting levels) abort...
|
|
* set IFELSE bit in elt.
|
|
* push elt on ifStack.
|
|
* if more than one elt on stack
|
|
* and outer level "ifelse" false
|
|
* set IGNORE bit, skipToNextDirective
|
|
* else
|
|
* evaluate expression of
|
|
* current "if"
|
|
* if expr true set CONDITION bit in elt
|
|
* else skipToNextDirective.
|
|
* ELSE : if no elt on stack or previous
|
|
* directive was "else", flag error, abort
|
|
* clear IFELSE bit in elt on stack.
|
|
* if current ifelse block is to
|
|
* be skipped (IGNORE bit is on
|
|
* in outer level if/else),skip...
|
|
* else FLIP condition bit.
|
|
* if "else" part is false
|
|
* skipToNextDirective.
|
|
* ENDIF : if no elt on stack, flag error,abort
|
|
* pop an elt from ifStack.
|
|
* if there are elts on stack
|
|
* and we are in a "false" block
|
|
* skipToNextDirective.
|
|
* end case
|
|
*
|
|
* modifies: ifStack if directives' stack, static to this module
|
|
* ifTop index of current element at top of stack
|
|
* line lexer's line count (thru calls to
|
|
* skipToNextDirective())
|
|
*
|
|
* returns : nothing
|
|
*
|
|
*/
|
|
|
|
|
|
LOCAL void NEAR
|
|
processIfs(s, kind)
|
|
char *s;
|
|
UCHAR kind;
|
|
{
|
|
UCHAR element; /* has its bits set and is pushed on the ifStack */
|
|
|
|
switch (kind) {
|
|
case IF_TYPE:
|
|
case IFDEF_TYPE:
|
|
case IFNDEF_TYPE:
|
|
if (ifTop == IFSTACKSIZE-1)
|
|
makeError(line, SYNTAX_TOO_MANY_IFS);
|
|
element = (UCHAR) 0;
|
|
SET(element, NMIFELSE);
|
|
pushIfStk(element);
|
|
if (ifTop && OFF(ifStack[ifTop-1], NMCONDITION)) {
|
|
SET(ifStkTop(), NMIGNORE);
|
|
skipToNextDirective();
|
|
}
|
|
else if (evalExpr(s, kind))
|
|
SET(ifStkTop(), NMCONDITION);
|
|
else
|
|
skipToNextDirective();
|
|
break;
|
|
case ELSE_TYPE:
|
|
if ((ifTop < 0) || (OFF(ifStkTop(), NMIFELSE)
|
|
&& OFF(ifStkTop(), NMELSEIF)))
|
|
makeError(line, SYNTAX_UNEXPECTED_ELSE);
|
|
CLEAR(ifStkTop(), NMIFELSE);
|
|
CLEAR(ifStkTop(), NMELSEIF);
|
|
if (ON(ifStkTop(), NMIGNORE))
|
|
skipToNextDirective();
|
|
else {
|
|
FLIP(ifStkTop(), NMCONDITION);
|
|
if (OFF(ifStkTop(), NMCONDITION))
|
|
skipToNextDirective();
|
|
}
|
|
break;
|
|
case ELSE_IF_TYPE:
|
|
case ELSE_IFDEF_TYPE:
|
|
case ELSE_IFNDEF_TYPE:
|
|
if ((ifTop < 0) || (OFF(ifStkTop(), NMIFELSE)
|
|
&& OFF(ifStkTop(), NMELSEIF)))
|
|
makeError(line, SYNTAX_UNEXPECTED_ELSE);
|
|
CLEAR(ifStkTop(), NMIFELSE);
|
|
SET(ifStkTop(), NMELSEIF);
|
|
if (ON(ifStkTop(), NMIGNORE))
|
|
skipToNextDirective();
|
|
else {
|
|
if (ON(ifStkTop(), NMCONDITION)) {
|
|
SET(ifStkTop(), NMIGNORE);
|
|
CLEAR(ifStkTop(), NMCONDITION);
|
|
skipToNextDirective();
|
|
}
|
|
else if (evalExpr(s, kind))
|
|
SET(ifStkTop(), NMCONDITION);
|
|
else
|
|
skipToNextDirective();
|
|
}
|
|
break;
|
|
case ENDIF_TYPE:
|
|
if (ifTop < 0)
|
|
makeError(line, SYNTAX_UNEXPECTED_ELSE);
|
|
popIfStk();
|
|
if (ifTop >= 0)
|
|
if (OFF(ifStkTop(), NMCONDITION))
|
|
skipToNextDirective();
|
|
default:
|
|
break; /* default should never happen */
|
|
} /* switch */
|
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* skipToNextDirective() -- skips to next line that has '!' in column zero
|
|
*
|
|
* actions : gets first char of the line to be skipped if it is
|
|
* not a directive ( has no '!' on column zero ).
|
|
* a "line" that is skipped may in fact span many
|
|
* lines ( by using sp-backslash-nl to continue...)
|
|
* comments in colZero are skipped as part of the previous
|
|
* line ('#' or ';' in tools.ini)
|
|
* comment char '#' elsewhere in line implies the end of
|
|
* that line (with the next newline / EOF)
|
|
* if a '!' is found in colZero, read in the next directive
|
|
* if the directive is NOT one of if/ifdef/ifndef/else/
|
|
* endif, keep skipping more lines and look for the
|
|
* next directive ( go to top of the routine here ).
|
|
* if EOF found before next directive, report error.
|
|
*
|
|
* modifies : line global lexer line count
|
|
*
|
|
* returns : nothing
|
|
*
|
|
*/
|
|
|
|
LOCAL void NEAR
|
|
skipToNextDirective()
|
|
{
|
|
register int c;
|
|
UCHAR type;
|
|
|
|
repeat:
|
|
|
|
for (c = GetTxtChr(file); (c != '!') && (c != EOF) ;c = GetTxtChr(file)) {
|
|
++line; /* lexer's line count */
|
|
|
|
do {
|
|
if (c == '\\') {
|
|
c = skipBackSlash(c, FROMSTREAM);
|
|
if (c == '!' && colZero)
|
|
break;
|
|
else
|
|
colZero = FALSE;
|
|
}
|
|
if (c == '#' || c == '\n' || c == EOF)
|
|
break;
|
|
c = GetTxtChr(file);
|
|
|
|
} while (TRUE);
|
|
|
|
if (c == '#')
|
|
for (c = GetTxtChr(file); (c != '\n') && (c != EOF); c = GetTxtChr(file));
|
|
if (c == EOF || c == '!')
|
|
break;
|
|
}
|
|
if (c == '!') {
|
|
if (prevDirPtr && (prevDirPtr != lbufPtr))
|
|
FREE(prevDirPtr);
|
|
prevDirPtr = readInOneLine();
|
|
getDirType(prevDirPtr, &type);
|
|
if (type > ENDIF_TYPE) { /* type is NOT one of the "if"s */
|
|
++line;
|
|
goto repeat;
|
|
}
|
|
}
|
|
else if (c == EOF)
|
|
makeError(line, SYNTAX_EOF_NO_DIRECTIVE);
|
|
|
|
} /* skipToNextDirective */
|
|
|