summaryrefslogtreecommitdiff
path: root/usr/src/lib/libpp/common/ppexpr.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libpp/common/ppexpr.c')
-rw-r--r--usr/src/lib/libpp/common/ppexpr.c697
1 files changed, 697 insertions, 0 deletions
diff --git a/usr/src/lib/libpp/common/ppexpr.c b/usr/src/lib/libpp/common/ppexpr.c
new file mode 100644
index 0000000000..5fd93afde5
--- /dev/null
+++ b/usr/src/lib/libpp/common/ppexpr.c
@@ -0,0 +1,697 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1986-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * preprocessor expression evaluation support
+ */
+
+#include "pplib.h"
+
+#include <regex.h>
+
+#define lex(c) ((((c)=peektoken)>=0?(peektoken=(-1)):((c)=pplex())),(c))
+#define unlex(c) (peektoken=(c))
+
+static int peektoken; /* expression lookahead token */
+static char* errmsg; /* subexpr() error message */
+
+/*
+ * exists predicate evaluation
+ */
+
+static int
+exists(int op, char* pred, register char* args)
+{
+ register int c;
+ register int type;
+ char* pptoken;
+ long state;
+ char file[MAXTOKEN + 1];
+
+ state = (pp.state & ~DISABLE);
+ PUSH_STRING(args);
+ pptoken = pp.token;
+ pp.token = file;
+ pp.state |= HEADER|PASSEOF;
+ type = pplex();
+ pp.state &= ~HEADER;
+ pp.token = pptoken;
+ switch (type)
+ {
+ case T_STRING:
+ case T_HEADER:
+ break;
+ default:
+ error(1, "%s: \"...\" or <...> argument expected", pred);
+ c = 0;
+ goto done;
+ }
+ if (op == X_EXISTS)
+ {
+ if ((c = pplex()) == ',')
+ {
+ while ((c = pplex()) == T_STRING)
+ {
+ if (pathaccess(pp.path, pp.token, file, NiL, 0))
+ {
+ pathcanon(pp.path, 0);
+ message((-2, "%s: %s found", pred, pp.path));
+ c = 1;
+ goto done;
+ }
+ if ((c = pplex()) != ',') break;
+ }
+ if (c) error(1, "%s: \"...\" arguments expected", pred);
+ strcpy(pp.path, file);
+ message((-2, "%s: %s not found", pred, file));
+ c = 0;
+ }
+ else c = ppsearch(file, type, SEARCH_EXISTS) >= 0;
+ }
+ else
+ {
+ register struct ppfile* fp;
+
+ fp = ppsetfile(file);
+ c = fp->flags || fp->guard == INC_IGNORE;
+ }
+ done:
+ while (pplex());
+ pp.state = state;
+ return c;
+}
+
+/*
+ * strcmp/match predicate evaluation
+ */
+
+static int
+compare(char* pred, char* args, int match)
+{
+ register int c;
+ char* pptoken;
+ long state;
+ regex_t re;
+ char tmp[MAXTOKEN + 1];
+
+ state = (pp.state & ~DISABLE);
+ PUSH_STRING(args);
+ pp.state |= PASSEOF;
+ pptoken = pp.token;
+ pp.token = tmp;
+ if (!pplex())
+ goto bad;
+ pp.token = pptoken;
+ if (pplex() != ',' || !pplex())
+ goto bad;
+ if (!match)
+ c = strcmp(tmp, pp.token);
+ else if ((c = regcomp(&re, pp.token, REG_AUGMENTED|REG_LENIENT|REG_NULL)) || (c = regexec(&re, tmp, NiL, 0, 0)) && c != REG_NOMATCH)
+ regfatal(&re, 3, c);
+ else
+ {
+ c = !c;
+ regfree(&re);
+ }
+ if ((pp.state & PASSEOF) && pplex())
+ goto bad;
+ pp.state = state;
+ return c;
+ bad:
+ pp.token = pptoken;
+ error(2, "%s: 2 arguments expected", pred);
+ while (pplex());
+ pp.state = state;
+ return 0;
+}
+
+/*
+ * #if predicate parse and evaluation
+ */
+
+static int
+predicate(int warn)
+{
+ register char* args;
+ register struct pplist* p;
+ register struct ppsymbol* sym;
+ register int type;
+ int index;
+
+ static char pred[MAXID + 1];
+
+ /*
+ * first gather the args
+ */
+
+ index = (int)hashref(pp.strtab, pp.token);
+ if (warn && peekchr() != '(') switch (index)
+ {
+ case X_DEFINED:
+ case X_EXISTS:
+ case X_INCLUDED:
+ case X_MATCH:
+ case X_NOTICED:
+ case X_OPTION:
+ case X_SIZEOF:
+ case X_STRCMP:
+ break;
+ default:
+ if (pp.macref) pprefmac(pp.token, REF_IF);
+ return 0;
+ }
+ strcpy(pred, pp.token);
+ pp.state |= DISABLE;
+ type = pppredargs();
+ pp.state &= ~DISABLE;
+ switch (type)
+ {
+ case T_ID:
+ case T_STRING:
+ break;
+ default:
+ unlex(type);
+ /*FALLTHROUGH*/
+ case 0:
+ if (index && !(pp.state & STRICT))
+ error(1, "%s: predicate argument expected", pred);
+ if (pp.macref) pprefmac(pred, REF_IF);
+ return 0;
+ }
+ args = pp.args;
+
+ /*
+ * now evaluate
+ */
+
+ debug((-6, "pred=%s args=%s", pred, args));
+ if ((pp.state & STRICT) && !(pp.mode & HOSTED)) switch (index)
+ {
+ case X_DEFINED:
+ case X_SIZEOF:
+ break;
+ default:
+ error(1, "%s(%s): non-standard predicate test", pred, args);
+ return 0;
+ }
+ switch (index)
+ {
+ case X_DEFINED:
+ if (type != T_ID) error(1, "%s: identifier argument expected", pred);
+ else if ((sym = pprefmac(args, REF_IF)) && sym->macro) return 1;
+ else if (args[0] == '_' && args[1] == '_' && !strncmp(args, "__STDPP__", 9))
+ {
+ if (pp.hosted == 1 && pp.in->prev->type == IN_FILE)
+ {
+ pp.mode |= HOSTED;
+ pp.flags |= PP_hosted;
+ }
+ return *(args + 9) ? (int)hashref(pp.strtab, args + 9) : 1;
+ }
+ break;
+ case X_EXISTS:
+ case X_INCLUDED:
+ return exists(index, pred, args);
+ case X_MATCH:
+ case X_STRCMP:
+ return compare(pred, args, index == X_MATCH);
+ case X_NOTICED:
+ if (type != T_ID) error(1, "%s: identifier argument expected", pred);
+ else if (((sym = pprefmac(args, REF_IF)) || (sym = ppsymref(pp.symtab, args))) && (sym->flags & SYM_NOTICED)) return 1;
+ break;
+ case X_OPTION:
+ return ppoption(args);
+ case X_SIZEOF:
+ error(2, "%s invalid in #%s expressions", pred, dirname(IF));
+ break;
+ default:
+ if (warn && !(pp.mode & HOSTED) && (sym = ppsymref(pp.symtab, pred)) && (sym->flags & SYM_PREDICATE))
+ error(1, "use #%s(%s) to disambiguate", pred, args);
+ if (p = (struct pplist*)hashget(pp.prdtab, pred))
+ {
+ if (!*args) return 1;
+ while (p)
+ {
+ if (streq(p->value, args)) return 1;
+ p = p->next;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * evaluate a long integer subexpression with precedence
+ * taken from the library routine streval()
+ * may be called recursively
+ *
+ * NOTE: all operands are evaluated as both the parse
+ * and evaluation are done on the fly
+ */
+
+static long
+subexpr(register int precedence, int* pun)
+{
+ register int c;
+ register long n;
+ register long x;
+ register int operand = 1;
+ int un = 0;
+ int xn;
+
+ switch (lex(c))
+ {
+ case 0:
+ case '\n':
+ unlex(c);
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "more tokens expected";
+ return 0;
+ case '-':
+ n = -subexpr(13, &un);
+ break;
+ case '+':
+ n = subexpr(13, &un);
+ break;
+ case '!':
+ n = !subexpr(13, &un);
+ break;
+ case '~':
+ n = ~subexpr(13, &un);
+ break;
+ default:
+ unlex(c);
+ n = 0;
+ operand = 0;
+ break;
+ }
+ un <<= 1;
+ for (;;)
+ {
+ switch (lex(c))
+ {
+ case 0:
+ case '\n':
+ goto done;
+ case ')':
+ if (!precedence)
+ {
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "too many )'s";
+ return 0;
+ }
+ goto done;
+ case '(':
+ n = subexpr(1, &un);
+ if (lex(c) != ')')
+ {
+ unlex(c);
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "closing ) expected";
+ return 0;
+ }
+ gotoperand:
+ if (operand)
+ {
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operator expected";
+ return 0;
+ }
+ operand = 1;
+ un <<= 1;
+ continue;
+ case '?':
+ if (precedence > 1) goto done;
+ un = 0;
+ if (lex(c) == ':')
+ {
+ if (!n) n = subexpr(2, &un);
+ else
+ {
+ x = pp.mode;
+ pp.mode |= INACTIVE;
+ subexpr(2, &xn);
+ pp.mode = x;
+ }
+ }
+ else
+ {
+ unlex(c);
+ x = subexpr(2, &xn);
+ if (lex(c) != ':')
+ {
+ unlex(c);
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = ": expected for ? operator";
+ return 0;
+ }
+ if (n)
+ {
+ n = x;
+ un = xn;
+ subexpr(2, &xn);
+ }
+ else n = subexpr(2, &un);
+ }
+ break;
+ case ':':
+ goto done;
+ case T_ANDAND:
+ case T_OROR:
+ xn = (c == T_ANDAND) ? 4 : 3;
+ if (precedence >= xn) goto done;
+ if ((n != 0) == (c == T_ANDAND)) n = subexpr(xn, &un) != 0;
+ else
+ {
+ x = pp.mode;
+ pp.mode |= INACTIVE;
+ subexpr(xn, &un);
+ pp.mode = x;
+ }
+ un = 0;
+ break;
+ case '|':
+ if (precedence > 4) goto done;
+ n |= subexpr(5, &un);
+ break;
+ case '^':
+ if (precedence > 5) goto done;
+ n ^= subexpr(6, &un);
+ break;
+ case '&':
+ if (precedence > 6) goto done;
+ n &= subexpr(7, &un);
+ break;
+ case T_EQ:
+ case T_NE:
+ if (precedence > 7) goto done;
+ n = (n == subexpr(8, &un)) == (c == T_EQ);
+ un = 0;
+ break;
+ case '<':
+ case T_LE:
+ case T_GE:
+ case '>':
+ if (precedence > 8) goto done;
+ x = subexpr(9, &un);
+ switch (c)
+ {
+ case '<':
+ switch (un)
+ {
+ case 01:
+ n = n < (unsigned long)x;
+ break;
+ case 02:
+ n = (unsigned long)n < x;
+ break;
+ case 03:
+ n = (unsigned long)n < (unsigned long)x;
+ break;
+ default:
+ n = n < x;
+ break;
+ }
+ break;
+ case T_LE:
+ switch (un)
+ {
+ case 01:
+ n = n <= (unsigned long)x;
+ break;
+ case 02:
+ n = (unsigned long)n <= x;
+ break;
+ case 03:
+ n = (unsigned long)n <= (unsigned long)x;
+ break;
+ default:
+ n = n <= x;
+ break;
+ }
+ break;
+ case T_GE:
+ switch (un)
+ {
+ case 01:
+ n = n >= (unsigned long)x;
+ break;
+ case 02:
+ n = (unsigned long)n >= x;
+ break;
+ case 03:
+ n = (unsigned long)n >= (unsigned long)x;
+ break;
+ default:
+ n = n >= x;
+ break;
+ }
+ break;
+ case '>':
+ switch (un)
+ {
+ case 01:
+ n = n > (unsigned long)x;
+ break;
+ case 02:
+ n = (unsigned long)n > x;
+ break;
+ case 03:
+ n = (unsigned long)n > (unsigned long)x;
+ break;
+ default:
+ n = n > x;
+ break;
+ }
+ break;
+ }
+ un = 0;
+ break;
+ case T_LSHIFT:
+ case T_RSHIFT:
+ if (precedence > 9) goto done;
+ x = subexpr(10, &un);
+ if (c == T_LSHIFT) n <<= x;
+ else n >>= x;
+ un >>= 1;
+ break;
+ case '+':
+ case '-':
+ if (precedence > 10) goto done;
+ x = subexpr(11, &un);
+ if (c == '+') n += x;
+ else n -= x;
+ break;
+ case '*':
+ case '/':
+ case '%':
+ if (precedence > 11) goto done;
+ x = subexpr(12, &un);
+ if (c == '*') n *= x;
+ else if (x == 0)
+ {
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "divide by zero";
+ return 0;
+ }
+ else if (c == '/') n /= x;
+ else n %= x;
+ break;
+ case '#':
+ pp.state |= DISABLE;
+ c = pplex();
+ pp.state &= ~DISABLE;
+ if (c != T_ID)
+ {
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "# must precede a predicate identifier";
+ return 0;
+ }
+ n = predicate(0);
+ goto gotoperand;
+ case T_ID:
+ n = predicate(1);
+ goto gotoperand;
+ case T_CHARCONST:
+ c = *(pp.toknxt - 1);
+ *(pp.toknxt - 1) = 0;
+ n = chrtoi(pp.token + 1);
+ *(pp.toknxt - 1) = c;
+ if (n & ~((1<<CHAR_BIT)-1))
+ {
+ if (!(pp.mode & HOSTED))
+ error(1, "'%s': multi-character character constants are not portable", pp.token);
+ }
+#if CHAR_MIN < 0
+ else n = (char)n;
+#endif
+ goto gotoperand;
+ case T_DECIMAL_U:
+ case T_DECIMAL_UL:
+ case T_OCTAL_U:
+ case T_OCTAL_UL:
+ case T_HEXADECIMAL_U:
+ case T_HEXADECIMAL_UL:
+ un |= 01;
+ /*FALLTHROUGH*/
+ case T_DECIMAL:
+ case T_DECIMAL_L:
+ case T_OCTAL:
+ case T_OCTAL_L:
+ case T_HEXADECIMAL:
+ case T_HEXADECIMAL_L:
+ n = strtoul(pp.token, NiL, 0);
+ if ((unsigned long)n > LONG_MAX) un |= 01;
+ goto gotoperand;
+ case T_WCHARCONST:
+ n = chrtoi(pp.token);
+ goto gotoperand;
+ default:
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid token";
+ return 0;
+ }
+ if (errmsg) return 0;
+ if (!operand) goto nooperand;
+ }
+ done:
+ unlex(c);
+ if (!operand)
+ {
+ nooperand:
+ if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operand expected";
+ return 0;
+ }
+ if (un) *pun |= 01;
+ return n;
+}
+
+/*
+ * preprocessor expression evaluator using modified streval(3)
+ * *pun!=0 if result is unsigned
+ */
+
+long
+ppexpr(int* pun)
+{
+ long n;
+ int opeektoken;
+ long ppstate;
+
+ ppstate = (pp.state & (CONDITIONAL|DISABLE|NOSPACE|STRIP));
+ pp.state &= ~(DISABLE|STRIP);
+ pp.state |= CONDITIONAL|NOSPACE;
+ opeektoken = peektoken;
+ peektoken = -1;
+ *pun = 0;
+ n = subexpr(0, pun);
+ if (peektoken == ':' && !errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid use of :";
+ if (errmsg)
+ {
+ error(2, "%s in expression", errmsg);
+ errmsg = 0;
+ n = 0;
+ }
+ peektoken = opeektoken;
+ pp.state &= ~(CONDITIONAL|NOSPACE);
+ pp.state |= ppstate;
+ if (*pun) debug((-4, "ppexpr() = %luU", n));
+ else debug((-4, "ppexpr() = %ld", n));
+ return n;
+}
+
+/*
+ * return non-zero if option s is set
+ */
+
+int
+ppoption(char* s)
+{
+ switch ((int)hashget(pp.strtab, s))
+ {
+ case X_ALLMULTIPLE:
+ return pp.mode & ALLMULTIPLE;
+ case X_BUILTIN:
+ return pp.mode & BUILTIN;
+ case X_CATLITERAL:
+ return pp.mode & CATLITERAL;
+ case X_COMPATIBILITY:
+ return pp.state & COMPATIBILITY;
+ case X_DEBUG:
+ return -error_info.trace;
+ case X_ELSEIF:
+ return pp.option & ELSEIF;
+ case X_FINAL:
+ return pp.option & FINAL;
+ case X_HOSTDIR:
+ return pp.mode & HOSTED;
+ case X_HOSTED:
+ return pp.flags & PP_hosted;
+ case X_INITIAL:
+ return pp.option & INITIAL;
+ case X_KEYARGS:
+ return pp.option & KEYARGS;
+ case X_LINEBASE:
+ return pp.flags & PP_linebase;
+ case X_LINEFILE:
+ return pp.flags & PP_linefile;
+ case X_LINETYPE:
+ return pp.flags & PP_linetype;
+ case X_PLUSCOMMENT:
+ return pp.option & PLUSCOMMENT;
+ case X_PLUSPLUS:
+ return pp.option & PLUSPLUS;
+ case X_PLUSSPLICE:
+ return pp.option & PLUSSPLICE;
+ case X_PRAGMAEXPAND:
+ return pp.option & PRAGMAEXPAND;
+ case X_PREDEFINED:
+ return pp.option & PREDEFINED;
+ case X_PREFIX:
+ return pp.option & PREFIX;
+ case X_PROTOTYPED:
+ return pp.option & PROTOTYPED;
+ case X_READONLY:
+ return pp.mode & READONLY;
+ case X_REGUARD:
+ return pp.option & REGUARD;
+ case X_SPACEOUT:
+ return pp.state & SPACEOUT;
+ case X_SPLICECAT:
+ return pp.option & SPLICECAT;
+ case X_SPLICESPACE:
+ return pp.option & SPLICESPACE;
+ case X_STRICT:
+ return pp.state & STRICT;
+ case X_STRINGSPAN:
+ return pp.option & STRINGSPAN;
+ case X_STRINGSPLIT:
+ return pp.option & STRINGSPLIT;
+ case X_TEST:
+ return pp.test;
+ case X_TEXT:
+ return !(pp.state & NOTEXT);
+ case X_TRANSITION:
+ return pp.state & TRANSITION;
+ case X_TRUNCATE:
+ return pp.truncate;
+ case X_WARN:
+ return pp.state & WARN;
+ default:
+ if (pp.state & WARN) error(1, "%s: unknown option name", s);
+ return 0;
+ }
+}