Rework the directive parsing code. Instead of using a lot of strcmp()s

on every line that starts with a dot use a minimal perfect hash
function and a single strcmp() on the first word after the dot
to find out whether it is really a directive call and, if yes, which
one. Then directly dispatch to a handler function for that directive
(or fall through to the dependency handling code). This makes the
directive parse a little bit more strict about the syntax: the directive
word must be followed by a character that is not alphanumerical and not
an underline (making .undefFOO illegal); .endif and .else can only be
followed by comments.
This commit is contained in:
Hartmut Brandt 2005-04-11 07:20:10 +00:00
parent 84aac27381
commit f520690c2c
6 changed files with 669 additions and 595 deletions

View File

@ -127,18 +127,21 @@ static Token CondT(Boolean);
static Token CondF(Boolean);
static Token CondE(Boolean);
static struct If {
char *form; /* Form of if */
int formlen; /* Length of form */
static const struct If {
Boolean doNot; /* TRUE if default function should be negated */
CondProc *defProc; /* Default function to apply */
Boolean isElse; /* actually el<XXX> */
} ifs[] = {
{ "ifdef", 5, FALSE, CondDoDefined },
{ "ifndef", 6, TRUE, CondDoDefined },
{ "ifmake", 6, FALSE, CondDoMake },
{ "ifnmake", 7, TRUE, CondDoMake },
{ "if", 2, FALSE, CondDoDefined },
{ NULL, 0, FALSE, NULL }
[COND_IF] = { FALSE, CondDoDefined, FALSE },
[COND_IFDEF] = { FALSE, CondDoDefined, FALSE },
[COND_IFNDEF] = { TRUE, CondDoDefined, FALSE },
[COND_IFMAKE] = { FALSE, CondDoMake, FALSE },
[COND_IFNMAKE] = { TRUE, CondDoMake, FALSE },
[COND_ELIF] = { FALSE, CondDoDefined, TRUE },
[COND_ELIFDEF] = { FALSE, CondDoDefined, TRUE },
[COND_ELIFNDEF] = { TRUE, CondDoDefined, TRUE },
[COND_ELIFMAKE] = { FALSE, CondDoMake, TRUE },
[COND_ELIFNMAKE] = { TRUE, CondDoMake, TRUE },
};
static Boolean condInvert; /* Invert the default function */
@ -153,7 +156,7 @@ static int condLineno[MAXIF]; /* Line numbers of the opening .if */
static int condTop = MAXIF; /* Top-most conditional */
static int skipIfLevel = 0; /* Depth of skipped conditionals */
static int skipIfLineno[MAXIF]; /* Line numbers of skipped .ifs */
static Boolean skipLine = FALSE; /* Whether the parse module is skipping
Boolean skipLine = FALSE; /* Whether the parse module is skipping
* lines */
/**
@ -1005,171 +1008,84 @@ CondE(Boolean doEval)
}
/**
* Cond_Eval --
* Evaluate the conditional in the passed line. The line
* looks like this:
* .<cond-type> <expr>
* where <cond-type> is any of if, ifmake, ifnmake, ifdef,
* ifndef, elif, elifmake, elifnmake, elifdef, elifndef
* and <expr> consists of &&, ||, !, make(target), defined(variable)
* and parenthetical groupings thereof.
*
* Results:
* COND_PARSE if should parse lines after the conditional
* COND_SKIP if should skip lines after the conditional
* COND_INVALID if not a valid conditional.
* Cond_If
* Handle .if<X> and .elif<X> directives.
* This function is called even when we're skipping.
*/
int
Cond_Eval(char *line, int lineno)
void
Cond_If(char *line, int code, int lineno)
{
struct If *ifp;
Boolean isElse;
Boolean value = FALSE;
int level; /* Level at which to report errors. */
const struct If *ifp;
Boolean value;
level = PARSE_FATAL;
ifp = &ifs[code];
for (line++; *line == ' ' || *line == '\t'; line++) {
continue;
}
/*
* Find what type of if we're dealing with. The result is left
* in ifp and isElse is set TRUE if it's an elif line.
*/
if (line[0] == 'e' && line[1] == 'l') {
line += 2;
isElse = TRUE;
} else if (strncmp(line, "endif", 5) == 0) {
/*
* End of a conditional section. If skipIfLevel is non-zero,
* that conditional was skipped, so lines following it should
* also be skipped. Hence, we return COND_SKIP. Otherwise,
* the conditional was read so succeeding lines should be
* parsed (think about it...) so we return COND_PARSE, unless
* this endif isn't paired with a decent if.
*/
if (ifp->isElse) {
if (condTop == MAXIF) {
Parse_Error(PARSE_FATAL, "if-less elif");
return;
}
if (skipIfLevel != 0) {
skipIfLevel -= 1;
return (COND_SKIP);
} else {
if (condTop == MAXIF) {
Parse_Error(level, "if-less endif");
return (COND_INVALID);
} else {
skipLine = FALSE;
condTop += 1;
return (COND_PARSE);
}
/*
* If skipping this conditional, just ignore
* the whole thing. If we don't, the user
* might be employing a variable that's
* undefined, for which there's an enclosing
* ifdef that we're skipping...
*/
skipIfLineno[skipIfLevel - 1] = lineno;
return;
}
} else {
isElse = FALSE;
} else if (skipLine) {
/*
* Don't even try to evaluate a conditional that's
* not an else if we're skipping things...
*/
skipIfLineno[skipIfLevel] = lineno;
skipIfLevel += 1;
return;
}
/*
* Figure out what sort of conditional it is -- what its default
* function is, etc. -- by looking in the table of valid "ifs"
* Initialize file-global variables for parsing
*/
for (ifp = ifs; ifp->form != NULL; ifp++) {
if (strncmp(ifp->form, line, ifp->formlen) == 0) {
break;
}
condDefProc = ifp->defProc;
condInvert = ifp->doNot;
while (*line == ' ' || *line == '\t') {
line++;
}
if (ifp->form == NULL) {
/*
* Nothing fit. If the first word on the line is actually
* "else", it's a valid conditional whose value is the inverse
* of the previous if we parsed.
*/
if (isElse && (line[0] == 's') && (line[1] == 'e')) {
if (condTop == MAXIF) {
Parse_Error(level, "if-less else");
return (COND_INVALID);
} else if (skipIfLevel == 0) {
value = !condStack[condTop];
lineno = condLineno[condTop];
} else {
return (COND_SKIP);
}
} else {
/*
* Not a valid conditional type. No error...
*/
return (COND_INVALID);
}
condExpr = line;
condPushBack = None;
} else {
if (isElse) {
if (condTop == MAXIF) {
Parse_Error(level, "if-less elif");
return (COND_INVALID);
} else if (skipIfLevel != 0) {
/*
* If skipping this conditional, just ignore
* the whole thing. If we don't, the user
* might be employing a variable that's
* undefined, for which there's an enclosing
* ifdef that we're skipping...
*/
skipIfLineno[skipIfLevel - 1] = lineno;
return (COND_SKIP);
}
} else if (skipLine) {
/*
* Don't even try to evaluate a conditional that's
* not an else if we're skipping things...
*/
skipIfLineno[skipIfLevel] = lineno;
skipIfLevel += 1;
return (COND_SKIP);
}
/*
* Initialize file-global variables for parsing
*/
condDefProc = ifp->defProc;
condInvert = ifp->doNot;
line += ifp->formlen;
while (*line == ' ' || *line == '\t') {
line++;
}
condExpr = line;
condPushBack = None;
switch (CondE(TRUE)) {
case True:
if (CondToken(TRUE) == EndOfFile) {
value = TRUE;
break;
}
switch (CondE(TRUE)) {
case True:
if (CondToken(TRUE) != EndOfFile)
goto err;
/*FALLTHRU*/
value = TRUE;
break;
case False:
if (CondToken(TRUE) == EndOfFile) {
value = FALSE;
break;
}
/*FALLTHRU*/
case False:
if (CondToken(TRUE) != EndOfFile)
goto err;
value = FALSE;
break;
case Err:
err:
Parse_Error(level, "Malformed conditional (%s)", line);
return (COND_INVALID);
default:
break;
}
case Err:
err: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
return;
default:
abort();
}
if (!isElse) {
if (!ifp->isElse) {
/* push this value */
condTop -= 1;
} else if ((skipIfLevel != 0) || condStack[condTop]) {
} else if (skipIfLevel != 0 || condStack[condTop]) {
/*
* If this is an else-type conditional, it should only take
* effect if its corresponding if was evaluated and FALSE.
@ -1178,7 +1094,7 @@ Cond_Eval(char *line, int lineno)
* stack unmolested so later elif's don't screw up...
*/
skipLine = TRUE;
return (COND_SKIP);
return;
}
if (condTop < 0) {
@ -1186,15 +1102,91 @@ Cond_Eval(char *line, int lineno)
* This is the one case where we can definitely proclaim a fatal
* error. If we don't, we're hosed.
*/
Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",
MAXIF);
return (COND_INVALID);
} else {
condStack[condTop] = value;
condLineno[condTop] = lineno;
skipLine = !value;
return (value ? COND_PARSE : COND_SKIP);
Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF);
return;
}
/* push */
condStack[condTop] = value;
condLineno[condTop] = lineno;
skipLine = !value;
}
/**
* Cond_Else
* Handle .else statement.
*/
void
Cond_Else(char *line __unused, int code __unused, int lineno __unused)
{
while (isspace((u_char)*line))
line++;
if (*line != '\0') {
Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'", line);
}
if (condTop == MAXIF) {
Parse_Error(PARSE_FATAL, "if-less else");
return;
}
if (skipIfLevel != 0)
return;
if (skipIfLevel != 0 || condStack[condTop]) {
/*
* An else should only take effect if its corresponding if was
* evaluated and FALSE.
* If its if was TRUE or skipped, we return COND_SKIP (and
* start skipping in case we weren't already), leaving the
* stack unmolested so later elif's don't screw up...
* XXX How does this work with two .else's?
*/
skipLine = TRUE;
return;
}
/* inverse value */
condStack[condTop] = !condStack[condTop];
skipLine = !condStack[condTop];
}
/**
* Cond_Endif
* Handle .endif statement.
*/
void
Cond_Endif(char *line __unused, int code __unused, int lineno __unused)
{
while (isspace((u_char)*line))
line++;
if (*line != '\0') {
Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'", line);
}
/*
* End of a conditional section. If skipIfLevel is non-zero,
* that conditional was skipped, so lines following it should
* also be skipped. Hence, we return COND_SKIP. Otherwise,
* the conditional was read so succeeding lines should be
* parsed (think about it...) so we return COND_PARSE, unless
* this endif isn't paired with a decent if.
*/
if (skipIfLevel != 0) {
skipIfLevel -= 1;
return;
}
if (condTop == MAXIF) {
Parse_Error(PARSE_FATAL, "if-less endif");
return;
}
/* pop */
skipLine = FALSE;
condTop += 1;
}
/**

View File

@ -48,7 +48,26 @@
#define COND_SKIP 1 /* Skip the next lines */
#define COND_INVALID 2 /* Not a conditional statement */
int Cond_Eval(char *, int);
enum {
COND_IF,
COND_IFDEF,
COND_IFNDEF,
COND_IFMAKE,
COND_IFNMAKE,
COND_ELSE,
COND_ELIF,
COND_ELIFDEF,
COND_ELIFNDEF,
COND_ELIFMAKE,
COND_ELIFNMAKE,
COND_ENDIF,
};
void Cond_If(char *, int, int);
void Cond_Else(char *, int, int);
void Cond_Endif(char *, int, int);
void Cond_End(void);
extern Boolean skipLine;
#endif /* cond_h_6e96ad7c */

View File

@ -78,144 +78,142 @@ static char *forVar; /* Iteration variable */
static Buffer *forBuf; /* Commands in loop */
static Lst forLst; /* List of items */
/*-
*-----------------------------------------------------------------------
* For_Eval --
/**
* For_For
* Evaluate the for loop in the passed line. The line
* looks like this:
* .for <variable> in <varlist>
* The line pointer points just behind the for.
*
* Results:
* TRUE: We found a for loop, or we are inside a for loop
* FALSE: We did not find a for loop, or we found the end of the for
* for loop.
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
* TRUE: Syntax ok.
* FALSE: Syntax error.
*/
int
For_Eval(char *line)
Boolean
For_For(char *line)
{
char *ptr;
char *sub;
char *wrd;
int level; /* Level at which to report errors. */
char *sub;
Buffer *buf;
size_t varlen;
ptr = line;
level = PARSE_FATAL;
if (forLevel == 0) {
/*
* maybe start of a for loop
*/
Buffer *buf;
size_t varlen;
/*
* Skip space between for and the variable.
*/
for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
;
for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
;
/*
* If we are not in a for loop quickly determine if
* the statement is a for.
*/
if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
!isspace((unsigned char)ptr[3]))
return (FALSE);
ptr += 3;
/*
* Grab the variable
*/
for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
;
/*
* we found a for loop, and now we are going to parse it.
*/
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
buf = Buf_Init(0);
Buf_AppendRange(buf, wrd, ptr);
forVar = Buf_GetAll(buf, &varlen);
/*
* Grab the variable
*/
buf = Buf_Init(0);
for (wrd = ptr; *ptr && !isspace((unsigned char)*ptr); ptr++)
;
Buf_AppendRange(buf, wrd, ptr);
if (varlen == 0) {
Buf_Destroy(buf, TRUE);
Parse_Error(PARSE_FATAL, "missing variable in for");
return (FALSE);
}
Buf_Destroy(buf, FALSE);
forVar = (char *)Buf_GetAll(buf, &varlen);
if (varlen == 0) {
/* XXXHB Buf_Destroy(buf, TRUE) */
Parse_Error(level, "missing variable in for");
return (0);
}
Buf_Destroy(buf, FALSE);
/*
* Skip to 'in'.
*/
while (*ptr && isspace((u_char)*ptr))
ptr++;
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
/*
* Grab the `in'
*/
if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
free(forVar);
Parse_Error(PARSE_FATAL, "missing `in' in for");
fprintf(stderr, "%s\n", ptr);
return (FALSE);
}
ptr += 3;
/*
* Grab the `in'
*/
if (ptr[0] != 'i' || ptr[1] != 'n' ||
!isspace((unsigned char)ptr[2])) {
/* XXXHB free(forVar) */
Parse_Error(level, "missing `in' in for");
printf("%s\n", ptr);
return (0);
}
ptr += 3;
/*
* Skip to values
*/
while (*ptr && isspace((u_char)*ptr))
ptr++;
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
/*
* Make a list with the remaining words
* XXX should use brk_string here.
*/
sub = Buf_Peel(Var_Subst(NULL, ptr, VAR_CMD, FALSE));
for (ptr = sub; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
;
/*
* Make a list with the remaining words
*/
sub = Buf_Peel(Var_Subst(NULL, ptr, VAR_CMD, FALSE));
for (ptr = sub; *ptr && isspace((unsigned char)*ptr); ptr++)
;
Lst_Init(&forLst);
buf = Buf_Init(0);
for (wrd = ptr; *ptr != '\0'; ptr++) {
if (isspace((unsigned char)*ptr)) {
Buf_AppendRange(buf, wrd, ptr);
Lst_AtFront(&forLst, Buf_Peel(buf));
buf = Buf_Init(0);
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
wrd = ptr--;
}
}
DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
if (ptr - wrd > 0) {
Lst_Init(&forLst);
buf = Buf_Init(0);
for (wrd = ptr; *ptr != '\0'; ptr++) {
if (isspace((u_char)*ptr)) {
Buf_AppendRange(buf, wrd, ptr);
Lst_AtFront(&forLst, Buf_Peel(buf));
} else {
Buf_Destroy(buf, TRUE);
}
free(sub);
forBuf = Buf_Init(0);
forLevel++;
return (1);
buf = Buf_Init(0);
while (*ptr != '\0' && isspace((u_char)*ptr))
ptr++;
wrd = ptr--;
}
}
DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
if (ptr - wrd > 0) {
Buf_AppendRange(buf, wrd, ptr);
Lst_AtFront(&forLst, Buf_Peel(buf));
} else {
Buf_Destroy(buf, TRUE);
}
free(sub);
forBuf = Buf_Init(0);
forLevel++;
return (TRUE);
}
/**
* For_Eval
* Eat a line of the .for body looking for embedded .for loops
* and the .endfor
*/
Boolean
For_Eval(char *line)
{
char *ptr;
ptr = line;
if (*ptr == '.') {
/*
* Need to check for 'endfor' and 'for' to find the end
* of our loop or to find embedded for loops.
*/
for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
;
/* XXX the isspace is wrong */
if (strncmp(ptr, "endfor", 6) == 0 &&
(isspace((unsigned char)ptr[6]) || !ptr[6])) {
(isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
DEBUGF(FOR, ("For: end for %d\n", forLevel));
if (--forLevel < 0) {
Parse_Error(level, "for-less endfor");
return (0);
if (forLevel == 0) {
/* should not be here */
abort();
}
forLevel--;
} else if (strncmp(ptr, "for", 3) == 0 &&
isspace((unsigned char)ptr[3])) {
isspace((u_char)ptr[3])) {
forLevel++;
DEBUGF(FOR, ("For: new loop %d\n", forLevel));
}
@ -227,10 +225,10 @@ For_Eval(char *line)
*/
Buf_Append(forBuf, line);
Buf_AddByte(forBuf, (Byte)'\n');
return (1);
return (TRUE);
}
return (0);
return (FALSE);
}
/*-

View File

@ -41,7 +41,10 @@
#ifndef for_h_9d770f33
#define for_h_9d770f33
int For_Eval(char *);
#include "sprite.h"
Boolean For_For(char *);
Boolean For_Eval(char *);
void For_Run(int);
#endif /* for_h_9d770f33 */

View File

@ -242,6 +242,79 @@ static struct {
{ ".WAIT", Wait, 0 },
};
/*
* Directive table. We use a hash table. This hash table has been generated
* with mph which can be found on the usual GNU mirrors. If you change the
* directives (adding, deleting, reordering) you need to create a new table
* and hash function (directive_hash). The command line to generate the
* table is:
*
* mph -d2 -m1 <tab | emitc -l -s
*
* Where tab is a file containing just the directive strings, one per line.
*
* While inporting the result of this the following changes have been made
* to the generated code:
*
* prefix the names of the g, T0 and T1 arrays with 'directive_'.
*
* make the type of the tables 'const [un]signed char'.
*
* make the hash function use the length for termination,
* not the trailing '\0'.
*/
static void parse_include(char *, int, int);
static void parse_message(char *, int, int);
static void parse_undef(char *, int, int);
static void parse_for(char *, int, int);
static void parse_endfor(char *, int, int);
static const signed char directive_g[] = {
16, 0, -1, 14, 5, 2, 2, -1, 0, 0,
-1, -1, 16, 11, -1, 15, -1, 14, 7, -1,
8, 6, 1, -1, -1, 0, 4, 6, -1, 0,
0, 2, 0, 13, -1, 14, -1, 0,
};
static const unsigned char directive_T0[] = {
11, 25, 14, 30, 14, 26, 23, 15, 9, 37,
27, 32, 27, 1, 17, 27, 35, 13, 8, 22,
8, 28, 7,
};
static const unsigned char directive_T1[] = {
19, 20, 31, 17, 29, 2, 7, 12, 1, 31,
11, 18, 11, 20, 10, 2, 15, 19, 4, 10,
13, 36, 3,
};
static const struct directive {
const char *name;
int code;
Boolean skip_flag; /* execute even when skipped */
void (*func)(char *, int, int);
} directives[] = {
{ "elif", COND_ELIF, TRUE, Cond_If },
{ "elifdef", COND_ELIFDEF, TRUE, Cond_If },
{ "elifmake", COND_ELIFMAKE, TRUE, Cond_If },
{ "elifndef", COND_ELIFNDEF, TRUE, Cond_If },
{ "elifnmake", COND_ELIFNMAKE, TRUE, Cond_If },
{ "else", COND_ELSE, TRUE, Cond_Else },
{ "endfor", 0, FALSE, parse_endfor },
{ "endif", COND_ENDIF, TRUE, Cond_Endif },
{ "error", 1, FALSE, parse_message },
{ "for", 0, FALSE, parse_for },
{ "if", COND_IF, TRUE, Cond_If },
{ "ifdef", COND_IFDEF, TRUE, Cond_If },
{ "ifmake", COND_IFMAKE, TRUE, Cond_If },
{ "ifndef", COND_IFNDEF, TRUE, Cond_If },
{ "ifnmake", COND_IFNMAKE, TRUE, Cond_If },
{ "include", 0, FALSE, parse_include },
{ "undef", 0, FALSE, parse_undef },
{ "warning", 0, FALSE, parse_message },
};
#define NDIRECTS (sizeof(directives) / sizeof(directives[0]))
/*-
*----------------------------------------------------------------------
* ParseFindKeyword --
@ -1551,220 +1624,6 @@ Parse_AddIncludeDir(char *dir)
Path_AddDir(&parseIncPath, dir);
}
/*---------------------------------------------------------------------
* ParseDoError --
* Handle error directive
*
* The input is the line minus the ".error". We substitute variables,
* print the message and exit(1) or just print a warning if the ".error"
* directive is malformed.
*
*---------------------------------------------------------------------
*/
static void
ParseDoError(char *errmsg)
{
Buffer *buf;
if (!isspace((unsigned char)*errmsg)) {
Parse_Error(PARSE_WARNING, "invalid syntax: .error%s", errmsg);
return;
}
while (isspace((unsigned char)*errmsg))
errmsg++;
buf = Var_Subst(NULL, errmsg, VAR_GLOBAL, FALSE);
Parse_Error(PARSE_FATAL, "%s", Buf_Data(buf));
Buf_Destroy(buf, TRUE);
/* Terminate immediately. */
exit(1);
}
/*---------------------------------------------------------------------
* ParseDoWarning --
* Handle warning directive
*
* The input is the line minus the ".warning". We substitute variables
* and print the message or just print a warning if the ".warning"
* directive is malformed.
*
*---------------------------------------------------------------------
*/
static void
ParseDoWarning(char *warnmsg)
{
Buffer *buf;
if (!isspace((unsigned char)*warnmsg)) {
Parse_Error(PARSE_WARNING, "invalid syntax: .warning%s",
warnmsg);
return;
}
while (isspace((unsigned char)*warnmsg))
warnmsg++;
buf = Var_Subst(NULL, warnmsg, VAR_GLOBAL, FALSE);
Parse_Error(PARSE_WARNING, "%s", Buf_Data(buf));
Buf_Destroy(buf, TRUE);
}
/*-
*---------------------------------------------------------------------
* ParseDoInclude --
* Push to another file.
*
* The input is the line minus the #include. A file spec is a string
* enclosed in <> or "". The former is looked for only in sysIncPath.
* The latter in . and the directories specified by -I command line
* options
*
* Results:
* None
*
* Side Effects:
* A structure is added to the includes Lst and readProc.
*---------------------------------------------------------------------
*/
static void
ParseDoInclude(char *file)
{
char *fullname; /* full pathname of file */
char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */
Boolean isSystem; /* TRUE if makefile is a system makefile */
Buffer *buf;
/*
* Skip to delimiter character so we know where to look
*/
while (*file == ' ' || *file == '\t') {
file++;
}
if (*file != '"' && *file != '<') {
Parse_Error(PARSE_FATAL,
".include filename must be delimited by '\"' or '<'");
return;
}
/*
* Set the search path on which to find the include file based on the
* characters which bracket its name. Angle-brackets imply it's
* a system Makefile while double-quotes imply it's a user makefile
*/
if (*file == '<') {
isSystem = TRUE;
endc = '>';
} else {
isSystem = FALSE;
endc = '"';
}
/*
* Skip to matching delimiter
*/
for (cp = ++file; *cp && *cp != endc; cp++) {
continue;
}
if (*cp != endc) {
Parse_Error(PARSE_FATAL,
"Unclosed %cinclude filename. '%c' expected", '.', endc);
return;
}
*cp = '\0';
/*
* Substitute for any variables in the file name before trying to
* find the thing.
*/
buf = Var_Subst(NULL, file, VAR_CMD, FALSE);
file = Buf_Peel(buf);
/*
* Now we know the file's name and its search path, we attempt to
* find the durn thing. A return of NULL indicates the file don't
* exist.
*/
if (!isSystem) {
/*
* Include files contained in double-quotes are first searched
* for relative to the including file's location. We don't want
* to cd there, of course, so we just tack on the old file's
* leading path components and call Dir_FindFile to see if
* we can locate the beast.
*/
char *prefEnd, *Fname;
/* Make a temporary copy of this, to be safe. */
Fname = estrdup(CURFILE->fname);
prefEnd = strrchr(Fname, '/');
if (prefEnd != (char *)NULL) {
char *newName;
*prefEnd = '\0';
if (file[0] == '/')
newName = estrdup(file);
else
newName = str_concat(Fname, file, STR_ADDSLASH);
fullname = Path_FindFile(newName, &parseIncPath);
if (fullname == NULL) {
fullname = Path_FindFile(newName,
&dirSearchPath);
}
free(newName);
*prefEnd = '/';
} else {
fullname = NULL;
}
free(Fname);
} else {
fullname = NULL;
}
if (fullname == NULL) {
/*
* System makefile or makefile wasn't found in same directory as
* included makefile. Search for it first on the -I search path,
* then on the .PATH search path, if not found in a -I
* directory.
* XXX: Suffix specific?
*/
fullname = Path_FindFile(file, &parseIncPath);
if (fullname == NULL) {
fullname = Path_FindFile(file, &dirSearchPath);
}
}
if (fullname == NULL) {
/*
* Still haven't found the makefile. Look for it on the system
* path as a last resort.
*/
fullname = Path_FindFile(file, &sysIncPath);
}
if (fullname == NULL) {
*cp = endc;
Parse_Error(PARSE_FATAL, "Could not find %s", file);
/* XXXHB free(file) */
return;
}
free(file);
/*
* We set up the name of the file to be the absolute
* name of the include file so error messages refer to the right
* place.
*/
ParsePushInput(fullname, NULL, NULL, 0);
}
/*-
*---------------------------------------------------------------------
* Parse_FromString --
@ -1809,7 +1668,6 @@ ParseTraditionalInclude(char *file)
{
char *fullname; /* full pathname of file */
char *cp; /* current position in file spec */
Buffer *buf;
/*
* Skip over whitespace
@ -1836,8 +1694,7 @@ ParseTraditionalInclude(char *file)
* Substitute for any variables in the file name before trying to
* find the thing.
*/
buf = Var_Subst(NULL, file, VAR_CMD, FALSE);
file = Buf_Peel(buf);
file = Buf_Peel(Var_Subst(NULL, file, VAR_CMD, FALSE));
/*
* Now we know the file's name, we attempt to find the durn thing.
@ -2249,6 +2106,312 @@ ParseFinishLine(void)
}
}
/**
* parse_include
* Parse an .include directive and push the file onto the input stack.
* The input is the line minus the .include. A file spec is a string
* enclosed in <> or "". The former is looked for only in sysIncPath.
* The latter in . and the directories specified by -I command line
* options
*/
static void
parse_include(char *file, int code __unused, int lineno __unused)
{
char *fullname; /* full pathname of file */
char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */
Boolean isSystem; /* TRUE if makefile is a system makefile */
char *prefEnd, *Fname;
char *newName;
/*
* Skip to delimiter character so we know where to look
*/
while (*file == ' ' || *file == '\t') {
file++;
}
if (*file != '"' && *file != '<') {
Parse_Error(PARSE_FATAL,
".include filename must be delimited by '\"' or '<'");
return;
}
/*
* Set the search path on which to find the include file based on the
* characters which bracket its name. Angle-brackets imply it's
* a system Makefile while double-quotes imply it's a user makefile
*/
if (*file == '<') {
isSystem = TRUE;
endc = '>';
} else {
isSystem = FALSE;
endc = '"';
}
/*
* Skip to matching delimiter
*/
for (cp = ++file; *cp != endc; cp++) {
if (*cp == '\0') {
Parse_Error(PARSE_FATAL,
"Unclosed .include filename. '%c' expected", endc);
return;
}
}
*cp = '\0';
/*
* Substitute for any variables in the file name before trying to
* find the thing.
*/
file = Buf_Peel(Var_Subst(NULL, file, VAR_CMD, FALSE));
/*
* Now we know the file's name and its search path, we attempt to
* find the durn thing. A return of NULL indicates the file don't
* exist.
*/
if (!isSystem) {
/*
* Include files contained in double-quotes are first searched
* for relative to the including file's location. We don't want
* to cd there, of course, so we just tack on the old file's
* leading path components and call Dir_FindFile to see if
* we can locate the beast.
*/
/* Make a temporary copy of this, to be safe. */
Fname = estrdup(CURFILE->fname);
prefEnd = strrchr(Fname, '/');
if (prefEnd != NULL) {
*prefEnd = '\0';
if (file[0] == '/')
newName = estrdup(file);
else
newName = str_concat(Fname, file, STR_ADDSLASH);
fullname = Path_FindFile(newName, &parseIncPath);
if (fullname == NULL) {
fullname = Path_FindFile(newName,
&dirSearchPath);
}
free(newName);
*prefEnd = '/';
} else {
fullname = NULL;
}
free(Fname);
} else {
fullname = NULL;
}
if (fullname == NULL) {
/*
* System makefile or makefile wasn't found in same directory as
* included makefile. Search for it first on the -I search path,
* then on the .PATH search path, if not found in a -I
* directory.
* XXX: Suffix specific?
*/
fullname = Path_FindFile(file, &parseIncPath);
if (fullname == NULL) {
fullname = Path_FindFile(file, &dirSearchPath);
}
}
if (fullname == NULL) {
/*
* Still haven't found the makefile. Look for it on the system
* path as a last resort.
*/
fullname = Path_FindFile(file, &sysIncPath);
}
if (fullname == NULL) {
*cp = endc;
Parse_Error(PARSE_FATAL, "Could not find %s", file);
free(file);
return;
}
free(file);
/*
* We set up the name of the file to be the absolute
* name of the include file so error messages refer to the right
* place.
*/
ParsePushInput(fullname, NULL, NULL, 0);
}
/**
* parse_message
* Parse a .warning or .error directive
*
* The input is the line minus the ".error"/".warning". We substitute
* variables, print the message and exit(1) (for .error) or just print
* a warning if the directive is malformed.
*/
static void
parse_message(char *line, int iserror, int lineno __unused)
{
if (!isspace((u_char)*line)) {
Parse_Error(PARSE_WARNING, "invalid syntax: .%s%s",
iserror ? "error" : "warning", line);
return;
}
while (isspace((u_char)*line))
line++;
line = Buf_Peel(Var_Subst(NULL, line, VAR_GLOBAL, FALSE));
Parse_Error(iserror ? PARSE_FATAL : PARSE_WARNING, "%s", line);
free(line);
if (iserror) {
/* Terminate immediately. */
exit(1);
}
}
/**
* parse_undef
* Parse an .undef directive.
*/
static void
parse_undef(char *line, int code __unused, int lineno __unused)
{
char *cp;
while (isspace((u_char)*line))
line++;
for (cp = line; !isspace((u_char)*cp) && *cp != '\0'; cp++) {
;
}
*cp = '\0';
cp = Buf_Peel(Var_Subst(NULL, line, VAR_CMD, FALSE));
Var_Delete(cp, VAR_GLOBAL);
free(cp);
}
/**
* parse_for
* Parse a .for directive.
*/
static void
parse_for(char *line, int code __unused, int lineno)
{
if (!For_For(line)) {
/* syntax error */
return;
}
line = NULL;
/*
* Skip after the matching endfor.
*/
do {
free(line);
line = ParseSkipLine(0, 1);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
"Unexpected end of file in for loop.\n");
return;
}
} while (For_Eval(line));
free(line);
/* execute */
For_Run(lineno);
}
/**
* parse_endfor
* Parse endfor. This may only happen if there was no matching .for.
*/
static void
parse_endfor(char *line __unused, int code __unused, int lineno __unused)
{
Parse_Error(PARSE_FATAL, "for-less endfor");
}
/**
* directive_hash
*/
static int
directive_hash(const u_char *key, size_t len)
{
unsigned f0, f1;
const u_char *kp = key;
if (len < 2 || len > 9)
return (-1);
for (f0 = f1 = 0; kp < key + len; ++kp) {
if (*kp < 97 || *kp > 119)
return (-1);
f0 += directive_T0[-97 + *kp];
f1 += directive_T1[-97 + *kp];
}
f0 %= 38;
f1 %= 38;
return (directive_g[f0] + directive_g[f1]) % 18;
}
/**
* parse_directive
* Got a line starting with a '.'. Check if this is a directive
* and parse it.
*
* return:
* TRUE if line was a directive, FALSE otherwise.
*/
static Boolean
parse_directive(char *line)
{
char *start;
char *cp;
int dir;
/*
* Get the keyword:
* .[[:space:]]*\([[:alpha:]][[:alnum:]_]*\).*
* \1 is the keyword.
*/
for (start = line; isspace((u_char)*start); start++) {
;
}
if (!isalpha((u_char)*start)) {
return (FALSE);
}
cp = start + 1;
while (isalnum((u_char)*cp) || *cp == '_') {
cp++;
}
dir = directive_hash(start, cp - start);
if (dir < 0 || dir >= (int)NDIRECTS ||
(size_t)(cp - start) != strlen(directives[dir].name) ||
strncmp(start, directives[dir].name, cp - start) != 0) {
/* not actually matched */
return (FALSE);
}
if (!skipLine || directives[dir].skip_flag)
(*directives[dir].func)(cp, directives[dir].code,
CURFILE->lineno);
return (TRUE);
}
/*-
*---------------------------------------------------------------------
@ -2270,8 +2433,6 @@ Parse_File(const char *name, FILE *stream)
{
char *cp; /* pointer into the line */
char *line; /* the line we're working on */
Buffer *buf;
int lineno;
inLine = FALSE;
fatals = 0;
@ -2279,90 +2440,12 @@ Parse_File(const char *name, FILE *stream)
ParsePushInput(estrdup(name), stream, NULL, 0);
while ((line = ParseReadLine()) != NULL) {
if (*line == '.') {
/*
* Lines that begin with the special character
* are either include or undef directives.
*/
for (cp = line + 1; isspace((unsigned char)*cp); cp++) {
continue;
}
if (strncmp(cp, "include", 7) == 0) {
ParseDoInclude(cp + 7);
goto nextLine;
} else if (strncmp(cp, "error", 5) == 0) {
ParseDoError(cp + 5);
goto nextLine;
} else if (strncmp(cp, "warning", 7) == 0) {
ParseDoWarning(cp + 7);
goto nextLine;
} else if (strncmp(cp, "undef", 5) == 0) {
char *cp2;
for (cp += 5; isspace((unsigned char)*cp);
cp++) {
continue;
}
for (cp2 = cp; !isspace((unsigned char)*cp2) &&
*cp2 != '\0'; cp2++) {
continue;
}
*cp2 = '\0';
buf = Var_Subst(NULL, cp, VAR_CMD, FALSE);
cp = Buf_Peel(buf);
Var_Delete(cp, VAR_GLOBAL);
goto nextLine;
} else if (For_Eval(line)) {
lineno = CURFILE->lineno;
do {
/*
* Skip after the matching end.
*/
free(line);
line = ParseSkipLine(0, 1);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
"Unexpected end of"
" file in for loop.\n");
goto nextLine;
}
} while (For_Eval(line));
For_Run(lineno);
goto nextLine;
} else {
/*
* The line might be a conditional. Ask the
* conditional module about it and act
* accordingly
*/
int cond = Cond_Eval(line, CURFILE->lineno);
if (cond == COND_SKIP) {
/*
* Skip to next conditional that
* evaluates to COND_PARSE.
*/
do {
free(line);
line = ParseSkipLine(1, 0);
} while (line && Cond_Eval(line,
CURFILE->lineno) != COND_PARSE);
goto nextLine;
}
if (cond == COND_PARSE)
goto nextLine;
}
if (*line == '.' && parse_directive(line + 1)) {
/* directive consumed */
goto nextLine;
}
if (*line == '#') {
/*
* If we're this far, the line must be
* a comment.
*/
if (skipLine || *line == '#') {
/* Skipping .if block or comment. */
goto nextLine;
}
@ -2434,7 +2517,7 @@ Parse_File(const char *name, FILE *stream)
* have an operator and we're in a dependency
* line's script, we assume it's actually a
* shell command and add it to the current
* list of targets.
* list of targets. XXX this comment seems wrong.
*/
cp = line;
if (isspace((unsigned char)line[0])) {
@ -2449,8 +2532,7 @@ Parse_File(const char *name, FILE *stream)
ParseFinishLine();
buf = Var_Subst(NULL, line, VAR_CMD, TRUE);
cp = Buf_Peel(buf);
cp = Buf_Peel(Var_Subst(NULL, line, VAR_CMD, TRUE));
free(line);
line = cp;
@ -2479,25 +2561,6 @@ Parse_File(const char *name, FILE *stream)
errx(1, "fatal errors encountered -- cannot continue");
}
/*-
*---------------------------------------------------------------------
* Parse_Init --
* initialize the parsing module
*
* Results:
* none
*
* Side Effects:
* the parseIncPath list is initialized...
*---------------------------------------------------------------------
*/
void
Parse_Init(void)
{
mainNode = NULL;
}
/*-
*-----------------------------------------------------------------------
* Parse_MainName --

View File

@ -54,7 +54,6 @@ Boolean Parse_IsVar(char *);
void Parse_DoVar(char *, struct GNode *);
void Parse_AddIncludeDir(char *);
void Parse_File(const char *, FILE *);
void Parse_Init(void);
void Parse_FromString(char *, int);
void Parse_MainName(struct Lst *);