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