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