summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
authorharti <harti@FreeBSD.org>2005-04-11 07:20:10 +0000
committerharti <harti@FreeBSD.org>2005-04-11 07:20:10 +0000
commite0e1cda47d1ab672740932980f0528e49c020775 (patch)
tree60d3956bc33947fb68e3718f818abf16c726d320 /usr.bin
parentf2968c80c994b77c5c1dafc45fce7be685d043e1 (diff)
downloadFreeBSD-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.c318
-rw-r--r--usr.bin/make/cond.h21
-rw-r--r--usr.bin/make/for.c216
-rw-r--r--usr.bin/make/for.h5
-rw-r--r--usr.bin/make/parse.c711
-rw-r--r--usr.bin/make/parse.h1
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 *);
OpenPOWER on IntegriCloud