/*********************************************************************** * * * 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 * * * ***********************************************************************/ #pragma prototyped /* * Glenn Fowler * AT&T Research * * include file search support */ #include "pplib.h" #define SEARCH_NEXT (SEARCH_USER<<1)/* search for next (uncover) */ #define SEARCH_SKIP (SEARCH_USER<<2)/* current binding skipped */ #define SEARCH_TEST (SEARCH_USER<<3)/* test for binding */ #define SEARCH_FOUND (SEARCH_USER<<4)/* current binding found */ #define COLUMN_TAB 7 #define COLUMN_MAX 72 #if ARCHIVE #include #include #endif /* * multiple include test * fp is a canonicalized ppfile pointer * * test * * INC_CLEAR can be included again * INC_TEST test if include required * ifndef guard symbol * * test!=INC_CLEAR returns 1 if file can be included again * * NOTE: * * (1) different hard links to the same file are treated as * different files * * (2) symbolic links in combination with .. may cause two * different files to be treated as the same file: * * "../h/" == "/usr/include/sys/../h/" -> "/usr/include/h/" * "h/" -> "/usr/include/h/" */ int ppmultiple(register struct ppfile* fp, register struct ppsymbol* test) { register struct ppsymbol* status; status = fp->guard; message((-3, "search: %s: status=%s%s test=%s", fp->name, status == INC_CLEAR ? "[CLEAR]" : status == INC_TEST ? "[ONCE]" : status == INC_IGNORE ? "[IGNORE]" : status->name, (pp.mode & HOSTED) ? "[HOSTED]" : "", test == INC_CLEAR ? "[CLEAR]" : test == INC_TEST ? "[TEST]" : test->name)); if (status == INC_IGNORE) { message((-2, "%s: ignored [%s]", fp->name, pp.ignore)); return 0; } if (test == INC_TEST) { if (status != INC_CLEAR) { if (status != INC_TEST && status->macro || !(pp.mode & ALLMULTIPLE) && !(pp.state & STRICT)) { if ((pp.mode & (ALLMULTIPLE|LOADING)) == LOADING) fp->guard = INC_IGNORE; if (pp.state & WARN) error(1, "%s: ignored -- already included", fp->name); else message((-3, "%s: ignored -- already included", fp->name)); return 0; } return 1; } if ((pp.mode & (ALLMULTIPLE|LOADING)) == LOADING) test = INC_IGNORE; } fp->guard = test; return 1; } /* * search for file using directories in dp */ static int search(register struct ppfile* fp, register struct ppdirs* dp, int type, int flags) { register char* prefix; register struct ppdirs* up; register struct ppfile* xp; struct ppfile* mp; int fd; int index; int need; int markhosted; char* t; if (!(pp.option & PREFIX)) prefix = 0; else if ((prefix = strrchr(fp->name, '/')) && prefix > fp->name) { *prefix = 0; t = ppsetfile(fp->name)->name; *prefix = '/'; prefix = t; } message((-3, "search: %s %s%s%s%s%s%s type=%s prefix=%s flags=|%s%s%s%s%s%s start=%s=\"%s\" pre=%s lcl=%s vnd=%s std=%s cur=%s", fp->name, (flags & SEARCH_INCLUDE) ? "include" : "exists", (flags & SEARCH_VENDOR) ? " vendor" : "", (flags & SEARCH_HOSTED) ? " hosted" : "", (flags & SEARCH_NEXT) ? " next" : "", (flags & SEARCH_SKIP) ? " skip" : "", (flags & SEARCH_TEST) ? " test" : "", type == T_HEADER ? "<*>" : "\"*\"", prefix, (fp->flags & INC_SELF) ? "SELF|" : "", (fp->flags & INC_EXISTS) ? "EXISTS|" : "", (fp->flags & INC_BOUND(INC_PREFIX)) ? "PREFIX|" : "", (fp->flags & INC_BOUND(INC_LOCAL)) ? "LOCAL|" : "", (fp->flags & INC_BOUND(INC_VENDOR)) ? "VENDOR|" : "", (fp->flags & INC_BOUND(INC_STANDARD)) ? "STANDARD|" : "", dp ? (dp->index == INC_PREFIX ? "pre" : dp->index == INC_LOCAL ? "lcl" : dp->index == INC_VENDOR ? "vnd" : "std") : NiL, dp ? dp->name : NiL, !(fp->flags & INC_MEMBER(INC_PREFIX)) && (xp = fp->bound[INC_PREFIX]) ? xp->name : NiL, !(fp->flags & INC_MEMBER(INC_LOCAL)) && (xp = fp->bound[INC_LOCAL]) ? xp->name : NiL, !(fp->flags & INC_MEMBER(INC_VENDOR)) && (xp = fp->bound[INC_VENDOR]) ? xp->name : NiL, !(fp->flags & INC_MEMBER(INC_STANDARD)) && (xp = fp->bound[INC_STANDARD]) ? xp->name : NiL, error_info.file )); if (flags & SEARCH_HOSTED) need = TYPE_HOSTED; else if (flags & SEARCH_VENDOR) need = TYPE_VENDOR; else need = TYPE_INCLUDE; for (index = -1; dp; dp = dp->next) if (dp->type & need) { message((-3, "search: fp=%s need=%02x index=%d dp=%s type=%02x index=%d", fp->name, need, index, dp->name, dp->type, dp->index)); #if ARCHIVE if (!(dp->type & (TYPE_ARCHIVE|TYPE_DIRECTORY))) { struct stat st; if (stat(dp->name, &st)) { message((-3, "search: omit %s", dp->name)); dp->type = 0; continue; } if (S_ISREG(st.st_mode)) { register char* s; char* e; int delimiter; int variant; unsigned long siz; unsigned long off; struct ppmember* ap; Sfio_t* sp; /* * check for vdb header archive */ if (!(sp = sfopen(NiL, dp->name, "r"))) { error(ERROR_SYSTEM|1, "%s: ignored -- cannot open", dp->name); dp->type = 0; continue; } variant = sfsprintf(pp.tmpbuf, MAXTOKEN, "%c%s%c%s:archive", VDB_DELIMITER, VDB_MAGIC, VDB_DELIMITER, pp.pass); if (!(s = sfgetr(sp, '\n', 1)) || !strneq(s, pp.tmpbuf, variant)) { sfclose(sp); error(1, "%s: ignored -- not a directory or archive", dp->name); dp->type = 0; continue; } /* * parse the options */ dp->type |= TYPE_ARCHIVE; for (s += variant;;) { while (*s == ' ') s++; e = s; for (t = 0; *s && *s != ' '; s++) if (*s == '=') { *s = 0; t = s + 1; } if (*s) *s++ = 0; if (!*e) break; switch ((int)hashref(pp.strtab, e)) { case X_CHECKPOINT: #if CHECKPOINT dp->type |= TYPE_CHECKPOINT; break; #else error(1, "preprocessor not compiled with checkpoint enabled"); goto notvdb; #endif case X_HIDE: if (t) error(1, "%s: %s: archive option value ignored", e); if (e = strrchr(dp->name, '/')) *e = 0; else dp->name = "."; break; case X_MAP: if (!t) error(1, "%s: archive option value expected", e); else dp->name = strdup(t); break; default: error(1, "%s: unknown archive option", e); break; } } if (sfseek(sp, -(VDB_LENGTH + 1), SEEK_END) <= 0 || !(s = sfgetr(sp, '\n', 1))) { notvdb: sfclose(sp); error(1, "%s: ignored -- cannot load archive", dp->name); dp->type = 0; continue; } if (variant = *s != 0) s++; else if (!(s = sfgetr(sp, '\n', 1))) goto notvdb; if (sfvalue(sp) != (VDB_LENGTH + variant)) goto notvdb; if (!strneq(s, VDB_DIRECTORY, sizeof(VDB_DIRECTORY) - 1)) goto notvdb; delimiter = s[VDB_OFFSET - 1]; off = strtol(s + VDB_OFFSET, NiL, 10) - sizeof(VDB_DIRECTORY); siz = strtol(s + VDB_SIZE, NiL, 10); if (sfseek(sp, off, SEEK_SET) != off) goto notvdb; if (!(s = sfreserve(sp, siz + 1, 0))) goto notvdb; s[siz] = 0; if (!strneq(s, VDB_DIRECTORY, sizeof(VDB_DIRECTORY)) - 1) goto notvdb; if (!(s = strchr(s, '\n'))) goto notvdb; s++; while (e = strchr(s, '\n')) { delimiter = variant ? *s++ : delimiter; if (!(t = strchr(s, delimiter))) break; *t = 0; if (!streq(s, VDB_DIRECTORY)) { pathcanon(s, 0); ap = newof(0, struct ppmember, 1, 0); ap->archive = dp; ap->offset = strtol(t + 1, &t, 10); ap->size = strtol(t + 1, NiL, 10); xp = ppsetfile(s); xp->flags |= INC_MEMBER(dp->index); xp->bound[dp->index] = (struct ppfile*)ap; if (pp.test & 0x0020) error(1, "VDB#%d %s %s index=%d data=<%lu,%lu>", __LINE__, dp->name, xp->name, index, ap->offset, ap->size); } s = e + 1; } if (sfseek(sp, 0L, SEEK_SET)) goto notvdb; if (!(pp.test & 0x4000) && #if POOL (pp.pool.input || !(dp->type & TYPE_CHECKPOINT)) #else !(dp->type & TYPE_CHECKPOINT) #endif && (dp->info.buffer = sfreserve(sp, off, 0))) dp->type |= TYPE_BUFFER; else { dp->info.sp = sp; #if POOL if (pp.pool.input) sfset(sp, SF_SHARE, 1); #endif } } else dp->type |= TYPE_DIRECTORY; } #endif if (streq(fp->name, ".")) continue; if (prefix && *fp->name != '/' && dp->index != INC_PREFIX) #if ARCHIVE if (dp->type & TYPE_DIRECTORY) #endif { for (up = dp->info.subdir; up; up = up->next) if (up->name == prefix) break; if (!up) { up = newof(0, struct ppdirs, 1, 0); up->name = prefix; up->type = dp->type; up->next = dp->info.subdir; dp->info.subdir = up; if (!*dp->name) t = prefix; else sfsprintf(t = pp.path, PATH_MAX - 1, "%s/%s", dp->name, prefix); if (eaccess(t, X_OK)) { message((-3, "search: omit %s", t)); continue; } up->type |= TYPE_HOSTED; } else if (!(up->type & TYPE_HOSTED)) continue; } mp = xp = 0; if (!(flags & SEARCH_NEXT) && index != dp->index && (!(need & TYPE_HOSTED) || dp->index == INC_STANDARD) && (!(need & TYPE_VENDOR) || dp->index == INC_VENDOR)) { if (index >= 0 && !(fp->flags & INC_MEMBER(index))) fp->flags |= INC_BOUND(index); index = dp->index; if (fp->flags & INC_BOUND(index)) { xp = fp->bound[index]; if (index == INC_PREFIX) { if (*fp->name == '/' || !*dp->name) strcpy(pp.path, fp->name); else sfsprintf(pp.path, PATH_MAX - 1, "%s/%s", dp->name, fp->name); pathcanon(pp.path, 0); if (!xp || !streq(xp->name, pp.path)) { fp->bound[index] = xp = ppsetfile(pp.path); if (dp->type & TYPE_HOSTED) xp->flags |= INC_HOSTED; if ((flags & SEARCH_INCLUDE) || (xp->flags & INC_EXISTS)) { if (!(flags & SEARCH_INCLUDE)) return 0; if (!ppmultiple(xp, INC_TEST)) { if (flags & SEARCH_TEST) pp.include = xp->name; return 0; } mp = xp; } } } else if (!xp) { while (dp->next && dp->next->index == index) dp = dp->next; message((-3, "search: omit %s/%s", dp->name, fp->name)); continue; } else { strcpy(pp.path, xp->name); if (!(flags & SEARCH_INCLUDE)) return 0; if (!ppmultiple(xp, INC_TEST)) { if (flags & SEARCH_TEST) pp.include = xp->name; return 0; } mp = xp; } } } if (!(fp->flags & INC_BOUND(index)) || (flags & SEARCH_NEXT)) { if (*fp->name == '/' || !*dp->name) strcpy(pp.path, fp->name); else sfsprintf(pp.path, PATH_MAX - 1, "%s/%s", dp->name, fp->name); pathcanon(pp.path, 0); if (!(flags & SEARCH_SKIP)) { int found; struct ppinstk* in; if (streq(error_info.file, pp.path)) found = 1; else { found = 0; for (in = pp.in; in; in = in->prev) if (in->type == IN_FILE && in->file && streq(in->file, pp.path)) { found = 1; break; } } if (found) { flags |= SEARCH_FOUND; continue; } if (!(flags & SEARCH_FOUND)) continue; } } if ((xp || (xp = ppgetfile(pp.path))) && (xp->flags & INC_SELF)) { if (xp->flags & INC_EXISTS) { if (!(flags & SEARCH_INCLUDE)) return 0; if (!(flags & SEARCH_NEXT) && mp != xp && (mp = xp) && !ppmultiple(xp, INC_TEST)) { if (flags & SEARCH_TEST) pp.include = xp->name; return 0; } } else if (*fp->name == '/') break; else continue; } message((-3, "search: file=%s path=%s", fp->name, pp.path)); #if ARCHIVE if (pp.test & 0x0040) error(1, "SEARCH#%d dir=%s%s%s%s%s file=%s%s path=%s index=%d", __LINE__, dp->name, (dp->type & TYPE_ARCHIVE) ? " ARCHIVE" : "", (dp->type & TYPE_BUFFER) ? " BUFFER" : "", (dp->type & TYPE_CHECKPOINT) ? " CHECKPOINT" : "", (dp->type & TYPE_DIRECTORY) ? " DIRECTORY" : "", fp->name, (fp->flags & INC_MEMBER(index)) ? " MEMBER" : "", pp.path, index); if ((fp->flags & INC_MEMBER(index)) && ((struct ppmember*)fp->bound[index])->archive == dp) { fd = 0; pp.member = (struct ppmember*)fp->bound[index]; if (pp.test & 0x0010) error(1, "SEARCH#%d file=%s path=%s index=%d data=<%lu,%lu>", __LINE__, fp->name, pp.path, index, pp.member->offset, pp.member->size); } else if (!(dp->type & TYPE_DIRECTORY)) continue; else #endif { pp.member = 0; fd = (flags & SEARCH_INCLUDE) ? open(pp.path, O_RDONLY) : eaccess(pp.path, R_OK); } if (fd >= 0) { pp.found = dp; if ((pp.option & (PLUSPLUS|NOPROTO)) == PLUSPLUS && !(pp.test & TEST_noproto)) { if (dp->c) pp.mode |= MARKC; else pp.mode &= ~MARKC; } if (xp) markhosted = xp->flags & INC_HOSTED; else if (!(markhosted = (dp->type & TYPE_HOSTED)) && dp->index == INC_PREFIX && (pp.mode & (FILEDEPS|HEADERDEPS|INIT)) == FILEDEPS) { up = dp; while ((up = up->next) && !streq(up->name, dp->name)); if (up && (up->type & TYPE_HOSTED)) markhosted = 1; } if (markhosted) pp.mode |= MARKHOSTED; else pp.mode &= ~MARKHOSTED; xp = ppsetfile(pp.path); if (markhosted) xp->flags |= INC_HOSTED; message((-2, "search: %s -> %s%s%s", fp->name, pp.path, (pp.mode & MARKC) ? " [C]" : "", (pp.mode & MARKHOSTED) ? " [hosted]" : "")); #if ARCHIVE if (!pp.member) { #endif fp->flags |= INC_BOUND(index); fp->bound[index] = xp; if ((index == INC_STANDARD || index == INC_VENDOR) && type != T_HEADER && !(fp->flags & INC_BOUND(INC_LOCAL))) { fp->flags |= INC_BOUND(INC_LOCAL); fp->bound[INC_LOCAL] = xp; } #if ARCHIVE } #endif xp->flags |= INC_SELF|INC_EXISTS; if (flags & SEARCH_INCLUDE) { if ((pp.prefix = prefix) || (pp.prefix = pp.in->prefix)) message((-2, "search: %s: prefix=%s", xp->name, pp.prefix)); if (!(pp.mode & ALLMULTIPLE)) { if (xp->guard == INC_CLEAR || xp == mp) xp->guard = INC_TEST; else { if (pp.state & WARN) error(1, "%s: ignored -- already included", xp->name); else message((-3, "%s: ignored -- already included", xp->name)); xp->guard = fp->guard = INC_IGNORE; #if ARCHIVE if (!pp.member) #endif if (fd > 0) close(fd); if (flags & SEARCH_TEST) pp.include = xp->name; return 0; } } pp.include = xp->name; if ((pp.mode & (FILEDEPS|INIT)) == FILEDEPS && ((pp.mode & HEADERDEPS) || !(pp.mode & MARKHOSTED)) && !(xp->flags & INC_LISTED)) { xp->flags |= INC_LISTED; if ((pp.column + strlen(xp->name)) >= COLUMN_MAX) { sfprintf(pp.filedeps.sp, " \\\n"); pp.column = COLUMN_TAB; index = '\t'; } else index = ' '; pp.column += sfprintf(pp.filedeps.sp, "%c%s", index, xp->name); } } return fd; } if (xp) xp->flags |= INC_SELF; if (errno == EMFILE) error(3, "%s: too many open files", fp->name); else if (errno != ENOENT && errno != ENOTDIR) error(ERROR_SYSTEM|1, "%s: cannot open file for reading", pp.path); if (*fp->name == '/') break; } strcpy(pp.path, fp->name); message((-2, "search: %s%s not found", (flags & SEARCH_NEXT) ? "next " : "", fp->name)); return -1; } /* * search for an include file * if (flags&SEARCH_INCLUDE) then * if file found then open read file descriptor returned * with pp.path set to the full path and * pp.prefix set to the directory prefix * otherwise 0 returned if file found but ignored * otherwise -1 returned * otherwise * if file found then 0 returned * otherwise -1 returned */ int ppsearch(char* file, int type, int flags) { register struct ppfile* fp; register char* s; register struct ppdirs* dp; struct oplist* cp; struct ppfile* xp; int dospath; int fd; int index; char name[MAXTOKEN + 1]; pp.include = 0; fd = -1; dospath = 0; again: pathcanon(file, 0); for (cp = pp.chop; cp; cp = cp->next) if (strneq(file, cp->value, cp->op)) { if (cp->value[cp->op + 1]) { sfsprintf(name, sizeof(name) - 1, "%s%s", cp->value + cp->op + 1, file + cp->op); message((-3, "chop: %s -> %s", file, name)); file = name; } else if (strchr(file + cp->op, '/')) { message((-3, "chop: %s -> %s", file, file + cp->op)); file += cp->op; } break; } fp = ppsetfile(file); while ((fp->flags & INC_MAPALL) || (fp->flags & INC_MAPHOSTED) && (pp.mode & HOSTED) || (fp->flags & INC_MAPNOHOSTED) && !(pp.mode & HOSTED)) { if (!(xp = fp->bound[type == T_HEADER ? INC_STANDARD : INC_LOCAL]) || xp == fp) break; message((-1, "map: %s -> %s", fp->name, xp->name)); fp = xp; } if ((fp->flags & INC_MAPNOLOCAL) && (pp.mode & HOSTED)) flags |= SEARCH_HOSTED; else if (pp.vendor) flags |= SEARCH_VENDOR; pp.original = fp; if (type == T_HEADER && strneq(fp->name, "...", 3) && (!fp->name[3] || fp->name[3] == '/')) { if (fp->name[3] == '/') { int n; int m; n = strlen(error_info.file); m = strlen(fp->name + 4); if (n < m || !streq(fp->name + 4, error_info.file + n - m)) { if ((fd = ppsearch(fp->name + 4, type, flags|SEARCH_TEST)) < 0) return -1; if (fd > 0) close(fd); s = error_info.file; error_info.file = pp.include; fd = ppsearch(fp->name + 4, type, flags|SEARCH_NEXT); error_info.file = s; return fd; } file = error_info.file + n - m; } else if (file = strrchr(error_info.file, '/')) file++; else file = error_info.file; flags |= SEARCH_NEXT; #if _HUH_2002_05_28 if (pp.in->prefix) { sfsprintf(name, sizeof(name) - 1, "%s/%s", pp.in->prefix, file); fp = ppsetfile(name); if ((fd = ppsearch(fp->name, type, flags)) >= 0) return fd; } #endif fp = ppsetfile(file); return ppsearch(fp->name, type, flags); } else if ((flags & SEARCH_INCLUDE) && fp->guard == INC_IGNORE) { strcpy(pp.path, fp->name); message((-2, "%s: ignored", fp->name)); return 0; } else if (!(flags & SEARCH_NEXT)) flags |= SEARCH_SKIP; pp.prefix = 0; if (type == T_HEADER) dp = pp.stddirs->next; else { dp = pp.lcldirs; if (dp == pp.firstdir) { /* * look in directory of including file first */ if (error_info.file && (s = strrchr(error_info.file, '/'))) { *s = 0; dp->name = ppsetfile(error_info.file)->name; *s = '/'; } else dp->name = ""; } else if (pp.in->prefix && pp.lcldirs != pp.firstdir) { /* * look in prefix directory of including file first */ if (*fp->name != '/') { if ((s = strchr(fp->name, '/')) && (fp->name[0] != '.' || fp->name[1] != '.' || fp->name[2] != '/')) { *s = 0; if (!streq(fp->name, pp.in->prefix)) fd = 0; *s = '/'; } else fd = 0; } if (fd >= 0) { sfsprintf(name, sizeof(name) - 1, "%s/%s", pp.in->prefix, fp->name); pathcanon(name, 0); xp = ppsetfile(name); if ((fd = search(xp, dp, type, flags)) >= 0) return fd; } } } if ((fd = search(fp, dp, type, flags)) < 0) { if ((pp.option & PLUSPLUS) && file != pp.tmpbuf) { s = file + strlen(file); while (s > file && *--s != '/' && *s != '\\' && *s != '.'); if (*s != '.') { sfsprintf(pp.tmpbuf, MAXTOKEN, "%s.h", file); file = pp.tmpbuf; goto again; } } /* * hackery for msdos files viewed through unix */ switch (dospath) { case 0: if (s = strchr(file, '\\')) { do *s++ = '/'; while (s = strchr(s, '\\')); pathcanon(file, 0); dospath = 1; goto again; } /*FALLTHROUGH*/ case 1: if (ppisid(file[0]) && file[1] == ':' && file[2] == '/') { file[1] = file[0]; file[0] = '/'; pathcanon(file, 0); dospath = 2; goto again; } break; case 2: file += 2; goto again; } if ((flags & (SEARCH_INCLUDE|SEARCH_NEXT)) == SEARCH_INCLUDE) { if (!(pp.mode & GENDEPS)) { if (!(pp.option & ALLPOSSIBLE) || pp.in->prev->prev) error(2, "%s: cannot find include file", file); } else if (!(pp.mode & INIT)) { xp = ppsetfile(file); if (!(xp->flags & INC_LISTED)) { xp->flags |= INC_LISTED; if ((pp.column + strlen(file)) >= COLUMN_MAX) { sfprintf(pp.filedeps.sp, " \\\n"); pp.column = COLUMN_TAB; index = '\t'; } else index = ' '; pp.column += sfprintf(pp.filedeps.sp, "%c%s", index, file); } } } } return fd; }