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