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