/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1986-2009 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * 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 * * * ***********************************************************************/ #pragma prototyped /* * Glenn Fowler * AT&T Research * * preprocessor stacked input stream support */ #include "pplib.h" /* * convert path to native representation */ #if 0 #include "../../lib/libast/path/pathnative.c" /* drop in 2002 */ #else /* Modified by gisburn 2006-08-18 for OpenSolaris ksh93-integration */ #include "../../libast/common/path/pathnative.c" #endif static char* native(register const char* s) { register int c; register struct ppfile* xp; int m; int n; static Sfio_t* np; static Sfio_t* qp; if (!s) return 0; if (!np && !(np = sfstropen()) || !qp && !(qp = sfstropen())) return (char*)s; n = PATH_MAX; do { m = n; n = pathnative(s, sfstrrsrv(np, m), m); } while (n > m); sfstrseek(np, n, SEEK_CUR); s = (const char*)sfstruse(np); for (;;) { switch (c = *s++) { case 0: break; case '\\': case '"': sfputc(qp, '\\'); /*FALLTHROUGH*/ default: sfputc(qp, c); continue; } break; } if (!(xp = ppsetfile(sfstruse(qp)))) return (char*)s; return xp->name; } /* * push stream onto input stack * used by the PUSH_type macros */ void pppush(register int t, register char* s, register char* p, int n) { register struct ppinstk* cur; PUSH(t, cur); cur->line = error_info.line; cur->file = error_info.file; switch (t) { case IN_FILE: if (pp.option & NATIVE) s = native(s); cur->flags |= IN_newline; cur->fd = n; cur->hide = ++pp.hide; cur->symbol = 0; #if CHECKPOINT if ((pp.mode & (DUMP|INIT)) == DUMP) { cur->index = newof(0, struct ppindex, 1, 0); if (pp.lastindex) pp.lastindex->next = cur->index; else pp.firstindex = cur->index; pp.lastindex = cur->index; cur->index->file = pp.original; cur->index->begin = ppoffset(); } #endif n = 1; #if CHECKPOINT if (!(pp.mode & DUMP)) #endif if (!cur->prev->prev && !(pp.state & COMPILE) && isatty(0)) cur->flags |= IN_flush; #if ARCHIVE if (pp.member) { switch (pp.member->archive->type & (TYPE_BUFFER|TYPE_CHECKPOINT)) { case 0: #if CHECKPOINT cur->buflen = pp.member->size; #endif p = (cur->buffer = oldof(0, char, 0, pp.member->size + PPBAKSIZ + 1)) + PPBAKSIZ; if (sfseek(pp.member->archive->info.sp, pp.member->offset, SEEK_SET) != pp.member->offset) error(3, "%s: archive seek error", pp.member->archive->name); if (sfread(pp.member->archive->info.sp, p, pp.member->size) != pp.member->size) error(3, "%s: archive read error", pp.member->archive->name); pp.member = 0; break; case TYPE_BUFFER: #if CHECKPOINT case TYPE_CHECKPOINT|TYPE_BUFFER: cur->buflen = pp.member->size; #endif p = cur->buffer = pp.member->archive->info.buffer + pp.member->offset; cur->flags |= IN_static; pp.member = 0; break; #if CHECKPOINT case TYPE_CHECKPOINT: p = cur->buffer = ""; cur->flags |= IN_static; break; #endif } cur->flags |= IN_eof|IN_newline; cur->fd = -1; } else #endif { if (lseek(cur->fd, 0L, SEEK_END) > 0 && !lseek(cur->fd, 0L, SEEK_SET)) cur->flags |= IN_regular; errno = 0; #if PROTOTYPE if (!(pp.option & NOPROTO) && !(pp.test & TEST_noproto) && ((pp.state & (COMPATIBILITY|TRANSITION)) == COMPATIBILITY || (pp.option & PLUSPLUS) || (pp.mode & EXTERNALIZE)) && (cur->buffer = pppopen(NiL, cur->fd, NiL, NiL, NiL, NiL, (PROTO_HEADER|PROTO_RETAIN)|(((pp.mode & EXTERNALIZE) || (pp.option & PROTOTYPED)) ? PROTO_FORCE : PROTO_PASS)|((pp.mode & EXTERNALIZE) ? PROTO_EXTERNALIZE : 0)|((pp.mode & MARKC) ? PROTO_PLUSPLUS : 0)))) { *(p = cur->buffer - 1) = 0; cur->buffer -= PPBAKSIZ; cur->flags |= IN_prototype; cur->fd = -1; } else #endif *(p = (cur->buffer = oldof(0, char, 0, PPBUFSIZ + PPBAKSIZ + 1)) + PPBAKSIZ) = 0; } if (pp.incref && !(pp.mode & INIT)) (*pp.incref)(error_info.file, s, error_info.line - 1, PP_SYNC_PUSH); if (pp.macref || (pp.option & IGNORELINE)) cur->flags |= IN_ignoreline; cur->prefix = pp.prefix; /*FALLTHROUGH*/ case IN_BUFFER: case IN_INIT: case IN_RESCAN: pushcontrol(); cur->control = pp.control; *pp.control = 0; cur->vendor = pp.vendor; if (cur->type != IN_RESCAN) { if (cur->type == IN_INIT) pp.mode |= MARKHOSTED; error_info.file = s; error_info.line = n; } if (pp.state & HIDDEN) { pp.state &= ~HIDDEN; pp.hidden = 0; if (!(pp.state & NOTEXT) && pplastout() != '\n') ppputchar('\n'); } pp.state |= NEWLINE; if (pp.mode & HOSTED) cur->flags |= IN_hosted; else cur->flags &= ~IN_hosted; if (pp.mode & (INIT|MARKHOSTED)) { pp.mode |= HOSTED; pp.flags |= PP_hosted; } switch (cur->type) { case IN_FILE: if (!(pp.mode & (INIT|MARKHOSTED))) { pp.mode &= ~HOSTED; pp.flags &= ~PP_hosted; } #if CATSTRINGS if (pp.state & JOINING) pp.state |= HIDDEN|SYNCLINE; else #endif if (pp.linesync) (*pp.linesync)(error_info.line, error_info.file); #if ARCHIVE && CHECKPOINT if (pp.member) ppload(NiL); #endif if (pp.mode & MARKC) { cur->flags |= IN_c; pp.mode &= ~MARKC; if (!(cur->prev->flags & IN_c)) { debug((-7, "PUSH in=%s next=%s [%s]", ppinstr(pp.in), pptokchr(*pp.in->nextchr), pp.in->nextchr)); PUSH_BUFFER("C", "extern \"C\" {\n", 1); return; } } else if (cur->prev->flags & IN_c) { debug((-7, "PUSH in=%s next=%s [%s]", ppinstr(pp.in), pptokchr(*pp.in->nextchr), pp.in->nextchr)); PUSH_BUFFER("C", "extern \"C++\" {\n", 1); return; } break; case IN_BUFFER: cur->buffer = p = strdup(p); break; default: cur->buffer = p; break; } cur->nextchr = p; break; #if DEBUG default: error(PANIC, "use PUSH_<%d>(...) instead of pppush(IN_<%d>, ...)", cur->type, cur->type); break; #endif } debug((-7, "PUSH in=%s next=%s", ppinstr(pp.in), pptokchr(*pp.in->nextchr))); } /* * external buffer push */ void ppinput(char* b, char* f, int n) { PUSH_BUFFER(f, b, n); } /* * return expanded value of buffer p */ char* ppexpand(register char* p) { register char* m; register int n; register int c; long restore; char* pptoken; char* ppmactop; struct ppmacstk* nextmacp; struct ppinstk* cur; debug((-7, "before expand: %s", p)); if (ppmactop = pp.mactop) { nextmacp = pp.macp->next; nextframe(pp.macp, pp.mactop); } restore = pp.state & (COLLECTING|DISABLE|STRIP); pp.state &= ~restore; pp.mode &= ~MARKMACRO; PUSH_STRING(p); cur = pp.in; pp.in->flags |= IN_expand; pptoken = pp.token; n = 2 * MAXTOKEN; pp.token = p = oldof(0, char, 0, n); m = p + MAXTOKEN; for (;;) { if (pplex()) { if ((pp.token = pp.toknxt) > m) { c = pp.token - p; p = newof(p, char, n += MAXTOKEN, 0); m = p + n - MAXTOKEN; pp.token = p + c; } if (pp.mode & MARKMACRO) { pp.mode &= ~MARKMACRO; *pp.token++ = MARK; *pp.token++ = 'X'; } } else if (pp.in == cur) break; } *pp.token = 0; if (ppmactop) pp.macp->next = nextmacp; debug((-7, "after expand: %s", p)); pp.token = pptoken; pp.state |= restore; pp.in = pp.in->prev; return p; } #if CHECKPOINT #define LOAD_FUNCTION (1<<0) #define LOAD_MULTILINE (1<<1) #define LOAD_NOEXPAND (1<<2) #define LOAD_PREDICATE (1<<3) #define LOAD_READONLY (1<<4) #define LOAD_VARIADIC (1<<5) /* * macro definition dump */ static int dump(const char* name, char* v, void* handle) { register struct ppmacro* mac; register struct ppsymbol* sym = (struct ppsymbol*)v; register int flags; NoP(name); NoP(handle); if ((mac = sym->macro) && !(sym->flags & (SYM_BUILTIN|SYM_PREDEFINED))) { ppprintf("%s", sym->name); ppputchar(0); flags = 0; if (sym->flags & SYM_FUNCTION) flags |= LOAD_FUNCTION; if (sym->flags & SYM_MULTILINE) flags |= LOAD_MULTILINE; if (sym->flags & SYM_NOEXPAND) flags |= LOAD_NOEXPAND; if (sym->flags & SYM_PREDICATE) flags |= LOAD_PREDICATE; if (sym->flags & SYM_READONLY) flags |= LOAD_READONLY; if (sym->flags & SYM_VARIADIC) flags |= LOAD_VARIADIC; ppputchar(flags); if (sym->flags & SYM_FUNCTION) { ppprintf("%d", mac->arity); ppputchar(0); if (mac->arity) { ppprintf("%s", mac->formals); ppputchar(0); } } ppprintf("%s", mac->value); ppputchar(0); } return(0); } /* * dump macro definitions for quick loading via ppload() */ void ppdump(void) { register struct ppindex* ip; unsigned long macro_offset; unsigned long index_offset; /* * NOTE: we assume '\0' does not occur in valid preprocessed output */ ppputchar(0); /* * output global flags */ macro_offset = ppoffset(); ppputchar(0); /* * output macro definitions */ hashwalk(pp.symtab, 0, dump, NiL); ppputchar(0); /* * output include file index */ index_offset = ppoffset(); ip = pp.firstindex; while (ip) { ppprintf("%s", ip->file->name); ppputchar(0); if (ip->file->guard != INC_CLEAR && ip->file->guard != INC_IGNORE && ip->file->guard != INC_TEST) ppprintf("%s", ip->file->guard->name); ppputchar(0); ppprintf("%lu", ip->begin); ppputchar(0); ppprintf("%lu", ip->end); ppputchar(0); ip = ip->next; } ppputchar(0); /* * output offset directory */ ppprintf("%010lu", macro_offset); ppputchar(0); ppprintf("%010lu", index_offset); ppputchar(0); ppflushout(); } /* * load text and macro definitions from a previous ppdump() * s is the string argument from the pragma (including quotes) */ void ppload(register char* s) { register char* b; register Sfio_t* sp; int m; char* g; char* t; unsigned long n; unsigned long p; unsigned long macro_offset; unsigned long index_offset; unsigned long file_offset; unsigned long file_size; unsigned long keep_begin; unsigned long keep_end; unsigned long skip_end; unsigned long next_begin; unsigned long next_end; struct ppfile* fp; struct ppsymbol* sym; struct ppmacro* mac; char* ip = 0; pp.mode |= LOADING; if (!(pp.state & STANDALONE)) error(3, "checkpoint load in standalone mode only"); #if ARCHIVE if (pp.member) { sp = pp.member->archive->info.sp; file_offset = pp.member->offset; file_size = pp.member->size; if (sfseek(sp, file_offset + 22, SEEK_SET) != file_offset + 22 || !(s = sfgetr(sp, '\n', 1))) error(3, "checkpoint magic error"); } else #endif { if (pp.in->type != IN_FILE) error(3, "checkpoint load from files only"); if (pp.in->flags & IN_prototype) pp.in->fd = pppdrop(pp.in->buffer + PPBAKSIZ); file_offset = 0; if (pp.in->fd >= 0) { if (!(sp = sfnew(NiL, NiL, SF_UNBOUND, pp.in->fd, SF_READ))) error(3, "checkpoint read error"); file_size = sfseek(sp, 0L, SEEK_END); } else { file_size = pp.in->buflen; if (!(sp = sfnew(NiL, pp.in->buffer + ((pp.in->flags & IN_static) ? 0 : PPBAKSIZ), file_size, -1, SF_READ|SF_STRING))) error(3, "checkpoint read error"); } } if (!streq(s, pp.checkpoint)) error(3, "checkpoint version %s does not match %s", s, pp.checkpoint); /* * get the macro and index offsets */ p = file_offset + file_size - 22; if ((n = sfseek(sp, p, SEEK_SET)) != p) error(3, "checkpoint directory seek error"); if (!(t = sfreserve(sp, 22, 0))) error(3, "checkpoint directory read error"); macro_offset = file_offset + strtol(t, &t, 10); index_offset = file_offset + strtol(t + 1, NiL, 10); /* * read the include index */ if (sfseek(sp, index_offset, SEEK_SET) != index_offset) error(3, "checkpoint index seek error"); if (!(s = sfreserve(sp, n - index_offset, 0))) error(3, "checkpoint index read error"); if (sfset(sp, 0, 0) & SF_STRING) b = s; else if (!(b = ip = memdup(s, n - index_offset))) error(3, "checkpoint index alloc error"); /* * loop on the index and copy the non-ignored chunks to the output */ ppcheckout(); p = PPBUFSIZ - (pp.outp - pp.outbuf); keep_begin = 0; keep_end = 0; skip_end = 0; while (*b) { fp = ppsetfile(b); while (*b++); g = b; while (*b++); next_begin = strtol(b, &t, 10); next_end = strtol(t + 1, &t, 10); if (pp.test & 0x0200) error(2, "%s: %s p=%lu next=<%lu,%lu> keep=<%lu,%lu> skip=<-,%lu> guard=%s", keyname(X_CHECKPOINT), fp->name, p, next_begin, next_end, keep_begin, keep_end, skip_end, fp->guard == INC_CLEAR ? "[CLEAR]" : fp->guard == INC_TEST ? "[TEST]" : fp->guard == INC_IGNORE ? "[IGNORE]" : fp->guard->name); b = t + 1; if (next_begin >= skip_end) { if (!ppmultiple(fp, INC_TEST)) { if (pp.test & 0x0100) error(2, "%s: %s IGNORE", keyname(X_CHECKPOINT), fp->name); if (!keep_begin && skip_end < next_begin) keep_begin = skip_end; if (keep_begin) { flush: if (sfseek(sp, file_offset + keep_begin, SEEK_SET) != file_offset + keep_begin) error(3, "checkpoint data seek error"); n = next_begin - keep_begin; if (pp.test & 0x0100) error(2, "%s: copy <%lu,%lu> n=%lu p=%lu", keyname(X_CHECKPOINT), keep_begin, next_begin - 1, n, p); while (n > p) { if (sfread(sp, pp.outp, p) != p) error(3, "checkpoint data read error"); PPWRITE(PPBUFSIZ); pp.outp = pp.outbuf; n -= p; p = PPBUFSIZ; } if (n) { if (sfread(sp, pp.outp, n) != n) error(3, "checkpoint data read error"); pp.outp += n; p -= n; } keep_begin = 0; if (keep_end <= next_end) keep_end = 0; } skip_end = next_end; } else if (!keep_begin) { if (skip_end) { keep_begin = skip_end; skip_end = 0; } else keep_begin = next_begin; if (keep_end < next_end) keep_end = next_end; } } if (*g && fp->guard != INC_IGNORE) fp->guard = ppsymset(pp.symtab, g); } if (keep_end) { if (!keep_begin) keep_begin = skip_end > next_end ? skip_end : next_end; next_begin = next_end = keep_end; g = b; goto flush; } if (pp.test & 0x0100) error(2, "%s: loop", keyname(X_CHECKPOINT)); /* * read the compacted definitions */ if (sfseek(sp, macro_offset, SEEK_SET) != macro_offset) error(3, "checkpoint macro seek error"); if (!(s = sfreserve(sp, index_offset - macro_offset, 0))) error(3, "checkpoint macro read error"); /* * read the flags */ while (*s) { #if _options_dumped_ if (streq(s, "OPTION")) /* ... */; else #endif error(3, "%-.48s: unknown flags in checkpoint file", s); } s++; /* * unpack and enter the definitions */ while (*s) { b = s; while (*s++); m = *s++; sym = ppsymset(pp.symtab, b); if (sym->macro) { if (m & LOAD_FUNCTION) { if (*s++ != '0') while (*s++); while (*s++); } if (pp.test & 0x1000) error(2, "checkpoint SKIP %s=%s [%s]", sym->name, s, sym->macro->value); while (*s++); } else { ppfsm(FSM_MACRO, b); sym->flags = 0; if (m & LOAD_FUNCTION) sym->flags |= SYM_FUNCTION; if (m & LOAD_MULTILINE) sym->flags |= SYM_MULTILINE; if (m & LOAD_NOEXPAND) sym->flags |= SYM_NOEXPAND; if (m & LOAD_PREDICATE) sym->flags |= SYM_PREDICATE; if (m & LOAD_READONLY) sym->flags |= SYM_READONLY; if (m & LOAD_VARIADIC) sym->flags |= SYM_VARIADIC; mac = sym->macro = newof(0, struct ppmacro, 1, 0); if (sym->flags & SYM_FUNCTION) { for (n = 0; *s >= '0' && *s <= '9'; n = n * 10 + *s++ - '0'); if (*s++) error(3, "%-.48: checkpoint macro arity botched", sym->name); if (mac->arity = n) { b = s; while (*s++); mac->formals = (char*)memcpy(oldof(0, char, 0, s - b), b, s - b); } } b = s; while (*s++); mac->size = s - b - 1; mac->value = (char*)memcpy(oldof(0, char, 0, mac->size + 1), b, mac->size + 1); if (pp.test & 0x1000) error(2, "checkpoint LOAD %s=%s", sym->name, mac->value); } } /* * we are now at EOF */ if (ip) { pp.in->fd = -1; free(ip); } #if ARCHIVE if (pp.member) pp.member = 0; else #endif { sfclose(sp); pp.in->flags |= IN_eof|IN_newline; pp.in->nextchr = pp.in->buffer + PPBAKSIZ; *pp.in->nextchr++ = 0; *pp.in->nextchr = 0; } pp.mode &= ~LOADING; } #endif