diff options
author | harti <harti@FreeBSD.org> | 2005-04-11 07:20:10 +0000 |
---|---|---|
committer | harti <harti@FreeBSD.org> | 2005-04-11 07:20:10 +0000 |
commit | e0e1cda47d1ab672740932980f0528e49c020775 (patch) | |
tree | 60d3956bc33947fb68e3718f818abf16c726d320 /usr.bin | |
parent | f2968c80c994b77c5c1dafc45fce7be685d043e1 (diff) | |
download | FreeBSD-src-e0e1cda47d1ab672740932980f0528e49c020775.zip FreeBSD-src-e0e1cda47d1ab672740932980f0528e49c020775.tar.gz |
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.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/make/cond.c | 318 | ||||
-rw-r--r-- | usr.bin/make/cond.h | 21 | ||||
-rw-r--r-- | usr.bin/make/for.c | 216 | ||||
-rw-r--r-- | usr.bin/make/for.h | 5 | ||||
-rw-r--r-- | usr.bin/make/parse.c | 711 | ||||
-rw-r--r-- | usr.bin/make/parse.h | 1 |
6 files changed, 673 insertions, 599 deletions
diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c index 6a9a771..1269eec 100644 --- a/usr.bin/make/cond.c +++ b/usr.bin/make/cond.c @@ -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. */ - - level = PARSE_FATAL; - - for (line++; *line == ' ' || *line == '\t'; line++) { - continue; - } + const struct If *ifp; + Boolean value; - /* - * 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; + ifp = &ifs[code]; - } 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 (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 (ifp->isElse) { + if (condTop == MAXIF) { + Parse_Error(PARSE_FATAL, "if-less elif"); + return; } - } else { - isElse = FALSE; - } - - /* - * Figure out what sort of conditional it is -- what its default - * function is, etc. -- by looking in the table of valid "ifs" - */ - for (ifp = ifs; ifp->form != NULL; ifp++) { - if (strncmp(ifp->form, line, ifp->formlen) == 0) { - break; - } - } - - 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); - } - - } 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) { + if (skipIfLevel != 0) { /* - * Don't even try to evaluate a conditional that's - * not an else if we're skipping things... + * 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] = lineno; - skipIfLevel += 1; - return (COND_SKIP); + skipIfLineno[skipIfLevel - 1] = lineno; + return; } + } else if (skipLine) { /* - * Initialize file-global variables for parsing + * Don't even try to evaluate a conditional that's + * not an else if we're skipping things... */ - condDefProc = ifp->defProc; - condInvert = ifp->doNot; + skipIfLineno[skipIfLevel] = lineno; + skipIfLevel += 1; + return; + } - line += ifp->formlen; + /* + * Initialize file-global variables for parsing + */ + condDefProc = ifp->defProc; + condInvert = ifp->doNot; - while (*line == ' ' || *line == '\t') { - line++; - } + while (*line == ' ' || *line == '\t') { + line++; + } - condExpr = line; - condPushBack = None; + 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; } /** diff --git a/usr.bin/make/cond.h b/usr.bin/make/cond.h index ecd9a80..9a8dbdc 100644 --- a/usr.bin/make/cond.h +++ b/usr.bin/make/cond.h @@ -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 */ diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c index 93d9788..823a7cc 100644 --- a/usr.bin/make/for.c +++ b/usr.bin/make/for.c @@ -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; - - 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; - - /* - * we found a for loop, and now we are going to parse it. - */ - while (*ptr && isspace((unsigned char)*ptr)) - ptr++; - - /* - * Grab the variable - */ - buf = Buf_Init(0); - for (wrd = ptr; *ptr && !isspace((unsigned char)*ptr); ptr++) - ; - Buf_AppendRange(buf, wrd, ptr); - 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); - - while (*ptr && isspace((unsigned char)*ptr)) - ptr++; + /* + * Skip space between for and the variable. + */ + for (ptr++; *ptr && isspace((u_char)*ptr); ptr++) + ; + + /* + * Grab the variable + */ + for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++) + ; + + buf = Buf_Init(0); + Buf_AppendRange(buf, wrd, ptr); + forVar = Buf_GetAll(buf, &varlen); + + if (varlen == 0) { + Buf_Destroy(buf, TRUE); + Parse_Error(PARSE_FATAL, "missing variable in for"); + return (FALSE); + } + Buf_Destroy(buf, FALSE); + + /* + * Skip to 'in'. + */ + while (*ptr && isspace((u_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; + + /* + * Skip to values + */ + while (*ptr && isspace((u_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++) + ; + + 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)); - /* - * 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); + buf = Buf_Init(0); + while (*ptr != '\0' && isspace((u_char)*ptr)) + ptr++; + wrd = ptr--; } - ptr += 3; + } + DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub)); - while (*ptr && isspace((unsigned char)*ptr)) - ptr++; + if (ptr - wrd > 0) { + Buf_AppendRange(buf, wrd, ptr); + Lst_AtFront(&forLst, Buf_Peel(buf)); + } else { + Buf_Destroy(buf, TRUE); + } + free(sub); - /* - * 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++) - ; + forBuf = Buf_Init(0); + forLevel++; + return (TRUE); +} - 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) { - Buf_AppendRange(buf, wrd, ptr); - Lst_AtFront(&forLst, Buf_Peel(buf)); - } else { - Buf_Destroy(buf, TRUE); - } - free(sub); +/** + * For_Eval + * Eat a line of the .for body looking for embedded .for loops + * and the .endfor + */ +Boolean +For_Eval(char *line) +{ + char *ptr; - forBuf = Buf_Init(0); - forLevel++; - return (1); - } + 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); } /*- diff --git a/usr.bin/make/for.h b/usr.bin/make/for.h index c6404b2..052e194 100644 --- a/usr.bin/make/for.h +++ b/usr.bin/make/for.h @@ -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 */ diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c index dd01953..a211c0f 100644 --- a/usr.bin/make/parse.c +++ b/usr.bin/make/parse.c @@ -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; @@ -2480,25 +2562,6 @@ Parse_File(const char *name, FILE *stream) } /*- - *--------------------------------------------------------------------- - * Parse_Init -- - * initialize the parsing module - * - * Results: - * none - * - * Side Effects: - * the parseIncPath list is initialized... - *--------------------------------------------------------------------- - */ -void -Parse_Init(void) -{ - - mainNode = NULL; -} - -/*- *----------------------------------------------------------------------- * Parse_MainName -- * Return a Lst of the main target to create for main()'s sake. If diff --git a/usr.bin/make/parse.h b/usr.bin/make/parse.h index 867835b..7917440 100644 --- a/usr.bin/make/parse.h +++ b/usr.bin/make/parse.h @@ -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 *); |