summaryrefslogtreecommitdiff
path: root/src/cmd/INIT/mamake.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/INIT/mamake.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/INIT/mamake.c')
-rw-r--r--src/cmd/INIT/mamake.c2375
1 files changed, 2375 insertions, 0 deletions
diff --git a/src/cmd/INIT/mamake.c b/src/cmd/INIT/mamake.c
new file mode 100644
index 0000000..1f85240
--- /dev/null
+++ b/src/cmd/INIT/mamake.c
@@ -0,0 +1,2375 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1994-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
+
+/*
+ * mamake -- MAM make
+ *
+ * coded for portability
+ */
+
+static char id[] = "\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\0\n";
+
+#if _PACKAGE_ast
+
+#include <ast.h>
+#include <error.h>
+
+static const char usage[] =
+"[-?\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\n]"
+USAGE_LICENSE
+"[+NAME?mamake - make abstract machine make]"
+"[+DESCRIPTION?\bmamake\b reads \amake abstract machine\a target and"
+" prerequisite file descriptions from a mamfile (see \b-f\b) and executes"
+" actions to update targets that are older than their prerequisites."
+" Mamfiles are generated by the \b--mam\b option of \bnmake\b(1) and"
+" \bgmake\b(1) and are portable to environments that only have"
+" \bsh\b(1) and \bcc\b(1).]"
+"[+?In practice \bmamake\b is used to bootstrap build \bnmake\b(1) and"
+" \bksh\b(1) in new environments. Mamfiles are used rather than"
+" old-\bmake\b makefiles because some features are not reliably supported"
+" across all \bmake\b variants:]{"
+" [+action execution?Multi-line actions are executed as a"
+" unit by \b$SHELL\b. There are some shell constructs"
+" that cannot be expressed in an old-\bmake\b makefile.]"
+" [+viewpathing?\bVPATH\b is properly interpreted. This allows"
+" source to be separate from generated files.]"
+" [+recursion?Ordered subdirectory recursion over unrelated"
+" makefiles.]"
+" }"
+"[+?\bmamprobe\b(1) is called to probe and generate system specific variable"
+" definitions. The probe information is regenerated when it is older"
+" than the \bmamprobe\b command.]"
+"[+?For compatibility with \bnmake\b(1) the \b-K\b option and the"
+" \brecurse\b and \bcc-*\b command line targets are ignored.]"
+"[e:?Explain reason for triggering action. Ignored if -F is on.]"
+"[f:?Read \afile\a instead of the default.]:[file:=Mamfile]"
+"[i:?Ignore action errors.]"
+"[k:?Continue after error with sibling prerequisites.]"
+"[n:?Print actions but do not execute. Recursion actions (see \b-r\b) are still"
+" executed. Use \b-N\b to disable recursion actions too.]"
+"[r:?Recursively make leaf directories matching \apattern\a. Only leaf"
+" directories containing a makefile named \bNmakefile\b, \bnmakefile\b,"
+" \bMakefile\b or \bmakefile\b are considered. The first makefile"
+" found in each leaf directory is scanned for leaf directory"
+" prerequisites; the recusion order is determined by a topological sort"
+" of these prerequisites.]:[pattern]"
+"[C:?Do all work in \adirectory\a. All messages will mention"
+" \adirectory\a.]:[directory]"
+"[D:?Set the debug trace level to \alevel\a. Higher levels produce more"
+" output.]#[level]"
+"[F:?Force all targets to be out of date.]"
+"[K:?Ignored.]"
+"[N:?Like \b-n\b but recursion actions (see \b-r\b) are also disabled.]"
+"[V:?Print the program version and exit.]"
+"[G:debug-symbols?Compile and link with debugging symbol options enabled.]"
+"[S:strip-symbols?Strip link-time static symbols from executables.]"
+
+"\n"
+"\n[ target ... ] [ name=value ... ]\n"
+"\n"
+
+"[+SEE ALSO?\bgmake\b(1), \bmake\b(1), \bmamprobe\b(1),"
+" \bnmake\b(1), \bsh\b(1)]"
+;
+
+#else
+
+#define elementsof(x) (sizeof(x)/sizeof(x[0]))
+#define newof(p,t,n,x) ((p)?(t*)realloc((char*)(p),sizeof(t)*(n)+(x)):(t*)calloc(1,sizeof(t)*(n)+(x)))
+
+#define NiL ((char*)0)
+
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#if !_PACKAGE_ast && defined(__STDC__)
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#define delimiter(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)==';'||(c)=='('||(c)==')'||(c)=='`'||(c)=='|'||(c)=='&'||(c)=='=')
+
+#define add(b,c) (((b)->nxt >= (b)->end) ? append(b, "") : NiL, *(b)->nxt++ = (c))
+#define get(b) ((b)->nxt-(b)->buf)
+#define set(b,o) ((b)->nxt=(b)->buf+(o))
+#define use(b) (*(b)->nxt=0,(b)->nxt=(b)->buf)
+
+#define CHUNK 1024
+#define KEY(a,b,c,d) ((((unsigned long)(a))<<15)|(((unsigned long)(b))<<10)|(((unsigned long)(c))<<5)|(((unsigned long)(d))))
+#define NOW ((unsigned long)time((time_t*)0))
+#define ROTATE(p,l,r,t) ((t)=(p)->l,(p)->l=(t)->r,(t)->r=(p),(p)=(t))
+
+#define RULE_active 0x0001 /* active target */
+#define RULE_dontcare 0x0002 /* ok if not found */
+#define RULE_error 0x0004 /* not found or not generated */
+#define RULE_exists 0x0008 /* target file exists */
+#define RULE_generated 0x0010 /* generated target */
+#define RULE_ignore 0x0020 /* ignore time */
+#define RULE_implicit 0x0040 /* implicit prerequisite */
+#define RULE_made 0x0080 /* already made */
+#define RULE_virtual 0x0100 /* not a file */
+
+#define STREAM_KEEP 0x0001 /* don't fclose() on pop() */
+#define STREAM_MUST 0x0002 /* push() file must exist */
+#define STREAM_PIPE 0x0004 /* pclose() on pop() */
+
+#ifndef S_IXUSR
+#define S_IXUSR 0100 /* owner execute permission */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0010 /* group execute permission */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001 /* other execute permission */
+#endif
+
+struct Rule_s;
+
+typedef struct stat Stat_t;
+typedef FILE Stdio_t;
+
+typedef struct Buf_s /* buffer stream */
+{
+ struct Buf_s* old; /* next dropped buffer */
+ char* end; /* 1 past end of buffer */
+ char* nxt; /* next char to add */
+ char* buf; /* buffer space */
+} Buf_t;
+
+typedef struct Dict_item_s /* dictionary item */
+{
+ struct Dict_item_s* left; /* left child */
+ struct Dict_item_s* right; /* right child */
+ void* value; /* user defined value */
+ char name[1];/* 0 terminated name */
+} Dict_item_t;
+
+typedef struct Dict_s /* dictionary handle */
+{
+ Dict_item_t* root; /* root item */
+} Dict_t;
+
+typedef struct List_s /* Rule_t list */
+{
+ struct List_s* next; /* next in list */
+ struct Rule_s* rule; /* list item */
+} List_t;
+
+typedef struct Rule_s /* rule item */
+{
+ char* name; /* unbound name */
+ char* path; /* bound path */
+ List_t* prereqs; /* prerequisites */
+ struct Rule_s* leaf; /* recursion leaf alias */
+ int flags; /* RULE_* flags */
+ int making; /* currently make()ing */
+ unsigned long time; /* modification time */
+} Rule_t;
+
+typedef struct Stream_s /* input file stream stack */
+{
+ Stdio_t* fp; /* read stream */
+ char* file; /* stream path */
+ unsigned long line; /* stream line */
+ int flags; /* stream flags */
+} Stream_t;
+
+typedef struct View_s /* viewpath level */
+{
+ struct View_s* next; /* next level in viewpath */
+ int node; /* viewpath node path length */
+ char dir[1]; /* viewpath level dir prefix */
+} View_t;
+
+static struct /* program state */
+{
+ Buf_t* buf; /* work buffer */
+ Buf_t* old; /* dropped buffers */
+ Buf_t* opt; /* option buffer */
+
+ Dict_t* leaf; /* recursion leaf dictionary */
+ Dict_t* libs; /* library dictionary */
+ Dict_t* rules; /* rule dictionary */
+ Dict_t* vars; /* variable dictionary */
+
+ View_t* view; /* viewpath levels */
+
+ char* directory; /* work in this directory */
+ char* id; /* command name */
+ char* file; /* first input file */
+ char* pwd; /* current directory */
+ char* recurse; /* recursion pattern */
+ char* shell; /* ${SHELL} */
+
+ int active; /* targets currently active */
+ int debug; /* negative of debug level */
+ int errors; /* some error(s) occurred */
+ int exec; /* execute actions */
+ int explain; /* explain actions */
+ int force; /* all targets out of date */
+ int ignore; /* ignore command errors */
+ int indent; /* debug indent */
+ int keepgoing; /* do siblings on error */
+ int never; /* never execute */
+ int peek; /* next line already in input */
+ int probed; /* probe already done */
+ int verified; /* don't bother with verify() */
+
+ Stream_t streams[4]; /* input file stream stack */
+ Stream_t* sp; /* input stream stack pointer */
+
+ char input[8*CHUNK]; /* input buffer */
+} state;
+
+static unsigned long make(Rule_t*);
+
+static char mamfile[] = "Mamfile";
+static char sh[] = "/bin/sh";
+
+extern char** environ;
+
+#if !_PACKAGE_ast
+
+#if defined(NeXT) || defined(__NeXT)
+#define getcwd(a,b) getwd(a)
+#endif
+
+/*
+ * emit usage message and exit
+ */
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: %s [-iknFKNV] [-f mamfile] [-r pattern] [-C directory] [-D level] [target ...] [name=value ...]\n", state.id);
+ exit(2);
+}
+
+#endif
+
+/*
+ * output error message identification
+ */
+
+static void
+identify(Stdio_t* sp)
+{
+ if (state.directory)
+ fprintf(sp, "%s [%s]: ", state.id, state.directory);
+ else
+ fprintf(sp, "%s: ", state.id);
+}
+
+/*
+ * emit error message
+ * level:
+ * <0 debug
+ * 0 info
+ * 1 warning
+ * 2 error
+ * >2 exit(level-2)
+ */
+
+static void
+report(int level, char* text, char* item, unsigned long stamp)
+{
+ int i;
+
+ if (level >= state.debug)
+ {
+ if (level)
+ identify(stderr);
+ if (level < 0)
+ {
+ fprintf(stderr, "debug%d: ", level);
+ for (i = 1; i < state.indent; i++)
+ fprintf(stderr, " ");
+ }
+ else
+ {
+ if (state.sp && state.sp->line)
+ {
+ if (state.sp->file)
+ fprintf(stderr, "%s: ", state.sp->file);
+ fprintf(stderr, "%ld: ", state.sp->line);
+ }
+ if (level == 1)
+ fprintf(stderr, "warning: ");
+ else if (level > 1)
+ state.errors = 1;
+ }
+ if (item)
+ fprintf(stderr, "%s: ", item);
+ fprintf(stderr, "%s", text);
+ if (stamp && state.debug <= -2)
+ fprintf(stderr, " %10lu", stamp);
+ fprintf(stderr, "\n");
+ if (level > 2)
+ exit(level - 2);
+ }
+}
+
+/*
+ * don't know how to make or exit code making
+ */
+
+static void
+dont(Rule_t* r, int code, int keepgoing)
+{
+ identify(stderr);
+ if (!code)
+ fprintf(stderr, "don't know how to make %s\n", r->name);
+ else
+ {
+ fprintf(stderr, "*** exit code %d making %s%s\n", code, r->name, state.ignore ? " ignored" : "");
+ unlink(r->name);
+ if (state.ignore)
+ return;
+ }
+ if (!keepgoing)
+ exit(1);
+ state.errors++;
+ r->flags |= RULE_error;
+}
+
+/*
+ * local strrchr()
+ */
+
+static char*
+last(register char* s, register int c)
+{
+ register char* r = 0;
+
+ for (r = 0; *s; s++)
+ if (*s == c)
+ r = s;
+ return r;
+}
+
+/*
+ * open a buffer stream
+ */
+
+static Buf_t*
+buffer(void)
+{
+ register Buf_t* buf;
+
+ if (buf = state.old)
+ state.old = state.old->old;
+ else if (!(buf = newof(0, Buf_t, 1, 0)) || !(buf->buf = newof(0, char, CHUNK, 0)))
+ report(3, "out of space [buffer]", NiL, (unsigned long)0);
+ buf->end = buf->buf + CHUNK;
+ buf->nxt = buf->buf;
+ return buf;
+}
+
+/*
+ * close a buffer stream
+ */
+
+static void
+drop(Buf_t* buf)
+{
+ buf->old = state.old;
+ state.old = buf;
+}
+
+/*
+ * append str length n to buffer and return the buffer base
+ */
+
+static char*
+appendn(Buf_t* buf, char* str, int n)
+{
+ int m;
+ int i;
+
+ if ((n + 1) >= (buf->end - buf->nxt))
+ {
+ i = buf->nxt - buf->buf;
+ m = (((buf->end - buf->buf) + n + CHUNK + 1) / CHUNK) * CHUNK;
+ if (!(buf->buf = newof(buf->buf, char, m, 0)))
+ report(3, "out of space [buffer resize]", NiL, (unsigned long)0);
+ buf->end = buf->buf + m;
+ buf->nxt = buf->buf + i;
+ }
+ memcpy(buf->nxt, str, n + 1);
+ buf->nxt += n;
+ return buf->buf;
+}
+
+/*
+ * append str to buffer and return the buffer base
+ * if str==0 then next pointer reset to base
+ */
+
+static char*
+append(Buf_t* buf, char* str)
+{
+ if (str)
+ return appendn(buf, str, strlen(str));
+ buf->nxt = buf->buf;
+ return buf->buf;
+}
+
+/*
+ * allocate space for s and return the copy
+ */
+
+static char*
+duplicate(char* s)
+{
+ char* t;
+ int n;
+
+ n = strlen(s);
+ if (!(t = newof(0, char, n, 1)))
+ report(3, "out of space [duplicate]", s, (unsigned long)0);
+ strcpy(t, s);
+ return t;
+}
+
+/*
+ * open a new dictionary
+ */
+
+static Dict_t*
+dictionary(void)
+{
+ Dict_t* dict;
+
+ if (!(dict = newof(0, Dict_t, 1, 0)))
+ report(3, "out of space [dictionary]", NiL, (unsigned long)0);
+ return dict;
+}
+
+/*
+ * return the value for item name in dictionary dict
+ * if value!=0 then name entry value is created if necessary and set
+ * uses top-down splaying (ala Tarjan and Sleator)
+ */
+
+static void*
+search(register Dict_t* dict, char* name, void* value)
+{
+ register int cmp;
+ register Dict_item_t* root;
+ register Dict_item_t* t;
+ register Dict_item_t* left;
+ register Dict_item_t* right;
+ register Dict_item_t* lroot;
+ register Dict_item_t* rroot;
+
+ root = dict->root;
+ left = right = lroot = rroot = 0;
+ while (root)
+ {
+ if (!(cmp = strcmp(name, root->name)))
+ break;
+ else if (cmp < 0)
+ {
+ if (root->left && (cmp = strcmp(name, root->left->name)) <= 0)
+ {
+ ROTATE(root, left, right, t);
+ if (!cmp)
+ break;
+ }
+ if (right)
+ right->left = root;
+ else
+ rroot = root;
+ right = root;
+ root = root->left;
+ right->left = 0;
+ }
+ else
+ {
+ if (root->right && (cmp = strcmp(name, root->right->name)) >= 0)
+ {
+ ROTATE(root, right, left, t);
+ if (!cmp)
+ break;
+ }
+ if (left)
+ left->right = root;
+ else
+ lroot = root;
+ left = root;
+ root = root->right;
+ left->right = 0;
+ }
+ }
+ if (root)
+ {
+ if (right)
+ right->left = root->right;
+ else
+ rroot = root->right;
+ if (left)
+ left->right = root->left;
+ else
+ lroot = root->left;
+ }
+ else if (value)
+ {
+ if (!(root = newof(0, Dict_item_t, 1, strlen(name))))
+ report(3, "out of space [dictionary]", name, (unsigned long)0);
+ strcpy(root->name, name);
+ }
+ if (root)
+ {
+ if (value)
+ root->value = value;
+ root->left = lroot;
+ root->right = rroot;
+ dict->root = root;
+ return value ? (void*)root->name : root->value;
+ }
+ if (left)
+ {
+ left->right = rroot;
+ dict->root = lroot;
+ }
+ else if (right)
+ {
+ right->left = lroot;
+ dict->root = rroot;
+ }
+ return 0;
+}
+
+/*
+ * low level for walk()
+ */
+
+static int
+apply(Dict_t* dict, Dict_item_t* item, int (*func)(Dict_item_t*, void*), void* handle)
+{
+ register Dict_item_t* right;
+
+ do
+ {
+ right = item->right;
+ if (item->left && apply(dict, item->left, func, handle))
+ return -1;
+ if ((*func)(item, handle))
+ return -1;
+ } while (item = right);
+ return 0;
+}
+
+/*
+ * apply func to each dictionary item
+ */
+
+static int
+walk(Dict_t* dict, int (*func)(Dict_item_t*, void*), void* handle)
+{
+ return dict->root ? apply(dict, dict->root, func, handle) : 0;
+}
+
+/*
+ * return a rule pointer for name
+ */
+
+static Rule_t*
+rule(char* name)
+{
+ Rule_t* r;
+
+ if (!(r = (Rule_t*)search(state.rules, name, NiL)))
+ {
+ if (!(r = newof(0, Rule_t, 1, 0)))
+ report(3, "out of space [rule]", name, (unsigned long)0);
+ r->name = (char*)search(state.rules, name, (void*)r);
+ }
+ return r;
+}
+
+/*
+ * prepend p onto rule r prereqs
+ */
+
+static void
+cons(Rule_t* r, Rule_t* p)
+{
+ register List_t* x;
+
+ for (x = r->prereqs; x && x->rule != p; x = x->next);
+ if (!x)
+ {
+ if (!(x = newof(0, List_t, 1, 0)))
+ report(3, "out of space [list]", r->name, (unsigned long)0);
+ x->rule = p;
+ x->next = r->prereqs;
+ r->prereqs = x;
+ }
+}
+
+/*
+ * initialize the viewpath
+ */
+
+static void
+view(void)
+{
+ register char* s;
+ register char* t;
+ register char* p;
+ register View_t* vp;
+
+ View_t* zp;
+ int c;
+ int n;
+
+ Stat_t st;
+ Stat_t ts;
+
+ char buf[CHUNK];
+
+ if (stat(".", &st))
+ report(3, "cannot stat", ".", (unsigned long)0);
+ if ((s = (char*)search(state.vars, "PWD", NiL)) && !stat(s, &ts) &&
+ ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
+ state.pwd = s;
+ if (!state.pwd)
+ {
+ if (!getcwd(buf, sizeof(buf) - 1))
+ report(3, "cannot determine PWD", NiL, (unsigned long)0);
+ state.pwd = duplicate(buf);
+ search(state.vars, "PWD", state.pwd);
+ }
+ if ((s = (char*)search(state.vars, "VPATH", NiL)) && *s)
+ {
+ zp = 0;
+ for (;;)
+ {
+ for (t = s; *t && *t != ':'; t++);
+ if (c = *t)
+ *t = 0;
+ if (!state.view)
+ {
+ /*
+ * determine the viewpath offset
+ */
+
+ if (stat(s, &st))
+ report(3, "cannot stat top view", s, (unsigned long)0);
+ if (stat(state.pwd, &ts))
+ report(3, "cannot stat", state.pwd, (unsigned long)0);
+ if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
+ p = ".";
+ else
+ {
+ p = state.pwd + strlen(state.pwd);
+ while (p > state.pwd)
+ if (*--p == '/')
+ {
+ if (p == state.pwd)
+ report(3, ". not under VPATH", s, (unsigned long)0);
+ *p = 0;
+ if (stat(state.pwd, &ts))
+ report(3, "cannot stat", state.pwd, (unsigned long)0);
+ *p = '/';
+ if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
+ {
+ p++;
+ break;
+ }
+ }
+ if (p <= state.pwd)
+ report(3, "cannot determine viewpath offset", s, (unsigned long)0);
+ }
+ }
+ n = strlen(s);
+ if (!(vp = newof(0, View_t, 1, strlen(p) + n + 1)))
+ report(3, "out of space [view]", s, (unsigned long)0);
+ vp->node = n + 1;
+ strcpy(vp->dir, s);
+ *(vp->dir + n) = '/';
+ strcpy(vp->dir + n + 1, p);
+ report(-4, vp->dir, "view", (unsigned long)0);
+ if (!state.view)
+ state.view = zp = vp;
+ else
+ zp = zp->next = vp;
+ if (!c)
+ break;
+ *t++ = c;
+ s = t;
+ }
+ }
+}
+
+/*
+ * return next '?' or '}' in nested '}'
+ */
+
+static char*
+cond(register char* s)
+{
+ register int n;
+
+ if (*s == '?')
+ s++;
+ n = 0;
+ for (;;)
+ {
+ switch (*s++)
+ {
+ case 0:
+ break;
+ case '{':
+ n++;
+ continue;
+ case '}':
+ if (!n--)
+ break;
+ continue;
+ case '?':
+ if (!n)
+ break;
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ return s - 1;
+}
+
+/*
+ * expand var refs from s into buf
+ */
+
+static void
+substitute(Buf_t* buf, register char* s)
+{
+ register char* t;
+ register char* v;
+ register char* q;
+ register char* b;
+ register int c;
+ register int n;
+ int a = 0;
+ int i;
+
+ while (c = *s++)
+ {
+ if (c == '$' && *s == '{')
+ {
+ b = s - 1;
+ i = 1;
+ for (n = *(t = ++s) == '-' ? 0 : '-'; (c = *s) && c != '?' && c != '+' && c != n && c != ':' && c != '=' && c != '[' && c != '}'; s++)
+ if (!isalnum(c) && c != '_')
+ i = 0;
+ *s = 0;
+ if (c == '[')
+ {
+ append(buf, b);
+ *s = c;
+ continue;
+ }
+ v = (char*)search(state.vars, t, NiL);
+ if ((c == ':' || c == '=') && (!v || c == ':' && !*v))
+ {
+ append(buf, b);
+ *s = c;
+ continue;
+ }
+ if (t[0] == 'A' && t[1] == 'R' && t[2] == 0)
+ a = 1;
+ *s = c;
+ if (c && c != '}')
+ {
+ n = 1;
+ for (t = ++s; *s; s++)
+ if (*s == '{')
+ n++;
+ else if (*s == '}' && !--n)
+ break;
+ }
+ switch (c)
+ {
+ case '?':
+ q = cond(t - 1);
+ if (v)
+ {
+ if (((q - t) != 1 || *t != '*') && strncmp(v, t, q - t))
+ v = 0;
+ }
+ else if (q == t)
+ v = s;
+ t = cond(q);
+ if (v)
+ {
+ if (t > q)
+ {
+ c = *t;
+ *t = 0;
+ substitute(buf, q + 1);
+ *t = c;
+ }
+ }
+ else
+ {
+ q = cond(t);
+ if (q > t)
+ {
+ c = *q;
+ *q = 0;
+ substitute(buf, t + 1);
+ *q = c;
+ }
+ }
+ break;
+ case '+':
+ case '-':
+ if ((v == 0 || *v == 0) == (c == '-'))
+ {
+ c = *s;
+ *s = 0;
+ substitute(buf, t);
+ *s = c;
+ break;
+ }
+ if (c != '-')
+ break;
+ /*FALLTHROUGH*/
+ case 0:
+ case '=':
+ case '}':
+ if (v)
+ {
+ if (a && t[0] == 'm' && t[1] == 'a' && t[2] == 'm' && t[3] == '_' && t[4] == 'l' && t[5] == 'i' && t[6] == 'b')
+ {
+ for (t = v; *t == ' '; t++);
+ for (; *t && *t != ' '; t++);
+ if (*t)
+ *t = 0;
+ else
+ t = 0;
+ substitute(buf, v);
+ if (t)
+ *t = ' ';
+ }
+ else
+ substitute(buf, v);
+ }
+ else if (i)
+ {
+ c = *s;
+ *s = 0;
+ append(buf, b);
+ *s = c;
+ continue;
+ }
+ break;
+ }
+ if (*s)
+ s++;
+ }
+ else
+ add(buf, c);
+ }
+}
+
+/*
+ * expand var refs from s into buf and return buf base
+ */
+
+static char*
+expand(Buf_t* buf, char* s)
+{
+ substitute(buf, s);
+ return use(buf);
+}
+
+/*
+ * stat() with .exe check
+ */
+
+static char*
+status(Buf_t* buf, int off, char* path, struct stat* st)
+{
+ int r;
+ char* s;
+ Buf_t* tmp;
+
+ if (!stat(path, st))
+ return path;
+ if (!(tmp = buf))
+ {
+ tmp = buffer();
+ off = 0;
+ }
+ if (off)
+ set(tmp, off);
+ else
+ append(tmp, path);
+ append(tmp, ".exe");
+ s = use(tmp);
+ r = stat(s, st);
+ if (!buf)
+ {
+ drop(tmp);
+ s = path;
+ }
+ if (r)
+ {
+ if (off)
+ s[off] = 0;
+ s = 0;
+ }
+ return s;
+}
+
+/*
+ * return path to file
+ */
+
+static char*
+find(Buf_t* buf, char* file, struct stat* st)
+{
+ char* s;
+ View_t* vp;
+ int node;
+ int c;
+ int o;
+
+ if (s = status(buf, 0, file, st))
+ {
+ report(-3, s, "find", (unsigned long)0);
+ return s;
+ }
+ if (vp = state.view)
+ {
+ node = 0;
+ if (*file == '/')
+ {
+ do
+ {
+ if (!strncmp(file, vp->dir, vp->node))
+ {
+ file += vp->node;
+ node = 2;
+ break;
+ }
+ } while (vp = vp->next);
+ }
+ else
+ vp = vp->next;
+ if (vp)
+ do
+ {
+ if (node)
+ {
+ c = vp->dir[vp->node];
+ vp->dir[vp->node] = 0;
+ append(buf, vp->dir);
+ vp->dir[vp->node] = c;
+ }
+ else
+ {
+ append(buf, vp->dir);
+ append(buf, "/");
+ }
+ append(buf, file);
+ o = get(buf);
+ s = use(buf);
+ if (s = status(buf, o, s, st))
+ {
+ report(-3, s, "find", (unsigned long)0);
+ return s;
+ }
+ } while (vp = vp->next);
+ }
+ return 0;
+}
+
+/*
+ * bind r to a file and return the modify time
+ */
+
+static unsigned long
+bind(Rule_t* r)
+{
+ char* s;
+ Buf_t* buf;
+ struct stat st;
+
+ buf = buffer();
+ if (s = find(buf, r->name, &st))
+ {
+ if (s != r->name)
+ r->path = duplicate(s);
+ r->time = st.st_mtime;
+ r->flags |= RULE_exists;
+ }
+ drop(buf);
+ return r->time;
+}
+
+/*
+ * pop the current input file
+ */
+
+static int
+pop(void)
+{
+ int r;
+
+ if (!state.sp)
+ report(3, "input stack underflow", NiL, (unsigned long)0);
+ if (!state.sp->fp || (state.sp->flags & STREAM_KEEP))
+ r = 0;
+ else if (state.sp->flags & STREAM_PIPE)
+ r = pclose(state.sp->fp);
+ else
+ r = fclose(state.sp->fp);
+ if (state.sp == state.streams)
+ state.sp = 0;
+ else
+ state.sp--;
+ return r;
+}
+
+/*
+ * push file onto the input stack
+ */
+
+static int
+push(char* file, Stdio_t* fp, int flags)
+{
+ char* path;
+ Buf_t* buf;
+ struct stat st;
+
+ if (!state.sp)
+ state.sp = state.streams;
+ else if (++state.sp >= &state.streams[elementsof(state.streams)])
+ report(3, "input stream stack overflow", NiL, (unsigned long)0);
+ if (state.sp->fp = fp)
+ state.sp->file = "pipeline";
+ else if (flags & STREAM_PIPE)
+ report(3, "pipe error", file, (unsigned long)0);
+ else if (!file || !strcmp(file, "-") || !strcmp(file, "/dev/stdin"))
+ {
+ flags |= STREAM_KEEP;
+ state.sp->file = "/dev/stdin";
+ state.sp->fp = stdin;
+ }
+ else
+ {
+ buf = buffer();
+ if (path = find(buf, file, &st))
+ {
+ if (!(state.sp->fp = fopen(path, "r")))
+ report(3, "cannot read", path, (unsigned long)0);
+ state.sp->file = duplicate(path);
+ drop(buf);
+ }
+ else
+ {
+ drop(buf);
+ pop();
+ if (flags & STREAM_MUST)
+ report(3, "not found", file, (unsigned long)0);
+ return 0;
+ }
+ }
+ state.sp->flags = flags;
+ state.sp->line = 0;
+ return 1;
+}
+
+/*
+ * return the next input line
+ */
+
+static char*
+input(void)
+{
+ char* e;
+
+ if (!state.sp)
+ report(3, "no input file stream", NiL, (unsigned long)0);
+ if (state.peek)
+ state.peek = 0;
+ else if (!fgets(state.input, sizeof(state.input), state.sp->fp))
+ return 0;
+ else if (*state.input && *(e = state.input + strlen(state.input) - 1) == '\n')
+ *e = 0;
+ state.sp->line++;
+ return state.input;
+}
+
+/*
+ * pass shell action s to ${SHELL:-/bin/sh}
+ * the -c wrapper ensures that scripts are run in the selected shell
+ * even on systems that otherwise demand #! magic (can you say cygwin)
+ */
+
+static int
+execute(register char* s)
+{
+ register int c;
+ Buf_t* buf;
+
+ if (!state.shell && (!(state.shell = (char*)search(state.vars, "SHELL", NiL)) || !strcmp(state.shell, sh)))
+ state.shell = sh;
+ buf = buffer();
+ append(buf, state.shell);
+ append(buf, " -c '");
+ while (c = *s++)
+ {
+ if (c == '\'')
+ {
+ add(buf, c);
+ for (s--; *s == c; s++)
+ {
+ add(buf, '\\');
+ add(buf, c);
+ }
+ }
+ add(buf, c);
+ }
+ add(buf, '\'');
+ s = use(buf);
+ report(-5, s, "exec", (unsigned long)0);
+ if ((c = system(s)) > 255)
+ c >>= 8;
+ drop(buf);
+ return c;
+}
+
+/*
+ * run action s to update r
+ */
+
+static unsigned long
+run(Rule_t* r, register char* s)
+{
+ register Rule_t* q;
+ register char* t;
+ register int c;
+ register View_t* v;
+ int i;
+ int j;
+ int x;
+ Stat_t st;
+ Buf_t* buf;
+
+ if (r->flags & RULE_error)
+ return r->time;
+ buf = buffer();
+ if (!strncmp(s, "mamake -r ", 10))
+ {
+ state.verified = 1;
+ x = !state.never;
+ }
+ else
+ x = state.exec;
+ if (x)
+ append(buf, "trap - 1 2 3 15\nPATH=.:$PATH\nset -x\n");
+ if (state.view)
+ {
+ do
+ {
+ for (; delimiter(*s); s++)
+ add(buf, *s);
+ for (t = s; *s && !delimiter(*s); s++);
+ c = *s;
+ *s = 0;
+ if (c == '=')
+ {
+ append(buf, t);
+ continue;
+ }
+ if ((q = (Rule_t*)search(state.rules, t, NiL)) && q->path && !(q->flags & RULE_generated))
+ append(buf, q->path);
+ else
+ {
+ append(buf, t);
+ if (*t == '-' && *(t + 1) == 'I' && (*(t + 2) || c))
+ {
+ if (*(t + 2))
+ i = 2;
+ else
+ {
+ for (i = 3; *(t + i) == ' ' || *(t + i) == '\t'; i++);
+ *s = c;
+ for (s = t + i; *s && *s != ' ' && *s != '\t' && *s != '\n'; s++);
+ c = *s;
+ *s = 0;
+ append(buf, t + 2);
+ }
+ if (*(t + i) && *(t + i) != '/')
+ {
+ v = state.view;
+ while (v = v->next)
+ {
+ add(buf, ' ');
+ for (j = 0; j < i; j++)
+ add(buf, *(t + j));
+ append(buf, v->dir);
+ if (*(t + i) != '.' || *(t + i + 1))
+ {
+ add(buf, '/');
+ append(buf, t + i);
+ }
+ }
+ }
+ }
+ }
+ } while (*s = c);
+ s = use(buf);
+ }
+ else if (x)
+ {
+ append(buf, s);
+ s = use(buf);
+ }
+ if (x)
+ {
+ if (c = execute(s))
+ dont(r, c, state.keepgoing);
+ if (status((Buf_t*)0, 0, r->name, &st))
+ {
+ r->time = st.st_mtime;
+ r->flags |= RULE_exists;
+ }
+ else
+ r->time = NOW;
+ }
+ else
+ {
+ fprintf(stdout, "%s\n", s);
+ if (state.debug)
+ fflush(stdout);
+ r->time = NOW;
+ r->flags |= RULE_exists;
+ }
+ drop(buf);
+ return r->time;
+}
+
+/*
+ * return the full path for s using buf workspace
+ */
+
+static char*
+path(Buf_t* buf, char* s, int must)
+{
+ register char* p;
+ register char* d;
+ register char* x;
+ char* e;
+ register int c;
+ int t;
+ int o;
+ Stat_t st;
+
+ for (e = s; *e && *e != ' ' && *e != '\t'; e++);
+ t = *e;
+ if ((x = status(buf, 0, s, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
+ return x;
+ if (!(p = (char*)search(state.vars, "PATH", NiL)))
+ report(3, "variable not defined", "PATH", (unsigned long)0);
+ do
+ {
+ for (d = p; *p && *p != ':'; p++);
+ c = *p;
+ *p = 0;
+ if (*d && (*d != '.' || *(d + 1)))
+ {
+ append(buf, d);
+ add(buf, '/');
+ }
+ *p = c;
+ if (t)
+ *e = 0;
+ append(buf, s);
+ if (t)
+ *e = t;
+ o = get(buf);
+ x = use(buf);
+ if ((x = status(buf, o, x, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
+ return x;
+ } while (*p++);
+ if (must)
+ report(3, "command not found", s, (unsigned long)0);
+ return 0;
+}
+
+/*
+ * generate (if necessary) and read the MAM probe information
+ * done on the first `setv CC ...'
+ */
+
+static void
+probe(void)
+{
+ register char* cc;
+ register char* s;
+ unsigned long h;
+ unsigned long q;
+ Buf_t* buf;
+ Buf_t* pro;
+ Buf_t* tmp;
+ struct stat st;
+
+ static char let[] = "ABCDEFGHIJKLMNOP";
+ static char cmd[] = "mamprobe";
+
+ if (!(cc = (char*)search(state.vars, "CC", NiL)))
+ cc = "cc";
+ buf = buffer();
+ s = path(buf, cmd, 1);
+ q = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime;
+ pro = buffer();
+ s = cc = path(pro, cc, 1);
+ for (h = 0; *s; s++)
+ h = h * 0x63c63cd9L + *s + 0x9c39c33dL;
+ if (!(s = (char*)search(state.vars, "INSTALLROOT", NiL)))
+ report(3, "variable must be defined", "INSTALLROOT", (unsigned long)0);
+ append(buf, s);
+ append(buf, "/lib/probe/C/mam/");
+ for (h &= 0xffffffffL; h; h >>= 4)
+ add(buf, let[h & 0xf]);
+ s = use(buf);
+ h = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime;
+ if (h < q || !push(s, (Stdio_t*)0, 0))
+ {
+ tmp = buffer();
+ append(tmp, cmd);
+ add(tmp, ' ');
+ append(tmp, s);
+ add(tmp, ' ');
+ append(tmp, cc);
+ if (execute(use(tmp)))
+ report(3, "cannot generate probe info", s, (unsigned long)0);
+ drop(tmp);
+ if (!push(s, (Stdio_t*)0, 0))
+ report(3, "cannot read probe info", s, (unsigned long)0);
+ }
+ drop(pro);
+ drop(buf);
+ make(rule(""));
+ pop();
+}
+
+/*
+ * add attributes in s to r
+ */
+
+static void
+attributes(register Rule_t* r, register char* s)
+{
+ register char* t;
+ register int n;
+
+ for (;;)
+ {
+ for (; *s == ' '; s++);
+ for (t = s; *s && *s != ' '; s++);
+ if (!(n = s - t))
+ break;
+ switch (*t)
+ {
+ case 'd':
+ if (n == 8 && !strncmp(t, "dontcare", n))
+ r->flags |= RULE_dontcare;
+ break;
+ case 'g':
+ if (n == 9 && !strncmp(t, "generated", n))
+ r->flags |= RULE_generated;
+ break;
+ case 'i':
+ if (n == 6 && !strncmp(t, "ignore", n))
+ r->flags |= RULE_ignore;
+ else if (n == 8 && !strncmp(t, "implicit", n))
+ r->flags |= RULE_implicit;
+ break;
+ case 'v':
+ if (n == 7 && !strncmp(t, "virtual", n))
+ r->flags |= RULE_virtual;
+ break;
+ }
+ }
+}
+
+/*
+ * define ${mam_libX} for library reference lib
+ */
+
+static char*
+require(char* lib, int dontcare)
+{
+ register int c;
+ char* s;
+ char* r;
+ FILE* f;
+ Buf_t* buf;
+ Buf_t* tmp;
+ struct stat st;
+
+ static int dynamic = -1;
+
+ if (dynamic < 0)
+ dynamic = (s = search(state.vars, "mam_cc_L", NiL)) ? atoi(s) : 0;
+ if (!(r = search(state.vars, lib, NiL)))
+ {
+ buf = buffer();
+ tmp = buffer();
+ s = 0;
+ for (;;)
+ {
+ if (s)
+ append(buf, s);
+ if (r = search(state.vars, "mam_cc_PREFIX_ARCHIVE", NiL))
+ append(buf, r);
+ append(buf, lib + 2);
+ if (r = search(state.vars, "mam_cc_SUFFIX_ARCHIVE", NiL))
+ append(buf, r);
+ r = expand(tmp, use(buf));
+ if (!stat(r, &st))
+ break;
+ if (s)
+ {
+ r = lib;
+ break;
+ }
+ s = "${INSTALLROOT}/lib/";
+ if (dynamic)
+ {
+ append(buf, s);
+ if (r = search(state.vars, "mam_cc_PREFIX_SHARED", NiL))
+ append(buf, r);
+ append(buf, lib + 2);
+ if (r = search(state.vars, "mam_cc_SUFFIX_SHARED", NiL))
+ append(buf, r);
+ r = expand(tmp, use(buf));
+ if (!stat(r, &st))
+ {
+ r = lib;
+ break;
+ }
+ }
+ }
+ if (r != lib)
+ r = duplicate(r);
+ search(state.vars, lib, r);
+ append(tmp, lib + 2);
+ append(tmp, ".req");
+ if (!(f = fopen(use(tmp), "r")))
+ {
+ append(tmp, "${INSTALLROOT}/lib/lib/");
+ append(tmp, lib + 2);
+ f = fopen(expand(buf, use(tmp)), "r");
+ }
+ if (f)
+ {
+ for (;;)
+ {
+ while ((c = fgetc(f)) == ' ' || c == '\t' || c == '\n');
+ if (c == EOF)
+ break;
+ do
+ {
+ add(tmp, c);
+ } while ((c = fgetc(f)) != EOF && c != ' ' && c != '\t' && c != '\n');
+ s = use(tmp);
+ if (s[0] && (s[0] != '-' || s[1]))
+ {
+ add(buf, ' ');
+ append(buf, require(s, 0));
+ }
+ }
+ fclose(f);
+ r = use(buf);
+ }
+ else if (dontcare)
+ {
+ append(tmp, "set -\n");
+ append(tmp, "cd /tmp\n");
+ append(tmp, "echo 'int main(){return 0;}' > x.${!-$$}.c\n");
+ append(tmp, "${CC} ${CCFLAGS} -o x.${!-$$}.x x.${!-$$}.c ");
+ append(tmp, r);
+ append(tmp, " >/dev/null 2>&1\n");
+ append(tmp, "c=$?\n");
+ append(tmp, "rm -f x.${!-$$}.[cox]\n");
+ append(tmp, "exit $c\n");
+ if (execute(expand(buf, use(tmp))))
+ r = "";
+ }
+ r = duplicate(r);
+ search(state.vars, lib, r);
+ append(tmp, "mam_lib");
+ append(tmp, lib + 2);
+ search(state.vars, use(tmp), r);
+ drop(tmp);
+ drop(buf);
+ }
+ return r;
+}
+
+/*
+ * input() until `done r'
+ */
+
+static unsigned long
+make(Rule_t* r)
+{
+ register char* s;
+ register char* t;
+ register char* u;
+ register char* v;
+ register Rule_t* q;
+ unsigned long z;
+ unsigned long x;
+ Buf_t* buf;
+ Buf_t* cmd;
+
+ r->making++;
+ if (r->flags & RULE_active)
+ state.active++;
+ if (*r->name)
+ {
+ z = bind(r);
+ state.indent++;
+ report(-1, r->name, "make", r->time);
+ }
+ else
+ z = 0;
+ buf = buffer();
+ cmd = 0;
+ while (s = input())
+ {
+ for (; *s == ' '; s++);
+ for (; isdigit(*s); s++);
+ for (; *s == ' '; s++);
+ for (u = s; *s && *s != ' '; s++);
+ if (*s)
+ {
+ for (*s++ = 0; *s == ' '; s++);
+ for (t = s; *s && *s != ' '; s++);
+ if (*s)
+ for (*s++ = 0; *s == ' '; s++);
+ v = s;
+ }
+ else
+ t = v = s;
+ switch (KEY(u[0], u[1], u[2], u[3]))
+ {
+ case KEY('b','i','n','d'):
+ if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && (s = require(t, !strcmp(v, "dontcare"))) && strncmp(r->name, "FEATURE/", 8) && strcmp(r->name, "configure.h"))
+ for (;;)
+ {
+ for (t = s; *s && *s != ' '; s++);
+ if (*s)
+ *s = 0;
+ else
+ s = 0;
+ if (*t)
+ {
+ q = rule(expand(buf, t));
+ attributes(q, v);
+ x = bind(q);
+ if (z < x)
+ z = x;
+ if (q->flags & RULE_error)
+ r->flags |= RULE_error;
+ }
+ if (!s)
+ break;
+ for (*s++ = ' '; *s == ' '; s++);
+ }
+ continue;
+ case KEY('d','o','n','e'):
+ q = rule(expand(buf, t));
+ if (q != r)
+ report(2, "improper done statement", t, (unsigned long)0);
+ attributes(r, v);
+ if (cmd && state.active && (state.force || r->time < z || !r->time && !z))
+ {
+ if (state.explain && !state.force)
+ {
+ if (!r->time)
+ fprintf(stderr, "%s [not found]\n", r->name);
+ else
+ fprintf(stderr, "%s [%lu] older than prerequisites [%lu]\n", r->name, r->time, z);
+ }
+ substitute(buf, use(cmd));
+ x = run(r, use(buf));
+ if (z < x)
+ z = x;
+ }
+ r->flags |= RULE_made;
+ if (!(r->flags & (RULE_dontcare|RULE_error|RULE_exists|RULE_generated|RULE_implicit|RULE_virtual)))
+ dont(r, 0, state.keepgoing);
+ break;
+ case KEY('e','x','e','c'):
+ r->flags |= RULE_generated;
+ if (r->path)
+ {
+ free(r->path);
+ r->path = 0;
+ r->time = 0;
+ }
+ if (state.active)
+ {
+ if (cmd)
+ add(cmd, '\n');
+ else
+ cmd = buffer();
+ append(cmd, v);
+ }
+ continue;
+ case KEY('m','a','k','e'):
+ q = rule(expand(buf, t));
+ if (!q->making)
+ {
+ attributes(q, v);
+ x = make(q);
+ if (!(q->flags & RULE_ignore) && z < x)
+ z = x;
+ if (q->flags & RULE_error)
+ r->flags |= RULE_error;
+ }
+ continue;
+ case KEY('p','r','e','v'):
+ q = rule(expand(buf, t));
+ if (!q->making)
+ {
+ if (!(q->flags & RULE_ignore) && z < q->time)
+ z = q->time;
+ if (q->flags & RULE_error)
+ r->flags |= RULE_error;
+ state.indent++;
+ report(-2, q->name, "prev", q->time);
+ state.indent--;
+ }
+ continue;
+ case KEY('s','e','t','v'):
+ if (!search(state.vars, t, NiL))
+ {
+ if (*v == '"')
+ {
+ s = v + strlen(v) - 1;
+ if (*s == '"')
+ {
+ *s = 0;
+ v++;
+ }
+ }
+ search(state.vars, t, duplicate(expand(buf, v)));
+ }
+ if (!state.probed && t[0] == 'C' && t[1] == 'C' && !t[2])
+ {
+ state.probed = 1;
+ probe();
+ }
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ drop(buf);
+ if (cmd)
+ drop(cmd);
+ if (*r->name)
+ {
+ report(-1, r->name, "done", z);
+ state.indent--;
+ }
+ if (r->flags & RULE_active)
+ state.active--;
+ r->making--;
+ return r->time = z;
+}
+
+/*
+ * verify that active targets were made
+ */
+
+static int
+verify(Dict_item_t* item, void* handle)
+{
+ Rule_t* r = (Rule_t*)item->value;
+
+ if ((r->flags & (RULE_active|RULE_error|RULE_made)) == RULE_active)
+ dont(r, 0, 1);
+ return 0;
+}
+
+/*
+ * return 1 if name is an initializer
+ */
+
+static int
+initializer(char* name)
+{
+ register char* s;
+
+ if (s = last(name, '/'))
+ s++;
+ else
+ s = name;
+ return s[0] == 'I' && s[1] == 'N' && s[2] == 'I' && s[3] == 'T';
+}
+
+/*
+ * update recursion leaf r and its prerequisites
+ */
+
+static int
+update(register Rule_t* r)
+{
+ register List_t* x;
+ Buf_t* buf;
+
+ static char cmd[] = "${MAMAKE} -C ";
+ static char arg[] = " ${MAMAKEARGS}";
+
+ r->flags |= RULE_made;
+ if (r->leaf)
+ r->leaf->flags |= RULE_made;
+ for (x = r->prereqs; x; x = x->next)
+ if (x->rule->leaf && !(x->rule->flags & RULE_made))
+ update(x->rule);
+ buf = buffer();
+ substitute(buf, cmd);
+ append(buf, r->name);
+ substitute(buf, arg);
+ run(r, use(buf));
+ drop(buf);
+ return 0;
+}
+
+/*
+ * scan makefile prereqs
+ */
+
+static int
+scan(Dict_item_t* item, void* handle)
+{
+ register Rule_t* r = (Rule_t*)item->value;
+ register char* s;
+ register char* t;
+ register char* u;
+ register char* w;
+ Rule_t* q;
+ int i;
+ int j;
+ int k;
+ int p;
+ Buf_t* buf;
+
+ static char* files[] =
+ {
+ "Nmakefile",
+ "nmakefile",
+ "Makefile",
+ "makefile"
+ };
+
+ /*
+ * drop non-leaf rules
+ */
+
+ if (!r->leaf)
+ return 0;
+
+ /*
+ * always make initializers
+ */
+
+ if (initializer(r->name))
+ {
+ if (!(r->flags & RULE_made))
+ update(r);
+ return 0;
+ }
+ buf = buffer();
+ for (i = 0; i < elementsof(files); i++)
+ {
+ append(buf, r->name);
+ add(buf, '/');
+ append(buf, files[i]);
+ if (push(use(buf), (Stdio_t*)0, 0))
+ {
+ while (s = input())
+ {
+ j = p = 0;
+ while (*s)
+ {
+ for (k = 1; (i = *s) == ' ' || i == '\t' || i == '"' || i == '\''; s++);
+ for (t = s; (i = *s) && i != ' ' && i != '\t' && i != '"' && i != '\'' && i != '\\' && i != ':'; s++)
+ if (i == '/')
+ t = s + 1;
+ else if (i == '.' && *(s + 1) != 'c' && *(s + 1) != 'C' && *(s + 1) != 'h' && *(s + 1) != 'H' && t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
+ *s = 0;
+ if (*s)
+ *s++ = 0;
+ if (!t[0])
+ k = 0;
+ else if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && t[2])
+ {
+ append(buf, "lib");
+ append(buf, t + 2);
+ t = use(buf);
+ }
+ else if (p)
+ {
+ if (t[0] == '+' && !t[1])
+ p = 2;
+ else if (p == 1)
+ {
+ if (i != ':' || strncmp(s, "command", 7))
+ {
+ append(buf, "lib");
+ append(buf, t);
+ t = use(buf);
+ }
+ if (i == ':')
+ while (*s && (*s == ' ' || *s == '\t'))
+ s++;
+ }
+ }
+ else if (i == ':')
+ {
+ if (j != ':' || !isupper(*t))
+ k = 0;
+ else if (!strcmp(t, "PACKAGE"))
+ {
+ p = 1;
+ k = 0;
+ }
+ else
+ for (u = t; *u; u++)
+ if (isupper(*u))
+ *u = tolower(*u);
+ else if (!isalnum(*u))
+ {
+ k = 0;
+ break;
+ }
+ }
+ else if (t[0] != 'l' || t[1] != 'i' || t[2] != 'b')
+ k = 0;
+ else
+ for (u = t + 3; *u; u++)
+ if (!isalnum(*u))
+ {
+ k = 0;
+ break;
+ }
+ if (k && ((q = (Rule_t*)search(state.leaf, t, NiL)) && q != r || *t++ == 'l' && *t++ == 'i' && *t++ == 'b' && *t && (q = (Rule_t*)search(state.leaf, t, NiL)) && q != r))
+ {
+ for (t = w = r->name; *w; w++)
+ if (*w == '/')
+ t = w + 1;
+ if (t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
+ t += 3;
+ for (u = w = q->name; *w; w++)
+ if (*w == '/')
+ u = w + 1;
+ if (strcmp(t, u))
+ cons(r, q);
+ }
+ j = i;
+ }
+ }
+ pop();
+ for (s = 0, w = r->name; *w; w++)
+ if (*w == '/')
+ s = w;
+ if (s)
+ {
+ if ((s - r->name) > 3 && *(s - 1) == 'b' && *(s - 2) == 'i' && *(s - 3) == 'l' && *(s - 4) != '/')
+ {
+ /*
+ * foolib : foo : libfoo
+ */
+
+ *(s - 3) = 0;
+ q = (Rule_t*)search(state.leaf, r->name, NiL);
+ if (q && q != r)
+ cons(r, q);
+ for (t = w = r->name; *w; w++)
+ if (*w == '/')
+ t = w + 1;
+ append(buf, "lib");
+ append(buf, t);
+ q = (Rule_t*)search(state.leaf, use(buf), NiL);
+ if (q && q != r)
+ cons(r, q);
+ *(s - 3) = 'l';
+ }
+ else if (((s - r->name) != 3 || *(s - 1) != 'b' || *(s - 2) != 'i' || *(s - 3) != 'l') && (*(s + 1) != 'l' || *(s + 2) != 'i' || *(s + 3) != 'b'))
+ {
+ /*
+ * huh/foobar : lib/libfoo
+ */
+
+ s++;
+ t = s + strlen(s);
+ while (--t > s)
+ {
+ append(buf, "lib/lib");
+ appendn(buf, s, t - s);
+ q = (Rule_t*)search(state.leaf, use(buf), NiL);
+ if (q && q != r)
+ cons(r, q);
+ }
+ }
+ }
+ break;
+ }
+ }
+ drop(buf);
+ return 0;
+}
+
+/*
+ * descend into op and its prereqs
+ */
+
+static int
+descend(Dict_item_t* item, void* handle)
+{
+ Rule_t* r = (Rule_t*)item->value;
+
+ if (!state.active && (!(r->flags & RULE_active) || !(r = (Rule_t*)search(state.leaf, r->name, NiL))))
+ return 0;
+ return r->leaf && !(r->flags & RULE_made) ? update(r) : 0;
+}
+
+/*
+ * append the non-leaf active targets to state.opt
+ */
+
+static int
+active(Dict_item_t* item, void* handle)
+{
+ Rule_t* r = (Rule_t*)item->value;
+
+ if (r->flags & RULE_active)
+ {
+ if (r->leaf || search(state.leaf, r->name, NiL))
+ state.active = 0;
+ else
+ {
+ add(state.opt, ' ');
+ append(state.opt, r->name);
+ }
+ }
+ return 0;
+}
+
+/*
+ * recurse on mamfiles in subdirs matching pattern
+ */
+
+static int
+recurse(char* pattern)
+{
+ register char* s;
+ register char* t;
+ Rule_t* r;
+ Buf_t* buf;
+ Buf_t* tmp;
+ struct stat st;
+
+ /*
+ * first determine the MAM subdirs
+ */
+
+ tmp = buffer();
+ buf = buffer();
+ state.exec = !state.never;
+ state.leaf = dictionary();
+ append(buf, "ls -d ");
+ append(buf, pattern);
+ s = use(buf);
+ push("recurse", popen(s, "r"), STREAM_PIPE);
+ while (s = input())
+ {
+ append(buf, s);
+ add(buf, '/');
+ append(buf, mamfile);
+ if (find(tmp, use(buf), &st))
+ {
+ r = rule(s);
+ if (t = last(r->name, '/'))
+ t++;
+ else
+ t = r->name;
+ r->leaf = rule(t);
+ search(state.leaf, t, r);
+ }
+ }
+ pop();
+ drop(buf);
+ drop(tmp);
+
+ /*
+ * grab the non-leaf active targets
+ */
+
+ if (!state.active)
+ {
+ state.active = 1;
+ walk(state.rules, active, NiL);
+ }
+ search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1));
+
+ /*
+ * scan the makefile and descend
+ */
+
+ walk(state.rules, scan, NiL);
+ state.view = 0;
+ walk(state.rules, descend, NiL);
+ return 0;
+}
+
+int
+main(int argc, char** argv)
+{
+ register char** e;
+ register char* s;
+ register char* t;
+ register char* v;
+ Buf_t* tmp;
+ int c;
+
+ /*
+ * initialize the state
+ */
+
+ state.id = "mamake";
+ state.active = 1;
+ state.exec = 1;
+ state.file = mamfile;
+ state.opt = buffer();
+ state.rules = dictionary();
+ state.vars = dictionary();
+ search(state.vars, "MAMAKE", *argv);
+
+ /*
+ * parse the options
+ */
+
+#if _PACKAGE_ast
+ error_info.id = state.id;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'e':
+ append(state.opt, " -e");
+ state.explain = 1;
+ continue;
+ case 'i':
+ append(state.opt, " -i");
+ state.ignore = 1;
+ continue;
+ case 'k':
+ append(state.opt, " -k");
+ state.keepgoing = 1;
+ continue;
+ case 'N':
+ state.never = 1;
+ /*FALLTHROUGH*/
+ case 'n':
+ append(state.opt, " -n");
+ state.exec = 0;
+ continue;
+ case 'F':
+ append(state.opt, " -F");
+ state.force = 1;
+ continue;
+ case 'K':
+ continue;
+ case 'V':
+ fprintf(stdout, "%s\n", id + 10);
+ exit(0);
+ case 'f':
+ append(state.opt, " -f ");
+ append(state.opt, opt_info.arg);
+ state.file = opt_info.arg;
+ continue;
+ case 'r':
+ state.recurse = opt_info.arg;
+ continue;
+ case 'C':
+ state.directory = opt_info.arg;
+ continue;
+ case 'D':
+ append(state.opt, " -D");
+ append(state.opt, opt_info.arg);
+ state.debug = -opt_info.num;
+ continue;
+ case 'G':
+ append(state.opt, " -G");
+ search(state.vars, "-debug-symbols", "1");
+ continue;
+ case 'S':
+ append(state.opt, " -S");
+ search(state.vars, "-strip-symbols", "1");
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ }
+ break;
+ }
+ if (error_info.errors)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ argv += opt_info.index;
+#else
+ while ((s = *++argv) && *s == '-')
+ {
+ if (*(s + 1) == '-')
+ {
+ if (!*(s + 2))
+ {
+ append(state.opt, " --");
+ argv++;
+ break;
+ }
+ for (t = s += 2; *t && *t != '='; t++);
+ if (!strncmp(s, "debug-symbols", t - s) && append(state.opt, " -G") || !strncmp(s, "strip-symbols", t - s) && append(state.opt, " -S"))
+ {
+ if (*t)
+ {
+ v = t + 1;
+ if (t > s && *(t - 1) == '+')
+ t--;
+ c = *t;
+ *t = 0;
+ }
+ else
+ {
+ c = 0;
+ v = "1";
+ }
+ search(state.vars, s - 1, v);
+ if (c)
+ *t = c;
+ continue;
+ }
+ usage();
+ break;
+ }
+ for (;;)
+ {
+ switch (*++s)
+ {
+ case 0:
+ break;
+ case 'e':
+ append(state.opt, " -e");
+ state.explain = 1;
+ continue;
+ case 'i':
+ append(state.opt, " -i");
+ state.ignore = 1;
+ continue;
+ case 'k':
+ append(state.opt, " -k");
+ state.keepgoing = 1;
+ continue;
+ case 'N':
+ state.never = 1;
+ /*FALLTHROUGH*/
+ case 'n':
+ append(state.opt, " -n");
+ state.exec = 0;
+ continue;
+ case 'F':
+ append(state.opt, " -F");
+ state.force = 1;
+ continue;
+ case 'G':
+ append(state.opt, " -G");
+ search(state.vars, "-debug-symbols", "1");
+ continue;
+ case 'K':
+ continue;
+ case 'S':
+ append(state.opt, " -S");
+ search(state.vars, "-strip-symbols", "1");
+ continue;
+ case 'V':
+ fprintf(stdout, "%s\n", id + 10);
+ exit(0);
+ case 'f':
+ case 'r':
+ case 'C':
+ case 'D':
+ t = s;
+ if (!*++s && !(s = *++argv))
+ {
+ report(2, "option value expected", t, (unsigned long)0);
+ usage();
+ }
+ else
+ switch (*t)
+ {
+ case 'f':
+ append(state.opt, " -f ");
+ append(state.opt, s);
+ state.file = s;
+ break;
+ case 'r':
+ state.recurse = s;
+ break;
+ case 'C':
+ state.directory = s;
+ break;
+ case 'D':
+ append(state.opt, " -D");
+ append(state.opt, s);
+ state.debug = -atoi(s);
+ break;
+ }
+ break;
+ default:
+ report(2, "unknown option", s, (unsigned long)0);
+ case '?':
+ usage();
+ break;
+ }
+ break;
+ }
+ }
+#endif
+
+ /*
+ * load the environment
+ */
+
+ for (e = environ; s = *e; e++)
+ for (t = s; *t; t++)
+ if (*t == '=')
+ {
+ *t = 0;
+ search(state.vars, s, t + 1);
+ *t = '=';
+ break;
+ }
+
+ /*
+ * grab the command line targets and variable definitions
+ */
+
+ while (s = *argv++)
+ {
+ for (t = s; *t; t++)
+ if (*t == '=')
+ {
+ v = t + 1;
+ if (t > s && *(t - 1) == '+')
+ t--;
+ c = *t;
+ *t = 0;
+ search(state.vars, s, v);
+ tmp = buffer();
+ append(tmp, s);
+ append(tmp, ".FORCE");
+ search(state.vars, use(tmp), v);
+ drop(tmp);
+ *t = c;
+ break;
+ }
+ if (!*t)
+ {
+ /*
+ * handle a few targets for nmake compatibility
+ */
+
+ if (*s == 'e' && !strncmp(s, "error 0 $(MAKEVERSION:", 22))
+ exit(1);
+ if (*s == 'r' && !strcmp(s, "recurse") || *s == 'c' && !strncmp(s, "cc-", 3))
+ continue;
+ rule(s)->flags |= RULE_active;
+ state.active = 0;
+ if (state.recurse)
+ continue;
+ }
+ add(state.opt, ' ');
+ add(state.opt, '\'');
+ append(state.opt, s);
+ add(state.opt, '\'');
+ }
+
+ /*
+ * initialize the views
+ */
+
+ if (state.directory && chdir(state.directory))
+ report(3, "cannot change working directory", NiL, (unsigned long)0);
+ view();
+
+ /*
+ * recursion drops out here
+ */
+
+ if (state.recurse)
+ return recurse(state.recurse);
+
+ /*
+ * read the mamfile(s) and bring the targets up to date
+ */
+
+ search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1));
+ push(state.file, (Stdio_t*)0, STREAM_MUST);
+ make(rule(""));
+ pop();
+
+ /*
+ * verify that active targets were made
+ */
+
+ if (!state.active && !state.verified)
+ walk(state.rules, verify, NiL);
+
+ /*
+ * done
+ */
+
+ return state.errors != 0;
+}