cond.c revision 1.59 1 /* $NetBSD: cond.c,v 1.59 2009/01/30 23:07:17 dsl Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
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 /*
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
72 #ifndef MAKE_NATIVE
73 static char rcsid[] = "$NetBSD: cond.c,v 1.59 2009/01/30 23:07:17 dsl Exp $";
74 #else
75 #include <sys/cdefs.h>
76 #ifndef lint
77 #if 0
78 static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";
79 #else
80 __RCSID("$NetBSD: cond.c,v 1.59 2009/01/30 23:07:17 dsl Exp $");
81 #endif
82 #endif /* not lint */
83 #endif
84
85 /*-
86 * cond.c --
87 * Functions to handle conditionals in a makefile.
88 *
89 * Interface:
90 * Cond_Eval Evaluate the conditional in the passed line.
91 *
92 */
93
94 #include <ctype.h>
95 #include <errno.h> /* For strtoul() error checking */
96
97 #include "make.h"
98 #include "hash.h"
99 #include "dir.h"
100 #include "buf.h"
101
102 /*
103 * The parsing of conditional expressions is based on this grammar:
104 * E -> F || E
105 * E -> F
106 * F -> T && F
107 * F -> T
108 * T -> defined(variable)
109 * T -> make(target)
110 * T -> exists(file)
111 * T -> empty(varspec)
112 * T -> target(name)
113 * T -> commands(name)
114 * T -> symbol
115 * T -> $(varspec) op value
116 * T -> $(varspec) == "string"
117 * T -> $(varspec) != "string"
118 * T -> "string"
119 * T -> ( E )
120 * T -> ! T
121 * op -> == | != | > | < | >= | <=
122 *
123 * 'symbol' is some other symbol to which the default function (condDefProc)
124 * is applied.
125 *
126 * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
127 * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||', TOK_NOT for '!',
128 * TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate the other terminal
129 * symbols, using either the default function or the function given in the
130 * terminal, and return the result as either TOK_TRUE or TOK_FALSE.
131 *
132 * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on error.
133 */
134 typedef enum {
135 TOK_AND, TOK_OR, TOK_NOT, TOK_TRUE, TOK_FALSE, TOK_LPAREN, TOK_RPAREN,
136 TOK_EOF, TOK_NONE, TOK_ERROR
137 } Token;
138
139 /*-
140 * Structures to handle elegantly the different forms of #if's. The
141 * last two fields are stored in condInvert and condDefProc, respectively.
142 */
143 static void CondPushBack(Token);
144 static int CondGetArg(char **, char **, const char *);
145 static Boolean CondDoDefined(int, const char *);
146 static int CondStrMatch(const void *, const void *);
147 static Boolean CondDoMake(int, const char *);
148 static Boolean CondDoExists(int, const char *);
149 static Boolean CondDoTarget(int, const char *);
150 static Boolean CondDoCommands(int, const char *);
151 static Boolean CondCvtArg(char *, double *);
152 static Token CondToken(Boolean);
153 static Token CondT(Boolean);
154 static Token CondF(Boolean);
155 static Token CondE(Boolean);
156 static int do_Cond_EvalExpression(Boolean *);
157
158 static const struct If {
159 const char *form; /* Form of if */
160 int formlen; /* Length of form */
161 Boolean doNot; /* TRUE if default function should be negated */
162 Boolean (*defProc)(int, const char *); /* Default function to apply */
163 } ifs[] = {
164 { "def", 3, FALSE, CondDoDefined },
165 { "ndef", 4, TRUE, CondDoDefined },
166 { "make", 4, FALSE, CondDoMake },
167 { "nmake", 5, TRUE, CondDoMake },
168 { "", 0, FALSE, CondDoDefined },
169 { NULL, 0, FALSE, NULL }
170 };
171
172 static const struct If *if_info; /* Info for current statement */
173 static char *condExpr; /* The expression to parse */
174 static Token condPushBack=TOK_NONE; /* Single push-back token used in
175 * parsing */
176
177 static unsigned int cond_depth = 0; /* current .if nesting level */
178 static unsigned int cond_min_depth = 0; /* depth at makefile open */
179
180 static int
181 istoken(const char *str, const char *tok, size_t len)
182 {
183 return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]);
184 }
185
186 /*-
187 *-----------------------------------------------------------------------
188 * CondPushBack --
189 * Push back the most recent token read. We only need one level of
190 * this, so the thing is just stored in 'condPushback'.
191 *
192 * Input:
193 * t Token to push back into the "stream"
194 *
195 * Results:
196 * None.
197 *
198 * Side Effects:
199 * condPushback is overwritten.
200 *
201 *-----------------------------------------------------------------------
202 */
203 static void
204 CondPushBack(Token t)
205 {
206 condPushBack = t;
207 }
208
209 /*-
211 *-----------------------------------------------------------------------
212 * CondGetArg --
213 * Find the argument of a built-in function.
214 *
215 * Input:
216 * parens TRUE if arg should be bounded by parens
217 *
218 * Results:
219 * The length of the argument and the address of the argument.
220 *
221 * Side Effects:
222 * The pointer is set to point to the closing parenthesis of the
223 * function call.
224 *
225 *-----------------------------------------------------------------------
226 */
227 static int
228 CondGetArg(char **linePtr, char **argPtr, const char *func)
229 {
230 char *cp;
231 int argLen;
232 Buffer buf;
233 int paren_depth;
234 char ch;
235
236 cp = *linePtr;
237 if (func != NULL)
238 /* Skip opening '(' - verfied by caller */
239 cp++;
240
241 if (*cp == '\0') {
242 /*
243 * No arguments whatsoever. Because 'make' and 'defined' aren't really
244 * "reserved words", we don't print a message. I think this is better
245 * than hitting the user with a warning message every time s/he uses
246 * the word 'make' or 'defined' at the beginning of a symbol...
247 */
248 *argPtr = NULL;
249 return (0);
250 }
251
252 while (*cp == ' ' || *cp == '\t') {
253 cp++;
254 }
255
256 /*
257 * Create a buffer for the argument and start it out at 16 characters
258 * long. Why 16? Why not?
259 */
260 Buf_Init(&buf, 16);
261
262 paren_depth = 0;
263 for (;;) {
264 ch = *cp;
265 if (ch == 0 || ch == ' ' || ch == '\t')
266 break;
267 if ((ch == '&' || ch == '|') && paren_depth == 0)
268 break;
269 if (*cp == '$') {
270 /*
271 * Parse the variable spec and install it as part of the argument
272 * if it's valid. We tell Var_Parse to complain on an undefined
273 * variable, so we don't do it too. Nor do we return an error,
274 * though perhaps we should...
275 */
276 char *cp2;
277 int len;
278 void *freeIt;
279
280 cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &freeIt);
281 Buf_AddBytes(&buf, strlen(cp2), cp2);
282 if (freeIt)
283 free(freeIt);
284 cp += len;
285 continue;
286 }
287 if (ch == '(')
288 paren_depth++;
289 else
290 if (ch == ')' && --paren_depth < 0)
291 break;
292 Buf_AddByte(&buf, *cp);
293 cp++;
294 }
295
296 *argPtr = Buf_GetAll(&buf, &argLen);
297 Buf_Destroy(&buf, FALSE);
298
299 while (*cp == ' ' || *cp == '\t') {
300 cp++;
301 }
302
303 if (func != NULL && *cp++ != ')') {
304 Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
305 func);
306 return (0);
307 }
308
309 *linePtr = cp;
310 return (argLen);
311 }
312
313 /*-
315 *-----------------------------------------------------------------------
316 * CondDoDefined --
317 * Handle the 'defined' function for conditionals.
318 *
319 * Results:
320 * TRUE if the given variable is defined.
321 *
322 * Side Effects:
323 * None.
324 *
325 *-----------------------------------------------------------------------
326 */
327 static Boolean
328 CondDoDefined(int argLen, const char *arg)
329 {
330 char *p1;
331 Boolean result;
332
333 if (Var_Value(arg, VAR_CMD, &p1) != NULL) {
334 result = TRUE;
335 } else {
336 result = FALSE;
337 }
338 if (p1)
339 free(p1);
340 return (result);
341 }
342
343 /*-
345 *-----------------------------------------------------------------------
346 * CondStrMatch --
347 * Front-end for Str_Match so it returns 0 on match and non-zero
348 * on mismatch. Callback function for CondDoMake via Lst_Find
349 *
350 * Results:
351 * 0 if string matches pattern
352 *
353 * Side Effects:
354 * None
355 *
356 *-----------------------------------------------------------------------
357 */
358 static int
359 CondStrMatch(const void *string, const void *pattern)
360 {
361 return(!Str_Match(string, pattern));
362 }
363
364 /*-
366 *-----------------------------------------------------------------------
367 * CondDoMake --
368 * Handle the 'make' function for conditionals.
369 *
370 * Results:
371 * TRUE if the given target is being made.
372 *
373 * Side Effects:
374 * None.
375 *
376 *-----------------------------------------------------------------------
377 */
378 static Boolean
379 CondDoMake(int argLen, const char *arg)
380 {
381 return Lst_Find(create, arg, CondStrMatch) != NULL;
382 }
383
384 /*-
386 *-----------------------------------------------------------------------
387 * CondDoExists --
388 * See if the given file exists.
389 *
390 * Results:
391 * TRUE if the file exists and FALSE if it does not.
392 *
393 * Side Effects:
394 * None.
395 *
396 *-----------------------------------------------------------------------
397 */
398 static Boolean
399 CondDoExists(int argLen, const char *arg)
400 {
401 Boolean result;
402 char *path;
403
404 path = Dir_FindFile(arg, dirSearchPath);
405 if (path != NULL) {
406 result = TRUE;
407 free(path);
408 } else {
409 result = FALSE;
410 }
411 if (DEBUG(COND)) {
412 fprintf(debug_file, "exists(%s) result is \"%s\"\n",
413 arg, path ? path : "");
414 }
415 return (result);
416 }
417
418 /*-
420 *-----------------------------------------------------------------------
421 * CondDoTarget --
422 * See if the given node exists and is an actual target.
423 *
424 * Results:
425 * TRUE if the node exists as a target and FALSE if it does not.
426 *
427 * Side Effects:
428 * None.
429 *
430 *-----------------------------------------------------------------------
431 */
432 static Boolean
433 CondDoTarget(int argLen, const char *arg)
434 {
435 GNode *gn;
436
437 gn = Targ_FindNode(arg, TARG_NOCREATE);
438 return (gn != NULL) && !OP_NOP(gn->type);
439 }
440
441 /*-
442 *-----------------------------------------------------------------------
443 * CondDoCommands --
444 * See if the given node exists and is an actual target with commands
445 * associated with it.
446 *
447 * Results:
448 * TRUE if the node exists as a target and has commands associated with
449 * it and FALSE if it does not.
450 *
451 * Side Effects:
452 * None.
453 *
454 *-----------------------------------------------------------------------
455 */
456 static Boolean
457 CondDoCommands(int argLen, const char *arg)
458 {
459 GNode *gn;
460
461 gn = Targ_FindNode(arg, TARG_NOCREATE);
462 return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
463 }
464
465 /*-
467 *-----------------------------------------------------------------------
468 * CondCvtArg --
469 * Convert the given number into a double.
470 * We try a base 10 or 16 integer conversion first, if that fails
471 * then we try a floating point conversion instead.
472 *
473 * Results:
474 * Sets 'value' to double value of string.
475 * Returns 'true' if the convertion suceeded
476 *
477 *-----------------------------------------------------------------------
478 */
479 static Boolean
480 CondCvtArg(char *str, double *value)
481 {
482 char *eptr, ech;
483 unsigned long l_val;
484 double d_val;
485
486 errno = 0;
487 l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
488 ech = *eptr;
489 if (ech == 0 && errno != ERANGE) {
490 d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
491 } else {
492 if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
493 return FALSE;
494 d_val = strtod(str, &eptr);
495 if (*eptr)
496 return FALSE;
497 }
498
499 *value = d_val;
500 return TRUE;
501 }
502
503 /*-
504 *-----------------------------------------------------------------------
505 * CondGetString --
506 * Get a string from a variable reference or an optionally quoted
507 * string. This is called for the lhs and rhs of string compares.
508 *
509 * Results:
510 * Sets freeIt if needed,
511 * Sets quoted if string was quoted,
512 * Returns NULL on error,
513 * else returns string - absent any quotes.
514 *
515 * Side Effects:
516 * Moves condExpr to end of this token.
517 *
518 *
519 *-----------------------------------------------------------------------
520 */
521 /* coverity:[+alloc : arg-*2] */
522 static char *
523 CondGetString(Boolean doEval, Boolean *quoted, void **freeIt)
524 {
525 Buffer buf;
526 char *cp;
527 char *str;
528 int len;
529 int qt;
530 char *start;
531
532 Buf_Init(&buf, 0);
533 str = NULL;
534 *freeIt = NULL;
535 *quoted = qt = *condExpr == '"' ? 1 : 0;
536 if (qt)
537 condExpr++;
538 for (start = condExpr; *condExpr && str == NULL; condExpr++) {
539 switch (*condExpr) {
540 case '\\':
541 if (condExpr[1] != '\0') {
542 condExpr++;
543 Buf_AddByte(&buf, *condExpr);
544 }
545 break;
546 case '"':
547 if (qt) {
548 condExpr++; /* we don't want the quotes */
549 goto got_str;
550 } else
551 Buf_AddByte(&buf, *condExpr); /* likely? */
552 break;
553 case ')':
554 case '!':
555 case '=':
556 case '>':
557 case '<':
558 case ' ':
559 case '\t':
560 if (!qt)
561 goto got_str;
562 else
563 Buf_AddByte(&buf, *condExpr);
564 break;
565 case '$':
566 /* if we are in quotes, then an undefined variable is ok */
567 str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval),
568 &len, freeIt);
569 if (str == var_Error) {
570 if (*freeIt) {
571 free(*freeIt);
572 *freeIt = NULL;
573 }
574 /*
575 * Even if !doEval, we still report syntax errors, which
576 * is what getting var_Error back with !doEval means.
577 */
578 str = NULL;
579 goto cleanup;
580 }
581 condExpr += len;
582 /*
583 * If the '$' was first char (no quotes), and we are
584 * followed by space, the operator or end of expression,
585 * we are done.
586 */
587 if ((condExpr == start + len) &&
588 (*condExpr == '\0' ||
589 isspace((unsigned char) *condExpr) ||
590 strchr("!=><)", *condExpr))) {
591 goto cleanup;
592 }
593 /*
594 * Nope, we better copy str to buf
595 */
596 for (cp = str; *cp; cp++) {
597 Buf_AddByte(&buf, *cp);
598 }
599 if (*freeIt) {
600 free(*freeIt);
601 *freeIt = NULL;
602 }
603 str = NULL; /* not finished yet */
604 condExpr--; /* don't skip over next char */
605 break;
606 default:
607 Buf_AddByte(&buf, *condExpr);
608 break;
609 }
610 }
611 got_str:
612 str = Buf_GetAll(&buf, NULL);
613 *freeIt = str;
614 cleanup:
615 Buf_Destroy(&buf, FALSE);
616 return str;
617 }
618
619 /*-
621 *-----------------------------------------------------------------------
622 * CondToken --
623 * Return the next token from the input.
624 *
625 * Results:
626 * A Token for the next lexical token in the stream.
627 *
628 * Side Effects:
629 * condPushback will be set back to TOK_NONE if it is used.
630 *
631 *-----------------------------------------------------------------------
632 */
633 static Token
634 compare_expression(Boolean doEval)
635 {
636 Token t;
637 char *lhs;
638 char *rhs;
639 char *op;
640 void *lhsFree;
641 void *rhsFree;
642 Boolean lhsQuoted;
643 Boolean rhsQuoted;
644 double left, right;
645
646 t = TOK_ERROR;
647 rhs = NULL;
648 lhsFree = rhsFree = FALSE;
649 lhsQuoted = rhsQuoted = FALSE;
650
651 /*
652 * Parse the variable spec and skip over it, saving its
653 * value in lhs.
654 */
655 lhs = CondGetString(doEval, &lhsQuoted, &lhsFree);
656 if (!lhs)
657 goto done;
658
659 /*
660 * Skip whitespace to get to the operator
661 */
662 while (isspace((unsigned char) *condExpr))
663 condExpr++;
664
665 /*
666 * Make sure the operator is a valid one. If it isn't a
667 * known relational operator, pretend we got a
668 * != 0 comparison.
669 */
670 op = condExpr;
671 switch (*condExpr) {
672 case '!':
673 case '=':
674 case '<':
675 case '>':
676 if (condExpr[1] == '=') {
677 condExpr += 2;
678 } else {
679 condExpr += 1;
680 }
681 break;
682 default:
683 if (!doEval) {
684 t = TOK_FALSE;
685 goto done;
686 }
687 /* For .ifxxx "..." check for non-empty string. */
688 if (lhsQuoted) {
689 t = lhs[0] != 0 ? TOK_TRUE : TOK_FALSE;
690 goto done;
691 }
692 /* For .ifxxx <number> compare against zero */
693 if (CondCvtArg(lhs, &left)) {
694 t = left != 0.0 ? TOK_TRUE : TOK_FALSE;
695 goto done;
696 }
697 /* For .if ${...} check for non-empty string (defProc is ifdef). */
698 if (if_info->form[0] == 0) {
699 t = lhs[0] != 0 ? TOK_TRUE : TOK_FALSE;
700 goto done;
701 }
702 /* Otherwise action default test ... */
703 t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot ? TOK_TRUE : TOK_FALSE;
704 goto done;
705 }
706
707 while (isspace((unsigned char)*condExpr))
708 condExpr++;
709
710 if (*condExpr == '\0') {
711 Parse_Error(PARSE_WARNING,
712 "Missing right-hand-side of operator");
713 goto done;
714 }
715
716 rhs = CondGetString(doEval, &rhsQuoted, &rhsFree);
717 if (!rhs)
718 goto done;
719
720 if (rhsQuoted || lhsQuoted) {
721 do_string_compare:
722 if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
723 Parse_Error(PARSE_WARNING,
724 "String comparison operator should be either == or !=");
725 goto done;
726 }
727
728 if (DEBUG(COND)) {
729 fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
730 lhs, rhs, op);
731 }
732 /*
733 * Null-terminate rhs and perform the comparison.
734 * t is set to the result.
735 */
736 if (*op == '=') {
737 t = strcmp(lhs, rhs) ? TOK_FALSE : TOK_TRUE;
738 } else {
739 t = strcmp(lhs, rhs) ? TOK_TRUE : TOK_FALSE;
740 }
741 } else {
742 /*
743 * rhs is either a float or an integer. Convert both the
744 * lhs and the rhs to a double and compare the two.
745 */
746
747 if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
748 goto do_string_compare;
749
750 if (DEBUG(COND)) {
751 fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
752 right, op);
753 }
754 switch(op[0]) {
755 case '!':
756 if (op[1] != '=') {
757 Parse_Error(PARSE_WARNING,
758 "Unknown operator");
759 goto done;
760 }
761 t = (left != right ? TOK_TRUE : TOK_FALSE);
762 break;
763 case '=':
764 if (op[1] != '=') {
765 Parse_Error(PARSE_WARNING,
766 "Unknown operator");
767 goto done;
768 }
769 t = (left == right ? TOK_TRUE : TOK_FALSE);
770 break;
771 case '<':
772 if (op[1] == '=') {
773 t = (left <= right ? TOK_TRUE : TOK_FALSE);
774 } else {
775 t = (left < right ? TOK_TRUE : TOK_FALSE);
776 }
777 break;
778 case '>':
779 if (op[1] == '=') {
780 t = (left >= right ? TOK_TRUE : TOK_FALSE);
781 } else {
782 t = (left > right ? TOK_TRUE : TOK_FALSE);
783 }
784 break;
785 }
786 }
787
788 done:
789 if (lhsFree)
790 free(lhsFree);
791 if (rhsFree)
792 free(rhsFree);
793 return t;
794 }
795
796 static int
797 get_mpt_arg(char **linePtr, char **argPtr, const char *func)
798 {
799 /*
800 * Use Var_Parse to parse the spec in parens and return
801 * TOK_TRUE if the resulting string is empty.
802 */
803 int length;
804 void *freeIt;
805 char *val;
806 char *cp = *linePtr;
807
808 /* We do all the work here and return the result as the length */
809 *argPtr = NULL;
810
811 val = Var_Parse(cp - 1, VAR_CMD, FALSE, &length, &freeIt);
812 /*
813 * Advance *linePtr to beyond the closing ). Note that
814 * we subtract one because 'length' is calculated from 'cp - 1'.
815 */
816 *linePtr = cp - 1 + length;
817
818 if (val == var_Error) {
819 free(freeIt);
820 return -1;
821 }
822
823 /* A variable is empty when it just contains spaces... 4/15/92, christos */
824 while (isspace(*(unsigned char *)val))
825 val++;
826
827 /*
828 * For consistency with the other functions we can't generate the
829 * true/false here.
830 */
831 length = *val ? 2 : 1;
832 if (freeIt)
833 free(freeIt);
834 return length;
835 }
836
837 static Boolean
838 CondDoEmpty(int arglen, const char *arg)
839 {
840 return arglen == 1;
841 }
842
843 static Token
844 compare_function(Boolean doEval)
845 {
846 static const struct fn_def {
847 const char *fn_name;
848 int fn_name_len;
849 int (*fn_getarg)(char **, char **, const char *);
850 Boolean (*fn_proc)(int, const char *);
851 } fn_defs[] = {
852 { "defined", 7, CondGetArg, CondDoDefined },
853 { "make", 4, CondGetArg, CondDoMake },
854 { "exists", 6, CondGetArg, CondDoExists },
855 { "empty", 5, get_mpt_arg, CondDoEmpty },
856 { "target", 6, CondGetArg, CondDoTarget },
857 { "commands", 8, CondGetArg, CondDoCommands },
858 { NULL, 0, NULL, NULL },
859 };
860 const struct fn_def *fn_def;
861 Token t;
862 char *arg = NULL;
863 int arglen;
864 char *cp = condExpr;
865 char *cp1;
866
867 for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
868 if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
869 continue;
870 cp += fn_def->fn_name_len;
871 /* There can only be whitespace before the '(' */
872 while (isspace(*(unsigned char *)cp))
873 cp++;
874 if (*cp != '(')
875 break;
876
877 arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name);
878 if (arglen <= 0) {
879 condExpr = cp;
880 return arglen < 0 ? TOK_ERROR : TOK_FALSE;
881 }
882 /* Evaluate the argument using the required function. */
883 t = !doEval || fn_def->fn_proc(arglen, arg) ? TOK_TRUE : TOK_FALSE;
884 if (arg)
885 free(arg);
886 condExpr = cp;
887 return t;
888 }
889
890 /* Push anything numeric through the compare expression */
891 cp = condExpr;
892 if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
893 return compare_expression(doEval);
894
895 /*
896 * Most likely we have a naked token to apply the default function to.
897 * However ".if a == b" gets here when the "a" is unquoted and doesn't
898 * start with a '$'. This surprises people.
899 * If what follows the function argument is a '=' or '!' then the syntax
900 * would be invalid if we did "defined(a)" - so instead treat as an
901 * expression.
902 */
903 arglen = CondGetArg(&cp, &arg, NULL);
904 for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++)
905 continue;
906 if (*cp1 == '=' || *cp1 == '!')
907 return compare_expression(doEval);
908 condExpr = cp;
909
910 /*
911 * Evaluate the argument using the default function.
912 * This path always treats .if as .ifdef. To get here the character
913 * after .if must have been taken literally, so the argument cannot
914 * be empty - even if it contained a variable expansion.
915 */
916 t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot ? TOK_TRUE : TOK_FALSE;
917 if (arg)
918 free(arg);
919 return t;
920 }
921
922 static Token
923 CondToken(Boolean doEval)
924 {
925 Token t;
926
927 t = condPushBack;
928 if (t != TOK_NONE) {
929 condPushBack = TOK_NONE;
930 return t;
931 }
932
933 while (*condExpr == ' ' || *condExpr == '\t') {
934 condExpr++;
935 }
936
937 switch (*condExpr) {
938
939 case '(':
940 condExpr++;
941 return TOK_LPAREN;
942
943 case ')':
944 condExpr++;
945 return TOK_RPAREN;
946
947 case '|':
948 if (condExpr[1] == '|') {
949 condExpr++;
950 }
951 condExpr++;
952 return TOK_OR;
953
954 case '&':
955 if (condExpr[1] == '&') {
956 condExpr++;
957 }
958 condExpr++;
959 return TOK_AND;
960
961 case '!':
962 condExpr++;
963 return TOK_NOT;
964
965 case '#':
966 case '\n':
967 case '\0':
968 return TOK_EOF;
969
970 case '"':
971 case '$':
972 return compare_expression(doEval);
973
974 default:
975 return compare_function(doEval);
976 }
977 }
978
979 /*-
980 *-----------------------------------------------------------------------
981 * CondT --
982 * Parse a single term in the expression. This consists of a terminal
983 * symbol or TOK_NOT and a terminal symbol (not including the binary
984 * operators):
985 * T -> defined(variable) | make(target) | exists(file) | symbol
986 * T -> ! T | ( E )
987 *
988 * Results:
989 * TOK_TRUE, TOK_FALSE or TOK_ERROR.
990 *
991 * Side Effects:
992 * Tokens are consumed.
993 *
994 *-----------------------------------------------------------------------
995 */
996 static Token
997 CondT(Boolean doEval)
998 {
999 Token t;
1000
1001 t = CondToken(doEval);
1002
1003 if (t == TOK_EOF) {
1004 /*
1005 * If we reached the end of the expression, the expression
1006 * is malformed...
1007 */
1008 t = TOK_ERROR;
1009 } else if (t == TOK_LPAREN) {
1010 /*
1011 * T -> ( E )
1012 */
1013 t = CondE(doEval);
1014 if (t != TOK_ERROR) {
1015 if (CondToken(doEval) != TOK_RPAREN) {
1016 t = TOK_ERROR;
1017 }
1018 }
1019 } else if (t == TOK_NOT) {
1020 t = CondT(doEval);
1021 if (t == TOK_TRUE) {
1022 t = TOK_FALSE;
1023 } else if (t == TOK_FALSE) {
1024 t = TOK_TRUE;
1025 }
1026 }
1027 return (t);
1028 }
1029
1030 /*-
1032 *-----------------------------------------------------------------------
1033 * CondF --
1034 * Parse a conjunctive factor (nice name, wot?)
1035 * F -> T && F | T
1036 *
1037 * Results:
1038 * TOK_TRUE, TOK_FALSE or TOK_ERROR
1039 *
1040 * Side Effects:
1041 * Tokens are consumed.
1042 *
1043 *-----------------------------------------------------------------------
1044 */
1045 static Token
1046 CondF(Boolean doEval)
1047 {
1048 Token l, o;
1049
1050 l = CondT(doEval);
1051 if (l != TOK_ERROR) {
1052 o = CondToken(doEval);
1053
1054 if (o == TOK_AND) {
1055 /*
1056 * F -> T && F
1057 *
1058 * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to
1059 * parse the r.h.s. anyway (to throw it away).
1060 * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no.
1061 */
1062 if (l == TOK_TRUE) {
1063 l = CondF(doEval);
1064 } else {
1065 (void)CondF(FALSE);
1066 }
1067 } else {
1068 /*
1069 * F -> T
1070 */
1071 CondPushBack(o);
1072 }
1073 }
1074 return (l);
1075 }
1076
1077 /*-
1079 *-----------------------------------------------------------------------
1080 * CondE --
1081 * Main expression production.
1082 * E -> F || E | F
1083 *
1084 * Results:
1085 * TOK_TRUE, TOK_FALSE or TOK_ERROR.
1086 *
1087 * Side Effects:
1088 * Tokens are, of course, consumed.
1089 *
1090 *-----------------------------------------------------------------------
1091 */
1092 static Token
1093 CondE(Boolean doEval)
1094 {
1095 Token l, o;
1096
1097 l = CondF(doEval);
1098 if (l != TOK_ERROR) {
1099 o = CondToken(doEval);
1100
1101 if (o == TOK_OR) {
1102 /*
1103 * E -> F || E
1104 *
1105 * A similar thing occurs for ||, except that here we make sure
1106 * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
1107 * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
1108 * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
1109 */
1110 if (l == TOK_FALSE) {
1111 l = CondE(doEval);
1112 } else {
1113 (void)CondE(FALSE);
1114 }
1115 } else {
1116 /*
1117 * E -> F
1118 */
1119 CondPushBack(o);
1120 }
1121 }
1122 return (l);
1123 }
1124
1125 /*-
1126 *-----------------------------------------------------------------------
1127 * Cond_EvalExpression --
1128 * Evaluate an expression in the passed line. The expression
1129 * consists of &&, ||, !, make(target), defined(variable)
1130 * and parenthetical groupings thereof.
1131 *
1132 * Results:
1133 * COND_PARSE if the condition was valid grammatically
1134 * COND_INVALID if not a valid conditional.
1135 *
1136 * (*value) is set to the boolean value of the condition
1137 *
1138 * Side Effects:
1139 * None.
1140 *
1141 *-----------------------------------------------------------------------
1142 */
1143 int
1144 Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint)
1145 {
1146 static const struct If *dflt_info;
1147 const struct If *sv_if_info = if_info;
1148 char *sv_condExpr = condExpr;
1149 Token sv_condPushBack = condPushBack;
1150 int rval;
1151
1152 while (*line == ' ' || *line == '\t')
1153 line++;
1154
1155 if (info == NULL && (info = dflt_info) == NULL) {
1156 /* Scan for the entry for .if - it can't be first */
1157 for (info = ifs; ; info++)
1158 if (info->form[0] == 0)
1159 break;
1160 dflt_info = info;
1161 }
1162
1163 if_info = info != NULL ? info : ifs + 4;
1164 condExpr = line;
1165 condPushBack = TOK_NONE;
1166
1167 rval = do_Cond_EvalExpression(value);
1168
1169 if (rval == COND_INVALID && eprint)
1170 Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
1171
1172 if_info = sv_if_info;
1173 condExpr = sv_condExpr;
1174 condPushBack = sv_condPushBack;
1175
1176 return rval;
1177 }
1178
1179 static int
1180 do_Cond_EvalExpression(Boolean *value)
1181 {
1182
1183 switch (CondE(TRUE)) {
1184 case TOK_TRUE:
1185 if (CondToken(TRUE) == TOK_EOF) {
1186 *value = TRUE;
1187 return COND_PARSE;
1188 }
1189 break;
1190 case TOK_FALSE:
1191 if (CondToken(TRUE) == TOK_EOF) {
1192 *value = FALSE;
1193 return COND_PARSE;
1194 }
1195 break;
1196 default:
1197 case TOK_ERROR:
1198 break;
1199 }
1200
1201 return COND_INVALID;
1202 }
1203
1204
1205 /*-
1207 *-----------------------------------------------------------------------
1208 * Cond_Eval --
1209 * Evaluate the conditional in the passed line. The line
1210 * looks like this:
1211 * .<cond-type> <expr>
1212 * where <cond-type> is any of if, ifmake, ifnmake, ifdef,
1213 * ifndef, elif, elifmake, elifnmake, elifdef, elifndef
1214 * and <expr> consists of &&, ||, !, make(target), defined(variable)
1215 * and parenthetical groupings thereof.
1216 *
1217 * Input:
1218 * line Line to parse
1219 *
1220 * Results:
1221 * COND_PARSE if should parse lines after the conditional
1222 * COND_SKIP if should skip lines after the conditional
1223 * COND_INVALID if not a valid conditional.
1224 *
1225 * Side Effects:
1226 * None.
1227 *
1228 * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
1229 * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF)
1230 * otherwise .else could be treated as '.elif 1'.
1231 *
1232 *-----------------------------------------------------------------------
1233 */
1234 int
1235 Cond_Eval(char *line)
1236 {
1237 #define MAXIF 64 /* maximum depth of .if'ing */
1238 enum if_states {
1239 IF_ACTIVE, /* .if or .elif part active */
1240 ELSE_ACTIVE, /* .else part active */
1241 SEARCH_FOR_ELIF, /* searching for .elif/else to execute */
1242 SKIP_TO_ELSE, /* has been true, but not seen '.else' */
1243 SKIP_TO_ENDIF /* nothing else to execute */
1244 };
1245 static enum if_states cond_state[MAXIF + 1] = { IF_ACTIVE };
1246
1247 const struct If *ifp;
1248 Boolean isElif;
1249 Boolean value;
1250 int level; /* Level at which to report errors. */
1251 enum if_states state;
1252
1253 level = PARSE_FATAL;
1254
1255 /* skip leading character (the '.') and any whitespace */
1256 for (line++; *line == ' ' || *line == '\t'; line++)
1257 continue;
1258
1259 /* Find what type of if we're dealing with. */
1260 if (line[0] == 'e') {
1261 if (line[1] != 'l') {
1262 if (!istoken(line + 1, "ndif", 4))
1263 return COND_INVALID;
1264 /* End of conditional section */
1265 if (cond_depth == cond_min_depth) {
1266 Parse_Error(level, "if-less endif");
1267 return COND_PARSE;
1268 }
1269 /* Return state for previous conditional */
1270 cond_depth--;
1271 if (cond_depth > MAXIF)
1272 return COND_SKIP;
1273 return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1274 }
1275
1276 /* Quite likely this is 'else' or 'elif' */
1277 line += 2;
1278 if (istoken(line, "se", 2)) {
1279 /* It is else... */
1280 if (cond_depth == cond_min_depth) {
1281 Parse_Error(level, "if-less else");
1282 return COND_PARSE;
1283 }
1284
1285 if (cond_depth > MAXIF)
1286 return COND_SKIP;
1287 state = cond_state[cond_depth];
1288 switch (state) {
1289 case SEARCH_FOR_ELIF:
1290 state = ELSE_ACTIVE;
1291 break;
1292 case ELSE_ACTIVE:
1293 case SKIP_TO_ENDIF:
1294 Parse_Error(PARSE_WARNING, "extra else");
1295 /* FALLTHROUGH */
1296 default:
1297 case IF_ACTIVE:
1298 case SKIP_TO_ELSE:
1299 state = SKIP_TO_ENDIF;
1300 break;
1301 }
1302 cond_state[cond_depth] = state;
1303 return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1304 }
1305 /* Assume for now it is an elif */
1306 isElif = TRUE;
1307 } else
1308 isElif = FALSE;
1309
1310 if (line[0] != 'i' || line[1] != 'f')
1311 /* Not an ifxxx or elifxxx line */
1312 return COND_INVALID;
1313
1314 /*
1315 * Figure out what sort of conditional it is -- what its default
1316 * function is, etc. -- by looking in the table of valid "ifs"
1317 */
1318 line += 2;
1319 for (ifp = ifs; ; ifp++) {
1320 if (ifp->form == NULL)
1321 return COND_INVALID;
1322 if (istoken(ifp->form, line, ifp->formlen)) {
1323 line += ifp->formlen;
1324 break;
1325 }
1326 }
1327
1328 /* Now we know what sort of 'if' it is... */
1329
1330 if (isElif) {
1331 if (cond_depth == cond_min_depth) {
1332 Parse_Error(level, "if-less elif");
1333 return COND_PARSE;
1334 }
1335 if (cond_depth > MAXIF)
1336 /* Error reported when we saw the .if ... */
1337 return COND_SKIP;
1338 state = cond_state[cond_depth];
1339 if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
1340 Parse_Error(PARSE_WARNING, "extra elif");
1341 cond_state[cond_depth] = SKIP_TO_ENDIF;
1342 return COND_SKIP;
1343 }
1344 if (state != SEARCH_FOR_ELIF) {
1345 /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
1346 cond_state[cond_depth] = SKIP_TO_ELSE;
1347 return COND_SKIP;
1348 }
1349 } else {
1350 /* Normal .if */
1351 if (cond_depth >= MAXIF) {
1352 cond_depth++;
1353 Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);
1354 return COND_SKIP;
1355 }
1356 state = cond_state[cond_depth];
1357 cond_depth++;
1358 if (state > ELSE_ACTIVE) {
1359 /* If we aren't parsing the data, treat as always false */
1360 cond_state[cond_depth] = SKIP_TO_ELSE;
1361 return COND_SKIP;
1362 }
1363 }
1364
1365 /* And evaluate the conditional expresssion */
1366 if (Cond_EvalExpression(ifp, line, &value, 1) == COND_INVALID) {
1367 /* Syntax error in conditional, error message already output. */
1368 /* Skip everything to matching .endif */
1369 cond_state[cond_depth] = SKIP_TO_ELSE;
1370 return COND_SKIP;
1371 }
1372
1373 if (!value) {
1374 cond_state[cond_depth] = SEARCH_FOR_ELIF;
1375 return COND_SKIP;
1376 }
1377 cond_state[cond_depth] = IF_ACTIVE;
1378 return COND_PARSE;
1379 }
1380
1381
1382
1383 /*-
1385 *-----------------------------------------------------------------------
1386 * Cond_End --
1387 * Make sure everything's clean at the end of a makefile.
1388 *
1389 * Results:
1390 * None.
1391 *
1392 * Side Effects:
1393 * Parse_Error will be called if open conditionals are around.
1394 *
1395 *-----------------------------------------------------------------------
1396 */
1397 void
1398 Cond_restore_depth(unsigned int saved_depth)
1399 {
1400 int open_conds = cond_depth - cond_min_depth;
1401
1402 if (open_conds != 0 || saved_depth > cond_depth) {
1403 Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
1404 open_conds == 1 ? "" : "s");
1405 cond_depth = cond_min_depth;
1406 }
1407
1408 cond_min_depth = saved_depth;
1409 }
1410
1411 unsigned int
1412 Cond_save_depth(void)
1413 {
1414 int depth = cond_min_depth;
1415
1416 cond_min_depth = cond_depth;
1417 return depth;
1418 }
1419