/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1990-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;
}