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