/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1986-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.eclipse.org/org/documents/epl-v10.html *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* 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 library control interface
*/
#include "pplib.h"
#include "pptab.h"
#include <ls.h>
#define REFONE (pp.truncate?(Hash_table_t*)0:pp.symtab)
#define REFALL (pp.truncate?pp.dirtab:pp.symtab)
#define ppiskey(t,v,p) (p=t,v>=p->value&&value<=(p+elementsof(t)-2)->value)
/*
* set option value
* initialization files have lowest precedence
*/
int
ppset(register long* p, register long op, int val)
{
long* r;
r = p == &pp.state ? &pp.ro_state : p == &pp.mode ? &pp.ro_mode : &pp.ro_option;
if ((pp.mode & INIT) && pp.in->type == IN_FILE && (*r & op))
{
debug((-7, "set %s %s skipped -- readonly", p == &pp.state ? "state" : p == &pp.mode ? "mode" : "option", p == &pp.state ? ppstatestr(*r) : p == &pp.mode ? ppmodestr(*r) : ppoptionstr(*r)));
return 0;
}
if (!pp.initialized && (!(pp.mode & INIT) || !(pp.mode & BUILTIN)) && (p != &pp.mode || !(op & BUILTIN)) && (p != &pp.option || !(op & PREDEFINED)))
{
*r |= op;
debug((-7, "set %s %s readonly", p == &pp.state ? "state" : p == &pp.mode ? "mode" : "option", p == &pp.state ? ppstatestr(*r) : p == &pp.mode ? ppmodestr(*r) : ppoptionstr(*r)));
}
if (val)
*p |= op;
else
*p &= ~op;
debug((-7, "set %s %s", p == &pp.state ? "state" : p == &pp.mode ? "mode" : "option", p == &pp.state ? ppstatestr(*r) : p == &pp.mode ? ppmodestr(*r) : ppoptionstr(*r)));
return 1;
}
/*
* initialize hash table with keywords from key
*/
static void
inithash(register Hash_table_t* tab, register struct ppkeyword* key)
{
register char* s;
for (; s = key->name; key++)
{
if (!ppisid(*s))
s++;
hashput(tab, s, key->value);
}
}
/*
* return ppkeyword table name given value
*/
char*
ppkeyname(register int value, int dir)
{
register char* s;
register struct ppkeyword* p;
if (dir && ppiskey(directives, value, p) || !dir && (ppiskey(options, value, p) || ppiskey(predicates, value, p) || ppiskey(variables, value, p)))
{
s = (p + (value - p->value))->name;
return s + !ppisid(*s);
}
#if DEBUG
error(PANIC, "no keyword table name for value=%d", value);
#endif
return "UNKNOWN";
}
/*
* add to the include maps
*/
void
ppmapinclude(char* file, register char* s)
{
register int c;
register struct ppdirs* dp;
int fd;
int flags;
int index;
int token;
char* t;
char* old_file;
long old_state;
struct ppfile* fp;
struct ppfile* mp;
old_file = error_info.file;
old_state = pp.state;
if (s)
PUSH_BUFFER("mapinclude", s, 1);
else if (file)
{
if (*file == '-')
{
if (!error_info.file)
{
error(1, "%s: input file name required for %s ignore", file, dirname(INCLUDE));
return;
}
s = t = strcopy(pp.tmpbuf, error_info.file);
c = *++file;
for (;;)
{
if (s <= pp.tmpbuf || *s == '/')
{
s = t;
break;
}
else if (*s == c)
break;
s--;
}
strcpy(s, file);
file = pp.tmpbuf;
}
if ((fd = ppsearch(file, INC_LOCAL, SEARCH_INCLUDE)) < 0)
return;
PUSH_FILE(file, fd);
}
else
return;
#if CATSTRINGS
pp.state |= (COMPILE|FILEPOP|HEADER|JOINING|STRIP|NOSPACE|PASSEOF);
#else
pp.state |= (COMPILE|FILEPOP|HEADER|STRIP|NOSPACE|PASSEOF);
#endif
pp.level++;
flags = INC_MAPALL;
fp = mp = 0;
for (;;)
{
switch (token = pplex())
{
case 0:
case T_STRING:
case T_HEADER:
if (fp)
{
fp->guard = INC_IGNORE;
for (dp = pp.firstdir->next; dp; dp = dp->next)
if (dp->name && (c = strlen(dp->name)) && !strncmp(dp->name, fp->name, c) && fp->name[c] == '/')
{
ppsetfile(fp->name + c + 1)->guard = INC_IGNORE;
break;
}
}
if (!token)
break;
pathcanon(pp.token, 0, 0);
fp = ppsetfile(pp.token);
if (mp)
{
mp->flags |= flags;
if (streq(fp->name, "."))
mp->flags |= INC_MAPNOLOCAL;
else
mp->bound[index] = fp;
fp = mp = 0;
}
else
index = token == T_HEADER ? INC_STANDARD : INC_LOCAL;
continue;
case '=':
if (!(mp = fp))
error(3, "%s: \"name\" = \"binding\" expected");
fp = 0;
continue;
case '\n':
continue;
case T_ID:
if (streq(pp.token, "all"))
{
flags = INC_MAPALL;
continue;
}
else if (streq(pp.token, "hosted"))
{
flags = INC_MAPHOSTED;
continue;
}
else if (streq(pp.token, "nohosted"))
{
flags = INC_MAPNOHOSTED;
continue;
}
/*FALLTHROUGH*/
default:
error(3, "%s unexpected in %s map list", pptokstr(pp.token, 0), dirname(INCLUDE));
break;
}
break;
}
pp.level--;
error_info.file = old_file;
pp.state = old_state;
}
/*
* return non-0 if file is identical to fd
*/
static int
identical(char* file, int fd)
{
struct stat a;
struct stat b;
return !stat(file, &a) && !fstat(fd, &b) && a.st_dev == b.st_dev && a.st_ino == b.st_ino;
}
/*
* compare up to pp.truncate chars
*
* NOTE: __STD* and symbols containing ' ' are not truncated
*/
static int
trunccomp(register char* a, register char* b)
{
return !strchr(b, ' ') && !strneq(b, "__STD", 5) ? strncmp(a, b, pp.truncate) : strcmp(a, b);
}
/*
* hash up to pp.truncate chars
*
* NOTE: __STD* and symbols containing ' ' are not truncated
*/
static unsigned int
trunchash(char* a)
{
int n;
return memhash(a, (n = strlen(a)) > pp.truncate && !strchr(a, ' ') && !strneq(a, "__STD", 5) ? pp.truncate : n);
}
#if DEBUG & TRACE_debug
/*
* append context to debug trace
*/
static int
context(Sfio_t* sp, int level, int flags)
{
static int state;
NoP(level);
NoP(flags);
if (error_info.trace <= -10 && pp.state != state)
{
state = pp.state;
sfprintf(sp, " %s", ppstatestr(pp.state));
}
return 1;
}
#endif
/*
* reset include guard
*/
static int
unguard(const char* name, char* v, void* handle)
{
register struct ppfile* fp = (struct ppfile*)v;
fp->guard = 0;
return 0;
}
/*
* reset macro definition
*/
static void
undefine(void* p)
{
struct ppmacro* mac = ((struct ppsymbol*)p)->macro;
if (mac)
{
if (mac->formals)
free(mac->formals);
free(mac->value);
free(mac);
}
}
/*
* return non-zero if its ok to ppop(op)
*/
static int
ppok(int op)
{
long n;
long* r;
r = &pp.ro_op[op >> 5];
n = 1L << op;
if ((pp.mode & INIT) && pp.in->type == IN_FILE && (*r & n))
{
debug((-7, "set op %d index %d skipped -- readonly", op, op >> 5));
return 0;
}
else if (!pp.initialized && (!(pp.mode & INIT) || !(pp.mode & BUILTIN)))
{
*r |= n;
debug((-7, "set op %d index %d readonly", op, op >> 5));
}
else
debug((-7, "set op %d index %d", op, op >> 5));
return 1;
}
/*
* pp operations
*
* NOTE: PP_INIT must be done before the first pplex() call
* PP_DONE must be done after the last pplex() call
* PP_INIT-PP_DONE must be done for each new PP_INPUT
*/
void
ppop(int op, ...)
{
va_list ap;
register char* p;
register struct ppkeyword* kp;
register char* s;
int c;
long n;
long* r;
char* t;
struct ppdirs* dp;
struct ppdirs* hp;
struct ppsymkey* key;
struct oplist* xp;
Sfio_t* sp;
struct stat st;
PPCOMMENT ppcomment;
PPLINESYNC pplinesync;
static int initialized;
va_start(ap, op);
switch (op)
{
case PP_ASSERT:
case PP_DEFINE:
case PP_DIRECTIVE:
case PP_OPTION:
case PP_READ:
case PP_UNDEF:
if (pp.initialized)
goto before;
if ((p = va_arg(ap, char*)) && *p)
{
if (pp.lastop)
pp.lastop = (pp.lastop->next = newof(0, struct oplist, 1, 0));
else
pp.firstop = pp.lastop = newof(0, struct oplist, 1, 0);
pp.lastop->op = op;
pp.lastop->value = p;
}
break;
case PP_BUILTIN:
pp.builtin = va_arg(ap, PPBUILTIN);
break;
case PP_CDIR:
p = va_arg(ap, char*);
c = va_arg(ap, int);
pp.cdir.path = 0;
if (!p)
pp.c = c;
else if (streq(p, "-"))
{
pp.c = c;
for (dp = pp.firstdir; dp; dp = dp->next)
dp->c = c;
}
else if (!pp.c)
{
if (!*p || stat((pathcanon(p, 0, 0), p), &st))
pp.c = c;
else
{
for (dp = pp.firstdir; dp; dp = dp->next)
{
if (!pp.c && (dp->c || dp->name && SAMEID(&dp->id, &st)))
pp.c = 1;
dp->c = pp.c == 1;
}
if (!pp.c)
{
pp.cdir.path = p;
SAVEID(&pp.cdir.id, &st);
}
}
}
break;
case PP_CHOP:
if (p = va_arg(ap, char*))
{
c = strlen(p);
xp = newof(0, struct oplist, 1, c + 1);
xp->value = ((char*)xp) + sizeof(struct oplist);
s = xp->value;
c = *p++;
while (*p && *p != c)
*s++ = *p++;
*s++ = '/';
xp->op = s - xp->value;
*s++ = 0;
if (*p && *++p && *p != c)
{
while (*p && *p != c)
*s++ = *p++;
*s++ = '/';
}
*s = 0;
xp->next = pp.chop;
pp.chop = xp;
}
break;
case PP_COMMENT:
if (pp.comment = va_arg(ap, PPCOMMENT))
pp.flags |= PP_comment;
else
pp.flags &= ~PP_comment;
break;
case PP_COMPATIBILITY:
if (ppset(&pp.state, COMPATIBILITY, va_arg(ap, int)))
{
#if COMPATIBLE
if (pp.initialized)
ppfsm(FSM_COMPATIBILITY, NiL);
#else
if (pp.state & COMPATIBILITY)
error(3, "preprocessor not compiled with compatibility dialect enabled [COMPATIBLE]");
#endif
if (pp.state & COMPATIBILITY)
pp.flags |= PP_compatibility;
else
pp.flags &= ~PP_compatibility;
}
break;
case PP_COMPILE:
if (pp.initialized)
goto before;
pp.state |= COMPILE;
if (!pp.symtab)
pp.symtab = hashalloc(NiL, HASH_name, "symbols", 0);
if (kp = va_arg(ap, struct ppkeyword*))
for (; s = kp->name; kp++)
{
n = SYM_LEX;
switch (*s)
{
case '-':
s++;
break;
case '+':
s++;
if (!(pp.option & PLUSPLUS))
break;
/*FALLTHROUGH*/
default:
n |= SYM_KEYWORD;
break;
}
if (key = ppkeyset(pp.symtab, s))
{
key->sym.flags = n;
key->lex = kp->value;
}
}
break;
case PP_DEBUG:
error_info.trace = va_arg(ap, int);
break;
case PP_DEFAULT:
if (p = va_arg(ap, char*))
p = strdup(p);
if (pp.ppdefault)
free(pp.ppdefault);
pp.ppdefault = p;
break;
case PP_DONE:
#if CHECKPOINT
if (pp.mode & DUMP)
ppdump();
#endif
if (pp.mode & FILEDEPS)
{
sfputc(pp.filedeps.sp, '\n');
if (pp.filedeps.sp == sfstdout)
sfsync(pp.filedeps.sp);
else
sfclose(pp.filedeps.sp);
}
if (pp.state & STANDALONE)
{
if ((pp.state & (NOTEXT|HIDDEN)) == HIDDEN && pplastout() != '\n')
ppputchar('\n');
ppflushout();
}
error_info.file = 0;
break;
case PP_DUMP:
ppset(&pp.mode, DUMP, va_arg(ap, int));
#if !CHECKPOINT
if (pp.mode & DUMP)
error(3, "preprocessor not compiled with checkpoint enabled [CHECKPOINT]");
#endif
break;
case PP_FILEDEPS:
if (n = va_arg(ap, int))
pp.filedeps.flags |= n;
else
pp.filedeps.flags = 0;
break;
case PP_FILENAME:
error_info.file = va_arg(ap, char*);
break;
case PP_HOSTDIR:
if (!(pp.mode & INIT))
pp.ro_mode |= HOSTED;
else if (pp.ro_mode & HOSTED)
break;
pp.ro_mode |= INIT;
p = va_arg(ap, char*);
c = va_arg(ap, int);
pp.hostdir.path = 0;
if (!p)
pp.hosted = c;
else if (streq(p, "-"))
{
if (pp.initialized)
ppset(&pp.mode, HOSTED, c);
else
{
pp.hosted = c ? 1 : 2;
for (dp = pp.firstdir; dp; dp = dp->next)
if (pp.hosted == 1)
dp->type |= TYPE_HOSTED;
else
dp->type &= ~TYPE_HOSTED;
}
}
else if (!pp.hosted)
{
if (!*p || stat((pathcanon(p, 0, 0), p), &st))
pp.hosted = 1;
else
{
for (dp = pp.firstdir; dp; dp = dp->next)
{
if (!pp.hosted && ((dp->type & TYPE_HOSTED) || dp->name && SAMEID(&dp->id, &st)))
pp.hosted = 1;
if (pp.hosted == 1)
dp->type |= TYPE_HOSTED;
else
dp->type &= ~TYPE_HOSTED;
}
if (!pp.hosted)
{
pp.hostdir.path = p;
SAVEID(&pp.hostdir.id, &st);
}
}
}
break;
case PP_ID:
p = va_arg(ap, char*);
c = va_arg(ap, int);
if (p)
ppfsm(c ? FSM_IDADD : FSM_IDDEL, p);
break;
case PP_IGNORE:
if (p = va_arg(ap, char*))
{
pathcanon(p, 0, 0);
ppsetfile(p)->guard = INC_IGNORE;
message((-3, "%s: ignore", p));
}
break;
case PP_IGNORELIST:
if (pp.initialized)
goto before;
pp.ignore = va_arg(ap, char*);
break;
case PP_INCLUDE:
if ((p = va_arg(ap, char*)) && *p)
{
pathcanon(p, 0, 0);
if (stat(p, &st))
break;
for (dp = pp.stddirs; dp = dp->next;)
if (dp->name && SAMEID(&dp->id, &st))
break;
if (pp.cdir.path && SAMEID(&pp.cdir.id, &st))
{
pp.cdir.path = 0;
pp.c = 1;
}
if (pp.hostdir.path && SAMEID(&pp.hostdir.id, &st))
{
pp.hostdir.path = 0;
pp.hosted = 1;
}
if ((pp.mode & INIT) && !(pp.ro_mode & INIT))
pp.hosted = 1;
c = dp && dp->c || pp.c == 1;
n = dp && (dp->type & TYPE_HOSTED) || pp.hosted == 1;
if (!dp || dp == pp.lastdir->next)
{
if (dp)
{
c = dp->c;
n = dp->type & TYPE_HOSTED;
}
dp = newof(0, struct ppdirs, 1, 0);
dp->name = p;
SAVEID(&dp->id, &st);
dp->type |= TYPE_INCLUDE;
dp->index = INC_LOCAL + pp.ignoresrc != 0;
dp->next = pp.lastdir->next;
pp.lastdir = pp.lastdir->next = dp;
}
dp->c = c;
if (n)
dp->type |= TYPE_HOSTED;
else
dp->type &= ~TYPE_HOSTED;
}
break;
case PP_INCREF:
pp.incref = va_arg(ap, PPINCREF);
break;
case PP_RESET:
pp.reset.on = 1;
break;
case PP_INIT:
if (pp.initialized)
{
error_info.errors = 0;
error_info.warnings = 0;
}
else
{
/*
* context initialization
*/
if (!initialized)
{
/*
* out of malloc is fatal
*/
memfatal();
/*
* initialize the error message interface
*/
error_info.version = (char*)pp.version;
#if DEBUG & TRACE_debug
error_info.auxilliary = context;
pptrace(0);
#endif
/*
* initialize pplex tables
*/
ppfsm(FSM_INIT, NiL);
/*
* fixed macro stack size -- room for improvement
*/
pp.macp = newof(0, struct ppmacstk, DEFMACSTACK, 0);
pp.macp->next = pp.macp + 1;
pp.maxmac = (char*)pp.macp + DEFMACSTACK;
initialized = 1;
/*
* initial include/if control stack
*/
pp.control = newof(0, long, pp.constack, 0);
pp.maxcon = pp.control + pp.constack - 1;
}
/*
* validate modes
*/
switch (pp.arg_mode)
{
case 'a':
case 'C':
ppop(PP_COMPATIBILITY, 0);
ppop(PP_TRANSITION, 1);
break;
case 'A':
case 'c':
ppop(PP_COMPATIBILITY, 0);
ppop(PP_STRICT, 1);
break;
case 'f':
ppop(PP_COMPATIBILITY, 1);
ppop(PP_PLUSPLUS, 1);
ppop(PP_TRANSITION, 1);
break;
case 'F':
ppop(PP_COMPATIBILITY, 0);
ppop(PP_PLUSPLUS, 1);
break;
case 'k':
case 's':
ppop(PP_COMPATIBILITY, 1);
ppop(PP_STRICT, 1);
break;
case 'o':
case 'O':
ppop(PP_COMPATIBILITY, 1);
ppop(PP_TRANSITION, 0);
break;
case 't':
ppop(PP_COMPATIBILITY, 1);
ppop(PP_TRANSITION, 1);
break;
}
if (!(pp.state & WARN) && !(pp.arg_style & STYLE_gnu))
ppop(PP_PEDANTIC, 1);
if (pp.state & PASSTHROUGH)
{
if (pp.state & COMPILE)
{
pp.state &= ~PASSTHROUGH;
error(1, "passthrough ignored for compile");
}
else
{
ppop(PP_COMPATIBILITY, 1);
ppop(PP_HOSTDIR, "-", 1);
ppop(PP_SPACEOUT, 1);
ppset(&pp.state, DISABLE, va_arg(ap, int));
}
}
/*
* create the hash tables
*/
if (!pp.symtab)
pp.symtab = hashalloc(NiL, HASH_name, "symbols", 0);
if (!pp.dirtab)
{
pp.dirtab = hashalloc(REFONE, HASH_name, "directives", 0);
inithash(pp.dirtab, directives);
}
if (!pp.filtab)
pp.filtab = hashalloc(REFALL, HASH_name, "files", 0);
if (!pp.prdtab)
pp.prdtab = hashalloc(REFALL, HASH_name, "predicates", 0);
if (!pp.strtab)
{
pp.strtab = hashalloc(REFALL, HASH_name, "strings", 0);
inithash(pp.strtab, options);
inithash(pp.strtab, predicates);
inithash(pp.strtab, variables);
}
pp.optflags[X_PROTOTYPED] = OPT_GLOBAL;
pp.optflags[X_SYSTEM_HEADER] = OPT_GLOBAL|OPT_PASS;
/*
* mark macros that are builtin predicates
*/
for (kp = predicates; s = kp->name; kp++)
{
if (!ppisid(*s))
s++;
ppassert(DEFINE, s, 0);
}
/*
* the remaining entry names must be allocated
*/
hashset(pp.dirtab, HASH_ALLOCATE);
hashset(pp.filtab, HASH_ALLOCATE);
hashset(pp.prdtab, HASH_ALLOCATE);
hashset(pp.strtab, HASH_ALLOCATE);
hashset(pp.symtab, HASH_ALLOCATE);
if (pp.test & TEST_nonoise)
{
c = error_info.trace;
error_info.trace = 0;
}
#if DEBUG
if (!(pp.test & TEST_noinit))
{
#endif
/*
* compose, push and read the builtin initialization script
*/
if (!(sp = sfstropen()))
error(3, "temporary buffer allocation error");
sfprintf(sp,
"\
#%s %s:%s \"/#<assert> /\" \"/assert /%s #/\"\n\
#%s %s:%s \"/#<unassert> /\" \"/unassert /%s #/\"\n\
",
dirname(PRAGMA),
pp.pass,
keyname(X_MAP),
dirname(DEFINE),
dirname(PRAGMA),
pp.pass,
keyname(X_MAP),
dirname(UNDEF));
if (pp.ppdefault && *pp.ppdefault)
{
if (pp.probe)
{
c = pp.lastdir->next->type;
pp.lastdir->next->type = 0;
}
if (ppsearch(pp.ppdefault, T_STRING, SEARCH_EXISTS) < 0)
{
free(pp.ppdefault);
if (!(pp.ppdefault = pathprobe("C", pp.pass, pp.probe ? pp.probe : PPPROBE, 0, pp.path, MAXTOKEN + 1, NiL, 0)))
error(1, "cannot determine default definitions for %s", pp.probe ? pp.probe : PPPROBE);
}
if (pp.probe)
pp.lastdir->next->type = c;
}
while (pp.firstop)
{
switch (pp.firstop->op)
{
case PP_ASSERT:
sfprintf(sp, "#%s #%s\n", dirname(DEFINE), pp.firstop->value);
break;
case PP_DEFINE:
if (*pp.firstop->value == '#')
sfprintf(sp, "#%s %s\n", dirname(DEFINE), pp.firstop->value);
else
{
if (s = strchr(pp.firstop->value, '='))
sfprintf(sp, "#%s %-.*s %s\n", dirname(DEFINE), s - pp.firstop->value, pp.firstop->value, s + 1);
else
sfprintf(sp, "#%s %s 1\n", dirname(DEFINE), pp.firstop->value);
}
break;
case PP_DIRECTIVE:
sfprintf(sp, "#%s\n", pp.firstop->value);
break;
case PP_OPTION:
if (s = strchr(pp.firstop->value, '='))
sfprintf(sp, "#%s %s:%-.*s %s\n", dirname(PRAGMA), pp.pass, s - pp.firstop->value, pp.firstop->value, s + 1);
else
sfprintf(sp, "#%s %s:%s\n", dirname(PRAGMA), pp.pass, pp.firstop->value);
break;
case PP_READ:
sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), pp.firstop->value);
break;
case PP_UNDEF:
sfprintf(sp, "#%s %s\n", dirname(UNDEF), pp.firstop->value);
break;
}
pp.lastop = pp.firstop;
pp.firstop = pp.firstop->next;
free(pp.lastop);
}
sfprintf(sp,
"\
#%s %s:%s\n\
#%s %s:%s\n\
#%s !#%s(%s)\n\
#%s !#%s(%s) || #%s(%s)\n\
"
, dirname(PRAGMA)
, pp.pass
, keyname(X_BUILTIN)
, dirname(PRAGMA)
, pp.pass
, keyname(X_PREDEFINED)
, dirname(IF)
, keyname(X_OPTION)
, keyname(X_PLUSPLUS)
, dirname(IF)
, keyname(X_OPTION)
, keyname(X_COMPATIBILITY)
, keyname(X_OPTION)
, keyname(X_TRANSITION)
);
sfprintf(sp,
"\
#%s #%s(%s)\n\
#%s %s:%s\n\
#%s %s:%s\n\
#%s __STRICT__ 1\n\
#%s\n\
#%s\n\
"
, dirname(IF)
, keyname(X_OPTION)
, keyname(X_STRICT)
, dirname(PRAGMA)
, pp.pass
, keyname(X_ALLMULTIPLE)
, dirname(PRAGMA)
, pp.pass
, keyname(X_READONLY)
, dirname(DEFINE)
, dirname(ENDIF)
, dirname(ENDIF)
);
for (kp = readonlys; s = kp->name; kp++)
{
if (!ppisid(*s))
s++;
sfprintf(sp, "#%s %s\n", dirname(UNDEF), s);
}
sfprintf(sp,
"\
#%s\n\
#%s __STDPP__ 1\n\
#%s %s:no%s\n\
"
, dirname(ENDIF)
, dirname(DEFINE)
, dirname(PRAGMA)
, pp.pass
, keyname(X_PREDEFINED)
);
if (!pp.truncate)
sfprintf(sp,
"\
#%s __STDPP__directive #(%s)\n\
"
, dirname(DEFINE)
, keyname(V_DIRECTIVE)
);
for (kp = variables; s = kp->name; kp++)
if (ppisid(*s) || *s++ == '+')
{
t = *s == '_' ? "" : "__";
sfprintf(sp, "#%s %s%s%s #(%s)\n" , dirname(DEFINE), t, s, t, s);
}
sfprintf(sp,
"\
#%s %s:no%s\n\
#%s %s:no%s\n\
"
, dirname(PRAGMA)
, pp.pass
, keyname(X_READONLY)
, dirname(PRAGMA)
, pp.pass
, keyname(X_BUILTIN)
);
if (pp.ppdefault && *pp.ppdefault)
sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), pp.ppdefault);
sfprintf(sp,
"\
#%s !defined(__STDC__) && (!#option(compatibility) || #option(transition))\n\
#%s __STDC__ #(STDC)\n\
#%s\n\
"
, dirname(IF)
, dirname(DEFINE)
, dirname(ENDIF)
);
t = sfstruse(sp);
debug((-9, "\n/* begin initialization */\n%s/* end initialization */", t));
ppcomment = pp.comment;
pp.comment = 0;
pplinesync = pp.linesync;
pp.linesync = 0;
PUSH_INIT(pp.pass, t);
pp.mode |= INIT;
while (pplex());
pp.mode &= ~INIT;
pp.comment = ppcomment;
pp.linesync = pplinesync;
pp.prefix = 0;
sfstrclose(sp);
if (error_info.trace)
for (dp = pp.firstdir; dp; dp = dp->next)
message((-1, "include directory %s%s%s%s", dp->name, (dp->type & TYPE_VENDOR) ? " [VENDOR]" : "", (dp->type & TYPE_HOSTED) ? " [HOSTED]" : "", dp->c ? " [C]" : ""));
#if DEBUG
}
if (pp.test & TEST_nonoise)
error_info.trace = c;
#endif
{
/*
* this is sleazy but at least it's
* hidden in the library
*/
#include <preroot.h>
#if FS_PREROOT
struct pplist* preroot;
if ((preroot = (struct pplist*)hashget(pp.prdtab, "preroot")))
setpreroot(NiL, preroot->value);
#endif
}
if (pp.ignoresrc)
{
if (pp.ignoresrc > 1 && pp.stddirs != pp.firstdir)
error(1, "directories up to and including %s are for \"...\" include files only", pp.stddirs->name);
pp.lcldirs = pp.lcldirs->next;
}
if (pp.ignore)
{
if (*pp.ignore)
ppmapinclude(pp.ignore, NiL);
else
pp.ignore = 0;
}
if (pp.standalone)
pp.state |= STANDALONE;
#if COMPATIBLE
ppfsm(FSM_COMPATIBILITY, NiL);
#endif
ppfsm(FSM_PLUSPLUS, NiL);
pp.initialized = 1;
if (pp.reset.on)
{
pp.reset.symtab = pp.symtab;
pp.symtab = 0;
pp.reset.ro_state = pp.ro_state;
pp.reset.ro_mode = pp.ro_mode;
pp.reset.ro_option = pp.ro_option;
}
}
if (pp.reset.on)
{
if (pp.symtab)
{
hashwalk(pp.filtab, 0, unguard, NiL);
hashfree(pp.symtab);
}
pp.symtab = hashalloc(NiL, HASH_name, "symbols", HASH_free, undefine, HASH_set, HASH_ALLOCATE|HASH_BUCKET, 0);
hashview(pp.symtab, pp.reset.symtab);
pp.ro_state = pp.reset.ro_state;
pp.ro_mode = pp.reset.ro_mode;
pp.ro_option = pp.reset.ro_option;
}
#if CHECKPOINT
if (pp.mode & DUMP)
{
if (!pp.pragma)
error(3, "#%s must be enabled for checkpoints", dirname(PRAGMA));
(*pp.pragma)(dirname(PRAGMA), pp.pass, keyname(X_CHECKPOINT), pp.checkpoint, 1);
}
#endif
if (n = pp.filedeps.flags)
{
if (!(n & PP_deps_file))
{
pp.state |= NOTEXT;
pp.option |= KEEPNOTEXT;
pp.linesync = 0;
}
if (n & PP_deps_generated)
pp.mode |= GENDEPS;
if (n & PP_deps_local)
pp.mode &= ~HEADERDEPS;
else if (!(pp.mode & FILEDEPS))
pp.mode |= HEADERDEPS;
pp.mode |= FILEDEPS;
}
/*
* push the main input file -- special case for hosted mark
*/
if (pp.firstdir->type & TYPE_HOSTED)
pp.mode |= MARKHOSTED;
else
pp.mode &= ~MARKHOSTED;
#if CHECKPOINT
if (!(pp.mode & DUMP))
#endif
{
if (!(p = error_info.file))
p = "";
else
{
error_info.file = 0;
if (*p)
{
pathcanon(p, 0, 0);
p = ppsetfile(p)->name;
}
}
PUSH_FILE(p, 0);
}
if (pp.mode & FILEDEPS)
{
if (s = strrchr(error_info.file, '/'))
s++;
else
s = error_info.file;
if (!*s)
s = "-";
s = strcpy(pp.tmpbuf, s);
if ((t = p = strrchr(s, '.')) && (*++p == 'c' || *p == 'C'))
{
if (c = *++p)
while (*++p == c);
if (*p)
t = 0;
else
t++;
}
if (!t)
{
t = s + strlen(s);
*t++ = '.';
}
*(t + 1) = 0;
if (pp.state & NOTEXT)
pp.filedeps.sp = sfstdout;
else
{
*t = 'd';
if (!(pp.filedeps.sp = sfopen(NiL, s, "w")))
error(ERROR_SYSTEM|3, "%s: cannot create", s);
}
*t = 'o';
pp.column = sfprintf(pp.filedeps.sp, "%s :", s);
if (*error_info.file)
pp.column += sfprintf(pp.filedeps.sp, " %s", error_info.file);
}
if (xp = pp.firsttx)
{
if (!(sp = sfstropen()))
error(3, "temporary buffer allocation error");
while (xp)
{
sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), xp->value);
xp = xp->next;
}
t = sfstruse(sp);
PUSH_BUFFER("options", t, 1);
sfstrclose(sp);
}
break;
case PP_INPUT:
#if CHECKPOINT && POOL
if (!(pp.mode & DUMP) || pp.pool.input)
#else
#if CHECKPOINT
if (!(pp.mode & DUMP))
#else
#if POOL
if (pp.pool.input)
#endif
#endif
#endif
{
p = va_arg(ap, char*);
if (!error_info.file)
error_info.file = p;
close(0);
if (open(p, O_RDONLY) != 0)
error(ERROR_SYSTEM|3, "%s: cannot read", p);
if (strmatch(p, "*.(s|S|as|AS|asm|ASM)"))
{
ppset(&pp.mode, CATLITERAL, 0);
ppop(PP_SPACEOUT, 1);
}
break;
}
/*FALLTHROUGH*/
case PP_TEXT:
if (pp.initialized)
goto before;
if ((p = va_arg(ap, char*)) && *p)
{
if (pp.lasttx)
pp.lasttx = pp.lasttx->next = newof(0, struct oplist, 1, 0);
else
pp.firsttx = pp.lasttx = newof(0, struct oplist, 1, 0);
pp.lasttx->op = op;
pp.lasttx->value = p;
}
break;
case PP_KEYARGS:
if (pp.initialized)
goto before;
ppset(&pp.option, KEYARGS, va_arg(ap, int));
if (pp.option & KEYARGS)
#if MACKEYARGS
ppset(&pp.mode, CATLITERAL, 1);
#else
error(3, "preprocessor not compiled with macro keyword arguments enabled [MACKEYARGS]");
#endif
break;
case PP_LINE:
if (ppok(op))
pp.linesync = va_arg(ap, PPLINESYNC);
break;
case PP_LINEBASE:
if (ppok(op))
{
if (va_arg(ap, int))
pp.flags |= PP_linebase;
else
pp.flags &= ~PP_linebase;
}
break;
case PP_LINEFILE:
if (ppok(op))
{
if (va_arg(ap, int))
pp.flags |= PP_linefile;
else
pp.flags &= ~PP_linefile;
}
break;
case PP_LINEID:
if (ppok(op))
{
if (!(p = va_arg(ap, char*)))
pp.lineid = "";
else if (*p != '-')
pp.lineid = strdup(p);
else
pp.option |= IGNORELINE;
}
break;
case PP_LINETYPE:
if (ppok(op))
{
if ((n = va_arg(ap, int)) >= 1)
pp.flags |= PP_linetype;
else
pp.flags &= ~PP_linetype;
if (n >= 2)
pp.flags |= PP_linehosted;
else
pp.flags &= ~PP_linehosted;
}
break;
case PP_LOCAL:
if (pp.initialized)
goto before;
pp.ignoresrc++;
pp.stddirs = pp.lastdir;
if (!(pp.ro_option & PREFIX))
pp.option &= ~PREFIX;
break;
case PP_MACREF:
pp.macref = va_arg(ap, PPMACREF);
break;
case PP_MULTIPLE:
ppset(&pp.mode, ALLMULTIPLE, va_arg(ap, int));
break;
case PP_NOHASH:
ppset(&pp.option, NOHASH, va_arg(ap, int));
break;
case PP_NOISE:
op = va_arg(ap, int);
ppset(&pp.option, NOISE, op);
ppset(&pp.option, NOISEFILTER, op < 0);
break;
case PP_OPTARG:
pp.optarg = va_arg(ap, PPOPTARG);
break;
case PP_OUTPUT:
pp.outfile = va_arg(ap, char*);
if (identical(pp.outfile, 0))
error(3, "%s: identical to input", pp.outfile);
close(1);
if (open(pp.outfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 1)
error(ERROR_SYSTEM|3, "%s: cannot create", pp.outfile);
break;
case PP_PASSTHROUGH:
if (!(pp.state & COMPILE))
ppset(&pp.state, PASSTHROUGH, va_arg(ap, int));
break;
case PP_PEDANTIC:
ppset(&pp.mode, PEDANTIC, va_arg(ap, int));
break;
case PP_PLUSCOMMENT:
if (ppset(&pp.option, PLUSCOMMENT, va_arg(ap, int)) && pp.initialized)
ppfsm(FSM_PLUSPLUS, NiL);
break;
case PP_PLUSPLUS:
if (ppset(&pp.option, PLUSPLUS, va_arg(ap, int)) && ppset(&pp.option, PLUSCOMMENT, va_arg(ap, int)) && pp.initialized)
ppfsm(FSM_PLUSPLUS, NiL);
break;
case PP_POOL:
if (pp.initialized)
goto before;
if (va_arg(ap, int))
{
#if POOL
pp.pool.input = dup(0);
pp.pool.output = dup(1);
p = "/dev/null";
if (!identical(p, 0))
{
if (!identical(p, 1))
ppop(PP_OUTPUT, p);
ppop(PP_INPUT, p);
}
#else
error(3, "preprocessor not compiled with input pool enabled [POOL]");
#endif
}
break;
case PP_PRAGMA:
pp.pragma = va_arg(ap, PPPRAGMA);
break;
case PP_PRAGMAFLAGS:
if (p = va_arg(ap, char*))
{
n = OPT_GLOBAL;
if (*p == '-')
p++;
else
n |= OPT_PASS;
if ((c = (int)hashref(pp.strtab, p)) > 0 && c <= X_last_option)
pp.optflags[c] = n;
}
break;
case PP_PROBE:
pp.probe = va_arg(ap, char*);
break;
case PP_QUOTE:
p = va_arg(ap, char*);
c = va_arg(ap, int);
if (p)
ppfsm(c ? FSM_QUOTADD : FSM_QUOTDEL, p);
break;
case PP_REGUARD:
ppset(&pp.option, REGUARD, va_arg(ap, int));
break;
case PP_RESERVED:
if ((pp.state & COMPILE) && (p = va_arg(ap, char*)))
{
if (!(sp = sfstropen()))
error(3, "temporary buffer allocation error");
sfputr(sp, p, -1);
p = sfstruse(sp);
if (s = strchr(p, '='))
*s++ = 0;
else
s = p;
while (*s == '_')
s++;
for (t = s + strlen(s); t > s && *(t - 1) == '_'; t--);
if (*t == '_')
*t = 0;
else
t = 0;
op = ((key = ppkeyref(pp.symtab, s)) && (key->sym.flags & SYM_LEX)) ? key->lex : T_NOISE;
if (pp.test & 0x0400)
error(1, "reserved#1 `%s' %d", s, op);
if (t)
*t = '_';
if (!(key = ppkeyget(pp.symtab, p)))
key = ppkeyset(pp.symtab, NiL);
else if (!(key->sym.flags & SYM_LEX))
{
struct ppsymbol tmp;
tmp = key->sym;
hashlook(pp.symtab, p, HASH_DELETE, NiL);
key = ppkeyset(pp.symtab, NiL);
key->sym.flags = tmp.flags;
key->sym.macro = tmp.macro;
key->sym.value = tmp.value;
key->sym.hidden = tmp.hidden;
}
if (!(key->sym.flags & SYM_KEYWORD))
{
key->sym.flags |= SYM_KEYWORD|SYM_LEX;
key->lex = op;
if (pp.test & 0x0400)
error(1, "reserved#2 `%s' %d", p, op);
}
sfstrclose(sp);
}
break;
case PP_SPACEOUT:
ppset(&pp.state, SPACEOUT, va_arg(ap, int));
break;
case PP_STANDALONE:
if (pp.initialized)
goto before;
pp.standalone = 1;
break;
case PP_STANDARD:
if ((pp.lastdir->next->name = ((p = va_arg(ap, char*)) && *p) ? p : NiL) && !stat(p, &st))
SAVEID(&pp.lastdir->next->id, &st);
for (dp = pp.firstdir; dp; dp = dp->next)
if (dp->name)
for (hp = pp.firstdir; hp != dp; hp = hp->next)
if (hp->name && SAMEID(&hp->id, &dp->id))
{
hp->c = dp->c;
if (dp->type & TYPE_HOSTED)
hp->type |= TYPE_HOSTED;
else
hp->type &= ~TYPE_HOSTED;
}
break;
case PP_STRICT:
if (ppset(&pp.state, STRICT, va_arg(ap, int)))
{
if (ppset(&pp.state, TRANSITION, 0))
pp.flags &= ~PP_transition;
if (pp.state & STRICT)
pp.flags |= PP_strict;
else
pp.flags &= ~PP_strict;
}
break;
case PP_TEST:
if (p = va_arg(ap, char*))
for (;;)
{
while (*p == ' ' || *p == '\t') p++;
for (s = p; n = *s; s++)
if (n == ',' || n == ' ' || n == '\t')
{
*s++ = 0;
break;
}
if (!*p)
break;
n = 0;
if (*p == 'n' && *(p + 1) == 'o')
{
p += 2;
op = 0;
}
else
op = 1;
if (streq(p, "count"))
n = TEST_count;
else if (streq(p, "hashcount"))
n = TEST_hashcount;
else if (streq(p, "hashdump"))
n = TEST_hashdump;
else if (streq(p, "hit"))
n = TEST_hit;
else if (streq(p, "init"))
n = TEST_noinit|TEST_INVERT;
else if (streq(p, "noise"))
n = TEST_nonoise|TEST_INVERT;
else if (streq(p, "proto"))
n = TEST_noproto|TEST_INVERT;
else if (*p >= '0' && *p <= '9')
n = strtoul(p, NiL, 0);
else
{
error(1, "%s: unknown test", p);
break;
}
if (n & TEST_INVERT)
{
n &= ~TEST_INVERT;
op = !op;
}
if (op)
pp.test |= n;
else
pp.test &= ~n;
p = s;
debug((-4, "test = 0%o", pp.test));
}
break;
case PP_TRANSITION:
if (ppset(&pp.state, TRANSITION, va_arg(ap, int)))
{
if (ppset(&pp.state, STRICT, 0))
pp.flags &= ~PP_strict;
if (pp.state & TRANSITION)
pp.flags |= PP_transition;
else
pp.flags &= ~PP_transition;
}
break;
case PP_TRUNCATE:
if (pp.initialized)
goto before;
if ((op = va_arg(ap, int)) < 0)
op = 0;
ppset(&pp.option, TRUNCATE, op);
if (pp.option & TRUNCATE)
{
Hash_bucket_t* b;
Hash_bucket_t* p;
Hash_position_t* pos;
Hash_table_t* tab;
pp.truncate = op;
tab = pp.symtab;
pp.symtab = hashalloc(NiL, HASH_set, tab ? HASH_ALLOCATE : 0, HASH_compare, trunccomp, HASH_hash, trunchash, HASH_name, "truncate", 0);
if (tab && (pos = hashscan(tab, 0)))
{
if (p = hashnext(pos))
do
{
b = hashnext(pos);
hashlook(pp.symtab, (char*)p, HASH_BUCKET|HASH_INSTALL, NiL);
} while (p = b);
hashdone(pos);
}
}
else
pp.truncate = 0;
break;
case PP_VENDOR:
p = va_arg(ap, char*);
c = va_arg(ap, int) != 0;
if (!p || !*p)
for (dp = pp.firstdir; dp; dp = dp->next)
dp->type &= ~TYPE_VENDOR;
else if (streq(p, "-"))
{
for (dp = pp.firstdir; dp; dp = dp->next)
if (c)
dp->type |= TYPE_VENDOR;
else
dp->type &= ~TYPE_VENDOR;
}
else if (!stat((pathcanon(p, 0, 0), p), &st))
{
c = 0;
for (dp = pp.firstdir; dp; dp = dp->next)
{
if (!c && ((dp->type & TYPE_VENDOR) || dp->name && SAMEID(&dp->id, &st)))
c = 1;
if (c)
dp->type |= TYPE_VENDOR;
else
dp->type &= ~TYPE_VENDOR;
}
}
break;
case PP_WARN:
ppset(&pp.state, WARN, va_arg(ap, int));
break;
before:
error(3, "ppop(%d): preprocessor operation must be done before PP_INIT", op);
break;
default:
error(3, "ppop(%d): invalid preprocessor operation", op);
break;
}
va_end(ap);
}