Annotation of src/usr.bin/make/cond.c, Revision 1.296
1.296 ! rillig 1: /* $NetBSD: cond.c,v 1.295 2021/12/11 10:41:31 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.233 rillig 72: /*
73: * Handling of conditionals in a makefile.
1.1 cgd 74: *
75: * Interface:
1.209 rillig 76: * Cond_EvalLine Evaluate the conditional directive, such as
77: * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
1.1 cgd 78: *
1.142 rillig 79: * Cond_EvalCondition
1.168 rillig 80: * Evaluate the conditional, which is either the argument
81: * of one of the .if directives or the condition in a
82: * ':?then:else' variable modifier.
1.117 rillig 83: *
84: * Cond_save_depth
85: * Cond_restore_depth
86: * Save and restore the nesting of the conditions, at
87: * the start and end of including another makefile, to
88: * ensure that in each makefile the conditional
89: * directives are well-balanced.
1.1 cgd 90: */
91:
1.99 rillig 92: #include <errno.h>
93:
94: #include "make.h"
95: #include "dir.h"
1.1 cgd 96:
1.144 rillig 97: /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
1.296 ! rillig 98: MAKE_RCSID("$NetBSD: cond.c,v 1.295 2021/12/11 10:41:31 rillig Exp $");
1.144 rillig 99:
1.1 cgd 100: /*
101: * The parsing of conditional expressions is based on this grammar:
1.285 rillig 102: * Or -> And ('||' And)*
1.288 rillig 103: * And -> Term ('&&' Term)*
1.239 rillig 104: * Term -> Function '(' Argument ')'
105: * Term -> Leaf Operator Leaf
106: * Term -> Leaf
107: * Term -> '(' Or ')'
108: * Term -> '!' Term
109: * Leaf -> "string"
110: * Leaf -> Number
111: * Leaf -> VariableExpression
112: * Leaf -> Symbol
113: * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
1.1 cgd 114: *
1.239 rillig 115: * 'Symbol' is an unquoted string literal to which the default function is
116: * applied.
1.1 cgd 117: *
1.121 rillig 118: * The tokens are scanned by CondToken, which returns:
1.239 rillig 119: * TOK_AND for '&&'
120: * TOK_OR for '||'
1.121 rillig 121: * TOK_NOT for '!'
122: * TOK_LPAREN for '('
123: * TOK_RPAREN for ')'
1.239 rillig 124: *
1.121 rillig 125: * Other terminal symbols are evaluated using either the default function or
126: * the function given in the terminal, they return either TOK_TRUE or
127: * TOK_FALSE.
1.1 cgd 128: */
1.164 rillig 129: typedef enum Token {
1.241 rillig 130: TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
1.215 rillig 131: TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
1.1 cgd 132: } Token;
133:
1.241 rillig 134: typedef enum CondResult {
135: CR_FALSE, CR_TRUE, CR_ERROR
136: } CondResult;
137:
1.251 rillig 138: typedef enum ComparisonOp {
139: LT, LE, GT, GE, EQ, NE
140: } ComparisonOp;
141:
1.150 rillig 142: typedef struct CondParser {
1.243 rillig 143:
144: /*
145: * The plain '.if ${VAR}' evaluates to true if the value of the
146: * expression has length > 0. The other '.if' variants delegate
147: * to evalBare instead.
148: */
1.260 rillig 149: bool plain;
1.243 rillig 150:
151: /* The function to apply on unquoted bare words. */
1.260 rillig 152: bool (*evalBare)(size_t, const char *);
153: bool negateEvalBare;
1.243 rillig 154:
1.276 rillig 155: /*
1.277 rillig 156: * Whether the left-hand side of a comparison may be an unquoted
1.276 rillig 157: * string. This is allowed for expressions of the form
158: * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is
159: * expanded before it is evaluated, due to ease of implementation.
160: * This means that at the point where the condition is evaluated,
161: * make cannot know anymore whether the left-hand side had originally
162: * been a variable expression or a plain word.
163: *
164: * In all other contexts, the left-hand side must either be a
165: * variable expression, a quoted string or a number.
166: */
1.277 rillig 167: bool leftUnquotedOK;
1.276 rillig 168:
1.215 rillig 169: const char *p; /* The remaining condition to parse */
170: Token curr; /* Single push-back token used in parsing */
171:
172: /*
173: * Whether an error message has already been printed for this
174: * condition. The first available error message is usually the most
175: * specific one, therefore it makes sense to suppress the standard
176: * "Malformed conditional" message.
177: */
1.260 rillig 178: bool printedError;
1.121 rillig 179: } CondParser;
1.1 cgd 180:
1.260 rillig 181: static CondResult CondParser_Or(CondParser *par, bool);
1.1 cgd 182:
1.93 rillig 183: static unsigned int cond_depth = 0; /* current .if nesting level */
184: static unsigned int cond_min_depth = 0; /* depth at makefile open */
1.1 cgd 185:
1.268 rillig 186: /* Names for ComparisonOp. */
1.278 rillig 187: static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
1.251 rillig 188:
1.260 rillig 189: static bool
1.280 rillig 190: is_token(const char *str, const char *tok, unsigned char len)
1.26 christos 191: {
1.280 rillig 192: return strncmp(str, tok, (size_t)len) == 0 && !ch_isalpha(str[len]);
1.26 christos 193: }
194:
1.183 rillig 195: static Token
1.260 rillig 196: ToToken(bool cond)
1.183 rillig 197: {
1.215 rillig 198: return cond ? TOK_TRUE : TOK_FALSE;
1.183 rillig 199: }
200:
1.116 rillig 201: static void
1.121 rillig 202: CondParser_SkipWhitespace(CondParser *par)
1.116 rillig 203: {
1.215 rillig 204: cpp_skip_whitespace(&par->p);
1.116 rillig 205: }
206:
1.233 rillig 207: /*
208: * Parse the argument of a built-in function.
1.112 rillig 209: *
210: * Arguments:
1.149 rillig 211: * *pp initially points at the '(',
212: * upon successful return it points right after the ')'.
1.112 rillig 213: *
214: * *out_arg receives the argument as string.
215: *
216: * func says whether the argument belongs to an actual function, or
217: * whether the parsed argument is passed to the default function.
218: *
1.233 rillig 219: * Return the length of the argument, or 0 on error.
220: */
1.161 rillig 221: static size_t
1.260 rillig 222: ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
1.215 rillig 223: char **out_arg)
224: {
225: const char *p = *pp;
226: Buffer argBuf;
227: int paren_depth;
228: size_t argLen;
229:
230: if (func != NULL)
231: p++; /* Skip opening '(' - verified by caller */
232:
233: if (*p == '\0') {
234: *out_arg = NULL; /* Missing closing parenthesis: */
235: return 0; /* .if defined( */
236: }
237:
238: cpp_skip_hspace(&p);
239:
240: Buf_InitSize(&argBuf, 16);
241:
242: paren_depth = 0;
243: for (;;) {
244: char ch = *p;
245: if (ch == '\0' || ch == ' ' || ch == '\t')
246: break;
247: if ((ch == '&' || ch == '|') && paren_depth == 0)
248: break;
249: if (*p == '$') {
250: /*
251: * Parse the variable expression and install it as
252: * part of the argument if it's valid. We tell
253: * Var_Parse to complain on an undefined variable,
254: * (XXX: but Var_Parse ignores that request)
255: * so we don't need to do it. Nor do we return an
256: * error, though perhaps we should.
257: */
1.261 rillig 258: VarEvalMode emode = doEval
1.259 rillig 259: ? VARE_UNDEFERR
1.258 rillig 260: : VARE_PARSE_ONLY;
1.229 rillig 261: FStr nestedVal;
1.261 rillig 262: (void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
1.215 rillig 263: /* TODO: handle errors */
1.229 rillig 264: Buf_AddStr(&argBuf, nestedVal.str);
265: FStr_Done(&nestedVal);
1.215 rillig 266: continue;
267: }
268: if (ch == '(')
269: paren_depth++;
270: else if (ch == ')' && --paren_depth < 0)
271: break;
272: Buf_AddByte(&argBuf, *p);
273: p++;
274: }
1.50 dsl 275:
1.254 rillig 276: argLen = argBuf.len;
277: *out_arg = Buf_DoneData(&argBuf);
1.215 rillig 278:
279: cpp_skip_hspace(&p);
280:
281: if (func != NULL && *p++ != ')') {
1.253 rillig 282: Parse_Error(PARSE_FATAL,
283: "Missing closing parenthesis for %s()", func);
1.260 rillig 284: par->printedError = true;
1.215 rillig 285: return 0;
286: }
287:
288: *pp = p;
289: return argLen;
1.1 cgd 290: }
1.90 rillig 291:
292: /* Test whether the given variable is defined. */
1.234 rillig 293: /*ARGSUSED*/
1.260 rillig 294: static bool
1.161 rillig 295: FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 296: {
1.256 rillig 297: FStr value = Var_Value(SCOPE_CMDLINE, arg);
1.260 rillig 298: bool result = value.str != NULL;
1.230 rillig 299: FStr_Done(&value);
1.215 rillig 300: return result;
1.1 cgd 301: }
1.90 rillig 302:
1.268 rillig 303: /* See if the given target is requested to be made. */
1.234 rillig 304: /*ARGSUSED*/
1.260 rillig 305: static bool
1.161 rillig 306: FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 307: {
1.215 rillig 308: StringListNode *ln;
1.166 rillig 309:
1.219 rillig 310: for (ln = opts.create.first; ln != NULL; ln = ln->next)
1.215 rillig 311: if (Str_Match(ln->datum, arg))
1.260 rillig 312: return true;
313: return false;
1.1 cgd 314: }
1.90 rillig 315:
316: /* See if the given file exists. */
1.234 rillig 317: /*ARGSUSED*/
1.260 rillig 318: static bool
1.161 rillig 319: FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 320: {
1.260 rillig 321: bool result;
1.215 rillig 322: char *path;
1.1 cgd 323:
1.220 rillig 324: path = Dir_FindFile(arg, &dirSearchPath);
1.215 rillig 325: DEBUG2(COND, "exists(%s) result is \"%s\"\n",
326: arg, path != NULL ? path : "");
327: result = path != NULL;
328: free(path);
329: return result;
1.1 cgd 330: }
1.90 rillig 331:
332: /* See if the given node exists and is an actual target. */
1.234 rillig 333: /*ARGSUSED*/
1.260 rillig 334: static bool
1.161 rillig 335: FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1 cgd 336: {
1.215 rillig 337: GNode *gn = Targ_FindNode(arg);
338: return gn != NULL && GNode_IsTarget(gn);
1.1 cgd 339: }
340:
1.233 rillig 341: /*
342: * See if the given node exists and is an actual target with commands
343: * associated with it.
344: */
1.234 rillig 345: /*ARGSUSED*/
1.260 rillig 346: static bool
1.161 rillig 347: FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.12 christos 348: {
1.215 rillig 349: GNode *gn = Targ_FindNode(arg);
1.218 rillig 350: return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
1.12 christos 351: }
1.90 rillig 352:
1.189 rillig 353: /*
1.90 rillig 354: * Convert the given number into a double.
355: * We try a base 10 or 16 integer conversion first, if that fails
356: * then we try a floating point conversion instead.
1.1 cgd 357: *
358: * Results:
1.260 rillig 359: * Returns true if the conversion succeeded.
1.189 rillig 360: * Sets 'out_value' to the converted number.
1.1 cgd 361: */
1.260 rillig 362: static bool
1.189 rillig 363: TryParseNumber(const char *str, double *out_value)
1.1 cgd 364: {
1.215 rillig 365: char *end;
366: unsigned long ul_val;
367: double dbl_val;
368:
369: if (str[0] == '\0') { /* XXX: why is an empty string a number? */
370: *out_value = 0.0;
1.260 rillig 371: return true;
1.215 rillig 372: }
1.189 rillig 373:
1.270 rillig 374: errno = 0;
1.215 rillig 375: ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
376: if (*end == '\0' && errno != ERANGE) {
377: *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
1.260 rillig 378: return true;
1.215 rillig 379: }
1.46 dsl 380:
1.215 rillig 381: if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
1.260 rillig 382: return false; /* skip the expensive strtod call */
1.215 rillig 383: dbl_val = strtod(str, &end);
384: if (*end != '\0')
1.260 rillig 385: return false;
1.189 rillig 386:
1.215 rillig 387: *out_value = dbl_val;
1.260 rillig 388: return true;
1.1 cgd 389: }
1.46 dsl 390:
1.260 rillig 391: static bool
1.140 rillig 392: is_separator(char ch)
393: {
1.267 rillig 394: return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
395: ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
1.140 rillig 396: }
397:
1.242 rillig 398: /*
1.248 rillig 399: * In a quoted or unquoted string literal or a number, parse a variable
400: * expression.
401: *
402: * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
403: */
1.260 rillig 404: static bool
1.248 rillig 405: CondParser_StringExpr(CondParser *par, const char *start,
1.274 rillig 406: bool doEval, bool quoted,
407: Buffer *buf, FStr *inout_str)
1.248 rillig 408: {
1.261 rillig 409: VarEvalMode emode;
1.248 rillig 410: const char *nested_p;
1.260 rillig 411: bool atStart;
1.248 rillig 412: VarParseResult parseResult;
413:
1.275 rillig 414: emode = doEval && quoted ? VARE_WANTRES
415: : doEval ? VARE_UNDEFERR
1.258 rillig 416: : VARE_PARSE_ONLY;
1.248 rillig 417:
418: nested_p = par->p;
419: atStart = nested_p == start;
1.261 rillig 420: parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, emode, inout_str);
1.248 rillig 421: /* TODO: handle errors */
422: if (inout_str->str == var_Error) {
423: if (parseResult == VPR_ERR) {
424: /*
1.249 rillig 425: * FIXME: Even if an error occurs, there is no
426: * guarantee that it is reported.
1.248 rillig 427: *
428: * See cond-token-plain.mk $$$$$$$$.
429: */
1.260 rillig 430: par->printedError = true;
1.248 rillig 431: }
432: /*
1.249 rillig 433: * XXX: Can there be any situation in which a returned
1.257 rillig 434: * var_Error needs to be freed?
1.248 rillig 435: */
436: FStr_Done(inout_str);
437: /*
1.249 rillig 438: * Even if !doEval, we still report syntax errors, which is
439: * what getting var_Error back with !doEval means.
1.248 rillig 440: */
441: *inout_str = FStr_InitRefer(NULL);
1.260 rillig 442: return false;
1.248 rillig 443: }
444: par->p = nested_p;
445:
446: /*
1.249 rillig 447: * If the '$' started the string literal (which means no quotes), and
448: * the variable expression is followed by a space, looks like a
449: * comparison operator or is the end of the expression, we are done.
1.248 rillig 450: */
451: if (atStart && is_separator(par->p[0]))
1.260 rillig 452: return false;
1.248 rillig 453:
454: Buf_AddStr(buf, inout_str->str);
455: FStr_Done(inout_str);
456: *inout_str = FStr_InitRefer(NULL); /* not finished yet */
1.260 rillig 457: return true;
1.248 rillig 458: }
459:
460: /*
1.266 rillig 461: * Parse a string from a variable expression or an optionally quoted
462: * string. This is called for the left-hand and right-hand sides of
463: * comparisons.
1.23 sjg 464: *
465: * Results:
1.90 rillig 466: * Returns the string, absent any quotes, or NULL on error.
1.266 rillig 467: * Sets out_quoted if the leaf was a quoted string literal.
1.23 sjg 468: */
1.227 rillig 469: static void
1.277 rillig 470: CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
1.260 rillig 471: FStr *out_str, bool *out_quoted)
1.23 sjg 472: {
1.215 rillig 473: Buffer buf;
1.228 rillig 474: FStr str;
1.260 rillig 475: bool quoted;
1.215 rillig 476: const char *start;
477:
478: Buf_Init(&buf);
1.228 rillig 479: str = FStr_InitRefer(NULL);
1.215 rillig 480: *out_quoted = quoted = par->p[0] == '"';
481: start = par->p;
482: if (quoted)
1.122 rillig 483: par->p++;
1.227 rillig 484:
1.228 rillig 485: while (par->p[0] != '\0' && str.str == NULL) {
1.215 rillig 486: switch (par->p[0]) {
487: case '\\':
488: par->p++;
489: if (par->p[0] != '\0') {
490: Buf_AddByte(&buf, par->p[0]);
491: par->p++;
492: }
493: continue;
494: case '"':
495: par->p++;
1.246 rillig 496: if (quoted)
497: goto got_str; /* skip the closing quote */
498: Buf_AddByte(&buf, '"');
1.215 rillig 499: continue;
500: case ')': /* see is_separator */
501: case '!':
502: case '=':
503: case '>':
504: case '<':
505: case ' ':
506: case '\t':
507: if (!quoted)
508: goto got_str;
509: Buf_AddByte(&buf, par->p[0]);
510: par->p++;
511: continue;
512: case '$':
1.248 rillig 513: if (!CondParser_StringExpr(par,
514: start, doEval, quoted, &buf, &str))
1.215 rillig 515: goto cleanup;
516: continue;
517: default:
1.277 rillig 518: if (!unquotedOK && !quoted && *start != '$' &&
1.215 rillig 519: !ch_isdigit(*start)) {
520: /*
521: * The left-hand side must be quoted,
1.277 rillig 522: * a variable expression or a number.
1.215 rillig 523: */
1.228 rillig 524: str = FStr_InitRefer(NULL);
1.215 rillig 525: goto cleanup;
526: }
527: Buf_AddByte(&buf, par->p[0]);
528: par->p++;
529: continue;
1.30 christos 530: }
1.23 sjg 531: }
1.93 rillig 532: got_str:
1.254 rillig 533: str = FStr_InitOwn(buf.data);
1.93 rillig 534: cleanup:
1.266 rillig 535: Buf_DoneData(&buf); /* XXX: memory leak on failure? */
1.227 rillig 536: *out_str = str;
1.23 sjg 537: }
1.89 rillig 538:
1.260 rillig 539: static bool
1.265 rillig 540: EvalBare(const CondParser *par, const char *arg, size_t arglen)
1.176 rillig 541: {
1.260 rillig 542: bool res = par->evalBare(arglen, arg);
1.243 rillig 543: return par->negateEvalBare ? !res : res;
1.176 rillig 544: }
545:
1.233 rillig 546: /*
547: * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
548: * ".if 0".
549: */
1.260 rillig 550: static bool
551: EvalNotEmpty(CondParser *par, const char *value, bool quoted)
1.131 rillig 552: {
1.215 rillig 553: double num;
1.131 rillig 554:
1.215 rillig 555: /* For .ifxxx "...", check for non-empty string. */
556: if (quoted)
557: return value[0] != '\0';
558:
559: /* For .ifxxx <number>, compare against zero */
560: if (TryParseNumber(value, &num))
561: return num != 0.0;
562:
563: /* For .if ${...}, check for non-empty string. This is different from
564: * the evaluation function from that .if variant, which would test
565: * whether a variable of the given name were defined. */
1.295 rillig 566: /*
567: * XXX: Whitespace should count as empty, just as in
568: * CondParser_FuncCallEmpty.
569: */
1.243 rillig 570: if (par->plain)
1.215 rillig 571: return value[0] != '\0';
1.131 rillig 572:
1.265 rillig 573: return EvalBare(par, value, strlen(value));
1.131 rillig 574: }
575:
1.133 rillig 576: /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
1.260 rillig 577: static bool
1.251 rillig 578: EvalCompareNum(double lhs, ComparisonOp op, double rhs)
1.130 rillig 579: {
1.251 rillig 580: DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
1.130 rillig 581:
1.251 rillig 582: switch (op) {
583: case LT:
584: return lhs < rhs;
585: case LE:
586: return lhs <= rhs;
587: case GT:
588: return lhs > rhs;
589: case GE:
590: return lhs >= rhs;
591: case NE:
592: return lhs != rhs;
593: default:
594: return lhs == rhs;
1.215 rillig 595: }
1.130 rillig 596: }
597:
1.133 rillig 598: static Token
1.252 rillig 599: EvalCompareStr(CondParser *par, const char *lhs,
600: ComparisonOp op, const char *rhs)
1.133 rillig 601: {
1.251 rillig 602: if (op != EQ && op != NE) {
1.252 rillig 603: Parse_Error(PARSE_FATAL,
1.251 rillig 604: "String comparison operator must be either == or !=");
1.260 rillig 605: par->printedError = true;
1.215 rillig 606: return TOK_ERROR;
607: }
1.133 rillig 608:
1.251 rillig 609: DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
610: lhs, rhs, opname[op]);
611: return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
1.133 rillig 612: }
613:
614: /* Evaluate a comparison, such as "${VAR} == 12345". */
615: static Token
1.260 rillig 616: EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
617: ComparisonOp op, const char *rhs, bool rhsQuoted)
1.133 rillig 618: {
1.215 rillig 619: double left, right;
1.133 rillig 620:
1.215 rillig 621: if (!rhsQuoted && !lhsQuoted)
622: if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
1.251 rillig 623: return ToToken(EvalCompareNum(left, op, right));
1.133 rillig 624:
1.252 rillig 625: return EvalCompareStr(par, lhs, op, rhs);
1.133 rillig 626: }
627:
1.260 rillig 628: static bool
1.251 rillig 629: CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
630: {
631: const char *p = par->p;
632:
633: if (p[0] == '<' && p[1] == '=') {
634: *out_op = LE;
635: goto length_2;
636: } else if (p[0] == '<') {
637: *out_op = LT;
638: goto length_1;
639: } else if (p[0] == '>' && p[1] == '=') {
640: *out_op = GE;
641: goto length_2;
642: } else if (p[0] == '>') {
643: *out_op = GT;
644: goto length_1;
645: } else if (p[0] == '=' && p[1] == '=') {
646: *out_op = EQ;
647: goto length_2;
648: } else if (p[0] == '!' && p[1] == '=') {
649: *out_op = NE;
650: goto length_2;
651: }
1.260 rillig 652: return false;
1.251 rillig 653:
654: length_2:
655: par->p = p + 2;
1.260 rillig 656: return true;
1.251 rillig 657: length_1:
658: par->p = p + 1;
1.260 rillig 659: return true;
1.251 rillig 660: }
661:
1.233 rillig 662: /*
663: * Parse a comparison condition such as:
1.129 rillig 664: *
665: * 0
666: * ${VAR:Mpattern}
667: * ${VAR} == value
668: * ${VAR:U0} < 12345
669: */
1.1 cgd 670: static Token
1.260 rillig 671: CondParser_Comparison(CondParser *par, bool doEval)
1.44 dsl 672: {
1.215 rillig 673: Token t = TOK_ERROR;
1.228 rillig 674: FStr lhs, rhs;
1.251 rillig 675: ComparisonOp op;
1.260 rillig 676: bool lhsQuoted, rhsQuoted;
1.215 rillig 677:
1.277 rillig 678: CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
1.228 rillig 679: if (lhs.str == NULL)
1.215 rillig 680: goto done_lhs;
681:
682: CondParser_SkipWhitespace(par);
683:
1.251 rillig 684: if (!CondParser_ComparisonOp(par, &op)) {
1.215 rillig 685: /* Unknown operator, compare against an empty string or 0. */
1.228 rillig 686: t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
1.215 rillig 687: goto done_lhs;
688: }
689:
690: CondParser_SkipWhitespace(par);
691:
692: if (par->p[0] == '\0') {
1.252 rillig 693: Parse_Error(PARSE_FATAL,
1.269 rillig 694: "Missing right-hand side of operator '%s'", opname[op]);
1.260 rillig 695: par->printedError = true;
1.215 rillig 696: goto done_lhs;
697: }
698:
1.277 rillig 699: CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
1.228 rillig 700: if (rhs.str == NULL)
1.215 rillig 701: goto done_rhs;
702:
703: if (!doEval) {
704: t = TOK_FALSE;
705: goto done_rhs;
706: }
1.79 sjg 707:
1.252 rillig 708: t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
1.58 dsl 709:
1.185 rillig 710: done_rhs:
1.228 rillig 711: FStr_Done(&rhs);
1.185 rillig 712: done_lhs:
1.228 rillig 713: FStr_Done(&lhs);
1.215 rillig 714: return t;
1.44 dsl 715: }
716:
1.233 rillig 717: /*
718: * The argument to empty() is a variable name, optionally followed by
719: * variable modifiers.
720: */
1.295 rillig 721: static bool
722: CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
1.47 dsl 723: {
1.295 rillig 724: const char *cp = par->p;
725: Token tok;
1.229 rillig 726: FStr val;
1.215 rillig 727:
1.295 rillig 728: if (!is_token(cp, "empty", 5))
729: return false;
730: cp += 5;
731:
732: cpp_skip_whitespace(&cp);
733: if (*cp != '(')
734: return false;
735:
736: cp--; /* Make cp[1] point to the '('. */
737: (void)Var_Parse(&cp, SCOPE_CMDLINE,
1.258 rillig 738: doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
1.215 rillig 739: /* TODO: handle errors */
740:
1.293 rillig 741: if (val.str == var_Error)
742: tok = TOK_ERROR;
743: else {
744: cpp_skip_whitespace(&val.str);
1.296 ! rillig 745: tok = val.str[0] != '\0' && doEval ? TOK_FALSE : TOK_TRUE;
1.215 rillig 746: }
747:
1.229 rillig 748: FStr_Done(&val);
1.294 rillig 749: *out_token = tok;
1.290 rillig 750: par->p = cp;
751: return true;
752: }
753:
1.263 rillig 754: /* Parse a function call expression, such as 'defined(${file})'. */
1.260 rillig 755: static bool
1.263 rillig 756: CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
1.44 dsl 757: {
1.215 rillig 758: static const struct fn_def {
1.279 rillig 759: const char fn_name[9];
760: unsigned char fn_name_len;
1.260 rillig 761: bool (*fn_eval)(size_t, const char *);
1.215 rillig 762: } fns[] = {
1.290 rillig 763: { "defined", 7, FuncDefined },
764: { "make", 4, FuncMake },
765: { "exists", 6, FuncExists },
766: { "target", 6, FuncTarget },
767: { "commands", 8, FuncCommands }
1.215 rillig 768: };
769: const struct fn_def *fn;
770: char *arg = NULL;
771: size_t arglen;
772: const char *cp = par->p;
1.273 rillig 773: const struct fn_def *last_fn = fns + sizeof fns / sizeof fns[0] - 1;
1.215 rillig 774:
1.272 rillig 775: for (fn = fns; !is_token(cp, fn->fn_name, fn->fn_name_len); fn++)
1.273 rillig 776: if (fn == last_fn)
1.272 rillig 777: return false;
778:
779: cp += fn->fn_name_len;
780: cpp_skip_whitespace(&cp);
781: if (*cp != '(')
782: return false;
1.215 rillig 783:
1.290 rillig 784: arglen = ParseFuncArg(par, &cp, doEval, fn->fn_name, &arg);
1.272 rillig 785: if (arglen == 0 || arglen == (size_t)-1) {
1.215 rillig 786: par->p = cp;
1.272 rillig 787: *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
1.260 rillig 788: return true;
1.44 dsl 789: }
1.200 rillig 790:
1.272 rillig 791: /* Evaluate the argument using the required function. */
792: *out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
793: free(arg);
794: par->p = cp;
795: return true;
1.196 rillig 796: }
797:
1.233 rillig 798: /*
1.266 rillig 799: * Parse a comparison such as '${VAR} == "value"', or a simple leaf without
800: * operator, which is a number, a variable expression or a string literal.
1.233 rillig 801: */
1.196 rillig 802: static Token
1.266 rillig 803: CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
1.196 rillig 804: {
1.215 rillig 805: Token t;
806: char *arg = NULL;
807: size_t arglen;
1.221 rillig 808: const char *cp;
1.215 rillig 809: const char *cp1;
810:
811: /* Push anything numeric through the compare expression */
812: cp = par->p;
813: if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
814: return CondParser_Comparison(par, doEval);
1.196 rillig 815:
1.215 rillig 816: /*
817: * Most likely we have a naked token to apply the default function to.
818: * However ".if a == b" gets here when the "a" is unquoted and doesn't
819: * start with a '$'. This surprises people.
820: * If what follows the function argument is a '=' or '!' then the
821: * syntax would be invalid if we did "defined(a)" - so instead treat
822: * as an expression.
823: */
1.266 rillig 824: /*
825: * XXX: Is it possible to have a variable expression evaluated twice
826: * at this point?
827: */
1.253 rillig 828: arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
1.215 rillig 829: cp1 = cp;
830: cpp_skip_whitespace(&cp1);
1.262 rillig 831: if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
1.215 rillig 832: return CondParser_Comparison(par, doEval);
833: par->p = cp;
834:
835: /*
836: * Evaluate the argument using the default function.
837: * This path always treats .if as .ifdef. To get here, the character
838: * after .if must have been taken literally, so the argument cannot
839: * be empty - even if it contained a variable expansion.
840: */
1.265 rillig 841: t = ToToken(!doEval || EvalBare(par, arg, arglen));
1.215 rillig 842: free(arg);
1.44 dsl 843: return t;
844: }
845:
1.121 rillig 846: /* Return the next token or comparison result from the parser. */
1.44 dsl 847: static Token
1.260 rillig 848: CondParser_Token(CondParser *par, bool doEval)
1.1 cgd 849: {
1.215 rillig 850: Token t;
851:
852: t = par->curr;
853: if (t != TOK_NONE) {
854: par->curr = TOK_NONE;
855: return t;
856: }
857:
858: cpp_skip_hspace(&par->p);
859:
860: switch (par->p[0]) {
861:
862: case '(':
863: par->p++;
864: return TOK_LPAREN;
865:
866: case ')':
867: par->p++;
868: return TOK_RPAREN;
869:
870: case '|':
871: par->p++;
872: if (par->p[0] == '|')
873: par->p++;
1.231 rillig 874: else if (opts.strict) {
1.215 rillig 875: Parse_Error(PARSE_FATAL, "Unknown operator '|'");
1.260 rillig 876: par->printedError = true;
1.215 rillig 877: return TOK_ERROR;
878: }
879: return TOK_OR;
880:
881: case '&':
882: par->p++;
883: if (par->p[0] == '&')
884: par->p++;
1.231 rillig 885: else if (opts.strict) {
1.215 rillig 886: Parse_Error(PARSE_FATAL, "Unknown operator '&'");
1.260 rillig 887: par->printedError = true;
1.215 rillig 888: return TOK_ERROR;
889: }
890: return TOK_AND;
1.47 dsl 891:
1.215 rillig 892: case '!':
893: par->p++;
894: return TOK_NOT;
1.47 dsl 895:
1.215 rillig 896: case '#': /* XXX: see unit-tests/cond-token-plain.mk */
897: case '\n': /* XXX: why should this end the condition? */
898: /* Probably obsolete now, from 1993-03-21. */
899: case '\0':
900: return TOK_EOF;
1.47 dsl 901:
1.215 rillig 902: case '"':
903: case '$':
904: return CondParser_Comparison(par, doEval);
1.47 dsl 905:
1.215 rillig 906: default:
1.290 rillig 907: if (CondParser_FuncCallEmpty(par, doEval, &t))
908: return t;
1.264 rillig 909: if (CondParser_FuncCall(par, doEval, &t))
910: return t;
1.266 rillig 911: return CondParser_ComparisonOrLeaf(par, doEval);
1.215 rillig 912: }
1.1 cgd 913: }
1.47 dsl 914:
1.289 rillig 915: /* Skip the next token if it equals t. */
916: static bool
917: CondParser_Skip(CondParser *par, Token t)
918: {
919: Token actual;
920:
921: actual = CondParser_Token(par, false);
922: if (actual == t)
923: return true;
924:
925: assert(par->curr == TOK_NONE);
926: assert(actual != TOK_NONE);
927: par->curr = actual;
928: return false;
929: }
930:
1.233 rillig 931: /*
1.239 rillig 932: * Term -> '(' Or ')'
933: * Term -> '!' Term
934: * Term -> Leaf Operator Leaf
935: * Term -> Leaf
1.1 cgd 936: */
1.241 rillig 937: static CondResult
1.260 rillig 938: CondParser_Term(CondParser *par, bool doEval)
1.1 cgd 939: {
1.241 rillig 940: CondResult res;
1.215 rillig 941: Token t;
1.1 cgd 942:
1.215 rillig 943: t = CondParser_Token(par, doEval);
1.241 rillig 944: if (t == TOK_TRUE)
945: return CR_TRUE;
946: if (t == TOK_FALSE)
947: return CR_FALSE;
1.1 cgd 948:
1.237 rillig 949: if (t == TOK_LPAREN) {
1.241 rillig 950: res = CondParser_Or(par, doEval);
951: if (res == CR_ERROR)
952: return CR_ERROR;
1.239 rillig 953: if (CondParser_Token(par, doEval) != TOK_RPAREN)
1.241 rillig 954: return CR_ERROR;
955: return res;
1.239 rillig 956: }
957:
958: if (t == TOK_NOT) {
1.241 rillig 959: res = CondParser_Term(par, doEval);
960: if (res == CR_TRUE)
961: res = CR_FALSE;
962: else if (res == CR_FALSE)
963: res = CR_TRUE;
964: return res;
1.239 rillig 965: }
1.236 rillig 966:
1.241 rillig 967: return CR_ERROR;
1.1 cgd 968: }
1.90 rillig 969:
1.233 rillig 970: /*
1.288 rillig 971: * And -> Term ('&&' Term)*
1.1 cgd 972: */
1.241 rillig 973: static CondResult
1.260 rillig 974: CondParser_And(CondParser *par, bool doEval)
1.1 cgd 975: {
1.288 rillig 976: CondResult res, rhs;
1.1 cgd 977:
1.288 rillig 978: res = CR_TRUE;
979: do {
980: if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
1.241 rillig 981: return CR_ERROR;
1.288 rillig 982: if (rhs == CR_FALSE) {
983: res = CR_FALSE;
984: doEval = false;
985: }
1.289 rillig 986: } while (CondParser_Skip(par, TOK_AND));
1.239 rillig 987:
988: return res;
1.1 cgd 989: }
1.90 rillig 990:
1.233 rillig 991: /*
1.285 rillig 992: * Or -> And ('||' And)*
1.1 cgd 993: */
1.241 rillig 994: static CondResult
1.260 rillig 995: CondParser_Or(CondParser *par, bool doEval)
1.1 cgd 996: {
1.285 rillig 997: CondResult res, rhs;
1.1 cgd 998:
1.285 rillig 999: res = CR_FALSE;
1000: do {
1001: if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
1.241 rillig 1002: return CR_ERROR;
1.285 rillig 1003: if (rhs == CR_TRUE) {
1004: res = CR_TRUE;
1005: doEval = false;
1006: }
1.289 rillig 1007: } while (CondParser_Skip(par, TOK_OR));
1.239 rillig 1008:
1009: return res;
1.1 cgd 1010: }
1.10 christos 1011:
1.89 rillig 1012: static CondEvalResult
1.260 rillig 1013: CondParser_Eval(CondParser *par, bool *out_value)
1.89 rillig 1014: {
1.241 rillig 1015: CondResult res;
1.128 rillig 1016:
1.215 rillig 1017: DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
1.128 rillig 1018:
1.260 rillig 1019: res = CondParser_Or(par, true);
1.241 rillig 1020: if (res == CR_ERROR)
1.215 rillig 1021: return COND_INVALID;
1.89 rillig 1022:
1.260 rillig 1023: if (CondParser_Token(par, false) != TOK_EOF)
1.215 rillig 1024: return COND_INVALID;
1.89 rillig 1025:
1.243 rillig 1026: *out_value = res == CR_TRUE;
1.215 rillig 1027: return COND_PARSE;
1.89 rillig 1028: }
1029:
1.233 rillig 1030: /*
1031: * Evaluate the condition, including any side effects from the variable
1.128 rillig 1032: * expressions in the condition. The condition consists of &&, ||, !,
1033: * function(arg), comparisons and parenthetical groupings thereof.
1.10 christos 1034: *
1035: * Results:
1036: * COND_PARSE if the condition was valid grammatically
1.153 rillig 1037: * COND_INVALID if not a valid conditional.
1.10 christos 1038: *
1.281 rillig 1039: * *out_value is set to the boolean value of the condition
1.10 christos 1040: */
1.142 rillig 1041: static CondEvalResult
1.260 rillig 1042: CondEvalExpression(const char *cond, bool *out_value, bool plain,
1043: bool (*evalBare)(size_t, const char *), bool negate,
1.277 rillig 1044: bool eprint, bool leftUnquotedOK)
1.10 christos 1045: {
1.215 rillig 1046: CondParser par;
1047: CondEvalResult rval;
1.10 christos 1048:
1.215 rillig 1049: cpp_skip_hspace(&cond);
1.10 christos 1050:
1.243 rillig 1051: par.plain = plain;
1052: par.evalBare = evalBare;
1053: par.negateEvalBare = negate;
1.277 rillig 1054: par.leftUnquotedOK = leftUnquotedOK;
1.215 rillig 1055: par.p = cond;
1056: par.curr = TOK_NONE;
1.260 rillig 1057: par.printedError = false;
1.10 christos 1058:
1.243 rillig 1059: rval = CondParser_Eval(&par, out_value);
1.56 dsl 1060:
1.215 rillig 1061: if (rval == COND_INVALID && eprint && !par.printedError)
1062: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
1.56 dsl 1063:
1.215 rillig 1064: return rval;
1.56 dsl 1065: }
1066:
1.233 rillig 1067: /*
1068: * Evaluate a condition in a :? modifier, such as
1069: * ${"${VAR}" == value:?yes:no}.
1070: */
1.142 rillig 1071: CondEvalResult
1.260 rillig 1072: Cond_EvalCondition(const char *cond, bool *out_value)
1.142 rillig 1073: {
1.260 rillig 1074: return CondEvalExpression(cond, out_value, true,
1.277 rillig 1075: FuncDefined, false, false, true);
1.142 rillig 1076: }
1.90 rillig 1077:
1.260 rillig 1078: static bool
1.224 rillig 1079: IsEndif(const char *p)
1080: {
1081: return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
1082: p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
1083: }
1084:
1.260 rillig 1085: static bool
1086: DetermineKindOfConditional(const char **pp, bool *out_plain,
1087: bool (**out_evalBare)(size_t, const char *),
1088: bool *out_negate)
1.244 rillig 1089: {
1090: const char *p = *pp;
1091:
1092: p += 2;
1.260 rillig 1093: *out_plain = false;
1.244 rillig 1094: *out_evalBare = FuncDefined;
1.260 rillig 1095: *out_negate = false;
1.244 rillig 1096: if (*p == 'n') {
1097: p++;
1.260 rillig 1098: *out_negate = true;
1.244 rillig 1099: }
1100: if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
1101: p += 3;
1102: } else if (is_token(p, "make", 4)) { /* .ifmake and .ifnmake */
1103: p += 4;
1104: *out_evalBare = FuncMake;
1105: } else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
1.260 rillig 1106: *out_plain = true;
1.244 rillig 1107: } else {
1108: /*
1109: * TODO: Add error message about unknown directive,
1110: * since there is no other known directive that starts
1111: * with 'el' or 'if'.
1112: *
1113: * Example: .elifx 123
1114: */
1.260 rillig 1115: return false;
1.244 rillig 1116: }
1117:
1118: *pp = p;
1.260 rillig 1119: return true;
1.244 rillig 1120: }
1121:
1.233 rillig 1122: /*
1123: * Evaluate the conditional directive in the line, which is one of:
1.207 rillig 1124: *
1.206 rillig 1125: * .if <cond>
1126: * .ifmake <cond>
1127: * .ifnmake <cond>
1128: * .ifdef <cond>
1129: * .ifndef <cond>
1130: * .elif <cond>
1131: * .elifmake <cond>
1132: * .elifnmake <cond>
1133: * .elifdef <cond>
1134: * .elifndef <cond>
1135: * .else
1136: * .endif
1.207 rillig 1137: *
1138: * In these directives, <cond> consists of &&, ||, !, function(arg),
1139: * comparisons, expressions, bare words, numbers and strings, and
1140: * parenthetical groupings thereof.
1.1 cgd 1141: *
1.108 rillig 1142: * Results:
1.207 rillig 1143: * COND_PARSE to continue parsing the lines that follow the
1.260 rillig 1144: * conditional (when <cond> evaluates to true)
1.108 rillig 1145: * COND_SKIP to skip the lines after the conditional
1.260 rillig 1146: * (when <cond> evaluates to false, or when a previous
1.108 rillig 1147: * branch has already been taken)
1.153 rillig 1148: * COND_INVALID if the conditional was not valid, either because of
1.108 rillig 1149: * a syntax error or because some variable was undefined
1150: * or because the condition could not be evaluated
1.1 cgd 1151: */
1.86 rillig 1152: CondEvalResult
1.222 rillig 1153: Cond_EvalLine(const char *line)
1.1 cgd 1154: {
1.215 rillig 1155: typedef enum IfState {
1156:
1.260 rillig 1157: /* None of the previous <cond> evaluated to true. */
1.215 rillig 1158: IFS_INITIAL = 0,
1159:
1.260 rillig 1160: /* The previous <cond> evaluated to true.
1.215 rillig 1161: * The lines following this condition are interpreted. */
1162: IFS_ACTIVE = 1 << 0,
1163:
1164: /* The previous directive was an '.else'. */
1165: IFS_SEEN_ELSE = 1 << 1,
1166:
1.260 rillig 1167: /* One of the previous <cond> evaluated to true. */
1.215 rillig 1168: IFS_WAS_ACTIVE = 1 << 2
1.207 rillig 1169:
1.215 rillig 1170: } IfState;
1.213 rillig 1171:
1.215 rillig 1172: static enum IfState *cond_states = NULL;
1173: static unsigned int cond_states_cap = 128;
1174:
1.260 rillig 1175: bool plain;
1176: bool (*evalBare)(size_t, const char *);
1177: bool negate;
1178: bool isElif;
1179: bool value;
1.215 rillig 1180: IfState state;
1181: const char *p = line;
1182:
1183: if (cond_states == NULL) {
1184: cond_states = bmake_malloc(
1185: cond_states_cap * sizeof *cond_states);
1186: cond_states[0] = IFS_ACTIVE;
1187: }
1188:
1189: p++; /* skip the leading '.' */
1190: cpp_skip_hspace(&p);
1191:
1.224 rillig 1192: if (IsEndif(p)) { /* It is an '.endif'. */
1.225 rillig 1193: if (p[5] != '\0') {
1194: Parse_Error(PARSE_FATAL,
1.282 rillig 1195: "The .endif directive does not take arguments");
1.225 rillig 1196: }
1.224 rillig 1197:
1198: if (cond_depth == cond_min_depth) {
1199: Parse_Error(PARSE_FATAL, "if-less endif");
1200: return COND_PARSE;
1201: }
1202:
1203: /* Return state for previous conditional */
1204: cond_depth--;
1205: return cond_states[cond_depth] & IFS_ACTIVE
1206: ? COND_PARSE : COND_SKIP;
1207: }
1208:
1.215 rillig 1209: /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1210: if (p[0] == 'e') {
1211: if (p[1] != 'l') {
1.224 rillig 1212: /*
1213: * Unknown directive. It might still be a
1.281 rillig 1214: * transformation rule like '.err.txt',
1.224 rillig 1215: * therefore no error message here.
1216: */
1217: return COND_INVALID;
1.215 rillig 1218: }
1.211 rillig 1219:
1.215 rillig 1220: /* Quite likely this is 'else' or 'elif' */
1221: p += 2;
1.216 rillig 1222: if (is_token(p, "se", 2)) { /* It is an 'else'. */
1.215 rillig 1223:
1.226 rillig 1224: if (p[2] != '\0')
1.215 rillig 1225: Parse_Error(PARSE_FATAL,
1.282 rillig 1226: "The .else directive "
1227: "does not take arguments");
1.215 rillig 1228:
1229: if (cond_depth == cond_min_depth) {
1230: Parse_Error(PARSE_FATAL, "if-less else");
1231: return COND_PARSE;
1232: }
1233:
1234: state = cond_states[cond_depth];
1235: if (state == IFS_INITIAL) {
1236: state = IFS_ACTIVE | IFS_SEEN_ELSE;
1237: } else {
1238: if (state & IFS_SEEN_ELSE)
1239: Parse_Error(PARSE_WARNING,
1240: "extra else");
1241: state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1242: }
1243: cond_states[cond_depth] = state;
1.211 rillig 1244:
1.215 rillig 1245: return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
1246: }
1247: /* Assume for now it is an elif */
1.260 rillig 1248: isElif = true;
1.215 rillig 1249: } else
1.260 rillig 1250: isElif = false;
1.211 rillig 1251:
1.215 rillig 1252: if (p[0] != 'i' || p[1] != 'f') {
1253: /*
1254: * Unknown directive. It might still be a transformation rule
1255: * like '.elisp.scm', therefore no error message here.
1256: */
1257: return COND_INVALID; /* Not an ifxxx or elifxxx line */
1.36 dsl 1258: }
1259:
1.244 rillig 1260: if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1.243 rillig 1261: return COND_INVALID;
1.206 rillig 1262:
1.215 rillig 1263: if (isElif) {
1264: if (cond_depth == cond_min_depth) {
1265: Parse_Error(PARSE_FATAL, "if-less elif");
1266: return COND_PARSE;
1267: }
1268: state = cond_states[cond_depth];
1269: if (state & IFS_SEEN_ELSE) {
1270: Parse_Error(PARSE_WARNING, "extra elif");
1271: cond_states[cond_depth] =
1272: IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1273: return COND_SKIP;
1274: }
1275: if (state != IFS_INITIAL) {
1276: cond_states[cond_depth] = IFS_WAS_ACTIVE;
1277: return COND_SKIP;
1278: }
1279: } else {
1280: /* Normal .if */
1281: if (cond_depth + 1 >= cond_states_cap) {
1282: /*
1283: * This is rare, but not impossible.
1284: * In meta mode, dirdeps.mk (only runs at level 0)
1285: * can need more than the default.
1286: */
1287: cond_states_cap += 32;
1288: cond_states = bmake_realloc(cond_states,
1289: cond_states_cap *
1290: sizeof *cond_states);
1291: }
1292: state = cond_states[cond_depth];
1293: cond_depth++;
1294: if (!(state & IFS_ACTIVE)) {
1295: /*
1296: * If we aren't parsing the data,
1297: * treat as always false.
1298: */
1299: cond_states[cond_depth] = IFS_WAS_ACTIVE;
1300: return COND_SKIP;
1301: }
1302: }
1303:
1304: /* And evaluate the conditional expression */
1.243 rillig 1305: if (CondEvalExpression(p, &value, plain, evalBare, negate,
1.277 rillig 1306: true, false) == COND_INVALID) {
1.215 rillig 1307: /* Syntax error in conditional, error message already output. */
1308: /* Skip everything to matching .endif */
1309: /* XXX: An extra '.else' is not detected in this case. */
1310: cond_states[cond_depth] = IFS_WAS_ACTIVE;
1311: return COND_SKIP;
1312: }
1313:
1314: if (!value) {
1315: cond_states[cond_depth] = IFS_INITIAL;
1316: return COND_SKIP;
1317: }
1318: cond_states[cond_depth] = IFS_ACTIVE;
1319: return COND_PARSE;
1.1 cgd 1320: }
1.10 christos 1321:
1.1 cgd 1322: void
1.37 dsl 1323: Cond_restore_depth(unsigned int saved_depth)
1.1 cgd 1324: {
1.215 rillig 1325: unsigned int open_conds = cond_depth - cond_min_depth;
1.37 dsl 1326:
1.215 rillig 1327: if (open_conds != 0 || saved_depth > cond_depth) {
1328: Parse_Error(PARSE_FATAL, "%u open conditional%s",
1329: open_conds, open_conds == 1 ? "" : "s");
1330: cond_depth = cond_min_depth;
1331: }
1.37 dsl 1332:
1.215 rillig 1333: cond_min_depth = saved_depth;
1.37 dsl 1334: }
1335:
1336: unsigned int
1337: Cond_save_depth(void)
1338: {
1.215 rillig 1339: unsigned int depth = cond_min_depth;
1.37 dsl 1340:
1.215 rillig 1341: cond_min_depth = cond_depth;
1342: return depth;
1.1 cgd 1343: }
CVSweb <webmaster@jp.NetBSD.org>