version 1.50, 2000/06/23 16:39:45 |
version 1.51, 2000/06/23 16:40:50 |
|
|
static void ParseTraditionalInclude __P((char *)); |
static void ParseTraditionalInclude __P((char *)); |
#endif |
#endif |
static void ParseLookupIncludeFile __P((char *, char *, Boolean)); |
static void ParseLookupIncludeFile __P((char *, char *, Boolean)); |
static char *ParseReadLine __P((void)); |
|
static char *ParseSkipLine __P((int)); |
|
static void ParseFinishLine __P((void)); |
static void ParseFinishLine __P((void)); |
|
|
/*- |
/*- |
|
|
* error messages are informative. */ |
* error messages are informative. */ |
Parse_FromFile(fullname, f); |
Parse_FromFile(fullname, f); |
} |
} |
} |
|
} |
|
|
|
/* ParseSkipLine(): |
|
* Grab the next line |
|
*/ |
|
static char * |
|
ParseSkipLine(skip) |
|
int skip; /* Skip lines that don't start with . */ |
|
{ |
|
char *line; |
|
int c, lastc; |
|
BUFFER buf; |
|
|
|
Buf_Init(&buf, MAKE_BSIZE); |
|
|
|
for (;;) { |
|
Buf_Reset(&buf); |
|
lastc = '\0'; |
|
|
|
while (((c = ParseReadc()) != '\n' || lastc == '\\') |
|
&& c != EOF) { |
|
if (c == '\n') { |
|
Buf_ReplaceLastChar(&buf, ' '); |
|
current->lineno++; |
|
|
|
while ((c = ParseReadc()) == ' ' || c == '\t'); |
|
|
|
if (c == EOF) |
|
break; |
|
} |
|
|
|
Buf_AddChar(&buf, c); |
|
lastc = c; |
|
} |
|
|
|
line = Buf_Retrieve(&buf); |
|
current->lineno++; |
|
/* allow for non-newline terminated lines while skipping */ |
|
if (line[0] == '.') |
|
break; |
|
|
|
if (c == EOF) { |
|
Parse_Error(PARSE_FATAL, "Unclosed conditional/for loop"); |
|
Buf_Destroy(&buf); |
|
return NULL; |
|
} |
|
if (skip == 0) |
|
break; |
|
|
|
} |
|
|
|
return line; |
|
} |
|
|
|
|
|
/*- |
|
*--------------------------------------------------------------------- |
|
* ParseReadLine -- |
|
* Read an entire line from the input file. Called only by Parse_File. |
|
* To facilitate escaped newlines and what have you, a character is |
|
* buffered in 'lastc', which is '\0' when no characters have been |
|
* read. When we break out of the loop, c holds the terminating |
|
* character and lastc holds a character that should be added to |
|
* the line (unless we don't read anything but a terminator). |
|
* |
|
* Results: |
|
* A line w/o its newline |
|
* |
|
* Side Effects: |
|
* Only those associated with reading a character |
|
*--------------------------------------------------------------------- |
|
*/ |
|
static char * |
|
ParseReadLine () |
|
{ |
|
BUFFER buf; /* Buffer for current line */ |
|
register int c; /* the current character */ |
|
register int lastc; /* The most-recent character */ |
|
Boolean semiNL; /* treat semi-colons as newlines */ |
|
Boolean ignDepOp; /* TRUE if should ignore dependency operators |
|
* for the purposes of setting semiNL */ |
|
Boolean ignComment; /* TRUE if should ignore comments (in a |
|
* shell command */ |
|
char *line; /* Result */ |
|
char *ep; /* to strip trailing blanks */ |
|
|
|
semiNL = FALSE; |
|
ignDepOp = FALSE; |
|
ignComment = FALSE; |
|
|
|
/* |
|
* Handle special-characters at the beginning of the line. Either a |
|
* leading tab (shell command) or pound-sign (possible conditional) |
|
* forces us to ignore comments and dependency operators and treat |
|
* semi-colons as semi-colons (by leaving semiNL FALSE). This also |
|
* discards completely blank lines. |
|
*/ |
|
for (;;) { |
|
c = ParseReadc(); |
|
|
|
if (c == '\t') { |
|
ignComment = ignDepOp = TRUE; |
|
break; |
|
} else if (c == '\n') { |
|
current->lineno++; |
|
} else if (c == '#') { |
|
ParseUnreadc(c); |
|
break; |
|
} else { |
|
/* |
|
* Anything else breaks out without doing anything |
|
*/ |
|
break; |
|
} |
|
} |
|
|
|
if (c != EOF) { |
|
lastc = c; |
|
Buf_Init(&buf, MAKE_BSIZE); |
|
|
|
while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) && |
|
(c != EOF)) |
|
{ |
|
test_char: |
|
switch(c) { |
|
case '\n': |
|
/* |
|
* Escaped newline: read characters until a non-space or an |
|
* unescaped newline and replace them all by a single space. |
|
* This is done by storing the space over the backslash and |
|
* dropping through with the next nonspace. If it is a |
|
* semi-colon and semiNL is TRUE, it will be recognized as a |
|
* newline in the code below this... |
|
*/ |
|
current->lineno++; |
|
lastc = ' '; |
|
while ((c = ParseReadc ()) == ' ' || c == '\t') { |
|
continue; |
|
} |
|
if (c == EOF || c == '\n') { |
|
goto line_read; |
|
} else { |
|
/* |
|
* Check for comments, semiNL's, etc. -- easier than |
|
* ParseUnreadc(c); continue; |
|
*/ |
|
goto test_char; |
|
} |
|
/*NOTREACHED*/ |
|
break; |
|
|
|
case ';': |
|
/* |
|
* Semi-colon: Need to see if it should be interpreted as a |
|
* newline |
|
*/ |
|
if (semiNL) { |
|
/* |
|
* To make sure the command that may be following this |
|
* semi-colon begins with a tab, we push one back into the |
|
* input stream. This will overwrite the semi-colon in the |
|
* buffer. If there is no command following, this does no |
|
* harm, since the newline remains in the buffer and the |
|
* whole line is ignored. |
|
*/ |
|
ParseUnreadc('\t'); |
|
goto line_read; |
|
} |
|
break; |
|
case '=': |
|
if (!semiNL) { |
|
/* |
|
* Haven't seen a dependency operator before this, so this |
|
* must be a variable assignment -- don't pay attention to |
|
* dependency operators after this. |
|
*/ |
|
ignDepOp = TRUE; |
|
} else if (lastc == ':' || lastc == '!') { |
|
/* |
|
* Well, we've seen a dependency operator already, but it |
|
* was the previous character, so this is really just an |
|
* expanded variable assignment. Revert semi-colons to |
|
* being just semi-colons again and ignore any more |
|
* dependency operators. |
|
* |
|
* XXX: Note that a line like "foo : a:=b" will blow up, |
|
* but who'd write a line like that anyway? |
|
*/ |
|
ignDepOp = TRUE; semiNL = FALSE; |
|
} |
|
break; |
|
case '#': |
|
if (!ignComment) { |
|
if ( |
|
#if 0 |
|
compatMake && |
|
#endif |
|
(lastc != '\\')) { |
|
/* |
|
* If the character is a hash mark and it isn't escaped |
|
* (or we're being compatible), the thing is a comment. |
|
* Skip to the end of the line. |
|
*/ |
|
do { |
|
c = ParseReadc(); |
|
} while ((c != '\n') && (c != EOF)); |
|
goto line_read; |
|
} else { |
|
/* |
|
* Don't add the backslash. Just let the # get copied |
|
* over. |
|
*/ |
|
lastc = c; |
|
continue; |
|
} |
|
} |
|
break; |
|
case ':': |
|
case '!': |
|
if (!ignDepOp && (c == ':' || c == '!')) { |
|
/* |
|
* A semi-colon is recognized as a newline only on |
|
* dependency lines. Dependency lines are lines with a |
|
* colon or an exclamation point. Ergo... |
|
*/ |
|
semiNL = TRUE; |
|
} |
|
break; |
|
} |
|
/* |
|
* Copy in the previous character and save this one in lastc. |
|
*/ |
|
Buf_AddChar(&buf, lastc); |
|
lastc = c; |
|
|
|
} |
|
line_read: |
|
current->lineno++; |
|
|
|
if (lastc != '\0') |
|
Buf_AddChar(&buf, lastc); |
|
line = Buf_Retrieve(&buf); |
|
|
|
/* |
|
* Strip trailing blanks and tabs from the line. |
|
* Do not strip a blank or tab that is preceeded by |
|
* a '\' |
|
*/ |
|
ep = line; |
|
while (*ep) |
|
++ep; |
|
while (ep > line + 1 && (ep[-1] == ' ' || ep[-1] == '\t')) { |
|
if (ep > line + 1 && ep[-2] == '\\') |
|
break; |
|
--ep; |
|
} |
|
*ep = 0; |
|
|
|
if (line[0] == '.') { |
|
/* |
|
* The line might be a conditional. Ask the conditional module |
|
* about it and act accordingly |
|
*/ |
|
switch (Cond_Eval (line)) { |
|
case COND_SKIP: |
|
/* |
|
* Skip to next conditional that evaluates to COND_PARSE. |
|
*/ |
|
do { |
|
free (line); |
|
line = ParseSkipLine(1); |
|
} while (line && Cond_Eval(line) != COND_PARSE); |
|
if (line == NULL) |
|
break; |
|
/*FALLTHRU*/ |
|
case COND_PARSE: |
|
free(line); |
|
line = ParseReadLine(); |
|
break; |
|
case COND_INVALID: |
|
{ |
|
For *loop; |
|
|
|
loop = For_Eval(line); |
|
if (loop != NULL) { |
|
Boolean ok; |
|
|
|
free(line); |
|
do { |
|
/* Find the matching endfor. */ |
|
line = ParseSkipLine(0); |
|
if (line == NULL) { |
|
Parse_Error(PARSE_FATAL, |
|
"Unexpected end of file in for loop.\n"); |
|
return line; |
|
} |
|
ok = For_Accumulate(loop, line); |
|
free(line); |
|
} while (ok); |
|
For_Run(loop); |
|
line = ParseReadLine(); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
return (line); |
|
|
|
} else { |
|
/* |
|
* Hit end-of-file, so return a NULL line to indicate this. |
|
*/ |
|
return((char *)NULL); |
|
} |
} |
} |
} |
|
|