static int intercept(Sfio_t* sp, int level, int flags) { register Rule_t* r; char* m; char* s; char* t; char* e; int n; int i; Sfio_t* tmp; NoP(sp); NoP(flags); if ((state.mam.level = level) > 0 && !state.hold && (r = internal.error) && (r->property & (P_functional|P_target)) == (P_functional|P_target) && !state.compileonly && !state.interrupt && (m = stakptr(0)) && (n = staktell()) > 0) { /* * make the error trap */ state.hold = m; while (*m && *m != ':') m++; while (isspace(*++m)); n -= m - state.hold; tmp = sfstropen(); sfprintf(tmp, "%d %-.*s", level, n, m); s = sfstruse(tmp); /* * return [ level | - ] [ message ] * level is new level or - to retain old * omitted message means it has been printed */ if (t = call(r, s)) { i = strtol(t, &e, 0); if (e > t) { t = e; level = i; } else if (*t == '-') t++; while (isspace(*t)) t++; } if (!t || !*t) { level |= ERROR_OUTPUT; message((-1, "suppressed %s message: `%-.*s'", level == 1 ? "warning" : "error", n, m)); } sfstrclose(tmp); state.hold = 0; } return level; }
static void commit(Joblist_t* job, register char* s) { register char* t; register char* v; register Rule_t* r; Stat_t st; if (t = strrchr(s, '/')) { *t = 0; if (r = bindfile(NiL, s, 0)) { if (!r->view) { *t = '/'; return; } if (*(v = r->name) != '/') { sfprintf(internal.nam, "%s/%s", state.view[r->view].root, v); v = sfstruse(internal.nam); } if (stat(v, &st)) r = 0; } if (r || state.targetcontext && (!r || !r->time) && (st.st_mode = (S_IRWXU|S_IRWXG|S_IRWXO)) && tmxsetmtime(&st, state.start)) { /* * why not mkdir -p here? */ commit(job, s); if (((job->flags & CO_ALWAYS) || state.exec && state.touch) && (mkdir(s, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || stat(s, &st))) error(1, "%s: cannot create target directory %s", job->target->name, s); if (state.mam.out) { Sfio_t* tmp = sfstropen(); sfprintf(tmp, "mkdir %s", s); dumpaction(state.mam.out, MAMNAME(job->target), sfstruse(tmp), NiL); sfstrclose(tmp); } r = makerule(s); if (r->dynamic & D_alias) oldname(r); r->view = 0; r->time = tmxgetmtime(&st); if (r->dynamic & D_scanned) unbind(NiL, (char*)r, NiL); } *t = '/'; } }
Time_t tmxduration(const char* s, char** e) { Time_t ns; Time_t ts; Time_t now; char* last; char* t; char* x; Sfio_t* f; int i; now = TMX_NOW; while (isspace(*s)) s++; if (*s == 'P' || *s == 'p') ns = tmxdate(s, &last, now) - now; else { ns = strtod(s, &last) * TMX_RESOLUTION; if (*last && (f = sfstropen())) { sfprintf(f, "exact %s", s); t = sfstruse(f); ts = tmxdate(t, &x, now); if ((i = x - t - 6) > (last - s)) { last = (char*)s + i; ns = ts - now; } else { sfprintf(f, "p%s", s); t = sfstruse(f); ts = tmxdate(t, &x, now); if ((i = x - t - 1) > (last - s)) { last = (char*)s + i; ns = ts - now; } } sfstrclose(f); } } if (e) *e = last; return ns; }
static int print(Expr_t* ex, Exnode_t* expr, void* env, Sfio_t* sp) { register Print_t* x; Extype_t v; Fmt_t fmt; if (!sp) { v = eval(ex, expr->data.print.descriptor, env); if (v.integer < 0 || v.integer >= elementsof(ex->file) || !(sp = ex->file[v.integer]) && !(sp = ex->file[v.integer] = sfnew(NiL, NiL, SF_UNBOUND, v.integer, SF_READ|SF_WRITE))) { exerror("printf: %d: invalid descriptor", v.integer); return 0; } } memset(&fmt, 0, sizeof(fmt)); fmt.fmt.version = SFIO_VERSION; fmt.fmt.extf = prformat; fmt.expr = ex; fmt.env = env; x = expr->data.print.args; if (x->format) do { if (x->arg) { fmt.fmt.form = x->format; fmt.args = x; sfprintf(sp, "%!", &fmt); } else sfputr(sp, x->format, -1); } while (x = x->next); else { v = eval(ex, x->arg->data.operand.left, env); fmt.fmt.form = v.string; fmt.actuals = x->arg; sfprintf(sp, "%!", &fmt); if (fmt.actuals->data.operand.right) exerror("printf: \"%s\": too many arguments", fmt.fmt.form); } if (fmt.tmp) sfstrclose(fmt.tmp); return 1; }
int vasprintf(char** s, const char* fmt, va_list args) { Sfio_t* f; int v; if (f = sfstropen()) { v = sfvprintf(f, fmt, args); if (!(*s = strdup(sfstruse(f)))) v = -1; sfstrclose(f); } else { *s = 0; v = -1; } return v; }
static int ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...) { va_list ap; Sfio_t* tp; char* s; int n; va_start(ap, format); if (from == to) n = sfvprintf(sp, format, ap); else if (tp = sfstropen()) { n = sfvprintf(tp, format, ap); s = sfstrbase(tp); ccmaps(s, n, from, to); n = sfwrite(sp, s, n); sfstrclose(tp); } else n = -1; return n; }
void ppbuiltin(void) { register int c; register char* p; register char* a; int n; int op; char* token; char* t; long number; long onumber; struct ppinstk* in; struct pplist* list; struct ppsymbol* sym; Sfio_t* sp; number = pp.state; pp.state |= DISABLE|FILEPOP|NOSPACE; token = pp.token; p = pp.token = pp.tmpbuf; *(a = pp.args) = 0; if ((c = pplex()) != T_ID) { error(2, "%s: #(<identifier>...) expected", p); *p = 0; } switch (op = (int)hashget(pp.strtab, p)) { case V_DEFAULT: n = 0; p = pp.token = pp.valbuf; if ((c = pplex()) == ',') { op = -1; c = pplex(); } pp.state &= ~NOSPACE; for (;;) { if (!c) { error(2, "%s in #(...) argument", pptokchr(c)); break; } if (c == '(') n++; else if (c == ')' && !n--) break; else if (c == ',' && !n && op > 0) op = 0; if (op) pp.token = pp.toknxt; c = pplex(); } *pp.token = 0; pp.token = token; pp.state = number; break; case V_EMPTY: p = pp.valbuf; if ((c = pplex()) == ')') *p = '1'; else { *p = '0'; n = 0; for (;;) { if (!c) { error(2, "%s in #(...) argument", pptokchr(c)); break; } if (c == '(') n++; else if (c == ')' && !n--) break; c = pplex(); } } *(p + 1) = 0; pp.token = token; pp.state = number; break; case V_ITERATE: n = 0; pp.token = pp.valbuf; if ((c = pplex()) != T_ID || !(sym = ppsymref(pp.symtab, pp.token)) || !sym->macro || sym->macro->arity != 1 || (c = pplex()) != ',') { error(2, "#(%s <macro(x)>, ...) expected", p); for (;;) { if (!c) { error(2, "%s in #(...) argument", pptokchr(c)); break; } if (c == '(') n++; else if (c == ')' && !n--) break; c = pplex(); } *pp.valbuf = 0; } else while (c != ')') { p = pp.token; if (pp.token > pp.valbuf) *pp.token++ = ' '; STRCOPY(pp.token, sym->name, a); *pp.token++ = '('; if (!c || !(c = pplex())) { pp.token = p; error(2, "%s in #(...) argument", pptokchr(c)); break; } pp.state &= ~NOSPACE; while (c) { if (c == '(') n++; else if (c == ')' && !n--) break; else if (c == ',' && !n) break; pp.token = pp.toknxt; c = pplex(); } *pp.token++ = ')'; pp.state |= NOSPACE; } p = pp.valbuf; pp.token = token; pp.state = number; break; default: pp.token = token; while (c != ')') { if (!c) { error(2, "%s in #(...) argument", pptokchr(c)); break; } if ((c = pplex()) == T_ID && !*a) strcpy(a, pp.token); } pp.state = number; switch (op) { case V_ARGC: c = -1; for (in = pp.in; in; in = in->prev) if ((in->type == IN_MACRO || in->type == IN_MULTILINE) && (in->symbol->flags & SYM_FUNCTION)) { c = *((unsigned char*)(pp.macp->arg[0] - 2)); break; } sfsprintf(p = pp.valbuf, MAXTOKEN, "%d", c); break; case V_BASE: p = (a = strrchr(error_info.file, '/')) ? a + 1 : error_info.file; break; case V_DATE: if (!(p = pp.date)) { time_t tm; time(&tm); a = p = ctime(&tm) + 4; *(p + 20) = 0; for (p += 7; *p = *(p + 9); p++); pp.date = p = strdup(a); } break; case V_FILE: p = error_info.file; break; case V_LINE: sfsprintf(p = pp.valbuf, MAXTOKEN, "%d", error_info.line); break; case V_PATH: p = pp.path; break; case V_SOURCE: p = error_info.file; for (in = pp.in; in->prev; in = in->prev) if (in->prev->type == IN_FILE && in->file) p = in->file; break; case V_STDC: p = pp.valbuf; p[0] = ((pp.state & (COMPATIBILITY|TRANSITION)) || (pp.mode & (HOSTED|HOSTEDTRANSITION)) == (HOSTED|HOSTEDTRANSITION)) ? '0' : '1'; p[1] = 0; break; case V_TIME: if (!(p = pp.time)) { time_t tm; time(&tm); p = ctime(&tm) + 11; *(p + 8) = 0; pp.time = p = strdup(p); } break; case V_VERSION: p = (char*)pp.version; break; case V_DIRECTIVE: pp.state |= NEWLINE; pp.mode |= RELAX; strcpy(p = pp.valbuf, "#"); break; case V_GETENV: if (!(p = getenv(a))) p = ""; break; case V_GETMAC: p = (sym = pprefmac(a, REF_NORMAL)) ? sym->macro->value : ""; break; case V_GETOPT: sfsprintf(p = pp.valbuf, MAXTOKEN, "%ld", ppoption(a)); break; case V_GETPRD: p = (list = (struct pplist*)hashget(pp.prdtab, a)) ? list->value : ""; break; case V__PRAGMA: if ((c = pplex()) == '(') { number = pp.state; pp.state |= NOSPACE|STRIP; c = pplex(); pp.state = number; if (c == T_STRING || c == T_WSTRING) { if (!(sp = sfstropen())) error(3, "temporary buffer allocation error"); sfprintf(sp, "#%s %s\n", dirname(PRAGMA), pp.token); a = sfstruse(sp); if ((c = pplex()) == ')') { pp.state |= NEWLINE; PUSH_BUFFER(p, a, 1); } sfstrclose(sp); } } if (c != ')') error(2, "%s: (\"...\") expected", p); return; case V_FUNCTION: #define BACK(a,p) ((a>p)?*--a:(number++?0:((p=pp.outbuf+PPBUFSIZ),(a=pp.outbuf+2*PPBUFSIZ),*--a))) #define PEEK(a,p) ((a>p)?*(a-1):(number?0:*(pp.outbuf+2*PPBUFSIZ-1))) number = pp.outbuf != pp.outb; a = pp.outp; p = pp.outb; op = 0; while (c = BACK(a, p)) { if (c == '"' || c == '\'') { op = 0; while ((n = BACK(a, p)) && n != c || PEEK(a, p) == '\\'); } else if (c == '\n') { token = a; while (c = BACK(a, p)) if (c == '\n') { a = token; break; } else if (c == '#' && PEEK(a, p) == '\n') break; } else if (c == ' ') /*ignore*/; else if (c == '{') /* '}' */ op = 1; else if (op == 1) { if (c == ')') { op = 2; n = 1; } else op = 0; } else if (op == 2) { if (c == ')') n++; else if (c == '(' && !--n) op = 3; } else if (op == 3) { if (ppisidig(c)) { for (t = p, token = a, onumber = number; ppisidig(PEEK(a, p)) && a >= p; BACK(a, p)); p = pp.valbuf + 1; if (a > token) { for (; a < pp.outbuf+2*PPBUFSIZ; *p++ = *a++); a = pp.outbuf; } for (; a <= token; *p++ = *a++); *p = 0; p = pp.valbuf + 1; if (streq(p, "for") || streq(p, "if") || streq(p, "switch") || streq(p, "while")) { op = 0; p = t; number = onumber; continue; } } else op = 0; break; } } if (op == 3) p = strncpy(pp.funbuf, p, sizeof(pp.funbuf) - 1); else if (*pp.funbuf) p = pp.funbuf; else p = "__FUNCTION__"; break; default: if (pp.builtin && (a = (*pp.builtin)(pp.valbuf, p, a))) p = a; break; } break; } if (strchr(p, MARK)) { a = pp.tmpbuf; strcpy(a, p); c = p != pp.valbuf; p = pp.valbuf + c; for (;;) { if (p < pp.valbuf + MAXTOKEN - 2) switch (*p++ = *a++) { case 0: break; case MARK: *p++ = MARK; /*FALLTHROUGH*/ default: continue; } break; } p = pp.valbuf + c; } if (p == pp.valbuf) PUSH_STRING(p); else { if (p == pp.valbuf + 1) *pp.valbuf = '"'; else { if (strlen(p) > MAXTOKEN - 2) error(1, "%-.16s: builtin value truncated", p); sfsprintf(pp.valbuf, MAXTOKEN, "\"%-.*s", MAXTOKEN - 2, p); } PUSH_QUOTE(pp.valbuf, 1); } }
int main(int argc, char** argv) { register char* s; register Rule_t* r; register List_t* p; int i; int args; int trace; char* t; char* buf; char* tok; Var_t* v; Stat_t st; Stat_t ds; Sfio_t* tmp; /* * initialize dynamic globals */ version = strdup(fmtident(version)); setlocale(LC_ALL, ""); error_info.id = idname; error_info.version = version; error_info.exit = finish; error_info.auxilliary = intercept; if (pathcheck(PATHCHECK, error_info.id, NiL)) return 1; error(-99, "startup"); settypes("*?[]", C_MATCH); settypes("+-|=", C_OPTVAL); settypes(" \t\n", C_SEP); settype(0, C_SEP); settypes(" \t\v\n:+&=;\"\\", C_TERMINAL); settype(0, C_TERMINAL); settypes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", C_ID1|C_ID2|C_VARIABLE1|C_VARIABLE2); settypes(".", C_VARIABLE1|C_VARIABLE2); settypes("0123456789", C_ID2|C_VARIABLE2); /* * close garbage fd's -- we'll be tidy from this point on * 3 may be /dev/tty on some systems * 0..9 for user redirection in shell * 10..19 left open by bugs in some shells * error_info.fd interited from parent * any close-on-exec fd's must have been done on our behalf */ i = 3; if (isatty(i)) i++; for (; i < 20; i++) if (i != error_info.fd && !fcntl(i, F_GETFD, 0)) close(i); /* * allocate the very temporary buffer streams */ internal.met = sfstropen(); internal.nam = sfstropen(); internal.tmp = sfstropen(); internal.val = sfstropen(); internal.wrk = sfstropen(); tmp = sfstropen(); sfstrrsrv(tmp, 2 * MAXNAME); /* * initialize the code and hash tables */ initcode(); inithash(); /* * set the default state */ state.alias = 1; state.exec = 1; state.global = 1; state.init = 1; #if DEBUG state.intermediate = 1; #endif state.io[0] = sfstdin; state.io[1] = sfstdout; state.io[2] = sfstderr; state.jobs = 1; state.pid = getpid(); state.readstate = MAXVIEW; state.scan = 1; state.start = CURTIME; state.stateview = -1; state.tabstops = 8; state.targetview = -1; #if BINDINDEX state.view[0].path = makerule("."); #else state.view[0].path = "."; #endif state.view[0].pathlen = 1; state.writeobject = state.writestate = "-"; /* * pwd initialization * * for project management, if . is group|other writeable * then change umask() for similar write protections */ buf = sfstrbase(tmp); internal.pwd = (s = getcwd(buf, MAXNAME)) ? strdup(s) : strdup("."); internal.pwdlen = strlen(internal.pwd); if (stat(".", &st)) error(3, "cannot stat ."); if (S_ISDIR(st.st_mode) && (st.st_mode & (S_IWGRP|S_IWOTH))) umask(umask(0) & ~(st.st_mode & (S_IWGRP|S_IWOTH))); /* * set some variable default values */ hashclear(table.var, HASH_ALLOCATE); setvar(external.make, argv[0], V_import); t = "lib/make"; setvar(external.lib, strdup((s = pathpath(t, argv[0], PATH_EXECUTE, buf, SF_BUFSIZE)) ? s : t), V_import); setvar(external.pwd, internal.pwd, V_import); setvar(external.version, version, V_import); hashset(table.var, HASH_ALLOCATE); /* * read the environment */ readenv(); if (v = getvar(external.nproc)) state.jobs = (int)strtol(v->value, NiL, 0); if ((v = getvar(external.pwd)) && !streq(v->value, internal.pwd)) { if (!stat(v->value, &st) && !stat(internal.pwd, &ds) && st.st_ino == ds.st_ino && st.st_dev == ds.st_dev) { free(internal.pwd); internal.pwd = strdup(v->value); internal.pwdlen = strlen(v->value); } else { v->property &= ~V_import; v->property |= V_free; v->value = strdup(internal.pwd); } } /* * initialize the internal rule pointers */ initrule(); /* * read the static initialization script */ sfputr(tmp, initstatic, -1); parse(NiL, sfstruse(tmp), "initstatic", NiL); /* * check and read the args file */ if (s = colonlist(tmp, external.args, 1, ' ')) { i = fs3d(0); tok = tokopen(s, 1); while (s = tokread(tok)) if (vecargs(vecfile(s), &argc, &argv) >= 0) break; else if (errno != ENOENT) error(1, "cannot read args file %s", s); tokclose(tok); fs3d(i); } state.argf = newof(0, int, argc, 0); /* * set the command line options * read the command line assignments * mark the command line scripts and targets */ state.init = 0; state.readonly = 1; state.argv = argv; state.argc = argc; if ((args = scanargs(state.argc, state.argv, state.argf)) < 0) return 1; state.readonly = 0; state.init = 1; if (state.base) state.readstate = 0; if (state.compileonly) { state.forceread = 1; state.virtualdot = 0; } /* * tone down the bootstrap noise */ if ((trace = error_info.trace) == -1) error_info.trace = 0; /* * check explicit environment overrides */ if (s = colonlist(tmp, external.import, 1, ' ')) { tok = tokopen(s, 1); while (s = tokread(tok)) { if (i = *s == '!') s++; if (v = getvar(s)) { if (i) v->property &= ~V_import; else v->property |= V_readonly; } } tokclose(tok); } /* * set up the traps */ inittrap(); /* * announce the version */ if (error_info.trace < 0) { errno = 0; error(error_info.trace, "%s [%d %s]", version, state.pid, timestr(state.start)); } /* * initialize the views */ state.global = 0; state.user = 1; initview(); /* * check for mam */ if (state.mam.out) { if (!state.mam.statix || *state.mam.label) error_info.write = mamerror; if (state.mam.regress || state.regress) { sfprintf(state.mam.out, "%sinfo mam %s %05d\n", state.mam.label, state.mam.type, state.mam.parent); if (state.mam.regress) sfprintf(state.mam.out, "%sinfo start regression\n", state.mam.label); } else { sfprintf(state.mam.out, "%sinfo mam %s %05d 1994-07-17 %s\n", state.mam.label, state.mam.type, state.mam.parent, version); if (!state.mam.statix || *state.mam.label) { sfprintf(state.mam.out, "%sinfo start %lu\n", state.mam.label, CURTIME); if (!state.mam.root || streq(state.mam.root, internal.pwd)) sfprintf(state.mam.out, "%sinfo pwd %s\n", state.mam.label, internal.pwd); else sfprintf(state.mam.out, "%sinfo pwd %s %s\n", state.mam.label, state.mam.root, mamname(makerule(internal.pwd))); buf = sfstrbase(tmp); if (state.fsview && !mount(NiL, buf, FS3D_GET|FS3D_ALL|FS3D_SIZE(sfstrsize(tmp)), NiL)) sfprintf(state.mam.out, "%sinfo view %s\n", state.mam.label, buf); } } } /* * read the dynamic initialization script */ if ((i = error_info.trace) > -20) error_info.trace = 0; sfputr(tmp, initdynamic, -1); parse(NiL, sfstruse(tmp), "initdynamic", NiL); error_info.trace = i; state.user = 0; state.init = 0; /* * read the explicit makefiles * readfile() handles the base and global rules * * NOTE: internal.tmplist is used to handle the effects of * load() on internal list pointers */ compref(NiL, 0); if (p = internal.makefiles->prereqs) { p = internal.tmplist->prereqs = listcopy(p); for (; p; p = p->next) readfile(p->rule->name, COMP_FILE, NiL); freelist(internal.tmplist->prereqs); internal.tmplist->prereqs = 0; } /* * if no explicit makefiles then try external.{convert,files} */ if (!state.makefile) { int sep; Sfio_t* exp; Sfio_t* imp; exp = 0; imp = sfstropen(); sep = 0; s = 0; if (*(t = getval(external.convert, VAL_PRIMARY))) { sfputr(tmp, t, 0); sfstrrsrv(tmp, MAXNAME); tok = tokopen(sfstrbase(tmp), 0); while (s = tokread(tok)) { if (!exp) exp = sfstropen(); if (t = colonlist(exp, s, 0, ' ')) { t = tokopen(t, 0); while (s = tokread(t)) { if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL)) break; if (sep) sfputc(imp, ','); else sep = 1; sfputr(imp, s, -1); } tokclose(t); if (s) break; } if (!(s = tokread(tok))) break; } tokclose(tok); sfstrseek(tmp, 0, SEEK_SET); } if (!s && (s = colonlist(tmp, external.files, 1, ' '))) { tok = tokopen(s, 1); while (s = tokread(tok)) { if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL)) break; if (sep) sfputc(imp, ','); else sep = 1; sfputr(imp, s, -1); } tokclose(tok); } if (!s) { /* * this readfile() pulls in the default base rules * that might resolve any delayed self-documenting * options in optcheck() */ if (readfile("-", COMP_FILE, NiL)) optcheck(1); if (*(s = sfstruse(imp))) error(state.errorid ? 1 : 3, "a makefile must be specified when %s omitted", s); else error(state.errorid ? 1 : 3, "a makefile must be specified"); } sfstrclose(imp); if (exp) sfstrclose(exp); } /* * validate external command line options */ optcheck(1); /* * check for listing of variable and rule definitions */ if (state.list) { dump(sfstdout, 0); return 0; } /* * check if makefiles to be compiled */ if (state.compile && !state.virtualdot && state.writeobject) { /* * make the compinit trap */ if (r = getrule(external.compinit)) { state.reading = 1; maketop(r, P_dontcare|P_foreground, NiL); state.reading = 0; } if (state.exec && state.objectfile) { message((-2, "compiling makefile input into %s", state.objectfile)); compile(state.objectfile, NiL); } /* * make the compdone trap */ if (r = getrule(external.compdone)) { state.reading = 1; maketop(r, P_dontcare|P_foreground, NiL); state.reading = 0; } } /* * makefile read cleanup */ if (state.compileonly) return 0; compref(NiL, 0); sfstrclose(tmp); state.compile = COMPILED; if (state.believe) { if (!state.maxview) state.believe = 0; else if (state.fsview) error(3, "%s: option currently works in 2d only", optflag(OPT_believe)->name); } /* * read the state file */ readstate(); /* * place the command line targets in internal.args */ if (internal.main->dynamic & D_dynamic) dynamic(internal.main); internal.args->prereqs = p = 0; for (i = args; i < state.argc; i++) if (state.argf[i] & ARG_TARGET) { List_t* q; q = cons(makerule(state.argv[i]), NiL); if (p) p = p->next = q; else internal.args->prereqs = p = q; } /* * the engine bootstrap is complete -- start user activities */ state.user = 1; /* * make the makeinit trap */ if (r = getrule(external.makeinit)) maketop(r, P_dontcare|P_foreground, NiL); /* * read the command line scripts */ for (i = args; i < state.argc; i++) if (state.argf[i] & ARG_SCRIPT) { state.reading = 1; parse(NiL, state.argv[i], "command line script", NiL); state.reading = 0; } /* * freeze the parameter files and candidate state variables */ state.user = 2; candidates(); /* * make the init trap */ if (r = getrule(external.init)) maketop(r, P_dontcare|P_foreground, NiL); /* * internal.args default to internal.main */ if (!internal.args->prereqs && internal.main->prereqs) internal.args->prereqs = listcopy(internal.main->prereqs); /* * turn up the volume again */ if (!error_info.trace) error_info.trace = trace; /* * make the prerequisites of internal.args */ if (internal.args->prereqs) while (internal.args->prereqs) { /* * we explicitly allow internal.args modifications */ r = internal.args->prereqs->rule; internal.making->prereqs = internal.args->prereqs; internal.args->prereqs = internal.args->prereqs->next; internal.making->prereqs->next = 0; maketop(r, 0, NiL); } else if (state.makefile) error(3, "%s: a main target must be specified", state.makefile); /* * finish up */ finish(0); return 0; }
static void execute(register Joblist_t* job) { register List_t* p; char* s; char* t; int flags; Rule_t* r; Var_t* v; Sfio_t* tmp; Sfio_t* att; Sfio_t* sp; att = sfstropen(); tmp = sfstropen(); restore(job, tmp, att); job->status = RUNNING; job->target->mark &= ~M_waiting; if (state.targetcontext || state.maxview && !state.fsview && *job->target->name != '/' && (!(job->target->dynamic & D_regular) || job->target->view)) commit(job, job->target->name); if ((state.mam.dynamic || state.mam.regress) && state.user && !(job->target->property & (P_after|P_before|P_dontcare|P_make|P_state|P_virtual))) sfprintf(state.mam.out, "%sinit %s %s\n", state.mam.label, mamname(job->target), timefmt(NiL, CURTIME)); t = sfstruse(tmp); if (!(job->flags & CO_ALWAYS)) { if (state.touch) { if (state.virtualdot) { state.virtualdot = 0; lockstate(1); } if (!(job->target->property & (P_attribute|P_virtual))) { acceptrule(job->target); if ((job->target->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = job->target->prereqs->rule->prereqs; p; p = p->next) if (p->rule != job->target) acceptrule(p->rule); } } else if (*t && (!state.silent || state.mam.regress)) dumpaction(state.mam.out ? state.mam.out : sfstdout, NiL, t, NiL); done(job, 0, NiL); } else { if (state.virtualdot && !notfile(job->target)) { state.virtualdot = 0; lockstate(1); } if (!state.coshell) { sp = sfstropen(); sfprintf(sp, "label=%s", idname); expand(sp, " $(" CO_ENV_OPTIONS ")"); flags = CO_ANY; if (state.cross) flags |= CO_CROSS; if (state.serialize && state.jobs > 1) flags |= CO_SERIALIZE; if (!(state.coshell = coopen(getval(CO_ENV_SHELL, VAL_PRIMARY), flags, sfstruse(sp)))) error(ERROR_SYSTEM|3, "coshell open error"); sfstrclose(sp); } if (p = internal.exports->prereqs) { Sfio_t* exp; exp = sfstropen(); do { if (v = getvar(p->rule->name)) { expand(exp, v->value); coexport(state.coshell, p->rule->name, sfstruse(exp)); } else if (s = strchr(p->rule->name, '=')) { *s = 0; expand(exp, s + 1); coexport(state.coshell, p->rule->name, sfstruse(exp)); *s = '='; } } while (p = p->next); sfstrclose(exp); #if 0 freelist(internal.exports->prereqs); #endif internal.exports->prereqs = 0; } if (job->flags & CO_DATAFILE) { static char* dot; static char* tmp; if (job->target->property & P_read) { if (!dot) dot = pathtemp(NiL, 0, null, idname, NiL); state.tmpfile = dot; } else { if (!tmp) tmp = pathtemp(NiL, 0, NiL, idname, NiL); state.tmpfile = tmp; } } #if !_HUH_1992_02_29 /* i386 and ftx m68k dump without this statement -- help */ message((-99, "execute: %s: t=0x%08x &t=0x%08x", job->target->name, t, &t)); #endif if (state.mam.out) dumpaction(state.mam.out, MAMNAME(job->target), t, NiL); if (r = getrule(external.makerun)) maketop(r, P_dontcare|P_foreground, NiL); if (!(job->cojob = coexec(state.coshell, t, job->flags, state.tmpfile, NiL, sfstruse(att)))) error(3, "%s: cannot send action to coshell", job->target->name); job->cojob->local = (void*)job; /* * grab semaphores */ if (job->target->dynamic & D_hassemaphore) { job->flags |= CO_SEMAPHORES; for (p = job->prereqs; p; p = p->next) if (p->rule->semaphore && --p->rule->semaphore == 1) p->rule->status = MAKING; } /* * check status and sync */ if (job->target->dynamic & D_hasafter) save(job); if (job->flags & (CO_DATAFILE|CO_FOREGROUND)) { complete(job->target, NiL, NiL, 0); if (job->target->property & (P_functional|P_read)) { if (sp = sfopen(NiL, state.tmpfile, "r")) { remove(state.tmpfile); if (job->target->property & P_read) parse(sp, NiL, job->target->name, NiL); else { char* e; sfmove(sp, tmp, SF_UNBOUND, -1); t = sfstrbase(tmp); e = sfstrseek(tmp, 0, SEEK_CUR); while (e > t && *(e - 1) == '\n') e--; sfstrseek(tmp, e - t, SEEK_SET); setvar(job->target->name, sfstruse(tmp), 0); } sfclose(sp); } else error(2, "%s: cannot read temporary data output file %s", job->target->name, state.tmpfile); state.tmpfile = 0; } } } sfstrclose(att); sfstrclose(tmp); }
static void restore(register Joblist_t* job, Sfio_t* buf, Sfio_t* att) { register char* s; register char* b; char* u; char* down; char* back; char* sep; int downlen; int localview; void* pos; Var_t* v; Sfio_t* opt; Sfio_t* tmp; Sfio_t* context; push(job); localview = state.localview; state.localview = state.mam.statix && !state.expandview && state.user && !(job->flags & CO_ALWAYS); if ((job->flags & CO_LOCALSTACK) || (job->target->dynamic & D_hasscope)) { register Rule_t* r; register List_t* p; job->flags |= CO_LOCALSTACK; pos = pushlocal(); opt = sfstropen(); if (job->target->dynamic & D_hasscope) for (p = job->prereqs; p; p = p->next) if ((r = p->rule)->dynamic & D_scope) { if (*r->name == '-') set(r->name, 1, opt); else parse(NiL, r->name, r->name, opt); } else if ((r->property & (P_make|P_local|P_use)) == (P_make|P_local) && r->action) parse(NiL, r->action, r->name, opt); } context = state.context; if (state.targetcontext && *(u = unbound(job->target)) != '/' && (s = strrchr(u, '/'))) { size_t n; int c; tmp = sfstropen(); downlen = s - u; *s = 0; sfprintf(tmp, "%s%c", u, 0); n = sfstrtell(tmp); c = '/'; do { if (u = strchr(u, '/')) u++; else c = 0; sfputr(tmp, "..", c); } while (c); *s = '/'; back = (down = sfstrbase(tmp)) + n; state.context = buf; buf = sfstropen(); state.localview++; } else state.context = 0; if (job->action) expand(buf, job->action); if (state.context) { s = sfstruse(buf); sep = strchr(s, '\n') ? "\n" : "; "; sfprintf(state.context, "{ cd %s%s", down, sep); while (b = strchr(s, MARK_CONTEXT)) { sfwrite(state.context, s, b - s); if (!(s = strchr(++b, MARK_CONTEXT))) error(PANIC, "unbalanced MARK_CONTEXT"); *s++ = 0; if (*b == '/' || (u = getbound(b)) && *u == '/') sfputr(state.context, b, -1); else if (*b) { if (strneq(b, down, downlen)) switch (*(b + downlen)) { case 0: sfputc(state.context, '.'); continue; case '/': sfputr(state.context, b + downlen + 1, -1); continue; } if (streq(b, ".")) sfputr(state.context, back, -1); else if (isspace(*b)) sfputr(state.context, b, -1); else sfprintf(state.context, "%s/%s", back, b); } } sfprintf(state.context, "%s%s}", s, sep); sfstrclose(tmp); sfstrclose(buf); } state.context = context; sfprintf(att, "label=%s", job->target->name); if ((v = getvar(CO_ENV_ATTRIBUTES)) && !(v->property & V_import)) sfprintf(att, ",%s", v->value); if (job->flags & CO_LOCALSTACK) { poplocal(pos); if (*(s = sfstruse(opt))) set(s, 1, NiL); sfclose(opt); } state.localview = localview; pop(job); }
void trigger(register Rule_t* r, Rule_t* a, char* action, Flags_t flags) { register Joblist_t* job; register List_t* p; List_t* prereqs; int n; /* * update flags */ if (!a) a = r; if (state.exec && !state.touch || (a->property & P_always) && (!state.never || (flags & CO_URGENT))) flags |= CO_ALWAYS; if ((a->property | r->property) & P_local) flags |= CO_LOCAL; if (!state.jobs || (r->property & P_foreground) || (r->property & (P_make|P_functional)) == P_functional || (r->dynamic & D_hasmake)) flags |= CO_FOREGROUND|CO_LOCAL; if (state.keepgoing || state.unwind) flags |= CO_KEEPGOING; if (state.silent) flags |= CO_SILENT; if (state.ignore) flags |= CO_IGNORE; if (r->property & (P_functional|P_read)) flags |= CO_DATAFILE; if (action) { message((-1, "triggering %s action%s%s", r->name, r == a ? null : " using ", r == a ? null : a->name)); if (state.exec) jobs.triggered = r; r->dynamic |= D_triggered; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) p->rule->dynamic |= D_triggered; if (!*action) action = 0; } if (state.coshell && (action && !(r->property & P_make) || (flags & CO_FOREGROUND))) { /* * the make thread blocks when too many jobs are outstanding */ n = (flags & CO_FOREGROUND) ? 0 : (state.jobs - 1); while ((cozombie(state.coshell) || cojobs(state.coshell) > n) && block(0)); if ((flags & CO_FOREGROUND) && r->active && r->active->parent && r->active->parent->prereqs && copending(state.coshell) > cojobs(state.coshell)) serial(r, r->active->parent->prereqs); } prereqs = r->prereqs; if (r->active && r->active->primary) { prereqs = cons(getrule(r->active->primary), prereqs); flags |= CO_PRIMARY; } if (r->property & P_make) { if (r->property & P_local) { r->status = EXISTS; return; } /* * make actions are done immediately, bypassing the job queue */ if (prereqs && complete(NiL, prereqs, NiL, 0)) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; else { if (action && cancel(r, prereqs)) r->status = EXISTS; else if ((r->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered) && (makebefore(r) || complete(NiL, prereqs, NiL, 0))) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; else { if (r->property & P_functional) setvar(r->name, null, 0); if (action) switch (parse(NiL, action, r->name, NiL)) { case EXISTS: if (!(r->property & (P_state|P_virtual))) statetime(r, 0); break; case FAILED: r->status = (r->property & P_dontcare) ? IGNORE : FAILED; break; case TOUCH: r->time = internal.internal->time; break; case UPDATE: if ((r->property & (P_state|P_virtual)) != (P_state|P_virtual)) r->time = CURTIME; break; } if (r->status == UPDATE) r->status = EXISTS; } } if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) { p->rule->status = r->status; p->rule->time = r->time; } if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered)) { if (r->status == FAILED) { if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0)) r->status = EXISTS; } else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0))) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; } } else { /* * only one repeat action at a time */ if ((r->property & P_repeat) && (r->property & (P_before|P_after)) && !(r->dynamic & D_hassemaphore)) { a = catrule(internal.semaphore->name, ".", r->name, 1); a->semaphore = 2; r->prereqs = append(r->prereqs, cons(a, NiL)); r->dynamic |= D_hassemaphore; } /* * check if any prerequisites are blocking execution * FAILED prerequisites cause the target to fail too */ n = READY; for (;;) { for (p = prereqs; p; p = p->next) { if ((a = p->rule)->dynamic & D_alias) a = makerule(a->name); if (a->property & P_after) continue; switch (a->status) { case FAILED: if (a->property & P_repeat) continue; r->status = (r->property & P_dontcare) ? IGNORE : FAILED; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED; return; case MAKING: if (a->active) error(1, "%s: prerequisite %s is active", r->name, a->name); else n = BLOCKED; break; } } if (n != READY) break; if (action) { if (cancel(r, prereqs)) return; if ((r->dynamic & D_intermediate) && r->must == 1) { n = INTERMEDIATE; jobs.intermediate++; break; } } if ((r->dynamic & (D_hasbefore|D_triggered)) != (D_hasbefore|D_triggered)) break; if (makebefore(r)) { r->status = (r->property & P_dontcare) ? IGNORE : FAILED; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED; return; } } if (action || n != READY) { /* * allocate a job cell and add to job list * the first READY job from the top is executed next */ if (job = jobs.freejob) jobs.freejob = jobs.freejob->next; else job = newof(0, Joblist_t, 1, 0); if (flags & CO_URGENT) { job->prev = 0; if (job->next = jobs.firstjob) jobs.firstjob->prev = job; else jobs.lastjob = job; jobs.firstjob = job; } else { job->next = 0; if (job->prev = jobs.lastjob) jobs.lastjob->next = job; else jobs.firstjob = job; jobs.lastjob = job; } /* * fill in the info */ job->target = r; job->prereqs = prereqs; job->status = n; job->flags = flags; job->action = action; r->status = MAKING; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule != r) p->rule->status = r->status; if (n == READY) { execute(job); if (r->dynamic & D_hasafter) save(job); } else save(job); jobstatus(); } else { if (r->status == UPDATE) r->status = EXISTS; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) if (p->rule->status == UPDATE) p->rule->status = EXISTS; if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered)) { if (r->status == FAILED) { if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0)) r->status = EXISTS; } else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0))) r->status = (r->property & P_dontcare) ? IGNORE : FAILED; if (r->status == EXISTS) { char* t; Sfio_t* tmp; tmp = sfstropen(); edit(tmp, r->name, KEEP, DELETE, DELETE); if (*(t = sfstruse(tmp))) newfile(r, t, r->time); sfstrclose(tmp); } } } if (r->dynamic & D_triggered) { r->time = CURTIME; if ((r->property & (P_joint|P_target)) == (P_joint|P_target)) for (p = r->prereqs->rule->prereqs; p; p = p->next) p->rule->time = r->time; } } }
static int golly(int argc, char* const* argv, const char* optstring, const struct option* longopts, int* longindex, int flags) { register char* s; register const struct option* o; register int c; char* t; if (!up || optstring != lastoptstring || longopts != lastlongopts) { if (!up && !(up = sfstropen()) || !(t = strdup(optstring))) return -1; sfprintf(up, "[-1p%d]", flags); for (o = longopts; o->name; o++) { if (o->flag || o->val <= 0 || o->val > UCHAR_MAX || !isalnum(o->val)) sfprintf(up, "\n[%d:%s]", UCHAR_MAX + 1 + (o - longopts), o->name); else { sfprintf(up, "\n[%c:%s]", o->val, o->name); if (s = strchr(t, o->val)) { *s++ = ' '; if (*s == ':') { *s++ = ' '; if (*s == ':') *s = ' '; } } } if (o->has_arg) { sfputc(up, ':'); if (o->has_arg == optional_argument) sfputc(up, '?'); sfprintf(up, "[string]"); } } s = t; while (c = *s++) if (c != ' ') { sfprintf(up, "\n[%c]", c); if (*s == ':') { sfputc(up, *s); if (*++s == ':') { sfputc(up, '?'); s++; } sfputc(up, '['); sfputc(up, ']'); } } sfputc(up, '\n'); free(t); if (!(usage = sfstruse(up))) return -1; lastoptstring = optstring; lastlongopts = longopts; } opt_info.index = (optind > 1 || optind == lastoptind) ? optind : 0; if (opt_info.index >= argc || !(c = optget((char**)argv, usage))) { sfstrclose(up); up = 0; c = -1; } else { if (c == ':' || c == '?') { if (opterr && (!optstring || *optstring != ':')) { if (!error_info.id) error_info.id = argv[0]; errormsg(NiL, c == '?' ? (ERROR_USAGE|4) : 2, "%s", opt_info.arg); } optopt = opt_info.option[1]; c = '?'; } optarg = opt_info.arg; if (c < 0) { o = longopts - c - UCHAR_MAX - 1; if (o->flag) { *o->flag = o->val; c = 0; } else c = o->val; } } lastoptind = optind = opt_info.index; return c; }