1 /* $OpenBSD: cond.c,v 1.55 2023/09/04 11:35:11 espie Exp $ */
2 /* $NetBSD: cond.c,v 1.7 1996/11/06 17:59:02 christos Exp $ */
3
4 /*
5 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
6 * Copyright (c) 1988, 1989 by Adam de Boor
7 * Copyright (c) 1989 by Berkeley Softworks
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Adam de Boor.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include <ctype.h>
39 #include <stddef.h>
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ohash.h>
45 #include "defines.h"
46 #include "dir.h"
47 #include "buf.h"
48 #include "cond.h"
49 #include "cond_int.h"
50 #include "condhashconsts.h"
51 #include "error.h"
52 #include "var.h"
53 #include "varname.h"
54 #include "targ.h"
55 #include "lowparse.h"
56 #include "str.h"
57 #include "main.h"
58 #include "gnode.h"
59 #include "lst.h"
60
61
62 /* The parsing of conditional expressions is based on this grammar:
63 * E -> F || E
64 * E -> F
65 * F -> T && F
66 * F -> T
67 * T -> defined(variable)
68 * T -> make(target)
69 * T -> exists(file)
70 * T -> empty(varspec)
71 * T -> target(name)
72 * T -> commands(name)
73 * T -> symbol
74 * T -> $(varspec) op value
75 * T -> $(varspec) == "string"
76 * T -> $(varspec) != "string"
77 * T -> "string" == "string"
78 * T -> "string" != "string"
79 * T -> number op number
80 * T -> ( E )
81 * T -> ! T
82 * op -> == | != | > | < | >= | <=
83 *
84 * 'symbol' is some other symbol to which the default function (condDefProc)
85 * is applied.
86 *
87 * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
88 * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
89 * LParen for '(', RParen for ')' and will evaluate the other terminal
90 * symbols, using either the default function or the function given in the
91 * terminal, and return the result as either true or False.
92 *
93 * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */
94 typedef enum {
95 False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err
96 } Token;
97
98 /*-
99 * Structures to handle elegantly the different forms of #if's. The
100 * last two fields are stored in condInvert and condDefProc, respectively.
101 */
102 static bool CondGetArg(const char **, struct Name *,
103 const char *, bool);
104 static bool CondDoDefined(struct Name *);
105 static bool CondDoMake(struct Name *);
106 static bool CondDoExists(struct Name *);
107 static bool CondDoTarget(struct Name *);
108 static bool CondDoTargetWithCommands(struct Name *);
109 static bool CondCvtArg(const char *, double *);
110 static Token CondToken(bool);
111 static Token CondT(bool);
112 static Token CondF(bool);
113 static Token CondE(bool);
114 static Token CondHandleVarSpec(bool);
115 static Token CondHandleDefault(bool);
116 static Token CondHandleComparison(char *, bool, bool);
117 static Token CondHandleString(bool);
118 static Token CondHandleNumber(bool);
119 static const char *find_cond(const char *);
120
121
122 struct If {
123 bool isElse; /* true for else forms */
124 bool doNot; /* true for embedded negation */
125 bool (*defProc)(struct Name *); /* function to apply */
126 };
127
128 static struct If ifs[] = {
129 { false,false, CondDoDefined }, /* if, ifdef */
130 { false,true, CondDoDefined }, /* ifndef */
131 { false,false, CondDoMake }, /* ifmake */
132 { false,true, CondDoMake }, /* ifnmake */
133 { true, false, CondDoDefined }, /* elif, elifdef */
134 { true, true, CondDoDefined }, /* elifndef */
135 { true, false, CondDoMake }, /* elifmake */
136 { true, true, CondDoMake }, /* elifnmake */
137 { true, false, NULL }
138 };
139
140 #define COND_IF_INDEX 0
141 #define COND_IFDEF_INDEX 0
142 #define COND_IFNDEF_INDEX 1
143 #define COND_IFMAKE_INDEX 2
144 #define COND_IFNMAKE_INDEX 3
145 #define COND_ELIF_INDEX 4
146 #define COND_ELIFDEF_INDEX 4
147 #define COND_ELIFNDEF_INDEX 5
148 #define COND_ELIFMAKE_INDEX 6
149 #define COND_ELIFNMAKE_INDEX 7
150 #define COND_ELSE_INDEX 8
151
152 static bool condInvert; /* Invert the default function */
153 static bool (*condDefProc)(struct Name *);
154 /* Default function to apply */
155 static const char *condExpr; /* The expression to parse */
156 static Token condPushBack=None; /* Single push-back token used in parsing */
157
158 #define MAXIF 30 /* greatest depth of #if'ing */
159
160 static struct {
161 bool value;
162 Location origin;
163 } condStack[MAXIF]; /* Stack of conditionals */
164
165 static int condTop = MAXIF; /* Top-most conditional */
166 static int skipIfLevel=0; /* Depth of skipped conditionals */
167 static bool skipLine = false; /* Whether the parse module is skipping lines */
168
169 static const char *
find_cond(const char * p)170 find_cond(const char *p)
171 {
172 for (;;p++) {
173 /* XXX: when *p == '\0', strchr() returns !NULL */
174 if (strchr(" \t)&|$", *p) != NULL)
175 return p;
176 }
177 }
178
179
180 /*-
181 *-----------------------------------------------------------------------
182 * CondGetArg --
183 * Find the argument of a built-in function.
184 *
185 * Results:
186 * true if evaluation went okay
187 *
188 * Side Effects:
189 * The line pointer is set to point to the closing parenthesis of the
190 * function call. The argument is filled.
191 *-----------------------------------------------------------------------
192 */
193 static bool
CondGetArg(const char ** linePtr,struct Name * arg,const char * func,bool parens)194 CondGetArg(const char **linePtr, struct Name *arg, const char *func,
195 bool parens) /* true if arg should be bounded by parens */
196 {
197 const char *cp;
198
199 cp = *linePtr;
200 /* Set things up to return faster in case of problem */
201 arg->s = cp;
202 arg->e = cp;
203 arg->tofree = false;
204
205 /* make and defined are not really keywords, so if CondGetArg doesn't
206 * work...
207 */
208 if (parens) {
209 while (ISSPACE(*cp))
210 cp++;
211 if (*cp == '(')
212 cp++;
213 else
214 return false;
215 }
216
217 if (*cp == '\0')
218 return false;
219
220 while (ISSPACE(*cp))
221 cp++;
222
223 cp = VarName_Get(cp, arg, NULL, true, find_cond);
224
225 while (ISSPACE(*cp))
226 cp++;
227 if (parens) {
228 if (*cp == ')')
229 cp++;
230 else {
231 Parse_Error(PARSE_WARNING,
232 "Missing closing parenthesis for %s()", func);
233 return false;
234 }
235 }
236
237 *linePtr = cp;
238 return true;
239 }
240
241 /*-
242 *-----------------------------------------------------------------------
243 * CondDoDefined --
244 * Handle the 'defined' function for conditionals.
245 *
246 * Results:
247 * true if the given variable is defined.
248 *-----------------------------------------------------------------------
249 */
250 static bool
CondDoDefined(struct Name * arg)251 CondDoDefined(struct Name *arg)
252 {
253 return Var_Definedi(arg->s, arg->e);
254 }
255
256 /*-
257 *-----------------------------------------------------------------------
258 * CondDoMake --
259 * Handle the 'make' function for conditionals.
260 *
261 * Results:
262 * true if the given target is currently being built,
263 * either explicitly on the command line, or implicitly as the
264 * default target.
265 *-----------------------------------------------------------------------
266 */
267 static bool
CondDoMake(struct Name * arg)268 CondDoMake(struct Name *arg)
269 {
270 LstNode ln;
271
272 for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) {
273 char *s = Lst_Datum(ln);
274 if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e))
275 return true;
276 }
277
278 return false;
279 }
280
281 /*-
282 *-----------------------------------------------------------------------
283 * CondDoExists --
284 * See if the given file exists.
285 *
286 * Results:
287 * true if the file exists and false if it does not.
288 *-----------------------------------------------------------------------
289 */
290 static bool
CondDoExists(struct Name * arg)291 CondDoExists(struct Name *arg)
292 {
293 bool result;
294 char *path;
295
296 if (arg->s == arg->e)
297 Parse_Error(PARSE_FATAL, "Empty file name in .if exists()");
298
299 path = Dir_FindFilei(arg->s, arg->e, defaultPath);
300 if (path != NULL) {
301 result = true;
302 free(path);
303 } else {
304 result = false;
305 }
306 return result;
307 }
308
309 /*-
310 *-----------------------------------------------------------------------
311 * CondDoTarget --
312 * See if the given node exists and is an actual target.
313 *
314 * Results:
315 * true if the node exists as a target and false if it does not.
316 *-----------------------------------------------------------------------
317 */
318 static bool
CondDoTarget(struct Name * arg)319 CondDoTarget(struct Name *arg)
320 {
321 GNode *gn;
322
323 gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE);
324 if (gn != NULL && !OP_NOP(gn->type))
325 return true;
326 else
327 return false;
328 }
329
330 /*-
331 *-----------------------------------------------------------------------
332 * CondDoTargetWithCommands --
333 * See if the given node exists and has commands.
334 *
335 * Results:
336 * true if the node is complete and false if it does not.
337 *-----------------------------------------------------------------------
338 */
339 static bool
CondDoTargetWithCommands(struct Name * arg)340 CondDoTargetWithCommands(struct Name *arg)
341 {
342 GNode *gn;
343
344 gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE);
345 if (gn != NULL && !OP_NOP(gn->type) && (gn->type & OP_HAS_COMMANDS))
346 return true;
347 else
348 return false;
349 }
350
351
352 /*-
353 *-----------------------------------------------------------------------
354 * CondCvtArg --
355 * Convert the given number into a double. If the number begins
356 * with 0x, it is interpreted as a hexadecimal integer
357 * and converted to a double from there. All other strings just have
358 * strtod called on them.
359 *
360 * Results:
361 * Sets 'value' to double value of string.
362 * Returns true if the string was a valid number, false o.w.
363 *
364 * Side Effects:
365 * Can change 'value' even if string is not a valid number.
366 *-----------------------------------------------------------------------
367 */
368 static bool
CondCvtArg(const char * str,double * value)369 CondCvtArg(const char *str, double *value)
370 {
371 if (*str == '0' && str[1] == 'x') {
372 long i;
373
374 for (str += 2, i = 0; *str; str++) {
375 int x;
376 if (ISDIGIT(*str))
377 x = *str - '0';
378 else if (ISXDIGIT(*str))
379 x = 10 + *str - (ISUPPER(*str) ? 'A' : 'a');
380 else
381 return false;
382 i = (i << 4) + x;
383 }
384 *value = (double) i;
385 return true;
386 }
387 else {
388 char *eptr;
389 *value = strtod(str, &eptr);
390 return *eptr == '\0';
391 }
392 }
393
394
395 static Token
CondHandleNumber(bool doEval)396 CondHandleNumber(bool doEval)
397 {
398 const char *end;
399 char *lhs;
400
401 end = condExpr;
402 while (*end != '\0' && !ISSPACE(*end) && strchr("!=><", *end) == NULL)
403 end++;
404 lhs = Str_dupi(condExpr, end);
405 condExpr = end;
406 return CondHandleComparison(lhs, true, doEval);
407 }
408
409 static Token
CondHandleVarSpec(bool doEval)410 CondHandleVarSpec(bool doEval)
411 {
412 char *lhs;
413 size_t varSpecLen;
414 bool doFree;
415
416 /* Parse the variable spec and skip over it, saving its
417 * value in lhs. */
418 lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree);
419 if (lhs == var_Error)
420 /* Even if !doEval, we still report syntax errors, which
421 * is what getting var_Error back with !doEval means. */
422 return Err;
423 condExpr += varSpecLen;
424
425 if (*condExpr && !ISSPACE(*condExpr) &&
426 strchr("!=><", *condExpr) == NULL) {
427 BUFFER buf;
428
429 Buf_Init(&buf, 0);
430
431 Buf_AddString(&buf, lhs);
432
433 if (doFree)
434 free(lhs);
435
436 for (;*condExpr && !ISSPACE(*condExpr); condExpr++)
437 Buf_AddChar(&buf, *condExpr);
438
439 lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval);
440 Buf_Destroy(&buf);
441 doFree = true;
442 }
443
444 return CondHandleComparison(lhs, doFree, doEval);
445 }
446
447 static Token
CondHandleString(bool doEval)448 CondHandleString(bool doEval)
449 {
450 char *lhs;
451 const char *begin;
452 BUFFER buf;
453
454 /* find the extent of the string */
455 begin = ++condExpr;
456 while (*condExpr && *condExpr != '"') {
457 condExpr++;
458 }
459
460 Buf_Init(&buf, 0);
461 Buf_Addi(&buf, begin, condExpr);
462 if (*condExpr == '"')
463 condExpr++;
464 lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval);
465 Buf_Destroy(&buf);
466 return CondHandleComparison(lhs, true, doEval);
467 }
468
469 static Token
CondHandleComparison(char * lhs,bool doFree,bool doEval)470 CondHandleComparison(char *lhs, bool doFree, bool doEval)
471 {
472 Token t;
473 const char *rhs;
474 const char *op;
475
476 t = Err;
477 /* Skip whitespace to get to the operator. */
478 while (ISSPACE(*condExpr))
479 condExpr++;
480
481 /* Make sure the operator is a valid one. If it isn't a
482 * known relational operator, pretend we got a
483 * != 0 comparison. */
484 op = condExpr;
485 switch (*condExpr) {
486 case '!':
487 case '=':
488 case '<':
489 case '>':
490 if (condExpr[1] == '=')
491 condExpr += 2;
492 else
493 condExpr += 1;
494 break;
495 default:
496 op = "!=";
497 rhs = "0";
498
499 goto do_compare;
500 }
501 while (ISSPACE(*condExpr))
502 condExpr++;
503 if (*condExpr == '\0') {
504 Parse_Error(PARSE_WARNING,
505 "Missing right-hand-side of operator");
506 goto error;
507 }
508 rhs = condExpr;
509 do_compare:
510 if (*rhs == '"') {
511 /* Doing a string comparison. Only allow == and != for
512 * operators. */
513 char *string;
514 const char *cp;
515 int qt;
516 BUFFER buf;
517
518 do_string_compare:
519 if ((*op != '!' && *op != '=') || op[1] != '=') {
520 Parse_Error(PARSE_WARNING,
521 "String comparison operator should be either == or !=");
522 goto error;
523 }
524
525 Buf_Init(&buf, 0);
526 qt = *rhs == '"' ? 1 : 0;
527
528 for (cp = &rhs[qt]; ((qt && *cp != '"') ||
529 (!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) {
530 if (*cp == '$') {
531 size_t len;
532
533 if (Var_ParseBuffer(&buf, cp, NULL, doEval,
534 &len)) {
535 cp += len;
536 continue;
537 }
538 } else if (*cp == '\\' && cp[1] != '\0')
539 /* Backslash escapes things -- skip over next
540 * character, if it exists. */
541 cp++;
542 Buf_AddChar(&buf, *cp++);
543 }
544
545 string = Buf_Retrieve(&buf);
546
547 if (DEBUG(COND))
548 printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
549 lhs, string, op);
550 /* Null-terminate rhs and perform the comparison.
551 * t is set to the result. */
552 if (*op == '=')
553 t = strcmp(lhs, string) ? False : True;
554 else
555 t = strcmp(lhs, string) ? True : False;
556 free(string);
557 if (rhs == condExpr) {
558 if (!qt && *cp == ')')
559 condExpr = cp;
560 else if (*cp == '\0')
561 condExpr = cp;
562 else
563 condExpr = cp + 1;
564 }
565 } else {
566 /* rhs is either a float or an integer. Convert both the
567 * lhs and the rhs to a double and compare the two. */
568 double left, right;
569 char *string;
570
571 if (!CondCvtArg(lhs, &left))
572 goto do_string_compare;
573 if (*rhs == '$') {
574 size_t len;
575 bool freeIt;
576
577 string = Var_Parse(rhs, NULL, doEval,&len,&freeIt);
578 if (string == var_Error)
579 right = 0.0;
580 else {
581 if (!CondCvtArg(string, &right)) {
582 if (freeIt)
583 free(string);
584 goto do_string_compare;
585 }
586 if (freeIt)
587 free(string);
588 if (rhs == condExpr)
589 condExpr += len;
590 }
591 } else {
592 if (!CondCvtArg(rhs, &right))
593 goto do_string_compare;
594 if (rhs == condExpr) {
595 /* Skip over the right-hand side. */
596 while (!ISSPACE(*condExpr) && *condExpr != '\0')
597 condExpr++;
598 }
599 }
600
601 if (DEBUG(COND))
602 printf("left = %f, right = %f, op = %.2s\n", left,
603 right, op);
604 switch (op[0]) {
605 case '!':
606 if (op[1] != '=') {
607 Parse_Error(PARSE_WARNING, "Unknown operator");
608 goto error;
609 }
610 t = left != right ? True : False;
611 break;
612 case '=':
613 if (op[1] != '=') {
614 Parse_Error(PARSE_WARNING, "Unknown operator");
615 goto error;
616 }
617 t = left == right ? True : False;
618 break;
619 case '<':
620 if (op[1] == '=')
621 t = left <= right ? True : False;
622 else
623 t = left < right ? True : False;
624 break;
625 case '>':
626 if (op[1] == '=')
627 t = left >= right ? True : False;
628 else
629 t = left > right ? True : False;
630 break;
631 }
632 }
633 error:
634 if (doFree)
635 free(lhs);
636 return t;
637 }
638
639 #define S(s) s, sizeof(s)-1
640 static struct operator {
641 const char *s;
642 size_t len;
643 bool (*proc)(struct Name *);
644 } ops[] = {
645 {S("defined"), CondDoDefined},
646 {S("make"), CondDoMake},
647 {S("exists"), CondDoExists},
648 {S("target"), CondDoTarget},
649 {S("commands"), CondDoTargetWithCommands},
650 {NULL, 0, NULL}
651 };
652
653 static Token
CondHandleDefault(bool doEval)654 CondHandleDefault(bool doEval)
655 {
656 bool t;
657 bool (*evalProc)(struct Name *);
658 bool invert = false;
659 struct Name arg;
660 size_t arglen;
661
662 evalProc = NULL;
663 if (strncmp(condExpr, "empty", 5) == 0) {
664 /* Use Var_Parse to parse the spec in parens and return
665 * True if the resulting string is empty. */
666 size_t length;
667 bool doFree;
668 char *val;
669
670 condExpr += 5;
671
672 for (arglen = 0; condExpr[arglen] != '(' &&
673 condExpr[arglen] != '\0';)
674 arglen++;
675
676 if (condExpr[arglen] != '\0') {
677 val = Var_Parse(&condExpr[arglen - 1], NULL,
678 doEval, &length, &doFree);
679 if (val == var_Error)
680 t = Err;
681 else {
682 /* A variable is empty when it just contains
683 * spaces... 4/15/92, christos */
684 char *p;
685 for (p = val; ISSPACE(*p); p++)
686 continue;
687 t = *p == '\0' ? True : False;
688 }
689 if (doFree)
690 free(val);
691 /* Advance condExpr to beyond the closing ). Note that
692 * we subtract one from arglen + length b/c length
693 * is calculated from condExpr[arglen - 1]. */
694 condExpr += arglen + length - 1;
695 return t;
696 } else
697 condExpr -= 5;
698 } else {
699 struct operator *op;
700
701 for (op = ops; op != NULL; op++)
702 if (strncmp(condExpr, op->s, op->len) == 0) {
703 condExpr += op->len;
704 if (CondGetArg(&condExpr, &arg, op->s, true))
705 evalProc = op->proc;
706 else
707 condExpr -= op->len;
708 break;
709 }
710 }
711 if (evalProc == NULL) {
712 /* The symbol is itself the argument to the default
713 * function. We advance condExpr to the end of the symbol
714 * by hand (the next whitespace, closing paren or
715 * binary operator) and set to invert the evaluation
716 * function if condInvert is true. */
717 invert = condInvert;
718 evalProc = condDefProc;
719 /* XXX should we ignore problems now ? */
720 CondGetArg(&condExpr, &arg, "", false);
721 }
722
723 /* Evaluate the argument using the set function. If invert
724 * is true, we invert the sense of the function. */
725 t = (!doEval || (*evalProc)(&arg) ?
726 (invert ? False : True) :
727 (invert ? True : False));
728 VarName_Free(&arg);
729 return t;
730 }
731
732 /*-
733 *-----------------------------------------------------------------------
734 * CondToken --
735 * Return the next token from the input.
736 *
737 * Results:
738 * A Token for the next lexical token in the stream.
739 *
740 * Side Effects:
741 * condPushback will be set back to None if it is used.
742 *-----------------------------------------------------------------------
743 */
744 static Token
CondToken(bool doEval)745 CondToken(bool doEval)
746 {
747
748 if (condPushBack != None) {
749 Token t;
750
751 t = condPushBack;
752 condPushBack = None;
753 return t;
754 }
755
756 while (ISSPACE(*condExpr))
757 condExpr++;
758 switch (*condExpr) {
759 case '(':
760 condExpr++;
761 return LParen;
762 case ')':
763 condExpr++;
764 return RParen;
765 case '|':
766 if (condExpr[1] == '|')
767 condExpr++;
768 condExpr++;
769 return Or;
770 case '&':
771 if (condExpr[1] == '&')
772 condExpr++;
773 condExpr++;
774 return And;
775 case '!':
776 condExpr++;
777 return Not;
778 case '\n':
779 case '\0':
780 return EndOfFile;
781 case '"':
782 return CondHandleString(doEval);
783 case '$':
784 return CondHandleVarSpec(doEval);
785 case '0': case '1': case '2': case '3': case '4':
786 case '5': case '6': case '7': case '8': case '9':
787 return CondHandleNumber(doEval);
788 default:
789 return CondHandleDefault(doEval);
790 }
791 }
792
793 /*-
794 *-----------------------------------------------------------------------
795 * CondT --
796 * Parse a single term in the expression. This consists of a terminal
797 * symbol or Not and a terminal symbol (not including the binary
798 * operators):
799 * T -> defined(variable) | make(target) | exists(file) | symbol
800 * T -> ! T | ( E )
801 *
802 * Results:
803 * True, False or Err.
804 *
805 * Side Effects:
806 * Tokens are consumed.
807 *-----------------------------------------------------------------------
808 */
809 static Token
CondT(bool doEval)810 CondT(bool doEval)
811 {
812 Token t;
813
814 t = CondToken(doEval);
815
816 if (t == EndOfFile)
817 /* If we reached the end of the expression, the expression
818 * is malformed... */
819 t = Err;
820 else if (t == LParen) {
821 /* T -> ( E ). */
822 t = CondE(doEval);
823 if (t != Err)
824 if (CondToken(doEval) != RParen)
825 t = Err;
826 } else if (t == Not) {
827 t = CondT(doEval);
828 if (t == True)
829 t = False;
830 else if (t == False)
831 t = True;
832 }
833 return t;
834 }
835
836 /*-
837 *-----------------------------------------------------------------------
838 * CondF --
839 * Parse a conjunctive factor (nice name, wot?)
840 * F -> T && F | T
841 *
842 * Results:
843 * True, False or Err
844 *
845 * Side Effects:
846 * Tokens are consumed.
847 *-----------------------------------------------------------------------
848 */
849 static Token
CondF(bool doEval)850 CondF(bool doEval)
851 {
852 Token l, o;
853
854 l = CondT(doEval);
855 if (l != Err) {
856 o = CondToken(doEval);
857
858 if (o == And) {
859 /* F -> T && F
860 *
861 * If T is False, the whole thing will be False, but we
862 * have to parse the r.h.s. anyway (to throw it away). If
863 * T is True, the result is the r.h.s., be it an Err or no.
864 * */
865 if (l == True)
866 l = CondF(doEval);
867 else
868 (void)CondF(false);
869 } else
870 /* F -> T. */
871 condPushBack = o;
872 }
873 return l;
874 }
875
876 /*-
877 *-----------------------------------------------------------------------
878 * CondE --
879 * Main expression production.
880 * E -> F || E | F
881 *
882 * Results:
883 * True, False or Err.
884 *
885 * Side Effects:
886 * Tokens are, of course, consumed.
887 *-----------------------------------------------------------------------
888 */
889 static Token
CondE(bool doEval)890 CondE(bool doEval)
891 {
892 Token l, o;
893
894 l = CondF(doEval);
895 if (l != Err) {
896 o = CondToken(doEval);
897
898 if (o == Or) {
899 /* E -> F || E
900 *
901 * A similar thing occurs for ||, except that here we
902 * make sure the l.h.s. is False before we bother to
903 * evaluate the r.h.s. Once again, if l is False, the
904 * result is the r.h.s. and once again if l is True, we
905 * parse the r.h.s. to throw it away. */
906 if (l == False)
907 l = CondE(doEval);
908 else
909 (void)CondE(false);
910 } else
911 /* E -> F. */
912 condPushBack = o;
913 }
914 return l;
915 }
916
917 /* Evaluate conditional in line.
918 * returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE,
919 * COND_ISUNDEF.
920 * A conditional line looks like this:
921 * <cond-type> <expr>
922 * where <cond-type> is any of if, ifmake, ifnmake, ifdef,
923 * ifndef, elif, elifmake, elifnmake, elifdef, elifndef
924 * and <expr> consists of &&, ||, !, make(target), defined(variable)
925 * and parenthetical groupings thereof.
926 */
927 int
Cond_Eval(const char * line)928 Cond_Eval(const char *line)
929 {
930 /* find end of keyword */
931 const char *end;
932 uint32_t k;
933 size_t len;
934 struct If *ifp;
935 bool value = false;
936 int level; /* Level at which to report errors. */
937
938 level = PARSE_FATAL;
939
940 for (end = line; ISLOWER(*end); end++)
941 ;
942 /* quick path: recognize special targets early on */
943 if (*end == '.' || *end == ':')
944 return COND_INVALID;
945 len = end - line;
946 k = ohash_interval(line, &end);
947 switch(k % MAGICSLOTS2) {
948 case K_COND_IF % MAGICSLOTS2:
949 if (k == K_COND_IF && len == strlen(COND_IF) &&
950 strncmp(line, COND_IF, len) == 0) {
951 ifp = ifs + COND_IF_INDEX;
952 } else
953 return COND_INVALID;
954 break;
955 case K_COND_IFDEF % MAGICSLOTS2:
956 if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) &&
957 strncmp(line, COND_IFDEF, len) == 0) {
958 ifp = ifs + COND_IFDEF_INDEX;
959 } else
960 return COND_INVALID;
961 break;
962 case K_COND_IFNDEF % MAGICSLOTS2:
963 if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) &&
964 strncmp(line, COND_IFNDEF, len) == 0) {
965 ifp = ifs + COND_IFNDEF_INDEX;
966 } else
967 return COND_INVALID;
968 break;
969 case K_COND_IFMAKE % MAGICSLOTS2:
970 if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) &&
971 strncmp(line, COND_IFMAKE, len) == 0) {
972 ifp = ifs + COND_IFMAKE_INDEX;
973 } else
974 return COND_INVALID;
975 break;
976 case K_COND_IFNMAKE % MAGICSLOTS2:
977 if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) &&
978 strncmp(line, COND_IFNMAKE, len) == 0) {
979 ifp = ifs + COND_IFNMAKE_INDEX;
980 } else
981 return COND_INVALID;
982 break;
983 case K_COND_ELIF % MAGICSLOTS2:
984 if (k == K_COND_ELIF && len == strlen(COND_ELIF) &&
985 strncmp(line, COND_ELIF, len) == 0) {
986 ifp = ifs + COND_ELIF_INDEX;
987 } else
988 return COND_INVALID;
989 break;
990 case K_COND_ELIFDEF % MAGICSLOTS2:
991 if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) &&
992 strncmp(line, COND_ELIFDEF, len) == 0) {
993 ifp = ifs + COND_ELIFDEF_INDEX;
994 } else
995 return COND_INVALID;
996 break;
997 case K_COND_ELIFNDEF % MAGICSLOTS2:
998 if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) &&
999 strncmp(line, COND_ELIFNDEF, len) == 0) {
1000 ifp = ifs + COND_ELIFNDEF_INDEX;
1001 } else
1002 return COND_INVALID;
1003 break;
1004 case K_COND_ELIFMAKE % MAGICSLOTS2:
1005 if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) &&
1006 strncmp(line, COND_ELIFMAKE, len) == 0) {
1007 ifp = ifs + COND_ELIFMAKE_INDEX;
1008 } else
1009 return COND_INVALID;
1010 break;
1011 case K_COND_ELIFNMAKE % MAGICSLOTS2:
1012 if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) &&
1013 strncmp(line, COND_ELIFNMAKE, len) == 0) {
1014 ifp = ifs + COND_ELIFNMAKE_INDEX;
1015 } else
1016 return COND_INVALID;
1017 break;
1018 case K_COND_ELSE % MAGICSLOTS2:
1019 /* valid conditional whose value is the inverse
1020 * of the previous if we parsed. */
1021 if (k == K_COND_ELSE && len == strlen(COND_ELSE) &&
1022 strncmp(line, COND_ELSE, len) == 0) {
1023 if (condTop == MAXIF) {
1024 Parse_Error(level, "if-less else");
1025 return COND_INVALID;
1026 } else if (skipIfLevel == 0) {
1027 value = !condStack[condTop].value;
1028 ifp = ifs + COND_ELSE_INDEX;
1029 } else
1030 return COND_SKIP;
1031 } else
1032 return COND_INVALID;
1033 break;
1034 case K_COND_ENDIF % MAGICSLOTS2:
1035 if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) &&
1036 strncmp(line, COND_ENDIF, len) == 0) {
1037 /* End of a conditional section. If skipIfLevel is
1038 * non-zero, that conditional was skipped, so lines
1039 * following it should also be skipped. Hence, we
1040 * return COND_SKIP. Otherwise, the conditional was
1041 * read so succeeding lines should be parsed (think
1042 * about it...) so we return COND_PARSE, unless this
1043 * endif isn't paired with a decent if. */
1044 if (skipIfLevel != 0) {
1045 skipIfLevel--;
1046 return COND_SKIP;
1047 } else {
1048 if (condTop == MAXIF) {
1049 Parse_Error(level, "if-less endif");
1050 return COND_INVALID;
1051 } else {
1052 skipLine = false;
1053 condTop++;
1054 return COND_PARSE;
1055 }
1056 }
1057 } else
1058 return COND_INVALID;
1059 break;
1060
1061 /* Recognize other keywords there, to simplify parser's task */
1062 case K_COND_FOR % MAGICSLOTS2:
1063 if (k == K_COND_FOR && len == strlen(COND_FOR) &&
1064 strncmp(line, COND_FOR, len) == 0)
1065 return COND_ISFOR;
1066 else
1067 return COND_INVALID;
1068 case K_COND_UNDEF % MAGICSLOTS2:
1069 if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) &&
1070 strncmp(line, COND_UNDEF, len) == 0)
1071 return COND_ISUNDEF;
1072 else
1073 return COND_INVALID;
1074 case K_COND_POISON % MAGICSLOTS2:
1075 if (k == K_COND_POISON && len == strlen(COND_POISON) &&
1076 strncmp(line, COND_POISON, len) == 0)
1077 return COND_ISPOISON;
1078 else
1079 return COND_INVALID;
1080 case K_COND_INCLUDE % MAGICSLOTS2:
1081 if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) &&
1082 strncmp(line, COND_INCLUDE, len) == 0)
1083 return COND_ISINCLUDE;
1084 else
1085 return COND_INVALID;
1086 default:
1087 /* Not a valid conditional type. No error... */
1088 return COND_INVALID;
1089 }
1090
1091 if (ifp->isElse) {
1092 if (condTop == MAXIF) {
1093 Parse_Error(level, "if-less elif");
1094 return COND_INVALID;
1095 } else if (skipIfLevel != 0 || condStack[condTop].value) {
1096 /*
1097 * Skip if we're meant to or is an else-type
1098 * conditional and previous corresponding one was
1099 * evaluated to true.
1100 */
1101 skipLine = true;
1102 return COND_SKIP;
1103 }
1104 } else if (skipLine) {
1105 /* Don't even try to evaluate a conditional that's not an else
1106 * if we're skipping things... */
1107 skipIfLevel++;
1108 return COND_SKIP;
1109 } else
1110 condTop--;
1111
1112 if (condTop < 0) {
1113 /* This is the one case where we can definitely proclaim a fatal
1114 * error. If we don't, we're hosed. */
1115 Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",
1116 MAXIF);
1117 condTop = 0;
1118 return COND_INVALID;
1119 }
1120
1121 if (ifp->defProc) {
1122 /* Initialize file-global variables for parsing. */
1123 condDefProc = ifp->defProc;
1124 condInvert = ifp->doNot;
1125
1126 line += len;
1127
1128 while (*line == ' ' || *line == '\t')
1129 line++;
1130
1131 condExpr = line;
1132 condPushBack = None;
1133
1134 switch (CondE(true)) {
1135 case True:
1136 if (CondToken(true) == EndOfFile) {
1137 value = true;
1138 break;
1139 }
1140 goto err;
1141 /* FALLTHROUGH */
1142 case False:
1143 if (CondToken(true) == EndOfFile) {
1144 value = false;
1145 break;
1146 }
1147 /* FALLTHROUGH */
1148 case Err:
1149 err:
1150 Parse_Error(level, "Malformed conditional (%s)", line);
1151 return COND_INVALID;
1152 default:
1153 break;
1154 }
1155 }
1156
1157 condStack[condTop].value = value;
1158 Parse_FillLocation(&condStack[condTop].origin);
1159 skipLine = !value;
1160 return value ? COND_PARSE : COND_SKIP;
1161 }
1162
1163 void
Cond_End(void)1164 Cond_End(void)
1165 {
1166 int i;
1167
1168 if (condTop != MAXIF) {
1169 Parse_Error(PARSE_FATAL, "%s%d open conditional%s",
1170 condTop == 0 ? "at least ": "", MAXIF-condTop,
1171 MAXIF-condTop == 1 ? "" : "s");
1172 for (i = MAXIF-1; i >= condTop; i--) {
1173 fprintf(stderr, "\t(%s:%lu)\n",
1174 condStack[i].origin.fname,
1175 condStack[i].origin.lineno);
1176 }
1177 }
1178 condTop = MAXIF;
1179 }
1180