diff options
author | Marc Espie <espie@cvs.openbsd.org> | 2002-06-11 21:12:12 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 2002-06-11 21:12:12 +0000 |
commit | b1c15445b12ccbda5a402ec56b487665b69747ee (patch) | |
tree | 39735876bf91b2f6bf39bfb577853773ffc0f705 /usr.bin | |
parent | e7478c049fc780b84e5d9267b380f93757391a22 (diff) |
This is the first step in sanitizing the conditional parser.
Change the conditional recognition algorithm:
scan for a sequence of alphabetic characters, hash it, and compare it against
a small table (using ohash functions).
This makes Cond_Eval entry more logical, and allows for some shortcuts in
recognizing .include, .for, .undef.
This also means that conditionals must have an intervening blank between
the keyword and the actual test, e.g.,
.ifA
will no longer work.
(but no-one actually uses this, and it's highly obfuscated)
Okay miod@.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/make/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/make/cond.c | 277 | ||||
-rw-r--r-- | usr.bin/make/cond.h | 5 | ||||
-rw-r--r-- | usr.bin/make/cond_int.h | 4 | ||||
-rw-r--r-- | usr.bin/make/for.c | 9 | ||||
-rw-r--r-- | usr.bin/make/generate.c | 4 | ||||
-rw-r--r-- | usr.bin/make/parse.c | 52 |
7 files changed, 237 insertions, 120 deletions
diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile index e5e0acd91ad..33174bfa2d2 100644 --- a/usr.bin/make/Makefile +++ b/usr.bin/make/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.34 2002/04/22 21:45:01 miod Exp $ +# $OpenBSD: Makefile,v 1.35 2002/06/11 21:12:11 espie Exp $ PROG= make CFLAGS+= -I${.OBJDIR} -I${.CURDIR} @@ -9,6 +9,7 @@ CFLAGS+=-DUSE_TIMESPEC CFLAGS+=-DHAS_BOOL_H CFLAGS+=-DHAS_PATHS_H CFLAGS+=-DHAS_EXTENDED_GETCWD +#CFLAGS+=-DHAS_STATS SRCS= arch.c buf.c cmd_exec.c compat.c cond.c dir.c error.c for.c \ init.c job.c lowparse.c main.c make.c memory.c parse.c \ @@ -28,7 +29,7 @@ CLEANFILES+= varhashconsts.h condhashconsts.h generate.o generate beforedepend: varhashconsts.h condhashconsts.h # may need tweaking if you add variable synonyms or change the hash function MAGICVARSLOTS=77 -MAGICCONDSLOTS=43 +MAGICCONDSLOTS=65 varhashconsts.h: generate ${.OBJDIR}/generate 1 ${MAGICVARSLOTS} >${.TARGET} @@ -47,6 +48,7 @@ regress: check # kludge for people who forget to make depend var.o: varhashconsts.h +cond.o: condhashconsts.h .if make(install) SUBDIR+= PSD.doc diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c index 4f2333e40eb..7dc574f802a 100644 --- a/usr.bin/make/cond.c +++ b/usr.bin/make/cond.c @@ -1,5 +1,5 @@ /* $OpenPackages$ */ -/* $OpenBSD: cond.c,v 1.27 2002/04/17 16:45:02 espie Exp $ */ +/* $OpenBSD: cond.c,v 1.28 2002/06/11 21:12:11 espie Exp $ */ /* $NetBSD: cond.c,v 1.7 1996/11/06 17:59:02 christos Exp $ */ /* @@ -50,6 +50,8 @@ #include "dir.h" #include "buf.h" #include "cond.h" +#include "cond_int.h" +#include "condhashconsts.h" #include "error.h" #include "var.h" #include "varname.h" @@ -59,6 +61,7 @@ #include "main.h" #include "gnode.h" #include "lst.h" +#include "ohash.h" /* The parsing of conditional expressions is based on this grammar: @@ -113,21 +116,37 @@ static Token CondHandleDefault(bool); static const char *find_cond(const char *); -static struct If { - char *form; /* Form of if */ - int formlen; /* Length of form */ - bool doNot; /* true if default function should be negated */ +struct If { + bool isElse; /* true for else forms */ + bool doNot; /* true for embedded negation */ bool (*defProc)(struct Name *); - /* Default function to apply */ -} 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 } + /* function to apply */ }; +static struct If ifs[] = { + { false, false, CondDoDefined }, /* if, ifdef */ + { false, true, CondDoDefined }, /* ifndef */ + { false, false, CondDoMake }, /* ifmake */ + { false, true, CondDoMake }, /* ifnmake */ + { true, false, CondDoDefined }, /* elif, elifdef */ + { true, true, CondDoDefined }, /* elifndef */ + { true, false, CondDoMake }, /* elifmake */ + { true, true, CondDoMake }, /* elifnmake */ + { true, false, NULL } +}; + +#define COND_IF_INDEX 0 +#define COND_IFDEF_INDEX 0 +#define COND_IFNDEF_INDEX 1 +#define COND_IFMAKE_INDEX 2 +#define COND_IFNMAKE_INDEX 3 +#define COND_ELIF_INDEX 4 +#define COND_ELIFDEF_INDEX 4 +#define COND_ELIFNDEF_INDEX 5 +#define COND_ELIFMAKE_INDEX 6 +#define COND_ELIFNMAKE_INDEX 7 +#define COND_ELSE_INDEX 8 + static bool condInvert; /* Invert the default function */ static bool (*condDefProc) /* Default function to apply */ (struct Name *); @@ -836,101 +855,196 @@ CondE(doEval) return l; } -/* A conditional line looks like this: - * <cond-type> <expr> +/* Evaluate conditional in line. + * returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE, + * COND_ISUNDEF. + * A conditional 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. */ int -Cond_Eval(line) - const char *line; /* Line to parse */ +Cond_Eval(const char *line) { - struct If *ifp; - bool isElse; - bool value = false; - int level; /* Level at which to report errors. */ + /* find end of keyword */ + const char *end; + u_int32_t k; + size_t len; + struct If *ifp; + bool value = false; + int level; /* Level at which to report errors. */ level = PARSE_FATAL; - /* Stuff we are looking for can be if*, elif*, else, or endif. - * otherwise, this is not our turf. */ - - /* 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) { + for (end = line; islower(*end); end++) + ; + /* quick path: recognize special targets early on */ + if (*end == '.' || *end == ':') + return COND_INVALID; + len = end - line; + k = ohash_interval(line, &end); + switch(k % MAGICSLOTS2) { + case K_COND_IF % MAGICSLOTS2: + if (k == K_COND_IF && len == strlen(COND_IF) && + strncmp(line, COND_IF, len) == 0) { + ifp = ifs + COND_IF_INDEX; + } else + return COND_INVALID; + break; + case K_COND_IFDEF % MAGICSLOTS2: + if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) && + strncmp(line, COND_IFDEF, len) == 0) { + ifp = ifs + COND_IFDEF_INDEX; + } else + return COND_INVALID; + break; + case K_COND_IFNDEF % MAGICSLOTS2: + if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) && + strncmp(line, COND_IFNDEF, len) == 0) { + ifp = ifs + COND_IFNDEF_INDEX; + } else + return COND_INVALID; + break; + case K_COND_IFMAKE % MAGICSLOTS2: + if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) && + strncmp(line, COND_IFMAKE, len) == 0) { + ifp = ifs + COND_IFMAKE_INDEX; + } else + return COND_INVALID; + break; + case K_COND_IFNMAKE % MAGICSLOTS2: + if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) && + strncmp(line, COND_IFNMAKE, len) == 0) { + ifp = ifs + COND_IFNMAKE_INDEX; + } else + return COND_INVALID; + break; + case K_COND_ELIF % MAGICSLOTS2: + if (k == K_COND_ELIF && len == strlen(COND_ELIF) && + strncmp(line, COND_ELIF, len) == 0) { + ifp = ifs + COND_ELIF_INDEX; + } else + return COND_INVALID; + break; + case K_COND_ELIFDEF % MAGICSLOTS2: + if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) && + strncmp(line, COND_ELIFDEF, len) == 0) { + ifp = ifs + COND_ELIFDEF_INDEX; + } else + return COND_INVALID; + break; + case K_COND_ELIFNDEF % MAGICSLOTS2: + if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) && + strncmp(line, COND_ELIFNDEF, len) == 0) { + ifp = ifs + COND_ELIFNDEF_INDEX; + } else + return COND_INVALID; + break; + case K_COND_ELIFMAKE % MAGICSLOTS2: + if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) && + strncmp(line, COND_ELIFMAKE, len) == 0) { + ifp = ifs + COND_ELIFMAKE_INDEX; + } else + return COND_INVALID; + break; + case K_COND_ELIFNMAKE % MAGICSLOTS2: + if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) && + strncmp(line, COND_ELIFNMAKE, len) == 0) { + ifp = ifs + COND_ELIFNMAKE_INDEX; + } else + return COND_INVALID; + break; + case K_COND_ELSE % MAGICSLOTS2: + /* valid conditional whose value is the inverse + * of the previous if we parsed. */ + if (k == K_COND_ELSE && len == strlen(COND_ELSE) && + strncmp(line, COND_ELSE, len) == 0) { + if (condTop == MAXIF) { + Parse_Error(level, "if-less else"); + return COND_INVALID; + } else if (skipIfLevel == 0) { + value = !condStack[condTop].value; + ifp = ifs + COND_ELSE_INDEX; + } else + return COND_SKIP; + } else + return COND_INVALID; + break; + case K_COND_ENDIF % MAGICSLOTS2: + if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) && + strncmp(line, COND_ENDIF, len) == 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; + if (skipIfLevel != 0) { + skipIfLevel -= 1; + return COND_SKIP; } else { - skipLine = false; - condTop += 1; - return COND_PARSE; + if (condTop == MAXIF) { + Parse_Error(level, "if-less endif"); + return COND_INVALID; + } else { + skipLine = false; + condTop += 1; + return COND_PARSE; + } } - } - } 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; + } else + return COND_INVALID; + break; + /* Recognize other keywords there, to simplify parser's task */ + case K_COND_FOR % MAGICSLOTS2: + if (k == K_COND_FOR && len == strlen(COND_FOR) && + strncmp(line, COND_FOR, len) == 0) + return COND_ISFOR; + else + return COND_INVALID; + case K_COND_UNDEF % MAGICSLOTS2: + if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) && + strncmp(line, COND_UNDEF, len) == 0) + return COND_ISUNDEF; + else + return COND_INVALID; + case K_COND_INCLUDE % MAGICSLOTS2: + if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) && + strncmp(line, COND_INCLUDE, len) == 0) + return COND_ISINCLUDE; + else + return COND_INVALID; + default: + /* Not a valid conditional type. No error... */ + return COND_INVALID; } - if (ifp->form == NULL) { - /* Nothing fits. 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].value; - else - return COND_SKIP; - } else - /* Not a valid conditional type. No error... */ + if (ifp->isElse) { + if (condTop == MAXIF) { + Parse_Error(level, "if-less elif"); 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... */ - return COND_SKIP; - } - } else if (skipLine) { - /* Don't even try to evaluate a conditional that's not an else if - * we're skipping things... */ - skipIfLevel += 1; + } 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... */ return COND_SKIP; } + } else if (skipLine) { + /* Don't even try to evaluate a conditional that's not an else if + * we're skipping things... */ + skipIfLevel += 1; + return COND_SKIP; + } + if (ifp->defProc) { /* Initialize file-global variables for parsing. */ condDefProc = ifp->defProc; condInvert = ifp->doNot; - line += ifp->formlen; + line += len; while (*line == ' ' || *line == '\t') line++; @@ -960,7 +1074,8 @@ Cond_Eval(line) break; } } - if (!isElse) + + if (!ifp->isElse) condTop -= 1; else if (skipIfLevel != 0 || condStack[condTop].value) { /* If this is an else-type conditional, it should only take effect diff --git a/usr.bin/make/cond.h b/usr.bin/make/cond.h index 7a98b3af0d5..8369b35462e 100644 --- a/usr.bin/make/cond.h +++ b/usr.bin/make/cond.h @@ -1,7 +1,7 @@ #ifndef COND_H #define COND_H /* $OpenPackages$ */ -/* $OpenBSD: cond.h,v 1.1 2001/05/23 12:34:41 espie Exp $ */ +/* $OpenBSD: cond.h,v 1.2 2002/06/11 21:12:11 espie Exp $ */ /* * Copyright (c) 2001 Marc Espie. @@ -36,6 +36,9 @@ #define COND_PARSE 0 /* Parse the next lines */ #define COND_SKIP 1 /* Skip the next lines */ #define COND_INVALID 2 /* Not a conditional statement */ +#define COND_ISFOR 3 +#define COND_ISUNDEF 4 +#define COND_ISINCLUDE 5 /* whattodo = Cond_Eval(line); * Parses a conditional expression (without the leading dot), diff --git a/usr.bin/make/cond_int.h b/usr.bin/make/cond_int.h index d00bed129f4..146b0aff558 100644 --- a/usr.bin/make/cond_int.h +++ b/usr.bin/make/cond_int.h @@ -1,5 +1,5 @@ /* $OpenPackages$ */ -/* $OpenBSD: cond_int.h,v 1.2 2001/05/23 12:34:41 espie Exp $ */ +/* $OpenBSD: cond_int.h,v 1.3 2002/06/11 21:12:11 espie Exp $ */ /* List of all keywords recognized by the make parser */ #define COND_IF "if" @@ -8,11 +8,13 @@ #define COND_IFMAKE "ifmake" #define COND_IFNMAKE "ifnmake" #define COND_ELSE "else" +#define COND_ELIF "elif" #define COND_ELIFDEF "elifdef" #define COND_ELIFNDEF "elifndef" #define COND_ELIFMAKE "elifmake" #define COND_ELIFNMAKE "elifnmake" #define COND_ENDIF "endif" #define COND_FOR "for" +#define COND_ENDFOR "endfor" #define COND_INCLUDE "include" #define COND_UNDEF "undef" diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c index c71378497d2..fa97c94c7e4 100644 --- a/usr.bin/make/for.c +++ b/usr.bin/make/for.c @@ -1,5 +1,5 @@ /* $OpenPackages$ */ -/* $OpenBSD: for.c,v 1.26 2001/05/29 12:53:40 espie Exp $ */ +/* $OpenBSD: for.c,v 1.27 2002/06/11 21:12:11 espie Exp $ */ /* $NetBSD: for.c,v 1.4 1996/11/06 17:59:05 christos Exp $ */ /* @@ -146,13 +146,6 @@ For_Eval(line) For *arg; unsigned long n; - /* 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(ptr[3])) - return NULL; - ptr += 4; - while (*ptr && isspace(*ptr)) ptr++; diff --git a/usr.bin/make/generate.c b/usr.bin/make/generate.c index 80e4d4a1668..4e5494d2041 100644 --- a/usr.bin/make/generate.c +++ b/usr.bin/make/generate.c @@ -1,5 +1,5 @@ /* $OpenPackages$ */ -/* $OpenBSD: generate.c,v 1.4 2001/05/23 12:34:43 espie Exp $ */ +/* $OpenBSD: generate.c,v 1.5 2002/06/11 21:12:11 espie Exp $ */ /* * Copyright (c) 2001 Marc Espie. @@ -69,12 +69,14 @@ char *table_cond[] = { M(COND_IFMAKE), M(COND_IFNMAKE), M(COND_ELSE), + M(COND_ELIF), M(COND_ELIFDEF), M(COND_ELIFNDEF), M(COND_ELIFMAKE), M(COND_ELIFNMAKE), M(COND_ENDIF), M(COND_FOR), + M(COND_ENDFOR), M(COND_INCLUDE), M(COND_UNDEF), NULL diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c index a4c4b54fe06..f7e90e12f70 100644 --- a/usr.bin/make/parse.c +++ b/usr.bin/make/parse.c @@ -1,5 +1,5 @@ /* $OpenPackages$ */ -/* $OpenBSD: parse.c,v 1.66 2001/11/11 12:35:02 espie Exp $ */ +/* $OpenBSD: parse.c,v 1.67 2002/06/11 21:12:11 espie Exp $ */ /* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ /* @@ -1406,35 +1406,31 @@ ParseIsCond(linebuf, copy, line) /* FALLTHROUGH */ case COND_PARSE: return true; - default: + case COND_ISFOR: { + For *loop; + + loop = For_Eval(line+3); + if (loop != NULL) { + bool ok; + do { + /* Find the matching endfor. */ + line = ParseReadLoopLine(linebuf); + if (line == NULL) { + Parse_Error(PARSE_FATAL, + "Unexpected end of file in for loop.\n"); + return false; + } + ok = For_Accumulate(loop, line); + } while (ok); + For_Run(loop); + return true; + } break; } - - { - For *loop; - - loop = For_Eval(line); - if (loop != NULL) { - bool ok; - do { - /* Find the matching endfor. */ - line = ParseReadLoopLine(linebuf); - if (line == NULL) { - Parse_Error(PARSE_FATAL, - "Unexpected end of file in for loop.\n"); - return false; - } - ok = For_Accumulate(loop, line); - } while (ok); - For_Run(loop); - return true; - } - } - - if (strncmp(line, "include", 7) == 0) { + case COND_ISINCLUDE: ParseDoInclude(line + 7); return true; - } else if (strncmp(line, "undef", 5) == 0) { + case COND_ISUNDEF: { char *cp; line+=5; @@ -1446,6 +1442,10 @@ ParseIsCond(linebuf, copy, line) Var_Delete(line); return true; } + default: + break; + } + return false; } |