xref: /original-bsd/usr.bin/make/cond.c (revision e59fb703)
1 /*
2  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
3  * Copyright (c) 1988, 1989 by Adam de Boor
4  * Copyright (c) 1989 by Berkeley Softworks
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Adam de Boor.
9  *
10  * %sccs.include.redist.c%
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)cond.c	5.6 (Berkeley) 06/01/90";
15 #endif /* not lint */
16 
17 /*-
18  * cond.c --
19  *	Functions to handle conditionals in a makefile.
20  *
21  * Interface:
22  *	Cond_Eval 	Evaluate the conditional in the passed line.
23  *
24  */
25 
26 #include    "make.h"
27 #include    <buf.h>
28 #include    <ctype.h>
29 
30 /*
31  * The parsing of conditional expressions is based on this grammar:
32  *	E -> F || E
33  *	E -> F
34  *	F -> T && F
35  *	F -> T
36  *	T -> defined(variable)
37  *	T -> make(target)
38  *	T -> exists(file)
39  *	T -> empty(varspec)
40  *	T -> target(name)
41  *	T -> symbol
42  *	T -> $(varspec) op value
43  *	T -> $(varspec) == "string"
44  *	T -> $(varspec) != "string"
45  *	T -> ( E )
46  *	T -> ! T
47  *	op -> == | != | > | < | >= | <=
48  *
49  * 'symbol' is some other symbol to which the default function (condDefProc)
50  * is applied.
51  *
52  * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
53  * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
54  * LParen for '(', RParen for ')' and will evaluate the other terminal
55  * symbols, using either the default function or the function given in the
56  * terminal, and return the result as either True or False.
57  *
58  * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
59  */
60 typedef enum {
61     And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err
62 } Token;
63 
64 /*-
65  * Structures to handle elegantly the different forms of #if's. The
66  * last two fields are stored in condInvert and condDefProc, respectively.
67  */
68 static Boolean	  CondDoDefined(),
69 		  CondDoMake();
70 
71 static struct If {
72     char	*form;	      /* Form of if */
73     int		formlen;      /* Length of form */
74     Boolean	doNot;	      /* TRUE if default function should be negated */
75     Boolean	(*defProc)(); /* Default function to apply */
76 } ifs[] = {
77     "ifdef",	  5,	  FALSE,  CondDoDefined,
78     "ifndef",	  6,	  TRUE,	  CondDoDefined,
79     "ifmake",	  6,	  FALSE,  CondDoMake,
80     "ifnmake",	  7,	  TRUE,	  CondDoMake,
81     "if",	  2,	  FALSE,  CondDoDefined,
82     (char *)0,	  0,	  FALSE,  (Boolean (*)())0,
83 };
84 
85 static Boolean	  condInvert;	    	/* Invert the default function */
86 static Boolean	  (*condDefProc)(); 	/* Default function to apply */
87 static char 	  *condExpr;	    	/* The expression to parse */
88 static Token	  condPushBack=None;	/* Single push-back token used in
89 					 * parsing */
90 
91 #define	MAXIF		30	  /* greatest depth of #if'ing */
92 
93 static Boolean	  condStack[MAXIF]; 	/* Stack of conditionals's values */
94 static int  	  condTop = MAXIF;  	/* Top-most conditional */
95 static int  	  skipIfLevel=0;    	/* Depth of skipped conditionals */
96 static Boolean	  skipLine = FALSE; 	/* Whether the parse module is skipping
97 					 * lines */
98 
99 static Token	  CondT(), CondF(), CondE();
100 
101 /*-
102  *-----------------------------------------------------------------------
103  * CondPushBack --
104  *	Push back the most recent token read. We only need one level of
105  *	this, so the thing is just stored in 'condPushback'.
106  *
107  * Results:
108  *	None.
109  *
110  * Side Effects:
111  *	condPushback is overwritten.
112  *
113  *-----------------------------------------------------------------------
114  */
115 static void
116 CondPushBack (t)
117     Token   	  t;	/* Token to push back into the "stream" */
118 {
119     condPushBack = t;
120 }
121 
122 /*-
123  *-----------------------------------------------------------------------
124  * CondGetArg --
125  *	Find the argument of a built-in function.
126  *
127  * Results:
128  *	The length of the argument and the address of the argument.
129  *
130  * Side Effects:
131  *	The pointer is set to point to the closing parenthesis of the
132  *	function call.
133  *
134  *-----------------------------------------------------------------------
135  */
136 static int
137 CondGetArg (linePtr, argPtr, func, parens)
138     char    	  **linePtr;
139     char    	  **argPtr;
140     char    	  *func;
141     Boolean 	  parens;   	/* TRUE if arg should be bounded by parens */
142 {
143     register char *cp;
144     int	    	  argLen;
145     register Buffer buf;
146 
147     cp = *linePtr;
148     if (parens) {
149 	while (*cp != '(' && *cp != '\0') {
150 	    cp++;
151 	}
152 	if (*cp == '(') {
153 	    cp++;
154 	}
155     }
156 
157     if (*cp == '\0') {
158 	/*
159 	 * No arguments whatsoever. Because 'make' and 'defined' aren't really
160 	 * "reserved words", we don't print a message. I think this is better
161 	 * than hitting the user with a warning message every time s/he uses
162 	 * the word 'make' or 'defined' at the beginning of a symbol...
163 	 */
164 	*argPtr = cp;
165 	return (0);
166     }
167 
168     while (*cp == ' ' || *cp == '\t') {
169 	cp++;
170     }
171 
172     /*
173      * Create a buffer for the argument and start it out at 16 characters
174      * long. Why 16? Why not?
175      */
176     buf = Buf_Init(16);
177 
178     while ((index(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) {
179 	if (*cp == '$') {
180 	    /*
181 	     * Parse the variable spec and install it as part of the argument
182 	     * if it's valid. We tell Var_Parse to complain on an undefined
183 	     * variable, so we don't do it too. Nor do we return an error,
184 	     * though perhaps we should...
185 	     */
186 	    char  	*cp2;
187 	    int		len;
188 	    Boolean	doFree;
189 
190 	    cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);
191 
192 	    Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
193 	    if (doFree) {
194 		free(cp2);
195 	    }
196 	    cp += len;
197 	} else {
198 	    Buf_AddByte(buf, (Byte)*cp);
199 	    cp++;
200 	}
201     }
202 
203     Buf_AddByte(buf, (Byte)'\0');
204     *argPtr = (char *)Buf_GetAll(buf, &argLen);
205     Buf_Destroy(buf, FALSE);
206 
207     while (*cp == ' ' || *cp == '\t') {
208 	cp++;
209     }
210     if (parens && *cp != ')') {
211 	Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()",
212 		     func);
213 	return (0);
214     } else if (parens) {
215 	/*
216 	 * Advance pointer past close parenthesis.
217 	 */
218 	cp++;
219     }
220 
221     *linePtr = cp;
222     return (argLen);
223 }
224 
225 /*-
226  *-----------------------------------------------------------------------
227  * CondDoDefined --
228  *	Handle the 'defined' function for conditionals.
229  *
230  * Results:
231  *	TRUE if the given variable is defined.
232  *
233  * Side Effects:
234  *	None.
235  *
236  *-----------------------------------------------------------------------
237  */
238 static Boolean
239 CondDoDefined (argLen, arg)
240     int	    argLen;
241     char    *arg;
242 {
243     char    savec = arg[argLen];
244     Boolean result;
245 
246     arg[argLen] = '\0';
247     if (Var_Value (arg, VAR_CMD) != (char *)NULL) {
248 	result = TRUE;
249     } else {
250 	result = FALSE;
251     }
252     arg[argLen] = savec;
253     return (result);
254 }
255 
256 /*-
257  *-----------------------------------------------------------------------
258  * CondStrMatch --
259  *	Front-end for Str_Match so it returns 0 on match and non-zero
260  *	on mismatch. Callback function for CondDoMake via Lst_Find
261  *
262  * Results:
263  *	0 if string matches pattern
264  *
265  * Side Effects:
266  *	None
267  *
268  *-----------------------------------------------------------------------
269  */
270 static int
271 CondStrMatch(string, pattern)
272     char    *string;
273     char    *pattern;
274 {
275     return(!Str_Match(string,pattern));
276 }
277 
278 /*-
279  *-----------------------------------------------------------------------
280  * CondDoMake --
281  *	Handle the 'make' function for conditionals.
282  *
283  * Results:
284  *	TRUE if the given target is being made.
285  *
286  * Side Effects:
287  *	None.
288  *
289  *-----------------------------------------------------------------------
290  */
291 static Boolean
292 CondDoMake (argLen, arg)
293     int	    argLen;
294     char    *arg;
295 {
296     char    savec = arg[argLen];
297     Boolean result;
298 
299     arg[argLen] = '\0';
300     if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) {
301 	result = FALSE;
302     } else {
303 	result = TRUE;
304     }
305     arg[argLen] = savec;
306     return (result);
307 }
308 
309 /*-
310  *-----------------------------------------------------------------------
311  * CondDoExists --
312  *	See if the given file exists.
313  *
314  * Results:
315  *	TRUE if the file exists and FALSE if it does not.
316  *
317  * Side Effects:
318  *	None.
319  *
320  *-----------------------------------------------------------------------
321  */
322 static Boolean
323 CondDoExists (argLen, arg)
324     int	    argLen;
325     char    *arg;
326 {
327     char    savec = arg[argLen];
328     Boolean result;
329     char    *path;
330 
331     arg[argLen] = '\0';
332     path = Dir_FindFile(arg, dirSearchPath);
333     if (path != (char *)NULL) {
334 	result = TRUE;
335 	free(path);
336     } else {
337 	result = FALSE;
338     }
339     arg[argLen] = savec;
340     return (result);
341 }
342 
343 /*-
344  *-----------------------------------------------------------------------
345  * CondDoTarget --
346  *	See if the given node exists and is an actual target.
347  *
348  * Results:
349  *	TRUE if the node exists as a target and FALSE if it does not.
350  *
351  * Side Effects:
352  *	None.
353  *
354  *-----------------------------------------------------------------------
355  */
356 static Boolean
357 CondDoTarget (argLen, arg)
358     int	    argLen;
359     char    *arg;
360 {
361     char    savec = arg[argLen];
362     Boolean result;
363     GNode   *gn;
364 
365     arg[argLen] = '\0';
366     gn = Targ_FindNode(arg, TARG_NOCREATE);
367     if ((gn != NILGNODE) && !OP_NOP(gn->type)) {
368 	result = TRUE;
369     } else {
370 	result = FALSE;
371     }
372     arg[argLen] = savec;
373     return (result);
374 }
375 
376 
377 /*-
378  *-----------------------------------------------------------------------
379  * CondCvtArg --
380  *	Convert the given number into a double. If the number begins
381  *	with 0x, or just x, it is interpreted as a hexadecimal integer
382  *	and converted to a double from there. All other strings just have
383  *	atof called on them.
384  *
385  * Results:
386  *	The double value of string.
387  *
388  * Side Effects:
389  *
390  *
391  *-----------------------------------------------------------------------
392  */
393 static double
394 CondCvtArg(str)
395     register char    	*str;
396 {
397     int	    	  	sign = 1;
398     double  	  	atof();
399 
400     if (*str == '-') {
401 	sign = -1;
402 	str++;
403     } else if (*str == '+') {
404 	str++;
405     }
406     if (((*str == '0') && (str[1] == 'x')) ||
407 	(*str == 'x'))
408     {
409 	register int i;
410 
411 	str += (*str == 'x') ? 1 : 2;
412 
413 	i = 0;
414 
415 	while (isxdigit(*str)) {
416 	    i *= 16;
417 	    if (*str <= '9') {
418 		i += *str - '0';
419 	    } else if (*str <= 'F') {
420 		i += *str - 'A' + 10;
421 	    } else {
422 		i += *str - 'a' + 10;
423 	    }
424 	    str++;
425 	}
426 	if (sign < 0) {
427 	    return((double)(-i));
428 	} else {
429 	    return((double)i);
430 	}
431     } else if (sign < 0) {
432 	return(- atof(str));
433     } else {
434 	return(atof(str));
435     }
436 }
437 
438 /*-
439  *-----------------------------------------------------------------------
440  * CondToken --
441  *	Return the next token from the input.
442  *
443  * Results:
444  *	A Token for the next lexical token in the stream.
445  *
446  * Side Effects:
447  *	condPushback will be set back to None if it is used.
448  *
449  *-----------------------------------------------------------------------
450  */
451 static Token
452 CondToken(doEval)
453     Boolean doEval;
454 {
455     Token	  t;
456 
457     if (condPushBack == None) {
458 	while (*condExpr == ' ' || *condExpr == '\t') {
459 	    condExpr++;
460 	}
461 	switch (*condExpr) {
462 	    case '(':
463 		t = LParen;
464 		condExpr++;
465 		break;
466 	    case ')':
467 		t = RParen;
468 		condExpr++;
469 		break;
470 	    case '|':
471 		if (condExpr[1] == '|') {
472 		    condExpr++;
473 		}
474 		condExpr++;
475 		t = Or;
476 		break;
477 	    case '&':
478 		if (condExpr[1] == '&') {
479 		    condExpr++;
480 		}
481 		condExpr++;
482 		t = And;
483 		break;
484 	    case '!':
485 		t = Not;
486 		condExpr++;
487 		break;
488 	    case '\n':
489 	    case '\0':
490 		t = EndOfFile;
491 		break;
492 	    case '$': {
493 		char	*lhs;
494 		char	*rhs;
495 		char	*op;
496 		int	varSpecLen;
497 		Boolean	doFree;
498 
499 		/*
500 		 * Parse the variable spec and skip over it, saving its
501 		 * value in lhs.
502 		 */
503 		t = Err;
504 		lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree);
505 		if (lhs == var_Error) {
506 		    /*
507 		     * Even if !doEval, we still report syntax errors, which
508 		     * is what getting var_Error back with !doEval means.
509 		     */
510 		    return(Err);
511 		}
512 		condExpr += varSpecLen;
513 
514 		/*
515 		 * Skip whitespace to get to the operator
516 		 */
517 		while (isspace(*condExpr)) {
518 		    condExpr++;
519 		}
520 		/*
521 		 * Make sure the operator is a valid one. If it isn't a
522 		 * known relational operator, pretend we got a
523 		 * != 0 comparison.
524 		 */
525 		op = condExpr;
526 		switch (*condExpr) {
527 		    case '!':
528 		    case '=':
529 		    case '<':
530 		    case '>':
531 			if (condExpr[1] == '=') {
532 			    condExpr += 2;
533 			} else {
534 			    condExpr += 1;
535 			}
536 			break;
537 		    default:
538 			op = "!=";
539 			rhs = "0";
540 
541 			goto do_compare;
542 		}
543 		while (isspace(*condExpr)) {
544 		    condExpr++;
545 		}
546 		if (*condExpr == '\0') {
547 		    Parse_Error(PARSE_WARNING,
548 				"Missing right-hand-side of operator");
549 		    goto error;
550 		}
551 		rhs = condExpr;
552 do_compare:
553 		if (*rhs == '"') {
554 		    /*
555 		     * Doing a string comparison. Only allow == and != for
556 		     * operators.
557 		     */
558 		    char    *string;
559 		    char    *cp, *cp2;
560 		    Buffer  buf;
561 
562 		    if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
563 			Parse_Error(PARSE_WARNING,
564 		"String comparison operator should be either == or !=");
565 			goto error;
566 		    }
567 
568 		    buf = Buf_Init(0);
569 
570 		    for (cp = rhs+1; (*cp != '"') && (*cp != '\0'); cp++) {
571 			if ((*cp == '\\') && (cp[1] != '\0')) {
572 			    /*
573 			     * Backslash escapes things -- skip over next
574 			     * character, if it exists.
575 			     */
576 			    cp++;
577 			    Buf_AddByte(buf, (Byte)*cp);
578 			} else if (*cp == '$') {
579 			    int	len;
580 			    Boolean freeIt;
581 
582 			    cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt);
583 			    if (cp2 != var_Error) {
584 				Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
585 				if (freeIt) {
586 				    free(cp2);
587 				}
588 				cp += len - 1;
589 			    } else {
590 				Buf_AddByte(buf, (Byte)*cp);
591 			    }
592 			} else {
593 			    Buf_AddByte(buf, (Byte)*cp);
594 			}
595 		    }
596 
597 		    Buf_AddByte(buf, (Byte)0);
598 
599 		    string = (char *)Buf_GetAll(buf, (int *)0);
600 		    Buf_Destroy(buf, FALSE);
601 
602 		    if (DEBUG(COND)) {
603 			printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
604 			       lhs, string, op);
605 		    }
606 		    /*
607 		     * Null-terminate rhs and perform the comparison.
608 		     * t is set to the result.
609 		     */
610 		    if (*op == '=') {
611 			t = strcmp(lhs, string) ? False : True;
612 		    } else {
613 			t = strcmp(lhs, string) ? True : False;
614 		    }
615 		    free(string);
616 		    if (rhs == condExpr) {
617 			condExpr = cp + 1;
618 		    }
619 		} else {
620 		    /*
621 		     * rhs is either a float or an integer. Convert both the
622 		     * lhs and the rhs to a double and compare the two.
623 		     */
624 		    double  	left, right;
625 		    char    	*string;
626 
627 		    left = CondCvtArg(lhs);
628 		    if (*rhs == '$') {
629 			int 	len;
630 			Boolean	freeIt;
631 
632 			string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt);
633 			if (string == var_Error) {
634 			    right = 0.0;
635 			} else {
636 			    right = CondCvtArg(string);
637 			    if (freeIt) {
638 				free(string);
639 			    }
640 			    if (rhs == condExpr) {
641 				condExpr += len;
642 			    }
643 			}
644 		    } else {
645 			right = CondCvtArg(rhs);
646 			if (rhs == condExpr) {
647 			    /*
648 			     * Skip over the right-hand side
649 			     */
650 			    while(!isspace(*condExpr) && (*condExpr != '\0')) {
651 				condExpr++;
652 			    }
653 			}
654 		    }
655 
656 		    if (DEBUG(COND)) {
657 			printf("left = %f, right = %f, op = %.2s\n", left,
658 			       right, op);
659 		    }
660 		    switch(op[0]) {
661 		    case '!':
662 			if (op[1] != '=') {
663 			    Parse_Error(PARSE_WARNING,
664 					"Unknown operator");
665 			    goto error;
666 			}
667 			t = (left != right ? True : False);
668 			break;
669 		    case '=':
670 			if (op[1] != '=') {
671 			    Parse_Error(PARSE_WARNING,
672 					"Unknown operator");
673 			    goto error;
674 			}
675 			t = (left == right ? True : False);
676 			break;
677 		    case '<':
678 			if (op[1] == '=') {
679 			    t = (left <= right ? True : False);
680 			} else {
681 			    t = (left < right ? True : False);
682 			}
683 			break;
684 		    case '>':
685 			if (op[1] == '=') {
686 			    t = (left >= right ? True : False);
687 			} else {
688 			    t = (left > right ? True : False);
689 			}
690 			break;
691 		    }
692 		}
693 error:
694 		if (doFree) {
695 		    free(lhs);
696 		}
697 		break;
698 	    }
699 	    default: {
700 		Boolean (*evalProc)();
701 		Boolean invert = FALSE;
702 		char	*arg;
703 		int	arglen;
704 
705 		if (strncmp (condExpr, "defined", 7) == 0) {
706 		    /*
707 		     * Use CondDoDefined to evaluate the argument and
708 		     * CondGetArg to extract the argument from the 'function
709 		     * call'.
710 		     */
711 		    evalProc = CondDoDefined;
712 		    condExpr += 7;
713 		    arglen = CondGetArg (&condExpr, &arg, "defined", TRUE);
714 		    if (arglen == 0) {
715 			condExpr -= 7;
716 			goto use_default;
717 		    }
718 		} else if (strncmp (condExpr, "make", 4) == 0) {
719 		    /*
720 		     * Use CondDoMake to evaluate the argument and
721 		     * CondGetArg to extract the argument from the 'function
722 		     * call'.
723 		     */
724 		    evalProc = CondDoMake;
725 		    condExpr += 4;
726 		    arglen = CondGetArg (&condExpr, &arg, "make", TRUE);
727 		    if (arglen == 0) {
728 			condExpr -= 4;
729 			goto use_default;
730 		    }
731 		} else if (strncmp (condExpr, "exists", 6) == 0) {
732 		    /*
733 		     * Use CondDoExists to evaluate the argument and
734 		     * CondGetArg to extract the argument from the
735 		     * 'function call'.
736 		     */
737 		    evalProc = CondDoExists;
738 		    condExpr += 6;
739 		    arglen = CondGetArg(&condExpr, &arg, "exists", TRUE);
740 		    if (arglen == 0) {
741 			condExpr -= 6;
742 			goto use_default;
743 		    }
744 		} else if (strncmp(condExpr, "empty", 5) == 0) {
745 		    /*
746 		     * Use Var_Parse to parse the spec in parens and return
747 		     * True if the resulting string is empty.
748 		     */
749 		    int	    length;
750 		    Boolean doFree;
751 		    char    *val;
752 
753 		    condExpr += 5;
754 
755 		    for (arglen = 0;
756 			 condExpr[arglen] != '(' && condExpr[arglen] != '\0';
757 			 arglen += 1)
758 		    {
759 			/* void */ ;
760 		    }
761 		    if (condExpr[arglen] != '\0') {
762 			val = Var_Parse(&condExpr[arglen - 1], VAR_CMD,
763 					doEval, &length, &doFree);
764 			if (val == var_Error) {
765 			    t = Err;
766 			} else {
767 			    t = (*val == '\0') ? True : False;
768 			}
769 			if (doFree) {
770 			    free(val);
771 			}
772 			/*
773 			 * Advance condExpr to beyond the closing ). Note that
774 			 * we subtract one from arglen + length b/c length
775 			 * is calculated from condExpr[arglen - 1].
776 			 */
777 			condExpr += arglen + length - 1;
778 		    } else {
779 			condExpr -= 5;
780 			goto use_default;
781 		    }
782 		    break;
783 		} else if (strncmp (condExpr, "target", 6) == 0) {
784 		    /*
785 		     * Use CondDoTarget to evaluate the argument and
786 		     * CondGetArg to extract the argument from the
787 		     * 'function call'.
788 		     */
789 		    evalProc = CondDoTarget;
790 		    condExpr += 6;
791 		    arglen = CondGetArg(&condExpr, &arg, "target", TRUE);
792 		    if (arglen == 0) {
793 			condExpr -= 6;
794 			goto use_default;
795 		    }
796 		} else {
797 		    /*
798 		     * The symbol is itself the argument to the default
799 		     * function. We advance condExpr to the end of the symbol
800 		     * by hand (the next whitespace, closing paren or
801 		     * binary operator) and set to invert the evaluation
802 		     * function if condInvert is TRUE.
803 		     */
804 		use_default:
805 		    invert = condInvert;
806 		    evalProc = condDefProc;
807 		    arglen = CondGetArg(&condExpr, &arg, "", FALSE);
808 		}
809 
810 		/*
811 		 * Evaluate the argument using the set function. If invert
812 		 * is TRUE, we invert the sense of the function.
813 		 */
814 		t = (!doEval || (* evalProc) (arglen, arg) ?
815 		     (invert ? False : True) :
816 		     (invert ? True : False));
817 		free(arg);
818 		break;
819 	    }
820 	}
821     } else {
822 	t = condPushBack;
823 	condPushBack = None;
824     }
825     return (t);
826 }
827 
828 /*-
829  *-----------------------------------------------------------------------
830  * CondT --
831  *	Parse a single term in the expression. This consists of a terminal
832  *	symbol or Not and a terminal symbol (not including the binary
833  *	operators):
834  *	    T -> defined(variable) | make(target) | exists(file) | symbol
835  *	    T -> ! T | ( E )
836  *
837  * Results:
838  *	True, False or Err.
839  *
840  * Side Effects:
841  *	Tokens are consumed.
842  *
843  *-----------------------------------------------------------------------
844  */
845 static Token
846 CondT(doEval)
847     Boolean doEval;
848 {
849     Token   t;
850 
851     t = CondToken(doEval);
852 
853     if (t == EndOfFile) {
854 	/*
855 	 * If we reached the end of the expression, the expression
856 	 * is malformed...
857 	 */
858 	t = Err;
859     } else if (t == LParen) {
860 	/*
861 	 * T -> ( E )
862 	 */
863 	t = CondE(doEval);
864 	if (t != Err) {
865 	    if (CondToken(doEval) != RParen) {
866 		t = Err;
867 	    }
868 	}
869     } else if (t == Not) {
870 	t = CondT(doEval);
871 	if (t == True) {
872 	    t = False;
873 	} else if (t == False) {
874 	    t = True;
875 	}
876     }
877     return (t);
878 }
879 
880 /*-
881  *-----------------------------------------------------------------------
882  * CondF --
883  *	Parse a conjunctive factor (nice name, wot?)
884  *	    F -> T && F | T
885  *
886  * Results:
887  *	True, False or Err
888  *
889  * Side Effects:
890  *	Tokens are consumed.
891  *
892  *-----------------------------------------------------------------------
893  */
894 static Token
895 CondF(doEval)
896     Boolean doEval;
897 {
898     Token   l, o;
899 
900     l = CondT(doEval);
901     if (l != Err) {
902 	o = CondToken(doEval);
903 
904 	if (o == And) {
905 	    /*
906 	     * F -> T && F
907 	     *
908 	     * If T is False, the whole thing will be False, but we have to
909 	     * parse the r.h.s. anyway (to throw it away).
910 	     * If T is True, the result is the r.h.s., be it an Err or no.
911 	     */
912 	    if (l == True) {
913 		l = CondF(doEval);
914 	    } else {
915 		(void) CondF(FALSE);
916 	    }
917 	} else {
918 	    /*
919 	     * F -> T
920 	     */
921 	    CondPushBack (o);
922 	}
923     }
924     return (l);
925 }
926 
927 /*-
928  *-----------------------------------------------------------------------
929  * CondE --
930  *	Main expression production.
931  *	    E -> F || E | F
932  *
933  * Results:
934  *	True, False or Err.
935  *
936  * Side Effects:
937  *	Tokens are, of course, consumed.
938  *
939  *-----------------------------------------------------------------------
940  */
941 static Token
942 CondE(doEval)
943     Boolean doEval;
944 {
945     Token   l, o;
946 
947     l = CondF(doEval);
948     if (l != Err) {
949 	o = CondToken(doEval);
950 
951 	if (o == Or) {
952 	    /*
953 	     * E -> F || E
954 	     *
955 	     * A similar thing occurs for ||, except that here we make sure
956 	     * the l.h.s. is False before we bother to evaluate the r.h.s.
957 	     * Once again, if l is False, the result is the r.h.s. and once
958 	     * again if l is True, we parse the r.h.s. to throw it away.
959 	     */
960 	    if (l == False) {
961 		l = CondE(doEval);
962 	    } else {
963 		(void) CondE(FALSE);
964 	    }
965 	} else {
966 	    /*
967 	     * E -> F
968 	     */
969 	    CondPushBack (o);
970 	}
971     }
972     return (l);
973 }
974 
975 /*-
976  *-----------------------------------------------------------------------
977  * Cond_Eval --
978  *	Evaluate the conditional in the passed line. The line
979  *	looks like this:
980  *	    #<cond-type> <expr>
981  *	where <cond-type> is any of if, ifmake, ifnmake, ifdef,
982  *	ifndef, elif, elifmake, elifnmake, elifdef, elifndef
983  *	and <expr> consists of &&, ||, !, make(target), defined(variable)
984  *	and parenthetical groupings thereof.
985  *
986  * Results:
987  *	COND_PARSE	if should parse lines after the conditional
988  *	COND_SKIP	if should skip lines after the conditional
989  *	COND_INVALID  	if not a valid conditional.
990  *
991  * Side Effects:
992  *	None.
993  *
994  *-----------------------------------------------------------------------
995  */
996 Cond_Eval (line)
997     char    	    *line;    /* Line to parse */
998 {
999     struct If	    *ifp;
1000     Boolean 	    isElse;
1001     Boolean 	    value;
1002     int	    	    level;  	/* Level at which to report errors. */
1003 
1004     level = PARSE_FATAL;
1005 
1006     for (line++; *line == ' ' || *line == '\t'; line++) {
1007 	continue;
1008     }
1009 
1010     /*
1011      * Find what type of if we're dealing with. The result is left
1012      * in ifp and isElse is set TRUE if it's an elif line.
1013      */
1014     if (line[0] == 'e' && line[1] == 'l') {
1015 	line += 2;
1016 	isElse = TRUE;
1017     } else if (strncmp (line, "endif", 5) == 0) {
1018 	/*
1019 	 * End of a conditional section. If skipIfLevel is non-zero, that
1020 	 * conditional was skipped, so lines following it should also be
1021 	 * skipped. Hence, we return COND_SKIP. Otherwise, the conditional
1022 	 * was read so succeeding lines should be parsed (think about it...)
1023 	 * so we return COND_PARSE, unless this endif isn't paired with
1024 	 * a decent if.
1025 	 */
1026 	if (skipIfLevel != 0) {
1027 	    skipIfLevel -= 1;
1028 	    return (COND_SKIP);
1029 	} else {
1030 	    if (condTop == MAXIF) {
1031 		Parse_Error (level, "if-less endif");
1032 		return (COND_INVALID);
1033 	    } else {
1034 		skipLine = FALSE;
1035 		condTop += 1;
1036 		return (COND_PARSE);
1037 	    }
1038 	}
1039     } else {
1040 	isElse = FALSE;
1041     }
1042 
1043     /*
1044      * Figure out what sort of conditional it is -- what its default
1045      * function is, etc. -- by looking in the table of valid "ifs"
1046      */
1047     for (ifp = ifs; ifp->form != (char *)0; ifp++) {
1048 	if (strncmp (ifp->form, line, ifp->formlen) == 0) {
1049 	    break;
1050 	}
1051     }
1052 
1053     if (ifp->form == (char *) 0) {
1054 	/*
1055 	 * Nothing fit. If the first word on the line is actually
1056 	 * "else", it's a valid conditional whose value is the inverse
1057 	 * of the previous if we parsed.
1058 	 */
1059 	if (isElse && (line[0] == 's') && (line[1] == 'e')) {
1060 	    if (condTop == MAXIF) {
1061 		Parse_Error (level, "if-less else");
1062 		return (COND_INVALID);
1063 	    } else if (skipIfLevel == 0) {
1064 		value = !condStack[condTop];
1065 	    } else {
1066 		return (COND_SKIP);
1067 	    }
1068 	} else {
1069 	    /*
1070 	     * Not a valid conditional type. No error...
1071 	     */
1072 	    return (COND_INVALID);
1073 	}
1074     } else {
1075 	if (isElse) {
1076 	    if (condTop == MAXIF) {
1077 		Parse_Error (level, "if-less elif");
1078 		return (COND_INVALID);
1079 	    } else if (skipIfLevel != 0) {
1080 		/*
1081 		 * If skipping this conditional, just ignore the whole thing.
1082 		 * If we don't, the user might be employing a variable that's
1083 		 * undefined, for which there's an enclosing ifdef that
1084 		 * we're skipping...
1085 		 */
1086 		return(COND_SKIP);
1087 	    }
1088 	} else if (skipLine) {
1089 	    /*
1090 	     * Don't even try to evaluate a conditional that's not an else if
1091 	     * we're skipping things...
1092 	     */
1093 	    skipIfLevel += 1;
1094 	    return(COND_SKIP);
1095 	}
1096 
1097 	/*
1098 	 * Initialize file-global variables for parsing
1099 	 */
1100 	condDefProc = ifp->defProc;
1101 	condInvert = ifp->doNot;
1102 
1103 	line += ifp->formlen;
1104 
1105 	while (*line == ' ' || *line == '\t') {
1106 	    line++;
1107 	}
1108 
1109 	condExpr = line;
1110 	condPushBack = None;
1111 
1112 	switch (CondE(TRUE)) {
1113 	    case True:
1114 		if (CondToken(TRUE) == EndOfFile) {
1115 		    value = TRUE;
1116 		    break;
1117 		}
1118 		goto err;
1119 		/*FALLTHRU*/
1120 	    case False:
1121 		if (CondToken(TRUE) == EndOfFile) {
1122 		    value = FALSE;
1123 		    break;
1124 		}
1125 		/*FALLTHRU*/
1126 	    case Err:
1127 	    err:
1128 		Parse_Error (level, "Malformed conditional (%s)",
1129 			     line);
1130 		return (COND_INVALID);
1131 	}
1132     }
1133     if (!isElse) {
1134 	condTop -= 1;
1135     } else if ((skipIfLevel != 0) || condStack[condTop]) {
1136 	/*
1137 	 * If this is an else-type conditional, it should only take effect
1138 	 * if its corresponding if was evaluated and FALSE. If its if was
1139 	 * TRUE or skipped, we return COND_SKIP (and start skipping in case
1140 	 * we weren't already), leaving the stack unmolested so later elif's
1141 	 * don't screw up...
1142 	 */
1143 	skipLine = TRUE;
1144 	return (COND_SKIP);
1145     }
1146 
1147     if (condTop < 0) {
1148 	/*
1149 	 * This is the one case where we can definitely proclaim a fatal
1150 	 * error. If we don't, we're hosed.
1151 	 */
1152 	Parse_Error (PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);
1153 	return (COND_INVALID);
1154     } else {
1155 	condStack[condTop] = value;
1156 	skipLine = !value;
1157 	return (value ? COND_PARSE : COND_SKIP);
1158     }
1159 }
1160 
1161 /*-
1162  *-----------------------------------------------------------------------
1163  * Cond_End --
1164  *	Make sure everything's clean at the end of a makefile.
1165  *
1166  * Results:
1167  *	None.
1168  *
1169  * Side Effects:
1170  *	Parse_Error will be called if open conditionals are around.
1171  *
1172  *-----------------------------------------------------------------------
1173  */
1174 void
1175 Cond_End()
1176 {
1177     if (condTop != MAXIF) {
1178 	Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop,
1179 		    MAXIF-condTop == 1 ? "" : "s");
1180     }
1181     condTop = MAXIF;
1182 }
1183