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