Annotation of src/usr.bin/make/cond.c, Revision 1.145
1.145 ! rillig 1: /* $NetBSD: cond.c,v 1.144 2020/09/13 15:15:51 rillig Exp $ */
1.6 christos 2:
1.1 cgd 3: /*
4: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
1.17 agc 5: * All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Adam de Boor.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. Neither the name of the University nor the names of its contributors
19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: */
34:
35: /*
1.1 cgd 36: * Copyright (c) 1988, 1989 by Adam de Boor
37: * Copyright (c) 1989 by Berkeley Softworks
38: * All rights reserved.
39: *
40: * This code is derived from software contributed to Berkeley by
41: * Adam de Boor.
42: *
43: * Redistribution and use in source and binary forms, with or without
44: * modification, are permitted provided that the following conditions
45: * are met:
46: * 1. Redistributions of source code must retain the above copyright
47: * notice, this list of conditions and the following disclaimer.
48: * 2. Redistributions in binary form must reproduce the above copyright
49: * notice, this list of conditions and the following disclaimer in the
50: * documentation and/or other materials provided with the distribution.
51: * 3. All advertising materials mentioning features or use of this software
52: * must display the following acknowledgement:
53: * This product includes software developed by the University of
54: * California, Berkeley and its contributors.
55: * 4. Neither the name of the University nor the names of its contributors
56: * may be used to endorse or promote products derived from this software
57: * without specific prior written permission.
58: *
59: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69: * SUCH DAMAGE.
70: */
71:
1.117 rillig 72: /* Handling of conditionals in a makefile.
1.1 cgd 73: *
74: * Interface:
1.142 rillig 75: * Cond_EvalLine Evaluate the conditional in the passed line.
1.1 cgd 76: *
1.142 rillig 77: * Cond_EvalCondition
1.117 rillig 78: * Evaluate the conditional in the passed line, which
79: * is either the argument of one of the .if directives
80: * or the condition in a :?true:false variable modifier.
81: *
82: * Cond_save_depth
83: * Cond_restore_depth
84: * Save and restore the nesting of the conditions, at
85: * the start and end of including another makefile, to
86: * ensure that in each makefile the conditional
87: * directives are well-balanced.
1.1 cgd 88: */
89:
1.99 rillig 90: #include <errno.h>
91:
92: #include "make.h"
93: #include "dir.h"
1.1 cgd 94:
1.144 rillig 95: /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
1.145 ! rillig 96: MAKE_RCSID("$NetBSD: cond.c,v 1.144 2020/09/13 15:15:51 rillig Exp $");
1.144 rillig 97:
1.1 cgd 98: /*
99: * The parsing of conditional expressions is based on this grammar:
100: * E -> F || E
101: * E -> F
102: * F -> T && F
103: * F -> T
104: * T -> defined(variable)
105: * T -> make(target)
106: * T -> exists(file)
107: * T -> empty(varspec)
108: * T -> target(name)
1.12 christos 109: * T -> commands(name)
1.1 cgd 110: * T -> symbol
111: * T -> $(varspec) op value
112: * T -> $(varspec) == "string"
113: * T -> $(varspec) != "string"
1.23 sjg 114: * T -> "string"
1.1 cgd 115: * T -> ( E )
116: * T -> ! T
117: * op -> == | != | > | < | >= | <=
118: *
1.96 rillig 119: * 'symbol' is some other symbol to which the default function is applied.
1.1 cgd 120: *
1.121 rillig 121: * The tokens are scanned by CondToken, which returns:
122: * TOK_AND for '&' or '&&'
123: * TOK_OR for '|' or '||'
124: * TOK_NOT for '!'
125: * TOK_LPAREN for '('
126: * TOK_RPAREN for ')'
127: * Other terminal symbols are evaluated using either the default function or
128: * the function given in the terminal, they return either TOK_TRUE or
129: * TOK_FALSE.
1.1 cgd 130: *
1.60 dsl 131: * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
132: *
1.125 rillig 133: * All non-terminal functions (CondParser_Expr, CondParser_Factor and
134: * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error.
1.1 cgd 135: */
136: typedef enum {
1.60 dsl 137: TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
138: TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
1.1 cgd 139: } Token;
140:
1.114 rillig 141: typedef struct {
142: const struct If *if_info; /* Info for current statement */
1.122 rillig 143: const char *p; /* The remaining condition to parse */
1.115 rillig 144: Token curr; /* Single push-back token used in parsing */
1.121 rillig 145: } CondParser;
1.1 cgd 146:
1.125 rillig 147: static Token CondParser_Expr(CondParser *par, Boolean);
148: static CondEvalResult CondParser_Eval(CondParser *par, Boolean *value);
1.1 cgd 149:
1.93 rillig 150: static unsigned int cond_depth = 0; /* current .if nesting level */
151: static unsigned int cond_min_depth = 0; /* depth at makefile open */
1.1 cgd 152:
1.68 sjg 153: /*
154: * Indicate when we should be strict about lhs of comparisons.
1.117 rillig 155: * In strict mode, the lhs must be a variable expression or a string literal
156: * in quotes. In non-strict mode it may also be an unquoted string literal.
157: *
1.142 rillig 158: * TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc)
159: * FALSE when CondEvalExpression is called from ApplyModifier_IfElse
1.78 rillig 160: * since lhs is already expanded and we cannot tell if
1.68 sjg 161: * it was a variable reference or not.
162: */
163: static Boolean lhsStrict;
164:
1.26 christos 165: static int
1.125 rillig 166: is_token(const char *str, const char *tok, size_t len)
1.26 christos 167: {
1.134 rillig 168: return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
1.26 christos 169: }
170:
1.115 rillig 171: /* Push back the most recent token read. We only need one level of this. */
1.1 cgd 172: static void
1.121 rillig 173: CondParser_PushBack(CondParser *par, Token t)
1.1 cgd 174: {
1.121 rillig 175: assert(par->curr == TOK_NONE);
1.115 rillig 176: assert(t != TOK_NONE);
177:
1.121 rillig 178: par->curr = t;
1.1 cgd 179: }
1.90 rillig 180:
1.116 rillig 181: static void
1.121 rillig 182: CondParser_SkipWhitespace(CondParser *par)
1.116 rillig 183: {
1.134 rillig 184: while (ch_isspace(par->p[0]))
1.122 rillig 185: par->p++;
1.116 rillig 186: }
187:
1.112 rillig 188: /* Parse the argument of a built-in function.
189: *
190: * Arguments:
1.127 rillig 191: * *linePtr initially points at the '(', upon successful return points
192: * right after the ')'.
1.112 rillig 193: *
194: * *out_arg receives the argument as string.
195: *
196: * func says whether the argument belongs to an actual function, or
197: * whether the parsed argument is passed to the default function.
198: *
199: * Return the length of the argument. */
1.1 cgd 200: static int
1.127 rillig 201: ParseFuncArg(const char **linePtr, Boolean doEval, const char *func,
202: char **out_arg) {
1.93 rillig 203: const char *cp;
204: Buffer buf;
205: int paren_depth;
206: char ch;
207: size_t argLen;
1.1 cgd 208:
209: cp = *linePtr;
1.56 dsl 210: if (func != NULL)
1.87 rillig 211: /* Skip opening '(' - verified by caller */
1.56 dsl 212: cp++;
1.1 cgd 213:
214: if (*cp == '\0') {
215: /*
216: * No arguments whatsoever. Because 'make' and 'defined' aren't really
217: * "reserved words", we don't print a message. I think this is better
218: * than hitting the user with a warning message every time s/he uses
219: * the word 'make' or 'defined' at the beginning of a symbol...
220: */
1.112 rillig 221: *out_arg = NULL;
1.77 rillig 222: return 0;
1.1 cgd 223: }
224:
225: while (*cp == ' ' || *cp == '\t') {
226: cp++;
227: }
228:
229: /*
230: * Create a buffer for the argument and start it out at 16 characters
231: * long. Why 16? Why not?
232: */
1.92 rillig 233: Buf_Init(&buf, 16);
1.7 christos 234:
1.56 dsl 235: paren_depth = 0;
236: for (;;) {
237: ch = *cp;
238: if (ch == 0 || ch == ' ' || ch == '\t')
239: break;
240: if ((ch == '&' || ch == '|') && paren_depth == 0)
241: break;
1.1 cgd 242: if (*cp == '$') {
243: /*
244: * Parse the variable spec and install it as part of the argument
245: * if it's valid. We tell Var_Parse to complain on an undefined
1.117 rillig 246: * variable, so we don't need to do it. Nor do we return an error,
1.1 cgd 247: * though perhaps we should...
248: */
1.93 rillig 249: void *freeIt;
1.81 rillig 250: VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0);
1.145 ! rillig 251: const char *cp2;
! 252: (void)Var_Parse(&cp, VAR_CMD, eflags, &cp2, &freeIt);
! 253: /* TODO: handle errors */
1.87 rillig 254: Buf_AddStr(&buf, cp2);
1.73 christos 255: free(freeIt);
1.56 dsl 256: continue;
1.1 cgd 257: }
1.56 dsl 258: if (ch == '(')
259: paren_depth++;
1.93 rillig 260: else if (ch == ')' && --paren_depth < 0)
261: break;
1.56 dsl 262: Buf_AddByte(&buf, *cp);
263: cp++;
1.1 cgd 264: }
265:
1.112 rillig 266: *out_arg = Buf_GetAll(&buf, &argLen);
1.50 dsl 267: Buf_Destroy(&buf, FALSE);
1.1 cgd 268:
269: while (*cp == ' ' || *cp == '\t') {
270: cp++;
271: }
1.56 dsl 272:
273: if (func != NULL && *cp++ != ')') {
1.25 christos 274: Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
1.93 rillig 275: func);
1.142 rillig 276: /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.77 rillig 277: return 0;
1.50 dsl 278: }
279:
1.1 cgd 280: *linePtr = cp;
1.77 rillig 281: return argLen;
1.1 cgd 282: }
1.90 rillig 283:
284: /* Test whether the given variable is defined. */
1.1 cgd 285: static Boolean
1.125 rillig 286: FuncDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 287: {
1.83 rillig 288: char *freeIt;
289: Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL;
1.84 rillig 290: bmake_free(freeIt);
1.77 rillig 291: return result;
1.1 cgd 292: }
1.90 rillig 293:
1.105 rillig 294: /* Wrapper around Str_Match, to be used by Lst_Find. */
1.104 rillig 295: static Boolean
1.90 rillig 296: CondFindStrMatch(const void *string, const void *pattern)
1.1 cgd 297: {
1.104 rillig 298: return Str_Match(string, pattern);
1.1 cgd 299: }
1.90 rillig 300:
301: /* See if the given target is being made. */
1.1 cgd 302: static Boolean
1.125 rillig 303: FuncMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 304: {
1.105 rillig 305: return Lst_Find(create, CondFindStrMatch, arg) != NULL;
1.1 cgd 306: }
1.90 rillig 307:
308: /* See if the given file exists. */
1.1 cgd 309: static Boolean
1.125 rillig 310: FuncExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 311: {
312: Boolean result;
1.93 rillig 313: char *path;
1.1 cgd 314:
315: path = Dir_FindFile(arg, dirSearchPath);
1.62 sjg 316: if (DEBUG(COND)) {
317: fprintf(debug_file, "exists(%s) result is \"%s\"\n",
1.93 rillig 318: arg, path ? path : "");
1.78 rillig 319: }
1.29 christos 320: if (path != NULL) {
1.1 cgd 321: result = TRUE;
322: free(path);
323: } else {
324: result = FALSE;
325: }
1.77 rillig 326: return result;
1.1 cgd 327: }
1.90 rillig 328:
329: /* See if the given node exists and is an actual target. */
1.1 cgd 330: static Boolean
1.125 rillig 331: FuncTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 332: {
1.93 rillig 333: GNode *gn;
1.1 cgd 334:
335: gn = Targ_FindNode(arg, TARG_NOCREATE);
1.77 rillig 336: return gn != NULL && !OP_NOP(gn->type);
1.1 cgd 337: }
338:
1.90 rillig 339: /* See if the given node exists and is an actual target with commands
340: * associated with it. */
1.12 christos 341: static Boolean
1.125 rillig 342: FuncCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.12 christos 343: {
1.93 rillig 344: GNode *gn;
1.12 christos 345:
346: gn = Targ_FindNode(arg, TARG_NOCREATE);
1.103 rillig 347: return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
1.12 christos 348: }
1.90 rillig 349:
1.1 cgd 350: /*-
1.90 rillig 351: * Convert the given number into a double.
352: * We try a base 10 or 16 integer conversion first, if that fails
353: * then we try a floating point conversion instead.
1.1 cgd 354: *
355: * Results:
1.4 cgd 356: * Sets 'value' to double value of string.
1.90 rillig 357: * Returns TRUE if the conversion succeeded.
1.1 cgd 358: */
1.46 dsl 359: static Boolean
1.125 rillig 360: TryParseNumber(const char *str, double *value)
1.1 cgd 361: {
1.46 dsl 362: char *eptr, ech;
363: unsigned long l_val;
364: double d_val;
365:
1.71 sjg 366: errno = 0;
1.70 sjg 367: if (!*str) {
1.71 sjg 368: *value = (double)0;
369: return TRUE;
1.70 sjg 370: }
1.46 dsl 371: l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
372: ech = *eptr;
1.139 rillig 373: if (ech == '\0' && errno != ERANGE) {
1.46 dsl 374: d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
1.19 sjg 375: } else {
1.139 rillig 376: if (ech != '\0' && ech != '.' && ech != 'e' && ech != 'E')
1.46 dsl 377: return FALSE;
378: d_val = strtod(str, &eptr);
379: if (*eptr)
380: return FALSE;
1.1 cgd 381: }
1.46 dsl 382:
383: *value = d_val;
384: return TRUE;
1.1 cgd 385: }
1.46 dsl 386:
1.140 rillig 387: static Boolean
388: is_separator(char ch)
389: {
390: return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch);
391: }
392:
1.1 cgd 393: /*-
1.117 rillig 394: * Parse a string from a variable reference or an optionally quoted
1.124 rillig 395: * string. This is called for the lhs and rhs of string comparisons.
1.23 sjg 396: *
397: * Results:
1.90 rillig 398: * Returns the string, absent any quotes, or NULL on error.
399: * Sets quoted if the string was quoted.
400: * Sets freeIt if needed.
1.23 sjg 401: */
1.117 rillig 402: /* coverity:[+alloc : arg-*3] */
1.82 rillig 403: static const char *
1.124 rillig 404: CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
405: Boolean *quoted, void **freeIt)
1.23 sjg 406: {
407: Buffer buf;
1.82 rillig 408: const char *str;
1.140 rillig 409: Boolean atStart;
410: const char *nested_p;
1.94 rillig 411: Boolean qt;
1.91 rillig 412: const char *start;
1.98 rillig 413: VarEvalFlags eflags;
1.23 sjg 414:
1.92 rillig 415: Buf_Init(&buf, 0);
1.23 sjg 416: str = NULL;
1.30 christos 417: *freeIt = NULL;
1.123 rillig 418: *quoted = qt = par->p[0] == '"' ? 1 : 0;
1.23 sjg 419: if (qt)
1.122 rillig 420: par->p++;
1.138 rillig 421: start = par->p;
422: while (par->p[0] && str == NULL) {
1.123 rillig 423: switch (par->p[0]) {
1.23 sjg 424: case '\\':
1.122 rillig 425: par->p++;
426: if (par->p[0] != '\0') {
1.123 rillig 427: Buf_AddByte(&buf, par->p[0]);
1.122 rillig 428: par->p++;
1.23 sjg 429: }
1.119 rillig 430: continue;
1.23 sjg 431: case '"':
432: if (qt) {
1.122 rillig 433: par->p++; /* we don't want the quotes */
1.23 sjg 434: goto got_str;
1.120 rillig 435: }
1.123 rillig 436: Buf_AddByte(&buf, par->p[0]); /* likely? */
1.122 rillig 437: par->p++;
1.119 rillig 438: continue;
1.23 sjg 439: case ')':
440: case '!':
441: case '=':
442: case '>':
443: case '<':
444: case ' ':
445: case '\t':
446: if (!qt)
447: goto got_str;
1.123 rillig 448: Buf_AddByte(&buf, par->p[0]);
1.122 rillig 449: par->p++;
1.119 rillig 450: continue;
1.23 sjg 451: case '$':
1.140 rillig 452: /* if we are in quotes, an undefined variable is ok */
1.98 rillig 453: eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) |
454: (doEval ? VARE_WANTRES : 0);
1.140 rillig 455: nested_p = par->p;
456: atStart = nested_p == start;
1.145 ! rillig 457: (void)Var_Parse(&nested_p, VAR_CMD, eflags, &str, freeIt);
! 458: /* TODO: handle errors */
1.23 sjg 459: if (str == var_Error) {
1.30 christos 460: if (*freeIt) {
461: free(*freeIt);
462: *freeIt = NULL;
463: }
1.23 sjg 464: /*
465: * Even if !doEval, we still report syntax errors, which
466: * is what getting var_Error back with !doEval means.
467: */
468: str = NULL;
469: goto cleanup;
470: }
1.140 rillig 471: par->p = nested_p;
472:
1.23 sjg 473: /*
1.140 rillig 474: * If the '$' started the string literal (which means no quotes),
475: * and the variable expression is followed by a space, looks like
476: * a comparison operator or is the end of the expression, we are
477: * done.
1.23 sjg 478: */
1.140 rillig 479: if (atStart && is_separator(par->p[0]))
1.23 sjg 480: goto cleanup;
1.113 rillig 481:
482: Buf_AddStr(&buf, str);
1.30 christos 483: if (*freeIt) {
484: free(*freeIt);
485: *freeIt = NULL;
486: }
1.93 rillig 487: str = NULL; /* not finished yet */
1.119 rillig 488: continue;
1.23 sjg 489: default:
1.134 rillig 490: if (strictLHS && !qt && *start != '$' && !ch_isdigit(*start)) {
1.68 sjg 491: /* lhs must be quoted, a variable reference or number */
492: if (*freeIt) {
493: free(*freeIt);
494: *freeIt = NULL;
495: }
496: str = NULL;
497: goto cleanup;
498: }
1.123 rillig 499: Buf_AddByte(&buf, par->p[0]);
1.122 rillig 500: par->p++;
1.119 rillig 501: continue;
1.23 sjg 502: }
503: }
1.93 rillig 504: got_str:
1.92 rillig 505: *freeIt = Buf_GetAll(&buf, NULL);
1.82 rillig 506: str = *freeIt;
1.93 rillig 507: cleanup:
1.50 dsl 508: Buf_Destroy(&buf, FALSE);
1.23 sjg 509: return str;
510: }
1.89 rillig 511:
1.117 rillig 512: /* The different forms of .if directives. */
1.89 rillig 513: static const struct If {
1.93 rillig 514: const char *form; /* Form of if */
1.97 rillig 515: size_t formlen; /* Length of form */
1.93 rillig 516: Boolean doNot; /* TRUE if default function should be negated */
517: Boolean (*defProc)(int, const char *); /* Default function to apply */
1.89 rillig 518: } ifs[] = {
1.125 rillig 519: { "def", 3, FALSE, FuncDefined },
520: { "ndef", 4, TRUE, FuncDefined },
521: { "make", 4, FALSE, FuncMake },
522: { "nmake", 5, TRUE, FuncMake },
523: { "", 0, FALSE, FuncDefined },
1.93 rillig 524: { NULL, 0, FALSE, NULL }
1.89 rillig 525: };
526:
1.131 rillig 527: /* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
528: * ".if 0". */
529: static Token
530: EvalNotEmpty(CondParser *par, const char *lhs, Boolean lhsQuoted)
531: {
532: double left;
533:
534: /* For .ifxxx "..." check for non-empty string. */
535: if (lhsQuoted)
536: return lhs[0] != '\0';
537:
538: /* For .ifxxx <number> compare against zero */
539: if (TryParseNumber(lhs, &left))
540: return left != 0.0;
541:
542: /* For .if ${...} check for non-empty string (defProc is ifdef). */
543: if (par->if_info->form[0] == '\0')
544: return lhs[0] != 0;
545:
546: /* Otherwise action default test ... */
547: return par->if_info->defProc(strlen(lhs), lhs) != par->if_info->doNot;
548: }
549:
1.133 rillig 550: /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
1.130 rillig 551: static Token
1.133 rillig 552: EvalCompareNum(double lhs, const char *op, double rhs)
1.130 rillig 553: {
1.133 rillig 554: if (DEBUG(COND))
1.135 rillig 555: fprintf(debug_file, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op);
1.130 rillig 556:
557: switch (op[0]) {
558: case '!':
559: if (op[1] != '=') {
1.132 rillig 560: Parse_Error(PARSE_WARNING, "Unknown operator");
1.142 rillig 561: /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.130 rillig 562: return TOK_ERROR;
563: }
1.133 rillig 564: return lhs != rhs;
1.130 rillig 565: case '=':
566: if (op[1] != '=') {
1.132 rillig 567: Parse_Error(PARSE_WARNING, "Unknown operator");
1.142 rillig 568: /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.130 rillig 569: return TOK_ERROR;
570: }
1.133 rillig 571: return lhs == rhs;
1.130 rillig 572: case '<':
1.133 rillig 573: return op[1] == '=' ? lhs <= rhs : lhs < rhs;
1.130 rillig 574: case '>':
1.133 rillig 575: return op[1] == '=' ? lhs >= rhs : lhs > rhs;
1.130 rillig 576: }
577: return TOK_ERROR;
578: }
579:
1.133 rillig 580: static Token
581: EvalCompareStr(const char *lhs, const char *op, const char *rhs)
582: {
1.135 rillig 583: if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) {
1.133 rillig 584: Parse_Error(PARSE_WARNING,
1.135 rillig 585: "String comparison operator must be either == or !=");
1.142 rillig 586: /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.133 rillig 587: return TOK_ERROR;
588: }
589:
590: if (DEBUG(COND)) {
591: fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
592: lhs, rhs, op);
593: }
594: return (*op == '=') == (strcmp(lhs, rhs) == 0);
595: }
596:
597: /* Evaluate a comparison, such as "${VAR} == 12345". */
598: static Token
599: EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
600: const char *rhs, Boolean rhsQuoted)
601: {
602: double left, right;
603:
604: if (!rhsQuoted && !lhsQuoted)
605: if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
606: return EvalCompareNum(left, op, right);
607:
608: return EvalCompareStr(lhs, op, rhs);
609: }
610:
1.129 rillig 611: /* Parse a comparison condition such as:
612: *
613: * 0
614: * ${VAR:Mpattern}
615: * ${VAR} == value
616: * ${VAR:U0} < 12345
617: */
1.1 cgd 618: static Token
1.125 rillig 619: CondParser_Comparison(CondParser *par, Boolean doEval)
1.44 dsl 620: {
1.132 rillig 621: Token t = TOK_ERROR;
622: const char *lhs, *op, *rhs;
623: void *lhsFree, *rhsFree;
624: Boolean lhsQuoted, rhsQuoted;
1.44 dsl 625:
626: rhs = NULL;
1.95 rillig 627: lhsFree = rhsFree = NULL;
1.44 dsl 628: lhsQuoted = rhsQuoted = FALSE;
1.78 rillig 629:
1.44 dsl 630: /*
631: * Parse the variable spec and skip over it, saving its
632: * value in lhs.
633: */
1.124 rillig 634: lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhsFree);
1.58 dsl 635: if (!lhs)
636: goto done;
637:
1.121 rillig 638: CondParser_SkipWhitespace(par);
1.44 dsl 639:
640: /*
641: * Make sure the operator is a valid one. If it isn't a
642: * known relational operator, pretend we got a
643: * != 0 comparison.
644: */
1.122 rillig 645: op = par->p;
1.123 rillig 646: switch (par->p[0]) {
1.93 rillig 647: case '!':
648: case '=':
649: case '<':
650: case '>':
1.122 rillig 651: if (par->p[1] == '=') {
652: par->p += 2;
1.93 rillig 653: } else {
1.122 rillig 654: par->p += 1;
1.93 rillig 655: }
656: break;
657: default:
1.137 rillig 658: t = doEval ? EvalNotEmpty(par, lhs, lhsQuoted) : TOK_FALSE;
1.93 rillig 659: goto done;
1.58 dsl 660: }
1.44 dsl 661:
1.121 rillig 662: CondParser_SkipWhitespace(par);
1.58 dsl 663:
1.123 rillig 664: if (par->p[0] == '\0') {
1.132 rillig 665: Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator");
1.142 rillig 666: /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.58 dsl 667: goto done;
1.44 dsl 668: }
1.58 dsl 669:
1.124 rillig 670: rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhsFree);
1.132 rillig 671: if (rhs == NULL)
1.58 dsl 672: goto done;
673:
1.79 sjg 674: if (!doEval) {
675: t = TOK_FALSE;
676: goto done;
677: }
678:
1.133 rillig 679: t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted);
1.58 dsl 680:
681: done:
1.73 christos 682: free(lhsFree);
683: free(rhsFree);
1.44 dsl 684: return t;
685: }
686:
1.47 dsl 687: static int
1.127 rillig 688: ParseEmptyArg(const char **linePtr, Boolean doEval,
689: const char *func MAKE_ATTR_UNUSED, char **argPtr)
1.47 dsl 690: {
1.106 rillig 691: void *val_freeIt;
1.82 rillig 692: const char *val;
1.111 rillig 693: int magic_res;
1.47 dsl 694:
695: /* We do all the work here and return the result as the length */
696: *argPtr = NULL;
697:
1.111 rillig 698: (*linePtr)--; /* Make (*linePtr)[1] point to the '('. */
1.145 ! rillig 699: (void)Var_Parse(linePtr, VAR_CMD, doEval ? VARE_WANTRES : 0,
! 700: &val, &val_freeIt);
! 701: /* TODO: handle errors */
1.111 rillig 702: /* If successful, *linePtr points beyond the closing ')' now. */
1.47 dsl 703:
704: if (val == var_Error) {
1.106 rillig 705: free(val_freeIt);
1.47 dsl 706: return -1;
707: }
708:
709: /* A variable is empty when it just contains spaces... 4/15/92, christos */
1.134 rillig 710: while (ch_isspace(val[0]))
1.47 dsl 711: val++;
712:
713: /*
714: * For consistency with the other functions we can't generate the
715: * true/false here.
716: */
1.111 rillig 717: magic_res = *val != '\0' ? 2 : 1;
1.106 rillig 718: free(val_freeIt);
1.111 rillig 719: return magic_res;
1.47 dsl 720: }
721:
722: static Boolean
1.125 rillig 723: FuncEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
1.47 dsl 724: {
1.112 rillig 725: /* Magic values ahead, see ParseEmptyArg. */
1.47 dsl 726: return arglen == 1;
727: }
728:
1.44 dsl 729: static Token
1.125 rillig 730: CondParser_Func(CondParser *par, Boolean doEval)
1.44 dsl 731: {
1.47 dsl 732: static const struct fn_def {
1.93 rillig 733: const char *fn_name;
1.97 rillig 734: size_t fn_name_len;
1.127 rillig 735: int (*fn_parse)(const char **, Boolean, const char *, char **);
736: Boolean (*fn_eval)(int, const char *);
1.47 dsl 737: } fn_defs[] = {
1.125 rillig 738: { "defined", 7, ParseFuncArg, FuncDefined },
739: { "make", 4, ParseFuncArg, FuncMake },
740: { "exists", 6, ParseFuncArg, FuncExists },
741: { "empty", 5, ParseEmptyArg, FuncEmpty },
742: { "target", 6, ParseFuncArg, FuncTarget },
743: { "commands", 8, ParseFuncArg, FuncCommands },
1.93 rillig 744: { NULL, 0, NULL, NULL },
1.47 dsl 745: };
746: const struct fn_def *fn_def;
1.93 rillig 747: Token t;
748: char *arg = NULL;
749: int arglen;
1.122 rillig 750: const char *cp = par->p;
1.91 rillig 751: const char *cp1;
1.44 dsl 752:
1.47 dsl 753: for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
1.125 rillig 754: if (!is_token(cp, fn_def->fn_name, fn_def->fn_name_len))
1.47 dsl 755: continue;
756: cp += fn_def->fn_name_len;
757: /* There can only be whitespace before the '(' */
1.134 rillig 758: while (ch_isspace(*cp))
1.47 dsl 759: cp++;
760: if (*cp != '(')
761: break;
1.44 dsl 762:
1.127 rillig 763: arglen = fn_def->fn_parse(&cp, doEval, fn_def->fn_name, &arg);
1.47 dsl 764: if (arglen <= 0) {
1.122 rillig 765: par->p = cp;
1.59 dsl 766: return arglen < 0 ? TOK_ERROR : TOK_FALSE;
1.44 dsl 767: }
1.47 dsl 768: /* Evaluate the argument using the required function. */
1.127 rillig 769: t = !doEval || fn_def->fn_eval(arglen, arg);
1.73 christos 770: free(arg);
1.122 rillig 771: par->p = cp;
1.44 dsl 772: return t;
773: }
774:
1.47 dsl 775: /* Push anything numeric through the compare expression */
1.122 rillig 776: cp = par->p;
1.134 rillig 777: if (ch_isdigit(cp[0]) || strchr("+-", cp[0]))
1.125 rillig 778: return CondParser_Comparison(par, doEval);
1.47 dsl 779:
1.44 dsl 780: /*
1.48 dsl 781: * Most likely we have a naked token to apply the default function to.
782: * However ".if a == b" gets here when the "a" is unquoted and doesn't
1.58 dsl 783: * start with a '$'. This surprises people.
1.48 dsl 784: * If what follows the function argument is a '=' or '!' then the syntax
785: * would be invalid if we did "defined(a)" - so instead treat as an
786: * expression.
787: */
1.127 rillig 788: arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
1.134 rillig 789: for (cp1 = cp; ch_isspace(*cp1); cp1++)
1.48 dsl 790: continue;
791: if (*cp1 == '=' || *cp1 == '!')
1.125 rillig 792: return CondParser_Comparison(par, doEval);
1.122 rillig 793: par->p = cp;
1.48 dsl 794:
795: /*
1.58 dsl 796: * Evaluate the argument using the default function.
1.117 rillig 797: * This path always treats .if as .ifdef. To get here, the character
1.58 dsl 798: * after .if must have been taken literally, so the argument cannot
799: * be empty - even if it contained a variable expansion.
1.44 dsl 800: */
1.121 rillig 801: t = !doEval || par->if_info->defProc(arglen, arg) != par->if_info->doNot;
1.73 christos 802: free(arg);
1.44 dsl 803: return t;
804: }
805:
1.121 rillig 806: /* Return the next token or comparison result from the parser. */
1.44 dsl 807: static Token
1.125 rillig 808: CondParser_Token(CondParser *par, Boolean doEval)
1.1 cgd 809: {
1.47 dsl 810: Token t;
811:
1.121 rillig 812: t = par->curr;
1.59 dsl 813: if (t != TOK_NONE) {
1.121 rillig 814: par->curr = TOK_NONE;
1.47 dsl 815: return t;
816: }
817:
1.122 rillig 818: while (par->p[0] == ' ' || par->p[0] == '\t') {
819: par->p++;
1.47 dsl 820: }
821:
1.122 rillig 822: switch (par->p[0]) {
1.47 dsl 823:
824: case '(':
1.122 rillig 825: par->p++;
1.59 dsl 826: return TOK_LPAREN;
1.47 dsl 827:
828: case ')':
1.122 rillig 829: par->p++;
1.59 dsl 830: return TOK_RPAREN;
1.47 dsl 831:
832: case '|':
1.122 rillig 833: par->p++;
834: if (par->p[0] == '|') {
835: par->p++;
1.47 dsl 836: }
1.59 dsl 837: return TOK_OR;
1.1 cgd 838:
1.47 dsl 839: case '&':
1.122 rillig 840: par->p++;
841: if (par->p[0] == '&') {
842: par->p++;
1.1 cgd 843: }
1.59 dsl 844: return TOK_AND;
1.47 dsl 845:
846: case '!':
1.122 rillig 847: par->p++;
1.59 dsl 848: return TOK_NOT;
1.4 cgd 849:
1.47 dsl 850: case '#':
851: case '\n':
852: case '\0':
1.59 dsl 853: return TOK_EOF;
1.47 dsl 854:
855: case '"':
856: case '$':
1.125 rillig 857: return CondParser_Comparison(par, doEval);
1.1 cgd 858:
1.47 dsl 859: default:
1.125 rillig 860: return CondParser_Func(par, doEval);
1.1 cgd 861: }
862: }
1.47 dsl 863:
1.117 rillig 864: /* Parse a single term in the expression. This consists of a terminal symbol
865: * or TOK_NOT and a term (not including the binary operators):
866: *
867: * T -> defined(variable) | make(target) | exists(file) | symbol
868: * T -> ! T | ( E )
1.1 cgd 869: *
870: * Results:
1.59 dsl 871: * TOK_TRUE, TOK_FALSE or TOK_ERROR.
1.1 cgd 872: */
873: static Token
1.125 rillig 874: CondParser_Term(CondParser *par, Boolean doEval)
1.1 cgd 875: {
1.93 rillig 876: Token t;
1.1 cgd 877:
1.125 rillig 878: t = CondParser_Token(par, doEval);
1.1 cgd 879:
1.59 dsl 880: if (t == TOK_EOF) {
1.1 cgd 881: /*
882: * If we reached the end of the expression, the expression
883: * is malformed...
884: */
1.59 dsl 885: t = TOK_ERROR;
886: } else if (t == TOK_LPAREN) {
1.1 cgd 887: /*
888: * T -> ( E )
889: */
1.125 rillig 890: t = CondParser_Expr(par, doEval);
1.59 dsl 891: if (t != TOK_ERROR) {
1.125 rillig 892: if (CondParser_Token(par, doEval) != TOK_RPAREN) {
1.59 dsl 893: t = TOK_ERROR;
1.1 cgd 894: }
895: }
1.59 dsl 896: } else if (t == TOK_NOT) {
1.125 rillig 897: t = CondParser_Term(par, doEval);
1.59 dsl 898: if (t == TOK_TRUE) {
899: t = TOK_FALSE;
900: } else if (t == TOK_FALSE) {
901: t = TOK_TRUE;
1.1 cgd 902: }
903: }
1.77 rillig 904: return t;
1.1 cgd 905: }
1.90 rillig 906:
1.117 rillig 907: /* Parse a conjunctive factor (nice name, wot?)
908: *
909: * F -> T && F | T
1.1 cgd 910: *
911: * Results:
1.59 dsl 912: * TOK_TRUE, TOK_FALSE or TOK_ERROR
1.1 cgd 913: */
914: static Token
1.125 rillig 915: CondParser_Factor(CondParser *par, Boolean doEval)
1.1 cgd 916: {
1.93 rillig 917: Token l, o;
1.1 cgd 918:
1.125 rillig 919: l = CondParser_Term(par, doEval);
1.59 dsl 920: if (l != TOK_ERROR) {
1.125 rillig 921: o = CondParser_Token(par, doEval);
1.1 cgd 922:
1.59 dsl 923: if (o == TOK_AND) {
1.1 cgd 924: /*
925: * F -> T && F
926: *
1.117 rillig 927: * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we
928: * have to parse the r.h.s. anyway (to throw it away).
929: * If T is TOK_TRUE, the result is the r.h.s., be it a TOK_ERROR
930: * or not.
1.1 cgd 931: */
1.59 dsl 932: if (l == TOK_TRUE) {
1.125 rillig 933: l = CondParser_Factor(par, doEval);
1.1 cgd 934: } else {
1.125 rillig 935: (void)CondParser_Factor(par, FALSE);
1.1 cgd 936: }
937: } else {
938: /*
939: * F -> T
940: */
1.121 rillig 941: CondParser_PushBack(par, o);
1.1 cgd 942: }
943: }
1.77 rillig 944: return l;
1.1 cgd 945: }
1.90 rillig 946:
1.117 rillig 947: /* Main expression production.
948: *
949: * E -> F || E | F
1.1 cgd 950: *
951: * Results:
1.59 dsl 952: * TOK_TRUE, TOK_FALSE or TOK_ERROR.
1.1 cgd 953: */
954: static Token
1.125 rillig 955: CondParser_Expr(CondParser *par, Boolean doEval)
1.1 cgd 956: {
1.93 rillig 957: Token l, o;
1.1 cgd 958:
1.125 rillig 959: l = CondParser_Factor(par, doEval);
1.59 dsl 960: if (l != TOK_ERROR) {
1.125 rillig 961: o = CondParser_Token(par, doEval);
1.1 cgd 962:
1.59 dsl 963: if (o == TOK_OR) {
1.1 cgd 964: /*
965: * E -> F || E
966: *
967: * A similar thing occurs for ||, except that here we make sure
1.59 dsl 968: * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
969: * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
970: * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
1.1 cgd 971: */
1.59 dsl 972: if (l == TOK_FALSE) {
1.125 rillig 973: l = CondParser_Expr(par, doEval);
1.1 cgd 974: } else {
1.125 rillig 975: (void)CondParser_Expr(par, FALSE);
1.1 cgd 976: }
977: } else {
978: /*
979: * E -> F
980: */
1.121 rillig 981: CondParser_PushBack(par, o);
1.1 cgd 982: }
983: }
1.77 rillig 984: return l;
1.1 cgd 985: }
1.10 christos 986:
1.89 rillig 987: static CondEvalResult
1.125 rillig 988: CondParser_Eval(CondParser *par, Boolean *value)
1.89 rillig 989: {
1.128 rillig 990: Token res;
991:
992: if (DEBUG(COND))
993: fprintf(debug_file, "CondParser_Eval: %s\n", par->p);
994:
995: res = CondParser_Expr(par, TRUE);
1.126 rillig 996: if (res != TOK_FALSE && res != TOK_TRUE)
1.137 rillig 997: return COND_INVALID;
1.89 rillig 998:
1.126 rillig 999: if (CondParser_Token(par, TRUE /* XXX: Why TRUE? */) != TOK_EOF)
1.137 rillig 1000: return COND_INVALID;
1.89 rillig 1001:
1.126 rillig 1002: *value = res == TOK_TRUE;
1003: return COND_PARSE;
1.89 rillig 1004: }
1005:
1.128 rillig 1006: /* Evaluate the condition, including any side effects from the variable
1007: * expressions in the condition. The condition consists of &&, ||, !,
1008: * function(arg), comparisons and parenthetical groupings thereof.
1.10 christos 1009: *
1010: * Results:
1011: * COND_PARSE if the condition was valid grammatically
1012: * COND_INVALID if not a valid conditional.
1013: *
1014: * (*value) is set to the boolean value of the condition
1015: */
1.142 rillig 1016: static CondEvalResult
1017: CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
1.143 rillig 1018: Boolean eprint, Boolean strictLHS)
1.10 christos 1019: {
1.56 dsl 1020: static const struct If *dflt_info;
1.121 rillig 1021: CondParser par;
1.56 dsl 1022: int rval;
1.10 christos 1023:
1.68 sjg 1024: lhsStrict = strictLHS;
1025:
1.128 rillig 1026: while (*cond == ' ' || *cond == '\t')
1027: cond++;
1.10 christos 1028:
1.56 dsl 1029: if (info == NULL && (info = dflt_info) == NULL) {
1030: /* Scan for the entry for .if - it can't be first */
1.93 rillig 1031: for (info = ifs;; info++)
1.57 enami 1032: if (info->form[0] == 0)
1.56 dsl 1033: break;
1034: dflt_info = info;
1035: }
1.75 riastrad 1036: assert(info != NULL);
1.56 dsl 1037:
1.121 rillig 1038: par.if_info = info;
1.128 rillig 1039: par.p = cond;
1.121 rillig 1040: par.curr = TOK_NONE;
1.10 christos 1041:
1.125 rillig 1042: rval = CondParser_Eval(&par, value);
1.56 dsl 1043:
1044: if (rval == COND_INVALID && eprint)
1.128 rillig 1045: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
1.56 dsl 1046:
1047: return rval;
1048: }
1049:
1.142 rillig 1050: CondEvalResult
1051: Cond_EvalCondition(const char *cond, Boolean *out_value)
1052: {
1.143 rillig 1053: return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
1.142 rillig 1054: }
1.90 rillig 1055:
1.108 rillig 1056: /* Evaluate the conditional in the passed line. The line looks like this:
1057: * .<cond-type> <expr>
1058: * In this line, <cond-type> is any of if, ifmake, ifnmake, ifdef, ifndef,
1059: * elif, elifmake, elifnmake, elifdef, elifndef.
1060: * In this line, <expr> consists of &&, ||, !, function(arg), comparisons
1061: * and parenthetical groupings thereof.
1.1 cgd 1062: *
1.36 dsl 1063: * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
1.86 rillig 1064: * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF),
1.36 dsl 1065: * otherwise .else could be treated as '.elif 1'.
1.108 rillig 1066: *
1067: * Results:
1068: * COND_PARSE to continue parsing the lines after the conditional
1069: * (when .if or .else returns TRUE)
1070: * COND_SKIP to skip the lines after the conditional
1071: * (when .if or .elif returns FALSE, or when a previous
1072: * branch has already been taken)
1073: * COND_INVALID if the conditional was not valid, either because of
1074: * a syntax error or because some variable was undefined
1075: * or because the condition could not be evaluated
1.1 cgd 1076: */
1.86 rillig 1077: CondEvalResult
1.142 rillig 1078: Cond_EvalLine(const char *line)
1.1 cgd 1079: {
1.93 rillig 1080: enum { MAXIF = 128 }; /* maximum depth of .if'ing */
1081: enum { MAXIF_BUMP = 32 }; /* how much to grow by */
1.36 dsl 1082: enum if_states {
1083: IF_ACTIVE, /* .if or .elif part active */
1084: ELSE_ACTIVE, /* .else part active */
1085: SEARCH_FOR_ELIF, /* searching for .elif/else to execute */
1.93 rillig 1086: SKIP_TO_ELSE, /* has been true, but not seen '.else' */
1.36 dsl 1087: SKIP_TO_ENDIF /* nothing else to execute */
1088: };
1.65 sjg 1089: static enum if_states *cond_state = NULL;
1.66 pgoyette 1090: static unsigned int max_if_depth = MAXIF;
1.36 dsl 1091:
1092: const struct If *ifp;
1.93 rillig 1093: Boolean isElif;
1094: Boolean value;
1095: enum if_states state;
1.1 cgd 1096:
1.65 sjg 1097: if (!cond_state) {
1098: cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
1099: cond_state[0] = IF_ACTIVE;
1100: }
1.36 dsl 1101: /* skip leading character (the '.') and any whitespace */
1102: for (line++; *line == ' ' || *line == '\t'; line++)
1.1 cgd 1103: continue;
1104:
1.36 dsl 1105: /* Find what type of if we're dealing with. */
1106: if (line[0] == 'e') {
1107: if (line[1] != 'l') {
1.125 rillig 1108: if (!is_token(line + 1, "ndif", 4))
1.36 dsl 1109: return COND_INVALID;
1110: /* End of conditional section */
1.37 dsl 1111: if (cond_depth == cond_min_depth) {
1.136 rillig 1112: Parse_Error(PARSE_FATAL, "if-less endif");
1.36 dsl 1113: return COND_PARSE;
1114: }
1115: /* Return state for previous conditional */
1116: cond_depth--;
1.93 rillig 1117: return cond_state[cond_depth] <= ELSE_ACTIVE
1118: ? COND_PARSE : COND_SKIP;
1.36 dsl 1119: }
1120:
1121: /* Quite likely this is 'else' or 'elif' */
1.1 cgd 1122: line += 2;
1.125 rillig 1123: if (is_token(line, "se", 2)) {
1.36 dsl 1124: /* It is else... */
1.37 dsl 1125: if (cond_depth == cond_min_depth) {
1.136 rillig 1126: Parse_Error(PARSE_FATAL, "if-less else");
1.43 dsl 1127: return COND_PARSE;
1.36 dsl 1128: }
1129:
1130: state = cond_state[cond_depth];
1131: switch (state) {
1132: case SEARCH_FOR_ELIF:
1133: state = ELSE_ACTIVE;
1134: break;
1135: case ELSE_ACTIVE:
1136: case SKIP_TO_ENDIF:
1137: Parse_Error(PARSE_WARNING, "extra else");
1138: /* FALLTHROUGH */
1139: default:
1140: case IF_ACTIVE:
1141: case SKIP_TO_ELSE:
1142: state = SKIP_TO_ENDIF;
1143: break;
1.1 cgd 1144: }
1.36 dsl 1145: cond_state[cond_depth] = state;
1146: return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1.1 cgd 1147: }
1.36 dsl 1148: /* Assume for now it is an elif */
1149: isElif = TRUE;
1150: } else
1151: isElif = FALSE;
1152:
1153: if (line[0] != 'i' || line[1] != 'f')
1154: /* Not an ifxxx or elifxxx line */
1155: return COND_INVALID;
1.7 christos 1156:
1.1 cgd 1157: /*
1158: * Figure out what sort of conditional it is -- what its default
1159: * function is, etc. -- by looking in the table of valid "ifs"
1160: */
1.36 dsl 1161: line += 2;
1.93 rillig 1162: for (ifp = ifs;; ifp++) {
1.36 dsl 1163: if (ifp->form == NULL)
1164: return COND_INVALID;
1.125 rillig 1165: if (is_token(ifp->form, line, ifp->formlen)) {
1.36 dsl 1166: line += ifp->formlen;
1.1 cgd 1167: break;
1168: }
1169: }
1170:
1.36 dsl 1171: /* Now we know what sort of 'if' it is... */
1172:
1173: if (isElif) {
1.37 dsl 1174: if (cond_depth == cond_min_depth) {
1.136 rillig 1175: Parse_Error(PARSE_FATAL, "if-less elif");
1.43 dsl 1176: return COND_PARSE;
1.36 dsl 1177: }
1.43 dsl 1178: state = cond_state[cond_depth];
1179: if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
1.36 dsl 1180: Parse_Error(PARSE_WARNING, "extra elif");
1.43 dsl 1181: cond_state[cond_depth] = SKIP_TO_ENDIF;
1182: return COND_SKIP;
1183: }
1.36 dsl 1184: if (state != SEARCH_FOR_ELIF) {
1185: /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
1186: cond_state[cond_depth] = SKIP_TO_ELSE;
1187: return COND_SKIP;
1.1 cgd 1188: }
1189: } else {
1.43 dsl 1190: /* Normal .if */
1.67 christos 1191: if (cond_depth + 1 >= max_if_depth) {
1.65 sjg 1192: /*
1193: * This is rare, but not impossible.
1194: * In meta mode, dirdeps.mk (only runs at level 0)
1195: * can need more than the default.
1196: */
1197: max_if_depth += MAXIF_BUMP;
1.93 rillig 1198: cond_state = bmake_realloc(cond_state,
1199: max_if_depth * sizeof(*cond_state));
1.1 cgd 1200: }
1.43 dsl 1201: state = cond_state[cond_depth];
1.36 dsl 1202: cond_depth++;
1203: if (state > ELSE_ACTIVE) {
1204: /* If we aren't parsing the data, treat as always false */
1205: cond_state[cond_depth] = SKIP_TO_ELSE;
1206: return COND_SKIP;
1.32 christos 1207: }
1.1 cgd 1208: }
1209:
1.109 rillig 1210: /* And evaluate the conditional expression */
1.143 rillig 1211: if (CondEvalExpression(ifp, line, &value, TRUE, TRUE) == COND_INVALID) {
1.43 dsl 1212: /* Syntax error in conditional, error message already output. */
1213: /* Skip everything to matching .endif */
1214: cond_state[cond_depth] = SKIP_TO_ELSE;
1215: return COND_SKIP;
1.36 dsl 1216: }
1217:
1218: if (!value) {
1219: cond_state[cond_depth] = SEARCH_FOR_ELIF;
1220: return COND_SKIP;
1221: }
1222: cond_state[cond_depth] = IF_ACTIVE;
1223: return COND_PARSE;
1.1 cgd 1224: }
1.10 christos 1225:
1.1 cgd 1226: void
1.37 dsl 1227: Cond_restore_depth(unsigned int saved_depth)
1.1 cgd 1228: {
1.37 dsl 1229: int open_conds = cond_depth - cond_min_depth;
1230:
1231: if (open_conds != 0 || saved_depth > cond_depth) {
1232: Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
1233: open_conds == 1 ? "" : "s");
1234: cond_depth = cond_min_depth;
1.1 cgd 1235: }
1.37 dsl 1236:
1237: cond_min_depth = saved_depth;
1238: }
1239:
1240: unsigned int
1241: Cond_save_depth(void)
1242: {
1243: int depth = cond_min_depth;
1244:
1245: cond_min_depth = cond_depth;
1246: return depth;
1.1 cgd 1247: }
CVSweb <webmaster@jp.NetBSD.org>