diff options
Diffstat (limited to 'usr/src/lib/libpp/common/ppcontrol.c')
-rw-r--r-- | usr/src/lib/libpp/common/ppcontrol.c | 2295 |
1 files changed, 2295 insertions, 0 deletions
diff --git a/usr/src/lib/libpp/common/ppcontrol.c b/usr/src/lib/libpp/common/ppcontrol.c new file mode 100644 index 0000000000..a5d9974927 --- /dev/null +++ b/usr/src/lib/libpp/common/ppcontrol.c @@ -0,0 +1,2295 @@ +/*********************************************************************** +* * +* 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 control directive support + */ + +#include "pplib.h" + +#include <regex.h> + +#define TOKOP_DUP (1<<0) +#define TOKOP_STRING (1<<1) +#define TOKOP_UNSET (1<<2) + +struct edit +{ + struct edit* next; + regex_t re; +}; + +struct map +{ + struct map* next; + regex_t re; + struct edit* edit; +}; + +#define RESTORE (COLLECTING|CONDITIONAL|DEFINITION|DIRECTIVE|DISABLE|EOF2NL|HEADER|NOSPACE|NOVERTICAL|PASSEOF|STRIP) + +/* + * common predicate assertion operations + * op is DEFINE or UNDEF + */ + +static void +assert(int op, char* pred, char* args) +{ + register struct pplist* a; + register struct ppsymbol* sym; + register struct pplist* p; + register struct pplist* q; + + if (!args) switch (op) + { + case DEFINE: + goto mark; + case UNDEF: + a = 0; + goto unmark; + } + if (a = (struct pplist*)hashget(pp.prdtab, pred)) + { + p = 0; + q = a; + while (q) + { + if (streq(q->value, args)) + { + if (op == DEFINE) return; + q = q->next; + if (p) p->next = q; + else a = q; + } + else + { + p = q; + q = q->next; + } + } + if (op == UNDEF) + { + unmark: + hashput(pp.prdtab, pred, a); + if (sym = ppsymref(pp.symtab, pred)) + sym->flags &= ~SYM_PREDICATE; + return; + } + } + if (op == DEFINE) + { + p = newof(0, struct pplist, 1, 0); + p->next = a; + p->value = strdup(args); + hashput(pp.prdtab, NiL, p); + mark: + if ((pp.state & COMPILE) && pp.truncate) return; + if (sym = ppsymset(pp.symtab, pred)) + sym->flags |= SYM_PREDICATE; + } +} + +/* + * tokenize string ppop() + * + * op PP_* op + * name option name + * s string of option values + * n option sense + * flags TOKOP_* flags + */ + +static void +tokop(int op, char* name, register char* s, register int n, int flags) +{ + register int c; + register char* t; + + if (!(flags & TOKOP_UNSET) && !n) error(2, "%s: option cannot be unset", name); + else if (!s) ppop(op, s, n); + else if (flags & TOKOP_STRING) + { + PUSH_LINE(s); + for (;;) + { + pp.state &= ~NOSPACE; + c = pplex(); + pp.state |= NOSPACE; + if (!c) break; + if (c != ' ') + ppop(op, (flags & TOKOP_DUP) ? strdup(pp.token) : pp.token, n); + } + POP_LINE(); + } + else do + { + while (*s == ' ') s++; + for (t = s; *t && *t != ' '; t++); + if (*t) *t++ = 0; + else t = 0; + if (*s) ppop(op, (flags & TOKOP_DUP) ? strdup(s) : s, n); + } while (s = t); +} + +/* + * return symbol pointer for next token macro (re)definition + */ + +static struct ppsymbol* +macsym(int tok) +{ + register struct ppsymbol* sym; + + if (tok != T_ID) + { + error(2, "%s: invalid macro name", pptokstr(pp.token, 0)); + return 0; + } + sym = pprefmac(pp.token, REF_CREATE); + if ((sym->flags & SYM_FINAL) && (pp.mode & HOSTED)) return 0; + if (sym->flags & (SYM_ACTIVE|SYM_READONLY)) + { + if (!(pp.option & ALLPOSSIBLE)) + error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active"); + return 0; + } + if (!sym->macro) sym->macro = newof(0, struct ppmacro, 1, 0); + return sym; +} + +/* + * get one space canonical pplex() line, sans '\n', and place in p + * x is max+1 pos in p + * 0 returned if line too large + * otherwise end of p ('\0') returned + */ + +static char* +getline(register char* p, char* x, int disable) +{ + register int c; + register char* s; + char* b; + long restore; + + restore = pp.state & (NOSPACE|STRIP); + pp.state &= ~(NEWLINE|NOSPACE|STRIP); + pp.state |= EOF2NL; + b = p; + while ((c = pplex()) != '\n') + { + if (disable) + { + if (c == ' ') + /*ignore*/; + else if (disable == 1) + disable = (c == T_ID && streq(pp.token, pp.pass)) ? 2 : 0; + else + { + disable = 0; + if (c == ':') + pp.state |= DISABLE; + } + } + s = pp.token; + while (*p = *s++) + if (++p >= x) + { + p = 0; + goto done; + } + } + if (p > b && *(p - 1) == ' ') + p--; + if (p >= x) + p = 0; + else + *p = 0; + done: + pp.state &= ~(NOSPACE|STRIP); + pp.state |= restore; + return p; +} + +/* + * regex error handler + */ + +void +regfatal(regex_t* p, int level, int code) +{ + char buf[128]; + + regerror(code, p, buf, sizeof(buf)); + regfree(p); + error(level, "regular expression: %s", buf); +} + +/* + * process a single directive line + */ + +int +ppcontrol(void) +{ + register char* p; + register int c; + register int n; + register char* s; + register struct ppmacro* mac; + register struct ppsymbol* sym; + struct edit* edit; + struct map* map; + struct ppfile* fp; + int o; + int directive; + long restore; + struct pptuple* rp; + struct pptuple* tp; + char* v; + int emitted; + + union + { + struct map* best; + struct ppinstk* inp; + struct pplist* list; + char* string; + struct ppsymbol* symbol; + int type; + PPLINESYNC linesync; + } var; + + static char __va_args__[] = "__VA_ARGS__"; + static int i0; + static int i1; + static int i2; + static int i3; + static int i4; + + static long n1; + static long n2; + static long n3; + + static char* p0; + static char* p1; + static char* p2; + static char* p3; + static char* p4; + static char* p5; + static char* p6; + + static struct ppmacro old; + static char* formargs[MAXFORMALS]; +#if MACKEYARGS + static char* formvals[MAXFORMALS]; +#endif + + emitted = 0; + if (pp.state & SKIPCONTROL) pp.level--; + restore = (pp.state & RESTORE)|NEWLINE; + if (pp.state & PASSTHROUGH) restore |= DISABLE; + else restore &= ~DISABLE; + pp.state &= ~(NEWLINE|RESTORE|SKIPCONTROL); + pp.state |= DIRECTIVE|DISABLE|EOF2NL|NOSPACE|NOVERTICAL; +#if COMPATIBLE + if ((pp.state & (COMPATIBILITY|STRICT)) == COMPATIBILITY || (pp.mode & HOSTED)) pp.state &= ~NOVERTICAL; +#else + if (pp.mode & HOSTED) pp.state &= ~NOVERTICAL; +#endif + switch (c = pplex()) + { + case T_DECIMAL: + case T_OCTAL: + if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "# <line> [ \"<file>\" [ <type> ] ]: non-standard directive"); + directive = INCLUDE; + goto linesync; + case T_ID: + switch (directive = (int)hashref(pp.dirtab, pp.token)) + { + case ELIF: + else_if: + if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev) + goto eatdirective; + if (pp.control <= pp.in->control) + { + error(2, "no matching #%s for #%s", dirname(IF), dirname(ELIF)); + goto eatdirective; + } + if (pp.control == (pp.in->control + 1)) pp.in->flags |= IN_noguard; + if (*pp.control & HADELSE) + { + error(2, "invalid #%s after #%s", dirname(ELIF), dirname(ELSE)); + *pp.control |= SKIP; + goto eatdirective; + } + if (*pp.control & KEPT) + { + *pp.control |= SKIP; + goto eatdirective; + } + if (directive == IFDEF || directive == IFNDEF) + { + *pp.control &= ~SKIP; + goto else_ifdef; + } + conditional: + if (ppexpr(&i1)) + { + *pp.control &= ~SKIP; + *pp.control |= KEPT; + } + else *pp.control |= SKIP; + c = (pp.state & NEWLINE) ? '\n' : ' '; + goto eatdirective; + case ELSE: + if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev) + goto eatdirective; + if ((pp.option & ELSEIF) && (c = pplex()) == T_ID && ((n = (int)hashref(pp.dirtab, pp.token)) == IF || n == IFDEF || n == IFNDEF)) + { + error(1, "#%s %s is non-standard -- use #%s", dirname(directive), dirname(n), dirname(ELIF)); + directive = n; + goto else_if; + } + if (pp.control <= pp.in->control) error(2, "no matching #%s for #%s", dirname(IF), dirname(ELSE)); + else + { + if (pp.control == (pp.in->control + 1)) pp.in->flags |= IN_noguard; + if (!(*pp.control & KEPT)) + { + *pp.control &= ~SKIP; + *pp.control |= HADELSE|KEPT; + } + else + { + if (*pp.control & HADELSE) error(2, "more than one #%s for #%s", dirname(ELSE), dirname(IF)); + *pp.control |= HADELSE|SKIP; + } + } + goto enddirective; + case ENDIF: + if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev) + goto eatdirective; + if (pp.control <= pp.in->control) error(2, "no matching #%s for #%s", dirname(IF), dirname(ENDIF)); + else if (--pp.control == pp.in->control && pp.in->symbol) + { + if (pp.in->flags & IN_endguard) pp.in->flags |= IN_noguard; + else + { + pp.in->flags &= ~IN_tokens; + pp.in->flags |= IN_endguard; + } + } + goto enddirective; + case IF: + case IFDEF: + case IFNDEF: + if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev) + goto eatdirective; + pushcontrol(); + SETIFBLOCK(pp.control); + if (*pp.control & SKIP) + { + *pp.control |= KEPT; + goto eatdirective; + } + if (directive == IF) goto conditional; + else_ifdef: + if ((c = pplex()) == T_ID) + { + sym = pprefmac(pp.token, REF_IF); + if (directive == IFNDEF && pp.control == pp.in->control + 1) + { + if (pp.in->flags & (IN_defguard|IN_endguard)) + pp.in->flags |= IN_noguard; + else + { + pp.in->flags |= IN_defguard; + if (!(pp.in->flags & IN_tokens)) + pp.in->symbol = sym ? sym : pprefmac(pp.token, REF_CREATE); + } + } + } + else + { + sym = 0; + if (!(pp.mode & HOSTED)) + error(1, "%s: invalid macro name", pptokstr(pp.token, 0)); + } + *pp.control |= ((sym != 0) == (directive == IFDEF)) ? KEPT : SKIP; + goto enddirective; + case INCLUDE: + if (*pp.control & SKIP) + { + pp.state |= HEADER; + c = pplex(); + pp.state &= ~HEADER; + goto eatdirective; + } + pp.state &= ~DISABLE; + pp.state |= HEADER|STRIP; + switch (c = pplex()) + { + case T_STRING: + p = pp.token; + do pp.token = pp.toknxt; while ((c = pplex()) == T_STRING); + *pp.token = 0; + pp.token = p; + /*FALLTHROUGH*/ + case T_HEADER: + header: + if (!*pp.token) + { + error(2, "#%s: null file name", dirname(INCLUDE)); + break; + } + if (*pp.token == '/' && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: reference to %s is not portable", dirname(INCLUDE), pp.token); + n = ppsearch(pp.token, c, SEARCH_INCLUDE); + break; + case '<': + /* + * HEADEREXPAND|HEADEREXPANDALL gets us here + */ + + if (!(p = pp.hdrbuf) && !(p = pp.hdrbuf = newof(0, char, MAXTOKEN, 0))) + error(3, "out of space"); + pp.state &= ~NOSPACE; + while ((c = pplex()) && c != '>') + { + v = p + 1; + STRCOPY(p, pp.token, s); + if (p == v && *(p - 1) == ' ' && pp.in->type != IN_MACRO) + p--; + } + pp.state |= NOSPACE; + *p++ = 0; + memcpy(pp.token, pp.hdrbuf, p - pp.hdrbuf); + c = T_HEADER; + goto header; + default: + error(2, "#%s: \"...\" or <...> argument expected", dirname(INCLUDE)); + goto eatdirective; + } + goto enddirective; + case 0: + { + regmatch_t match[10]; + + /*UNDENT*/ + p = pp.valbuf; + *p++ = '#'; + STRCOPY(p, pp.token, s); + p0 = p; + pp.mode |= EXPOSE; + pp.state |= HEADER; + p6 = getline(p, &pp.valbuf[MAXTOKEN], 0); + pp.state &= ~HEADER; + pp.mode &= ~EXPOSE; + if (!p6) + { + *p0 = 0; + error(2, "%s: directive too long", pp.valbuf); + c = 0; + goto eatdirective; + } + p1 = p2 = p3 = p4 = 0; + p5 = *p ? p + 1 : 0; + checkmap: + i0 = *p0; + p = pp.valbuf; + var.best = 0; + n = 0; + for (map = (struct map*)pp.maps; map; map = map->next) + if (!(i1 = regexec(&map->re, p, elementsof(match), match, 0))) + { + if ((c = match[0].rm_eo - match[0].rm_so) > n) + { + n = c; + var.best = map; + } + } + else if (i1 != REG_NOMATCH) + regfatal(&map->re, 3, i1); + c = '\n'; + if (map = var.best) + { + if ((pp.state & (STRICT|WARN)) && !(pp.mode & (HOSTED|RELAX))) + { + *p0 = 0; + if (!(pp.state & WARN) || strcmp(p + 1, dirname(PRAGMA))) + error(1, "%s: non-standard directive", p); + *p0 = i0; + } + if (!(*pp.control & SKIP)) + { + n = 0; + for (edit = map->edit; edit; edit = edit->next) + if (!(i0 = regexec(&edit->re, p, elementsof(match), match, 0))) + { + n++; + if (i0 = regsubexec(&edit->re, p, elementsof(match), match)) + regfatal(&edit->re, 3, i0); + p = edit->re.re_sub->re_buf; + if (edit->re.re_sub->re_flags & REG_SUB_STOP) + break; + } + else if (i0 != REG_NOMATCH) + regfatal(&edit->re, 3, i0); + if (n && *p) + { + p1 = s = oldof(0, char, 0, strlen(p) + 32); + while (*s = *p++) s++; + debug((-4, "map: %s", p1)); + *s++ = '\n'; + *s = 0; + error_info.line++; + PUSH_RESCAN(p1); + error_info.line--; + directive = LINE; + } + } + goto donedirective; + } + if (directive != PRAGMA && (!(*pp.control & SKIP) || !(pp.mode & (HOSTED|RELAX)))) + { + *p0 = 0; + error(1, "%s: unknown directive", pptokstr(pp.valbuf, 0)); + *p0 = i0; + } + pass: + if (!(*pp.control & SKIP) && pp.pragma && !(pp.state & NOTEXT) && (directive == PRAGMA || !(pp.mode & INIT))) + { + *p0 = 0; + if (p2) *p2 = 0; + if (p4) + { + if (p4 == p5) + { + p5 = strcpy(pp.tmpbuf, p5); + if (p = strchr(p5, MARK)) + { + s = p; + while (*p) + if ((*s++ = *p++) == MARK && *p == MARK) p++; + *s = 0; + } + } + *p4 = 0; + } + if (p = (char*)memchr(pp.valbuf + 1, MARK, p6 - pp.valbuf - 1)) + { + s = p; + while (p < p6) switch (*s++ = *p++) + { + case 0: + s = p; + break; + case MARK: + p++; + break; + } + *s = 0; + } + (*pp.pragma)(pp.valbuf + 1, p1, p3, p5, (pp.state & COMPILE) || (pp.mode & INIT) != 0); + emitted = 1; + } + goto donedirective; + + /*INDENT*/ + } + } + if (*pp.control & SKIP) goto eatdirective; + switch (directive) + { +#if MACDEF + case ENDMAC: + c = pplex(); + error(2, "no matching #%s for #%s", dirname(MACDEF), dirname(ENDMAC)); + goto enddirective; +#endif +#if MACDEF + case MACDEF: + if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: non-standard directive", pp.token); + /*FALLTHROUGH*/ +#endif + case DEFINE: + n2 = error_info.line; + if ((c = pplex()) == '#' && directive == DEFINE) + goto assertion; + if (c == '<') + { + n = 1; + c = pplex(); + } + else + n = 0; + if (!(sym = macsym(c))) + goto eatdirective; + if (pp.truncate) + ppfsm(FSM_MACRO, pp.token); + mac = sym->macro; + if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev && mac->value) + goto eatdirective; + if (n) + goto tuple; + old = *mac; + i0 = sym->flags; + sym->flags &= ~(SYM_BUILTIN|SYM_EMPTY|SYM_FINAL|SYM_FUNCTION|SYM_INIT|SYM_INITIAL|SYM_MULTILINE|SYM_NOEXPAND|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC); +#if MACDEF + if (directive == MACDEF) + sym->flags |= SYM_MULTILINE; +#endif + mac->arity = 0; + mac->formals = 0; + mac->value = 0; + pp.state &= ~NOSPACE; + pp.state |= DEFINITION|NOEXPAND; + switch (c = pplex()) + { + case '(': + sym->flags |= SYM_FUNCTION; + pp.state |= NOSPACE; +#if MACKEYARGS + if (pp.option & KEYARGS) + { + n = 2 * MAXTOKEN; + p = mac->formals = oldof(0, char, 0, n); + if ((c = pplex()) == T_ID) for (;;) + { + if (mac->arity < MAXFORMALS) + { + if (mac->arity) p++; + formargs[mac->arity] = p; + STRAPP(p, pp.token, s); + formvals[mac->arity++] = p1 = p; + if (mac->arity == 1) *p++ = ' '; + *p++ = ' '; + *p = 0; + } + else error(2, "%s: formal argument %s ignored", sym->name, pp.token); + switch (c = pplex()) + { + case '=': + c = pplex(); + break; + case ',': + break; + default: + goto endformals; + } + pp.state &= ~NOSPACE; + p0 = 0; + for (;;) + { + switch (c) + { + case '\n': + goto endformals; + case '(': + p0++; + break; + case ')': + if (!p0--) + { + if (p > formvals[mac->arity - 1] && *(p - 1) == ' ') *--p = 0; + goto endformals; + } + break; + case ',': + if (!p0) + { + if (p > formvals[mac->arity - 1] && *(p - 1) == ' ') *--p = 0; + goto nextformal; + } + break; + case ' ': + if (p > formvals[mac->arity - 1] && *(p - 1) == ' ') continue; + break; + } + STRCOPY(p, pp.token, s); + if (p > &mac->formals[n - MAXTOKEN] && (s = newof(mac->formals, char, n += MAXTOKEN, 0)) != mac->formals) + { + n1 = s - mac->formals; + for (n = 0; n < mac->arity; n++) + { + formargs[n] += n1; + formvals[n] += n1; + } + c = p - mac->formals; + mac->formals = s; + p = mac->formals + c; + } + c = pplex(); + } + nextformal: + pp.state |= NOSPACE; + if ((c = pplex()) != T_ID) + { + c = ','; + break; + } + } + endformals: /*NOP*/; + } + else +#endif + { + p = mac->formals = oldof(0, char, 0, MAXFORMALS * (MAXID + 1)); + c = pplex(); +#if COMPATIBLE + if ((pp.state & COMPATIBILITY) && c == ',') + { + if ((pp.state & WARN) && !(pp.mode & HOSTED)) + error(1, "%s: macro formal argument expected", sym->name); + while ((c = pplex()) == ','); + } +#endif + for (;;) + { + if (c == T_VARIADIC) + { + if (sym->flags & SYM_VARIADIC) + error(2, "%s: %s: duplicate macro formal argument", sym->name, pp.token); + sym->flags |= SYM_VARIADIC; + v = __va_args__; + } + else if (c == T_ID) + { + v = pp.token; + if (sym->flags & SYM_VARIADIC) + error(2, "%s: %s: macro formal argument cannot follow ...", sym->name, v); + else if (streq(v, __va_args__)) + error(2, "%s: %s: invalid macro formal argument", sym->name, v); + } + else + break; + if (mac->arity < MAXFORMALS) + { + for (n = 0; n < mac->arity; n++) + if (streq(formargs[n], v)) + error(2, "%s: %s: duplicate macro formal argument", sym->name, v); + formargs[mac->arity++] = p; + STRAPP(p, v, s); + } + else + error(2, "%s: %s: macro formal argument ignored", sym->name, v); + if ((c = pplex()) == ',') + { + c = pplex(); +#if COMPATIBLE + if ((pp.state & COMPATIBILITY) && c == ',') + { + if ((pp.state & WARN) && !(pp.mode & HOSTED)) + error(1, "%s: macro formal argument expected", sym->name); + while ((c = pplex()) == ','); + } +#endif + } + else if (c != T_VARIADIC) + break; + else + { + if (sym->flags & SYM_VARIADIC) + error(2, "%s: %s: duplicate macro formal argument", sym->name, pp.token); + sym->flags |= SYM_VARIADIC; + c = pplex(); + break; + } + } + if (mac->arity && (s = newof(mac->formals, char, p - mac->formals, 0)) != mac->formals) + { + n1 = s - mac->formals; + for (n = 0; n < mac->arity; n++) + formargs[n] += n1; + mac->formals = s; + } + } + if (!mac->arity) + { + free(mac->formals); + mac->formals = 0; + } + switch (c) + { + case ')': +#if MACKEYARGS + pp.state |= NOEXPAND|NOSPACE; +#else + pp.state |= NOEXPAND; +#endif + c = pplex(); + break; + default: + error(2, "%s: invalid macro formal argument list", sym->name); + if (mac->formals) + { + free(mac->formals); + mac->formals = 0; + mac->arity = 0; + } + free(mac); + sym->macro = 0; + goto eatdirective; + } + pp.state &= ~NOSPACE; + break; + case ' ': + case '\t': + c = pplex(); + break; + } + n = 2 * MAXTOKEN; +#if MACKEYARGS + p1 = p; +#endif + p = mac->value = oldof(0, char, 0, n); + var.type = 0; + n1 = 0; +#if MACDEF + i2 = i3 = 0; + n3 = pp.state; +#endif + if ((pp.option & PLUSPLUS) && (pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY) + switch (c) + { + case '+': + case '-': + case '&': + case '|': + case '<': + case '>': + case ':': + case '=': + *p++ = ' '; + break; + } + o = 0; + for (;;) + { + switch (c) + { + case T_ID: + for (c = 0; c < mac->arity; c++) + if (streq(formargs[c], pp.token)) + { +#if COMPATIBLE + if (!(pp.state & COMPATIBILITY)) +#endif + if (var.type != TOK_TOKCAT && p > mac->value && *(p - 1) != ' ' && !(pp.option & PRESERVE)) *p++ = ' '; + *p++ = MARK; +#if COMPATIBLE + if ((pp.state & (COMPATIBILITY|TRANSITION)) == COMPATIBILITY) *p++ = 'C'; + else +#endif + *p++ = (n1 || var.type == TOK_TOKCAT) ? 'C' : 'A'; + *p++ = c + ARGOFFSET; + var.type = TOK_FORMAL|TOK_ID; + c = '>'; + goto checkvalue; + } + if (var.type == TOK_BUILTIN) switch ((int)hashget(pp.strtab, pp.token)) + { + case V_DEFAULT: + case V_EMPTY: + sym->flags |= SYM_EMPTY; + break; + } + else if (pp.hiding && (var.symbol = ppsymref(pp.symtab, pp.token)) && var.symbol->hidden) + { + for (var.inp = pp.in; var.inp->type != IN_FILE && var.inp->prev; var.inp = var.inp->prev); + p += sfsprintf(p, MAXTOKEN, "_%d_%s_hIDe", var.inp->hide, pp.token); + var.type = TOK_ID; + goto checkvalue; + } + var.type = TOK_ID; + break; + case '#': + var.type = 0; +#if MACDEF + if (!(sym->flags & (SYM_FUNCTION|SYM_MULTILINE))) break; +#else + if (!(sym->flags & SYM_FUNCTION)) break; +#endif + pp.state |= NOSPACE; + c = pplex(); + if (c == '@') + { + c = pplex(); + i4 = 'S'; + } + else i4 = 'Q'; + pp.state &= ~NOSPACE; + if (c != T_ID) c = mac->arity; + else for (c = 0; c < mac->arity; c++) + if (streq(formargs[c], pp.token)) + break; + if (c >= mac->arity) + { +#if MACDEF + if (sym->flags & SYM_MULTILINE) + { + if (n3 & NEWLINE) + { + pp.state &= ~NOEXPAND; + switch ((int)hashref(pp.dirtab, pp.token)) + { + case ENDMAC: + if (!i2--) goto gotdefinition; + break; + case INCLUDE: + /* PARSE HEADER constant */ + break; + case MACDEF: + i2++; + break; + } + *p++ = '#'; + } + } + else +#endif +#if COMPATIBLE + if (pp.state & COMPATIBILITY) *p++ = '#'; + else +#endif + error(2, "# must precede a formal parameter"); + } + else + { + if (p > mac->value && ppisidig(*(p - 1)) && !(pp.option & PRESERVE)) *p++ = ' '; + *p++ = MARK; + *p++ = i4; + *p++ = c + ARGOFFSET; + goto checkvalue; + } + break; + case T_TOKCAT: + if (p <= mac->value) error(2, "%s lhs operand omitted", pp.token); + else + { + if (*(p - 1) == ' ') p--; + if (var.type == (TOK_FORMAL|TOK_ID)) *(p - 2) = 'C'; + } + pp.state |= NOSPACE; + c = pplex(); + pp.state &= ~NOSPACE; + if (c == '\n') error(2, "%s rhs operand omitted", pptokchr(T_TOKCAT)); + var.type = TOK_TOKCAT; + continue; + case '(': + if (*pp.token == '#') + { + var.type = TOK_BUILTIN; + n1++; + } + else + { + var.type = 0; + if (n1) n1++; + } + break; + case ')': + var.type = 0; + if (n1) n1--; + break; + case T_STRING: + case T_CHARCONST: + pp.state &= ~NOEXPAND; + var.type = 0; + if (strchr(pp.token, MARK)) pp.state &= ~NOEXPAND; +#if COMPATIBLE + /*UNDENT*/ + + if ((sym->flags & SYM_FUNCTION) && (pp.state & (COMPATIBILITY|TRANSITION))) + { + char* v; + + s = pp.token; + for (;;) + { + if (!*s) goto checkvalue; + if (ppisid(*s)) + { + v = s; + while (ppisid(*++s)); + i1 = *s; + *s = 0; + for (c = 0; c < mac->arity; c++) + if (streq(formargs[c], v)) + { + *p++ = MARK; + *p++ = 'C'; + *p++ = c + ARGOFFSET; + if (!(pp.mode & HOSTED) && (!(pp.state & COMPATIBILITY) || (pp.state & WARN))) switch (*pp.token) + { + case '"': + error(1, "use the # operator to \"...\" quote macro arguments"); + break; + case '\'': + error(1, "macro arguments should be '...' quoted before substitution"); + break; + } + goto quotearg; + } + STRCOPY2(p, v); + quotearg: + *s = i1; + } + else *p++ = *s++; + } + } + /*INDENT*/ +#endif + break; + case '\n': +#if MACDEF + if (sym->flags & SYM_MULTILINE) + { + if (pp.state & EOF2NL) + { + error_info.line++; + pp.state |= HIDDEN; + pp.hidden++; + var.type = 0; + if (!i3++) + goto checkvalue; + break; + } + pp.state |= EOF2NL; + error(2, "%s: missing #%s", sym->name, dirname(ENDMAC)); + } +#endif + goto gotdefinition; + case 0: + c = '\n'; + goto gotdefinition; +#if COMPATIBLE + case ' ': + if (pp.state & COMPATIBILITY) var.type = 0; + if (pp.option & PRESERVE) break; + if (p > mac->value && *(p - 1) != ' ') *p++ = ' '; + goto checkvalue; + case '\t': + if (var.type & TOK_ID) + { + while ((c = pplex()) == '\t'); + if (c == T_ID) + { + if (var.type == (TOK_FORMAL|TOK_ID)) *(p - 2) = 'C'; + var.type = TOK_TOKCAT; + if (pp.state & WARN) error(1, "use the ## operator to concatenate macro arguments"); + } + else var.type = 0; + continue; + } + var.type = 0; + if (pp.option & PRESERVE) break; + if (p > mac->value && *(p - 1) != ' ') *p++ = ' '; + goto checkvalue; +#endif + case MARK: + pp.state &= ~NOEXPAND; + /*FALLTHROUGH*/ + + default: + var.type = 0; + break; + } + STRCOPY(p, pp.token, s); + checkvalue: + o = c; + if (p > &mac->value[n - MAXTOKEN] && (s = newof(mac->value, char, n += MAXTOKEN, 0)) != mac->value) + { + c = p - mac->value; + mac->value = s; + p = mac->value + c; + } +#if MACDEF + n3 = pp.state; +#endif + c = pplex(); + } + gotdefinition: + while (p > mac->value && *(p - 1) == ' ') p--; + if (p > mac->value && (pp.option & PLUSPLUS) && (pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY) + switch (o) + { + case '+': + case '-': + case '&': + case '|': + case '<': + case '>': + case ':': + case '=': + *p++ = ' '; + break; + } + *p = 0; +#if MACKEYARGS + if (!mac->arity) /* ok */; + else if (pp.option & KEYARGS) + { + p0 = mac->formals; + mac->formkeys = newof(0, struct ppkeyarg, n, p1 - p0 + 1); + s = (char*)&mac->formkeys[mac->arity]; + (void)memcpy(s, p0, p1 - p0 + 1); + free(p0); + for (n = 0; n < mac->arity; n++) + { + mac->formkeys[n].name = s + (formargs[n] - p0); + mac->formkeys[n].value = s + (formvals[n] - p0); + } + } + else +#endif + for (n = 1; n < mac->arity; n++) + *(formargs[n] - 1) = ','; + if (old.value) + { + if ((i0 & SYM_FUNCTION) != (sym->flags & SYM_FUNCTION) || old.arity != mac->arity || !streq(old.value, mac->value)) goto redefined; + if (!old.formals) + { + if (mac->formals) goto redefined; + } + else if (mac->formals) + { +#if MACKEYARGS + if (pp.option & KEYARGS) + { + for (n = 0; n < mac->arity; n++) + if (!streq(mac->formkeys[n].name, old.formkeys[n].name) || !streq(mac->formkeys[n].value, old.formkeys[n].value)) + goto redefined; + } + else +#endif + if (!streq(mac->formals, old.formals)) goto redefined; + } +#if MACKEYARGS + if (pp.option & KEYARGS) + { + if (mac->formkeys) free(mac->formkeys); + mac->formkeys = old.formkeys; + } + else +#endif + { + if (mac->formals) free(mac->formals); + mac->formals = old.formals; + } + free(mac->value); + mac->value = old.value; + goto benign; + redefined: + if (!(pp.mode & HOSTED) || !(i0 & SYM_INITIAL)) + error(1, "%s redefined", sym->name); +#if MACKEYARGS + if ((pp.option & KEYARGS) && mac->formkeys) + free(mac->formkeys); +#endif +#if MACKEYARGS + if (!(pp.option & KEYARGS)) +#endif + if (old.formals) free(old.formals); + free(old.value); + } + else if (!pp.truncate) ppfsm(FSM_MACRO, sym->name); + mac->value = newof(mac->value, char, (mac->size = p - mac->value) + 1, 0); + if ((pp.option & (DEFINITIONS|PREDEFINITIONS|REGUARD)) && !sym->hidden && !(sym->flags & SYM_MULTILINE) && ((pp.option & PREDEFINITIONS) || !(pp.mode & INIT)) && ((pp.option & (DEFINITIONS|PREDEFINITIONS)) || !(pp.state & NOTEXT))) + { + ppsync(); + ppprintf("#%s %s", dirname(DEFINE), sym->name); + if (sym->flags & SYM_FUNCTION) + { + ppputchar('('); + if (mac->formals) + ppprintf("%s", mac->formals); + ppputchar(')'); + } + if ((p = mac->value) && *p) + { + ppputchar(' '); + i0 = 0; + while (n = *p++) + { + if (n != MARK || (n = *p++) == MARK) + { + ppputchar(n); + i0 = ppisid(n); + } + else + { + if (n == 'Q') + ppputchar('#'); + else if (i0) + { + ppputchar('#'); + ppputchar('#'); + } + s = formargs[*p++ - ARGOFFSET]; + while ((n = *s++) && n != ',') + ppputchar(n); + if (ppisid(*p) || *p == MARK) + { + ppputchar('#'); + ppputchar('#'); + } + i0 = 0; + } + ppcheckout(); + } + } + emitted = 1; + } + benign: + if (pp.mode & BUILTIN) sym->flags |= SYM_BUILTIN; + if (pp.option & FINAL) sym->flags |= SYM_FINAL; + if (pp.mode & INIT) sym->flags |= SYM_INIT; + if (pp.option & INITIAL) sym->flags |= SYM_INITIAL; + if (pp.state & NOEXPAND) sym->flags |= SYM_NOEXPAND; + if (pp.option & PREDEFINED) sym->flags |= SYM_PREDEFINED; + if (pp.mode & READONLY) sym->flags |= SYM_READONLY; + if (pp.macref) (*pp.macref)(sym, error_info.file, n2, mac ? error_info.line - n2 + 1 : REF_UNDEF, mac ? strsum(mac->value, (long)mac->arity) : 0L); + break; + assertion: + c = pplex(); + if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s #%s: assertions are non-standard", dirname(directive), pptokstr(pp.token, 0)); + if (c != T_ID) + { + error(2, "%s: invalid predicate name", pptokstr(pp.token, 0)); + goto eatdirective; + } + switch ((int)hashref(pp.strtab, pp.token)) + { + case X_DEFINED: + case X_EXISTS: + case X_STRCMP: + error(2, "%s is a builtin predicate", pp.token); + goto eatdirective; + case X_SIZEOF: + error(2, "%s cannot be a predicate", pp.token); + goto eatdirective; + } + strcpy(pp.tmpbuf, pp.token); + switch (pppredargs()) + { + case T_ID: + case T_STRING: + assert(directive, pp.tmpbuf, pp.args); + break; + case 0: + assert(directive, pp.tmpbuf, NiL); + break; + default: + error(2, "invalid predicate argument list"); + goto eatdirective; + } + break; + tuple: + pp.state |= DEFINITION|NOEXPAND|NOSPACE; + rp = 0; + tp = mac->tuple; + if (!tp && !mac->value) + ppfsm(FSM_MACRO, sym->name); + while ((c = pplex()) && c != '>' && c != '\n') + { + for (; tp; tp = tp->nomatch) + if (streq(tp->token, pp.token)) + break; + if (!tp) + { + if (!(tp = newof(0, struct pptuple, 1, strlen(pp.token)))) + error(3, "out of space"); + strcpy(tp->token, pp.token); + if (rp) + { + tp->nomatch = rp; + rp->nomatch = tp; + } + else + { + tp->nomatch = mac->tuple; + mac->tuple = tp; + } + } + rp = tp; + tp = tp->match; + } + pp.state &= ~NOSPACE; + if (!rp || c != '>') + error(2, "%s: > omitted in tuple macro definition", sym->name); + else + { + n = 2 * MAXTOKEN; + p = v = oldof(0, char, 0, n); + while ((c = pplex()) && c != '\n') + if (p > v || c != ' ') + { + STRCOPY(p, pp.token, s); + if (p > &v[n - MAXTOKEN] && (s = newof(v, char, n += MAXTOKEN, 0)) != v) + { + c = p - v; + v = s; + p = v + c; + } + } + while (p > v && *(p - 1) == ' ') + p--; + n = p - v; + tp = newof(0, struct pptuple, 1, n); + strcpy(tp->token, v); + tp->match = rp->match; + rp->match = tp; + } + goto benign; + case WARNING: + if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: non-standard directive", pp.token); + /*FALLTHROUGH*/ + case ERROR: + pp.state &= ~DISABLE; + p = pp.tmpbuf; + while ((c = pplex()) != '\n') + if (p + strlen(pp.token) < &pp.tmpbuf[MAXTOKEN]) + { + STRCOPY(p, pp.token, s); + pp.state &= ~NOSPACE; + } + *p = 0; + p = *pp.tmpbuf ? pp.tmpbuf : ((directive == WARNING) ? "user warning" : "user error"); + n = (directive == WARNING) ? 1 : 3; + error(n, "%s", p); + break; + case LET: + n2 = error_info.line; + if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: non-standard directive", pp.token); + if (!(sym = macsym(c = pplex()))) goto eatdirective; + if ((c = pplex()) != '=') + { + error(2, "%s: = expected", sym->name); + goto eatdirective; + } + sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_MULTILINE|SYM_PREDEFINED|SYM_VARIADIC); + mac = sym->macro; + mac->arity = 0; + if (mac->value) + { + if (!(sym->flags & SYM_REDEFINE) && !sym->hidden) + error(1, "%s: redefined", sym->name); +#if MACKEYARGS + if ((pp.option & KEYARGS) && mac->formkeys) free(mac->formkeys); + else +#endif + free(mac->formals); + mac->formals = 0; + n = strlen(mac->value) + 1; + } + else + { + ppfsm(FSM_MACRO, sym->name); + n = 0; + } + n1 = ppexpr(&i1); + if (i1) c = sfsprintf(pp.tmpbuf, MAXTOKEN, "%luU", n1); + else c = sfsprintf(pp.tmpbuf, MAXTOKEN, "%ld", n1); + if (n < ++c) + { + if (mac->value) free(mac->value); + mac->value = oldof(0, char, 0, c); + } + strcpy(mac->value, pp.tmpbuf); + sym->flags |= SYM_REDEFINE; + c = (pp.state & NEWLINE) ? '\n' : ' '; + goto benign; + case LINE: + pp.state &= ~DISABLE; + if ((c = pplex()) == '#') + { + c = pplex(); + directive = INCLUDE; + } + if (c != T_DECIMAL && c != T_OCTAL) + { + error(1, "#%s: line number expected", dirname(LINE)); + goto eatdirective; + } + linesync: + n = error_info.line; + error_info.line = strtol(pp.token, NiL, 0); + if (error_info.line == 0 && directive == LINE && (pp.state & STRICT) && !(pp.mode & HOSTED)) + error(1, "#%s: line number should be > 0", dirname(LINE)); + pp.state &= ~DISABLE; + pp.state |= STRIP; + switch (c = pplex()) + { + case T_STRING: + s = error_info.file; + if (*(p = pp.token)) pathcanon(p, 0); + fp = ppsetfile(p); + error_info.file = fp->name; + if (error_info.line == 1) + ppmultiple(fp, INC_TEST); + switch (c = pplex()) + { + case '\n': + break; + case T_DECIMAL: + case T_OCTAL: + if (directive == LINE && (pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: integer file type argument is non-standard", dirname(LINE)); + break; + default: + error(1, "#%s: integer file type argument expected", dirname(LINE)); + break; + } + if (directive == LINE) pp.in->flags &= ~IN_ignoreline; + else if (pp.incref) + { + if (error_info.file != s) + { + switch (*pp.token) + { + case PP_sync_push: + if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT); + else (*pp.incref)(s, error_info.file, n, PP_SYNC_PUSH); + break; + case PP_sync_pop: + if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT); + else (*pp.incref)(s, error_info.file, n - 1, PP_SYNC_POP); + break; + case PP_sync_ignore: + if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT); + else + { + (*pp.incref)(s, error_info.file, n, PP_SYNC_IGNORE); + error_info.file = s; + } + break; + default: + if (*s) + { + if (fp == pp.insert) + pp.insert = 0; + else if (error_info.line == 1 && !pp.insert) + (*pp.incref)(s, error_info.file, n, PP_SYNC_PUSH); + else + { + if (!pp.insert) pp.insert = ppgetfile(s); + (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT); + } + } + break; + } + } + } + break; + case '\n': + break; + default: + error(1, "#%s: \"file-name\" expected", dirname(LINE)); + break; + } + if (directive == LINE && (pp.in->flags & IN_ignoreline)) + error_info.line = n + 1; + else + { + pp.hidden = 0; + pp.state &= ~HIDDEN; + if (pp.linesync) + { +#if CATSTRINGS + if (pp.state & JOINING) pp.state |= HIDDEN|SYNCLINE; + else +#endif + { + s = pp.lineid; + n = pp.flags; + if (directive == LINE) + { + pp.flags &= ~PP_linetype; + if (pp.macref) pp.lineid = dirname(LINE); + } + (*pp.linesync)(error_info.line, error_info.file); + pp.flags = n; + pp.lineid = s; + } + } + } + directive = LINE; + break; + case PRAGMA: + /* + * #pragma [STDC] [pass:] [no]option [arg ...] + * + * pragma args are not expanded by default + * + * if STDC is present then it is silently passed on + * + * if pass is pp.pass then the option is used + * and verified but is not passed on + * + * if pass is omitted then the option is passed on + * + * otherwise if pass is non-null and not pp.pass then + * the option is passed on but not used + * + * if the line does not match this form then + * it is passed on unchanged + * + * #directive pass: option [...] + * ^ ^ ^ ^ ^ ^ ^ ^ + * pp.valbuf p0 p1 p2 p3 p4 p5 p6 + * + * p? 0 if component omitted + * i0 0 if ``no''option + */ + + p = pp.valbuf; + *p++ = '#'; + STRCOPY(p, pp.token, s); + p0 = p; + if (pp.option & PRAGMAEXPAND) + pp.state &= ~DISABLE; + if (!(p6 = getline(p, &pp.valbuf[MAXTOKEN], !!(pp.option & PRAGMAEXPAND)))) + { + *p0 = 0; + error(2, "%s: directive too long", pp.valbuf); + c = 0; + goto eatdirective; + } + p1 = ++p; + while (ppisid(*p)) + p++; + if (p == p1) + { + p5 = p; + p4 = 0; + p3 = 0; + p2 = 0; + p1 = 0; + } + else if (*p != ':') + { + p5 = *p ? p + (*p == ' ') : 0; + p4 = p; + p3 = p1; + p2 = 0; + p1 = 0; + } + else + { + p2 = p++; + p3 = p; + while (ppisid(*p)) + p++; + if (p == p3) + { + p4 = p1; + p3 = 0; + p2 = 0; + p1 = 0; + } + else + p4 = p; + p5 = *p4 ? p4 + (*p4 == ' ') : 0; + } + if (!p1 && p3 && (p4 - p3) == 4 && strneq(p3, "STDC", 4)) + goto pass; + if ((pp.state & WARN) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: non-standard directive", dirname(PRAGMA)); + i0 = !p3 || *p3 != 'n' || *(p3 + 1) != 'o'; + if (!p3) + goto checkmap; + if (p1) + { + *p2 = 0; + n = streq(p1, pp.pass); + *p2 = ':'; + if (!n) + goto checkmap; + } + else + n = 0; + i2 = *p4; + *p4 = 0; + if (((i1 = (int)hashref(pp.strtab, p3 + (i0 ? 0 : 2))) < 1 || i1 > X_last_option) && (i0 || (i1 = (int)hashref(pp.strtab, p3)) > X_last_option)) + i1 = 0; + if ((pp.state & (COMPATIBILITY|STRICT)) == STRICT && !(pp.mode & (HOSTED|RELAX))) + { + if (pp.optflags[i1] & OPT_GLOBAL) + goto donedirective; + if (n || (pp.mode & WARN)) + { + n = 0; + error(1, "#%s: non-standard directive ignored", dirname(PRAGMA)); + } + i1 = 0; + } + if (!n) + { + if (!(pp.optflags[i1] & OPT_GLOBAL)) + { + *p4 = i2; + goto checkmap; + } + if (!(pp.optflags[i1] & OPT_PASS)) + n = 1; + } + else if (!i1) + error(2, "%s: unknown option", p1); + else if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "%s: non-standard option", p1); + p = p5; + switch (i1) + { + case X_ALLMULTIPLE: + ppop(PP_MULTIPLE, i0); + break; + case X_ALLPOSSIBLE: + setoption(ALLPOSSIBLE, i0); + break; + case X_BUILTIN: + setmode(BUILTIN, i0); + break; + case X_CATLITERAL: + setmode(CATLITERAL, i0); + if (pp.mode & CATLITERAL) + setoption(STRINGSPLIT, 0); + break; + case X_CDIR: + tokop(PP_CDIR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP); + break; + case X_CHECKPOINT: +#if CHECKPOINT + ppload(p); +#else + error(3, "%s: preprocessor not compiled with checkpoint enabled", p3); +#endif + break; + case X_CHOP: + tokop(PP_CHOP, p3, p, i0, TOKOP_UNSET|TOKOP_STRING); + break; + case X_COMPATIBILITY: + ppop(PP_COMPATIBILITY, i0); + break; + case X_DEBUG: + error_info.trace = i0 ? (p ? -strtol(p, NiL, 0) : -1) : 0; + break; + case X_ELSEIF: + setoption(ELSEIF, i0); + break; + case X_EXTERNALIZE: + setmode(EXTERNALIZE, i0); + break; + case X_FINAL: + setoption(FINAL, i0); + break; + case X_HEADEREXPAND: + setoption(HEADEREXPAND, i0); + break; + case X_HEADEREXPANDALL: + setoption(HEADEREXPANDALL, i0); + break; + case X_HIDE: + case X_NOTE: + PUSH_LINE(p); + /* UNDENT...*/ + while (c = pplex()) + { + if (c != T_ID) error(1, "%s: %s: identifier expected", p3, pp.token); + else if (sym = ppsymset(pp.symtab, pp.token)) + { + if (i1 == X_NOTE) + { + sym->flags &= ~SYM_NOTICED; + ppfsm(FSM_MACRO, sym->name); + } + else if (i0) + { + if (!sym->hidden && !(sym->hidden = newof(0, struct pphide, 1, 0))) + error(3, "out of space"); + if (!sym->macro) + ppfsm(FSM_MACRO, sym->name); + if (!sym->hidden->level++) + { + pp.hiding++; + if (sym->macro && !(sym->flags & (SYM_ACTIVE|SYM_READONLY))) + { + sym->hidden->macro = sym->macro; + sym->macro = 0; + sym->hidden->flags = sym->flags; + sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC); + } + } + } + else if (sym->hidden) + { + if ((mac = sym->macro) && !(sym->flags & (SYM_ACTIVE|SYM_READONLY))) + { + if (mac->formals) free(mac->formals); + free(mac->value); + free(mac); + sym->macro = 0; + sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC); + } + if (!--sym->hidden->level) + { + pp.hiding--; + if (sym->hidden->macro) + { + sym->macro = sym->hidden->macro; + sym->flags = sym->hidden->flags; + } + free(sym->hidden); + sym->hidden = 0; + } + } + } + } + /*...INDENT*/ + POP_LINE(); + break; + case X_HOSTDIR: + tokop(PP_HOSTDIR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP); + break; + case X_HOSTED: + setmode(HOSTED, i0); + break; + case X_HOSTEDTRANSITION: + setmode(HOSTEDTRANSITION, i0); + break; + case X_ID: + tokop(PP_ID, p3, p, i0, TOKOP_UNSET|TOKOP_STRING); + break; + case X_IGNORE: + tokop(PP_IGNORE, p3, p, i0, TOKOP_UNSET|TOKOP_STRING); + break; + case X_INCLUDE: + tokop(PP_INCLUDE, p3, p, i0, TOKOP_STRING|TOKOP_DUP); + break; + case X_INITIAL: + setoption(INITIAL, i0); + break; + case X_KEYARGS: + ppop(PP_KEYARGS, i0); + break; + case X_LINE: + if (pp.linesync) pp.olinesync = pp.linesync; + pp.linesync = i0 ? pp.olinesync : (PPLINESYNC)0; + break; + case X_LINEBASE: + ppop(PP_LINEBASE, i0); + break; + case X_LINEFILE: + ppop(PP_LINEFILE, i0); + break; + case X_LINEID: + ppop(PP_LINEID, i0 ? p : (char*)0); + break; + case X_LINETYPE: + ppop(PP_LINETYPE, i0 ? (p ? strtol(p, NiL, 0) : 1) : 0); + break; + case X_MACREF: + if (!p) + { + if (i0 && !pp.macref) + { + ppop(PP_LINETYPE, 1); + ppop(PP_MACREF, ppmacref); + } + else error(2, "%s: option cannot be unset", p3); + } + else if (s = strchr(p, ' ')) + { + if (pp.macref && (s = strchr(p, ' '))) + { + *s++ = 0; + c = strtol(s, NiL, 0); + var.type = pp.truncate; + pp.truncate = PPTOKSIZ; + (*pp.macref)(pprefmac(p, REF_CREATE), error_info.file, error_info.line - (c == REF_NORMAL ? 2 : 1), c, (s = strchr(s, ' ')) ? strtol(s, NiL, 0) : 0L); + pp.truncate = var.type; + } + error_info.line -= 2; + } + break; + case X_MAP: + /*UNDENT*/ + /* + * #pragma pp:map [id ...] "/from/[,/to/]" [ "/old/new/[glnu]" ... ] + */ + + if (!i0) + { + error(2, "%s: option cannot be unset", p3); + goto donedirective; + } + if (!p5) + { + error(2, "%s: address argument expected", p3); + goto donedirective; + } + PUSH_LINE(p5); + while ((c = pplex()) == T_ID) + { + sfsprintf(pp.tmpbuf, MAXTOKEN, "__%s__", s = pp.token); + if (c = (int)hashget(pp.dirtab, s)) + { + hashput(pp.dirtab, 0, 0); + hashput(pp.dirtab, pp.tmpbuf, c); + } + if (c = (int)hashget(pp.strtab, s)) + { + hashput(pp.strtab, 0, 0); + hashput(pp.strtab, pp.tmpbuf, c); + } + } + if (c != T_STRING || !*(s = pp.token)) + { + if (c) + error(2, "%s: %s: address argument expected", p3, pptokstr(pp.token, 0)); + goto eatmap; + } + map = newof(0, struct map, 1, 0); + + /* + * /from/ + */ + + if (i0 = regcomp(&map->re, s, REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL)) + regfatal(&map->re, 3, i0); + if (*(s += map->re.re_npat)) + { + error(2, "%s: invalid characters after pattern: %s ", p3, s); + goto eatmap; + } + + /* + * /old/new/[flags] + */ + + edit = 0; + while ((c = pplex()) == T_STRING) + { + if (!*(s = pp.token)) + { + error(2, "%s: substitution argument expected", p3); + goto eatmap; + } + if (edit) + edit = edit->next = newof(0, struct edit, 1, 0); + else + edit = map->edit = newof(0, struct edit, 1, 0); + if (!(i0 = regcomp(&edit->re, s, REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL)) && !(i0 = regsubcomp(&edit->re, s += edit->re.re_npat, NiL, 0, 0))) + s += edit->re.re_npat; + if (i0) + regfatal(&edit->re, 3, i0); + if (*s) + { + error(2, "%s: invalid characters after substitution: %s ", p3, s); + goto eatmap; + } + } + if (c) + { + error(2, "%s: %s: substitution argument expected", p3, pptokstr(pp.token, 0)); + goto eatmap; + } + map->next = (struct map*)pp.maps; + pp.maps = (char*)map; + eatmap: + POP_LINE(); + /*INDENT*/ + break; + case X_MAPINCLUDE: + ppmapinclude(NiL, p5); + break; + case X_MODERN: + setoption(MODERN, i0); + break; + case X_MULTIPLE: + n = 1; + if (pp.in->type == IN_FILE) + ppmultiple(ppsetfile(error_info.file), i0 ? INC_CLEAR : INC_TEST); + break; + case X_NATIVE: + setoption(NATIVE, i0); + break; + case X_OPSPACE: + ppfsm(FSM_OPSPACE, i0 ? p4 : (char*)0); + break; + case X_PASSTHROUGH: + ppop(PP_PASSTHROUGH, i0); + break; + case X_PEDANTIC: + ppop(PP_PEDANTIC, i0); + break; + case X_PLUSCOMMENT: + ppop(PP_PLUSCOMMENT, i0); + break; + case X_PLUSPLUS: + ppop(PP_PLUSPLUS, i0); + break; + case X_PLUSSPLICE: + setoption(PLUSSPLICE, i0); + break; + case X_PRAGMAEXPAND: + setoption(PRAGMAEXPAND, i0); + break; + case X_PRAGMAFLAGS: + tokop(PP_PRAGMAFLAGS, p3, p, i0, 0); + break; + case X_PREDEFINED: + setoption(PREDEFINED, i0); + break; + case X_PREFIX: + setoption(PREFIX, i0); + break; + case X_PRESERVE: + setoption(PRESERVE, i0); + if (pp.option & PRESERVE) + { + setmode(CATLITERAL, 0); + ppop(PP_COMPATIBILITY, 1); + ppop(PP_TRANSITION, 0); + ppop(PP_PLUSCOMMENT, 1); + ppop(PP_SPACEOUT, 1); + setoption(STRINGSPAN, 1); + setoption(STRINGSPLIT, 0); + ppop(PP_HOSTDIR, "-", 1); + } + break; + case X_PROTOTYPED: + /* + * this option doesn't bump the token count + */ + + n = 1; + directive = ENDIF; +#if PROTOTYPE + setoption(PROTOTYPED, i0); +#else + error(1, "preprocessor not compiled with prototype conversion enabled"); +#endif + break; + case X_PROTO: + setoption(NOPROTO, !i0); + break; + case X_QUOTE: + tokop(PP_QUOTE, p3, p, i0, TOKOP_UNSET|TOKOP_STRING); + break; + case X_READONLY: + setmode(READONLY, i0); + break; + case X_REGUARD: + setoption(REGUARD, i0); + break; + case X_RESERVED: + tokop(PP_RESERVED, p3, p, i0, 0); + break; + case X_SPACEOUT: + if (!(pp.state & (COMPATIBILITY|COMPILE))) + ppop(PP_SPACEOUT, i0); + break; + case X_SPLICECAT: + setoption(SPLICECAT, i0); + break; + case X_SPLICESPACE: + setoption(SPLICESPACE, i0); + break; + case X_STANDARD: + tokop(PP_STANDARD, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP); + break; + case X_STRICT: + ppop(PP_STRICT, i0); + break; + case X_STRINGSPAN: + setoption(STRINGSPAN, i0); + break; + case X_STRINGSPLIT: + setoption(STRINGSPLIT, i0); + if (pp.option & STRINGSPLIT) + setmode(CATLITERAL, 0); + break; + case X_SYSTEM_HEADER: + if (i0) + { + pp.mode |= HOSTED; + pp.flags |= PP_hosted; + pp.in->flags |= IN_hosted; + } + else + { + pp.mode &= ~HOSTED; + pp.flags &= ~PP_hosted; + pp.in->flags &= ~PP_hosted; + } + break; + case X_TEST: + ppop(PP_TEST, p); + break; + case X_TEXT: + if (!(pp.option & KEEPNOTEXT)) + setstate(NOTEXT, !i0); + break; + case X_TRANSITION: + ppop(PP_TRANSITION, i0); + if (pp.state & TRANSITION) ppop(PP_COMPATIBILITY, i0); + break; + case X_TRUNCATE: + ppop(PP_TRUNCATE, i0 ? (p ? strtol(p, NiL, 0) : TRUNCLENGTH) : 0); + break; + case X_VENDOR: + tokop(PP_VENDOR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP); + break; + case X_VERSION: + if (!(*pp.control & SKIP) && pp.pragma && !(pp.state & NOTEXT)) + { + sfsprintf(pp.tmpbuf, MAXTOKEN, "\"%s\"", pp.version); + (*pp.pragma)(dirname(PRAGMA), pp.pass, p3, pp.tmpbuf, !n); + if (pp.linesync && !n) + (*pp.linesync)(error_info.line, error_info.file); + emitted = 1; + } + break; + case X_WARN: + ppop(PP_WARN, i0); + break; + case X_ZEOF: + setoption(ZEOF, i0); + break; +#if DEBUG + case 0: + case X_INCLUDED: + case X_NOTICED: + case X_OPTION: + case X_STATEMENT: + break; + default: + error(PANIC, "%s: option recognized but not implemented", pp.valbuf); + break; +#endif + } + *p4 = i2; + if (!n) + goto checkmap; + goto donedirective; + case RENAME: + if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX))) + error(1, "#%s: non-standard directive", pp.token); + if ((c = pplex()) != T_ID) + { + error(1, "%s: invalid macro name", pptokstr(pp.token, 0)); + goto eatdirective; + } + if (!(sym = pprefmac(pp.token, REF_DELETE)) || !sym->macro) + goto eatdirective; + if (sym->flags & (SYM_ACTIVE|SYM_READONLY)) + { + if (!(pp.option & ALLPOSSIBLE)) + error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active"); + goto eatdirective; + } + if ((c = pplex()) != T_ID) + { + error(1, "%s: invalid macro name", pptokstr(pp.token, 0)); + goto eatdirective; + } + var.symbol = pprefmac(pp.token, REF_CREATE); + if (mac = var.symbol->macro) + { + if (var.symbol->flags & (SYM_ACTIVE|SYM_READONLY)) + { + if (!(pp.option & ALLPOSSIBLE)) + error(2, "%s: macro is %s", var.symbol->name, (var.symbol->flags & SYM_READONLY) ? "readonly" : "active"); + goto eatdirective; + } + if (!(pp.mode & HOSTED) || !(var.symbol->flags & SYM_INITIAL)) + error(1, "%s redefined", var.symbol->name); + if (mac->formals) free(mac->formals); + free(mac->value); + free(mac); + } + ppfsm(FSM_MACRO, var.symbol->name); + var.symbol->flags = sym->flags; + sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC); + var.symbol->macro = sym->macro; + sym->macro = 0; + break; + case UNDEF: + if ((c = pplex()) != T_ID) + { + error(1, "%s: invalid macro name", pptokstr(pp.token, 0)); + goto eatdirective; + } + if (sym = pprefmac(pp.token, REF_DELETE)) + { + if (mac = sym->macro) + { + if (sym->flags & (SYM_ACTIVE|SYM_READONLY)) + { + if (!(pp.option & ALLPOSSIBLE)) + error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active"); + goto eatdirective; + } + if (mac->formals) free(mac->formals); + free(mac->value); + free(mac); + mac = sym->macro = 0; + } + if ((pp.option & (DEFINITIONS|PREDEFINITIONS|REGUARD)) && !sym->hidden && !(sym->flags & SYM_MULTILINE) && ((pp.option & PREDEFINITIONS) || !(pp.mode & INIT)) && ((pp.option & (DEFINITIONS|PREDEFINITIONS)) || !(pp.state & NOTEXT))) + { + ppsync(); + ppprintf("#%s %s", dirname(UNDEF), sym->name); + emitted = 1; + } + sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC); + n2 = error_info.line; + goto benign; + } + else pprefmac(pp.token, REF_UNDEF); + break; +#if DEBUG + default: + error(PANIC, "#%s: directive recognized but not implemented", pp.token); + goto eatdirective; +#endif + } + break; + case '\n': + break; + default: + error(1, "%s: invalid directive name", pptokstr(pp.token, 0)); + goto eatdirective; + } + enddirective: +#if COMPATIBLE + if (c != '\n' && !(pp.state & COMPATIBILITY)) +#else + if (c != '\n') +#endif + { + pp.state |= DISABLE|NOSPACE; + if ((c = pplex()) != '\n' && (pp.mode & (HOSTED|PEDANTIC)) == PEDANTIC) + error(1, "%s: invalid characters after directive", pptokstr(pp.token, 0)); + } + eatdirective: + if (c != '\n') + { + pp.state |= DISABLE; + while (pplex() != '\n'); + } + donedirective: +#if _HUH_2002_05_09 + if (!(pp.state & EOF2NL)) + error(2, "%s in directive", pptokchr(0)); +#endif + pp.state &= ~RESTORE; + pp.mode &= ~RELAX; + if (!(*pp.control & SKIP)) + { + pp.state |= restore; + switch (directive) + { + case LINE: + return 0; + case INCLUDE: + if (pp.include) + { + error_info.line++; + PUSH_FILE(pp.include, n); + if (!pp.vendor && (pp.found->type & TYPE_VENDOR)) + pp.vendor = 1; + pp.include = 0; + return 0; + } + if (pp.incref) + (*pp.incref)(error_info.file, ppgetfile(pp.path)->name, error_info.line, PP_SYNC_IGNORE); + else if (pp.linesync && pp.macref) + { + pp.flags |= PP_lineignore; + (*pp.linesync)(error_info.line, ppgetfile(pp.path)->name); + } + /*FALLTHROUGH*/ + default: + pp.in->flags |= IN_tokens; + /*FALLTHROUGH*/ + case ENDIF: + error_info.line++; + if (emitted) + { + ppputchar('\n'); + ppcheckout(); + } + else + { + pp.state |= HIDDEN; + pp.hidden++; + } + return 0; + } + } + pp.state |= restore|HIDDEN|SKIPCONTROL; + pp.hidden++; + pp.level++; + error_info.line++; + return 0; +} + +/* + * grow the pp nesting control stack + */ + +void +ppnest(void) +{ + register struct ppinstk* ip; + int oz; + int nz; + long adjust; + long* op; + long* np; + + oz = pp.constack; + op = pp.maxcon - oz + 1; + nz = oz * 2; + np = newof(op, long, nz, 0); + if (adjust = (np - op)) + { + ip = pp.in; + do + { + if (ip->control) + ip->control += adjust; + } while (ip = ip->prev); + } + pp.control = np + oz; + pp.constack = nz; + pp.maxcon = np + nz - 1; +} |