1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1994-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21
22/*
23 * mamake -- MAM make
24 *
25 * coded for portability
26 */
27
28static char id[] = "\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\0\n";
29
30#if _PACKAGE_ast
31
32#include <ast.h>
33#include <error.h>
34
35static const char usage[] =
36"[-?\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\n]"
37USAGE_LICENSE
38"[+NAME?mamake - make abstract machine make]"
39"[+DESCRIPTION?\bmamake\b reads \amake abstract machine\a target and"
40"	prerequisite file descriptions from a mamfile (see \b-f\b) and executes"
41"	actions to update targets that are older than their prerequisites."
42"	Mamfiles are generated by the \b--mam\b option of \bnmake\b(1) and"
43"	\bgmake\b(1) and are portable to environments that only have"
44"	\bsh\b(1) and \bcc\b(1).]"
45"[+?In practice \bmamake\b is used to bootstrap build \bnmake\b(1) and"
46"	\bksh\b(1) in new environments. Mamfiles are used rather than"
47"	old-\bmake\b makefiles because some features are not reliably supported"
48"	across all \bmake\b variants:]{"
49"		[+action execution?Multi-line actions are executed as a"
50"			unit by \b$SHELL\b. There are some shell constructs"
51"			that cannot be expressed in an old-\bmake\b makefile.]"
52"		[+viewpathing?\bVPATH\b is properly interpreted. This allows"
53"			source to be separate from generated files.]"
54"		[+recursion?Ordered subdirectory recursion over unrelated"
55"			makefiles.]"
56"	}"
57"[+?\bmamprobe\b(1) is called to probe and generate system specific variable"
58"	definitions. The probe information is regenerated when it is older"
59"	than the \bmamprobe\b command.]"
60"[+?For compatibility with \bnmake\b(1) the \b-K\b option and the"
61"	\brecurse\b and \bcc-*\b command line targets are ignored.]"
62"[e:?Explain reason for triggering action. Ignored if -F is on.]"
63"[f:?Read \afile\a instead of the default.]:[file:=Mamfile]"
64"[i:?Ignore action errors.]"
65"[k:?Continue after error with sibling prerequisites.]"
66"[n:?Print actions but do not execute. Recursion actions (see \b-r\b) are still"
67"	executed. Use \b-N\b to disable recursion actions too.]"
68"[r:?Recursively make leaf directories matching \apattern\a. Only leaf"
69"	directories containing a makefile named \bNmakefile\b, \bnmakefile\b,"
70"	\bMakefile\b or \bmakefile\b are considered. The first makefile"
71"	found in each leaf directory is scanned for leaf directory"
72"	prerequisites; the recusion order is determined by a topological sort"
73"	of these prerequisites.]:[pattern]"
74"[C:?Do all work in \adirectory\a. All messages will mention"
75"	\adirectory\a.]:[directory]"
76"[D:?Set the debug trace level to \alevel\a. Higher levels produce more"
77"	output.]#[level]"
78"[F:?Force all targets to be out of date.]"
79"[K:?Ignored.]"
80"[N:?Like \b-n\b but recursion actions (see \b-r\b) are also disabled.]"
81"[V:?Print the program version and exit.]"
82"[G:debug-symbols?Compile and link with debugging symbol options enabled.]"
83"[S:strip-symbols?Strip link-time static symbols from executables.]"
84
85"\n"
86"\n[ target ... ] [ name=value ... ]\n"
87"\n"
88
89"[+SEE ALSO?\bgmake\b(1), \bmake\b(1), \bmamprobe\b(1),"
90"	\bnmake\b(1), \bsh\b(1)]"
91;
92
93#else
94
95#define elementsof(x)	(sizeof(x)/sizeof(x[0]))
96#define newof(p,t,n,x)	((p)?(t*)realloc((char*)(p),sizeof(t)*(n)+(x)):(t*)calloc(1,sizeof(t)*(n)+(x)))
97
98#define NiL		((char*)0)
99
100#endif
101
102#include <stdio.h>
103#include <unistd.h>
104#include <ctype.h>
105#include <sys/types.h>
106#include <sys/stat.h>
107#include <time.h>
108
109#if !_PACKAGE_ast && defined(__STDC__)
110#include <stdlib.h>
111#include <string.h>
112#endif
113
114#define delimiter(c)	((c)==' '||(c)=='\t'||(c)=='\n'||(c)==';'||(c)=='('||(c)==')'||(c)=='`'||(c)=='|'||(c)=='&'||(c)=='=')
115
116#define add(b,c)	(((b)->nxt >= (b)->end) ? append(b, "") : NiL, *(b)->nxt++ = (c))
117#define get(b)		((b)->nxt-(b)->buf)
118#define set(b,o)	((b)->nxt=(b)->buf+(o))
119#define use(b)		(*(b)->nxt=0,(b)->nxt=(b)->buf)
120
121#define CHUNK		1024
122#define KEY(a,b,c,d)	((((unsigned long)(a))<<15)|(((unsigned long)(b))<<10)|(((unsigned long)(c))<<5)|(((unsigned long)(d))))
123#define NOW		((unsigned long)time((time_t*)0))
124#define ROTATE(p,l,r,t)	((t)=(p)->l,(p)->l=(t)->r,(t)->r=(p),(p)=(t))
125
126#define RULE_active	0x0001		/* active target		*/
127#define RULE_dontcare	0x0002		/* ok if not found		*/
128#define RULE_error	0x0004		/* not found or not generated	*/
129#define RULE_exists	0x0008		/* target file exists		*/
130#define RULE_generated	0x0010		/* generated target		*/
131#define RULE_ignore	0x0020		/* ignore time			*/
132#define RULE_implicit	0x0040		/* implicit prerequisite	*/
133#define RULE_made	0x0080		/* already made			*/
134#define RULE_virtual	0x0100		/* not a file			*/
135
136#define STREAM_KEEP	0x0001		/* don't fclose() on pop()	*/
137#define STREAM_MUST	0x0002		/* push() file must exist	*/
138#define STREAM_PIPE	0x0004		/* pclose() on pop()		*/
139
140#ifndef S_IXUSR
141#define S_IXUSR		0100		/* owner execute permission	*/
142#endif
143#ifndef S_IXGRP
144#define S_IXGRP		0010		/* group execute permission	*/
145#endif
146#ifndef S_IXOTH
147#define S_IXOTH		0001		/* other execute permission	*/
148#endif
149
150struct Rule_s;
151
152typedef struct stat Stat_t;
153typedef FILE Stdio_t;
154
155typedef struct Buf_s			/* buffer stream		*/
156{
157	struct Buf_s*	old;		/* next dropped buffer		*/
158	char*		end;		/* 1 past end of buffer		*/
159	char*		nxt;		/* next char to add		*/
160	char*		buf;		/* buffer space			*/
161} Buf_t;
162
163typedef struct Dict_item_s		/* dictionary item		*/
164{
165	struct Dict_item_s*	left;	/* left child			*/
166	struct Dict_item_s*	right;	/* right child			*/
167	void*			value;	/* user defined value		*/
168	char			name[1];/* 0 terminated name		*/
169} Dict_item_t;
170
171typedef struct Dict_s			/* dictionary handle		*/
172{
173	Dict_item_t*	root;		/* root item			*/
174} Dict_t;
175
176typedef struct List_s			/* Rule_t list			*/
177{
178	struct List_s*	next;		/* next in list			*/
179	struct Rule_s*	rule;		/* list item			*/
180} List_t;
181
182typedef struct Rule_s			/* rule item			*/
183{
184	char*		name;		/* unbound name			*/
185	char*		path;		/* bound path			*/
186	List_t*		prereqs;	/* prerequisites		*/
187	struct Rule_s*	leaf;		/* recursion leaf alias		*/
188	int		flags;		/* RULE_* flags			*/
189	int		making;		/* currently make()ing		*/
190	unsigned long	time;		/* modification time		*/
191} Rule_t;
192
193typedef struct Stream_s			/* input file stream stack	*/
194{
195	Stdio_t*	fp;		/* read stream			*/
196	char*		file;		/* stream path			*/
197	unsigned long	line;		/* stream line			*/
198	int		flags;		/* stream flags			*/
199} Stream_t;
200
201typedef struct View_s			/* viewpath level		*/
202{
203	struct View_s*	next;		/* next level in viewpath	*/
204	int		node;		/* viewpath node path length	*/
205	char		dir[1];		/* viewpath level dir prefix	*/
206} View_t;
207
208static struct				/* program state		*/
209{
210	Buf_t*		buf;		/* work buffer			*/
211	Buf_t*		old;		/* dropped buffers		*/
212	Buf_t*		opt;		/* option buffer		*/
213
214	Dict_t*		leaf;		/* recursion leaf dictionary	*/
215	Dict_t*		libs;		/* library dictionary		*/
216	Dict_t*		rules;		/* rule dictionary		*/
217	Dict_t*		vars;		/* variable dictionary		*/
218
219	View_t*		view;		/* viewpath levels		*/
220
221	char*		directory;	/* work in this directory	*/
222	char*		id;		/* command name			*/
223	char*		file;		/* first input file		*/
224	char*		pwd;		/* current directory		*/
225	char*		recurse;	/* recursion pattern		*/
226	char*		shell;		/* ${SHELL}			*/
227
228	int		active;		/* targets currently active	*/
229	int		debug;		/* negative of debug level	*/
230	int		errors;		/* some error(s) occurred	*/
231	int		exec;		/* execute actions		*/
232	int		explain;	/* explain actions		*/
233	int		force;		/* all targets out of date	*/
234	int		ignore;		/* ignore command errors	*/
235	int		indent;		/* debug indent			*/
236	int		keepgoing;	/* do siblings on error		*/
237	int		never;		/* never execute		*/
238	int		peek;		/* next line already in input	*/
239	int		probed;		/* probe already done		*/
240	int		verified;	/* don't bother with verify()	*/
241
242	Stream_t	streams[4];	/* input file stream stack	*/
243	Stream_t*	sp;		/* input stream stack pointer	*/
244
245	char		input[8*CHUNK];	/* input buffer			*/
246} state;
247
248static unsigned long	make(Rule_t*);
249
250static char		mamfile[] = "Mamfile";
251static char		sh[] = "/bin/sh";
252
253extern char**		environ;
254
255#if !_PACKAGE_ast
256
257#if defined(NeXT) || defined(__NeXT)
258#define getcwd(a,b)	getwd(a)
259#endif
260
261/*
262 * emit usage message and exit
263 */
264
265static void
266usage()
267{
268	fprintf(stderr, "Usage: %s [-iknFKNV] [-f mamfile] [-r pattern] [-C directory] [-D level] [target ...] [name=value ...]\n", state.id);
269	exit(2);
270}
271
272#endif
273
274/*
275 * output error message identification
276 */
277
278static void
279identify(Stdio_t* sp)
280{
281	if (state.directory)
282		fprintf(sp, "%s [%s]: ", state.id, state.directory);
283	else
284		fprintf(sp, "%s: ", state.id);
285}
286
287/*
288 * emit error message
289 * level:
290 *	<0	debug
291 *	 0	info
292 *	 1	warning
293 *	 2	error
294 *	>2	exit(level-2)
295 */
296
297static void
298report(int level, char* text, char* item, unsigned long stamp)
299{
300	int	i;
301
302	if (level >= state.debug)
303	{
304		if (level)
305			identify(stderr);
306		if (level < 0)
307		{
308			fprintf(stderr, "debug%d: ", level);
309			for (i = 1; i < state.indent; i++)
310				fprintf(stderr, "  ");
311		}
312		else
313		{
314			if (state.sp && state.sp->line)
315			{
316				if (state.sp->file)
317					fprintf(stderr, "%s: ", state.sp->file);
318				fprintf(stderr, "%ld: ", state.sp->line);
319			}
320			if (level == 1)
321				fprintf(stderr, "warning: ");
322			else if (level > 1)
323				state.errors = 1;
324		}
325		if (item)
326			fprintf(stderr, "%s: ", item);
327		fprintf(stderr, "%s", text);
328		if (stamp && state.debug <= -2)
329			fprintf(stderr, " %10lu", stamp);
330		fprintf(stderr, "\n");
331		if (level > 2)
332			exit(level - 2);
333	}
334}
335
336/*
337 * don't know how to make or exit code making
338 */
339
340static void
341dont(Rule_t* r, int code, int keepgoing)
342{
343	identify(stderr);
344	if (!code)
345		fprintf(stderr, "don't know how to make %s\n", r->name);
346	else
347	{
348		fprintf(stderr, "*** exit code %d making %s%s\n", code, r->name, state.ignore ? " ignored" : "");
349		unlink(r->name);
350		if (state.ignore)
351			return;
352	}
353	if (!keepgoing)
354		exit(1);
355	state.errors++;
356	r->flags |= RULE_error;
357}
358
359/*
360 * local strrchr()
361 */
362
363static char*
364last(register char* s, register int c)
365{
366	register char*	r = 0;
367
368	for (r = 0; *s; s++)
369		if (*s == c)
370			r = s;
371	return r;
372}
373
374/*
375 * open a buffer stream
376 */
377
378static Buf_t*
379buffer(void)
380{
381	register Buf_t*	buf;
382
383	if (buf = state.old)
384		state.old = state.old->old;
385	else if (!(buf = newof(0, Buf_t, 1, 0)) || !(buf->buf = newof(0, char, CHUNK, 0)))
386		report(3, "out of space [buffer]", NiL, (unsigned long)0);
387	buf->end = buf->buf + CHUNK;
388	buf->nxt = buf->buf;
389	return buf;
390}
391
392/*
393 * close a buffer stream
394 */
395
396static void
397drop(Buf_t* buf)
398{
399	buf->old = state.old;
400	state.old = buf;
401}
402
403/*
404 * append str length n to buffer and return the buffer base
405 */
406
407static char*
408appendn(Buf_t* buf, char* str, int n)
409{
410	int	m;
411	int	i;
412
413	if ((n + 1) >= (buf->end - buf->nxt))
414	{
415		i = buf->nxt - buf->buf;
416		m = (((buf->end - buf->buf) + n + CHUNK + 1) / CHUNK) * CHUNK;
417		if (!(buf->buf = newof(buf->buf, char, m, 0)))
418			report(3, "out of space [buffer resize]", NiL, (unsigned long)0);
419		buf->end = buf->buf + m;
420		buf->nxt = buf->buf + i;
421	}
422	memcpy(buf->nxt, str, n + 1);
423	buf->nxt += n;
424	return buf->buf;
425}
426
427/*
428 * append str to buffer and return the buffer base
429 * if str==0 then next pointer reset to base
430 */
431
432static char*
433append(Buf_t* buf, char* str)
434{
435	if (str)
436		return appendn(buf, str, strlen(str));
437	buf->nxt = buf->buf;
438	return buf->buf;
439}
440
441/*
442 * allocate space for s and return the copy
443 */
444
445static char*
446duplicate(char* s)
447{
448	char*	t;
449	int	n;
450
451	n = strlen(s);
452	if (!(t = newof(0, char, n, 1)))
453		report(3, "out of space [duplicate]", s, (unsigned long)0);
454	strcpy(t, s);
455	return t;
456}
457
458/*
459 * open a new dictionary
460 */
461
462static Dict_t*
463dictionary(void)
464{
465	Dict_t*	dict;
466
467	if (!(dict = newof(0, Dict_t, 1, 0)))
468		report(3, "out of space [dictionary]", NiL, (unsigned long)0);
469	return dict;
470}
471
472/*
473 * return the value for item name in dictionary dict
474 * if value!=0 then name entry value is created if necessary and set
475 * uses top-down splaying (ala Tarjan and Sleator)
476 */
477
478static void*
479search(register Dict_t* dict, char* name, void* value)
480{
481	register int		cmp;
482	register Dict_item_t*	root;
483	register Dict_item_t*	t;
484	register Dict_item_t*	left;
485	register Dict_item_t*	right;
486	register Dict_item_t*	lroot;
487	register Dict_item_t*	rroot;
488
489	root = dict->root;
490	left = right = lroot = rroot = 0;
491	while (root)
492	{
493		if (!(cmp = strcmp(name, root->name)))
494			break;
495		else if (cmp < 0)
496		{
497			if (root->left && (cmp = strcmp(name, root->left->name)) <= 0)
498			{
499				ROTATE(root, left, right, t);
500				if (!cmp)
501					break;
502			}
503			if (right)
504				right->left = root;
505			else
506				rroot = root;
507			right = root;
508			root = root->left;
509			right->left = 0;
510		}
511		else
512		{
513			if (root->right && (cmp = strcmp(name, root->right->name)) >= 0)
514			{
515				ROTATE(root, right, left, t);
516				if (!cmp)
517					break;
518			}
519			if (left)
520				left->right = root;
521			else
522				lroot = root;
523			left = root;
524			root = root->right;
525			left->right = 0;
526		}
527	}
528	if (root)
529	{
530		if (right)
531			right->left = root->right;
532		else
533			rroot = root->right;
534		if (left)
535			left->right = root->left;
536		else
537			lroot = root->left;
538	}
539	else if (value)
540	{
541		if (!(root = newof(0, Dict_item_t, 1, strlen(name))))
542			report(3, "out of space [dictionary]", name, (unsigned long)0);
543		strcpy(root->name, name);
544	}
545	if (root)
546	{
547		if (value)
548			root->value = value;
549		root->left = lroot;
550		root->right = rroot;
551		dict->root = root;
552		return value ? (void*)root->name : root->value;
553	}
554	if (left)
555	{
556		left->right = rroot;
557		dict->root = lroot;
558	}
559	else if (right)
560	{
561		right->left = lroot;
562		dict->root = rroot;
563	}
564	return 0;
565}
566
567/*
568 * low level for walk()
569 */
570
571static int
572apply(Dict_t* dict, Dict_item_t* item, int (*func)(Dict_item_t*, void*), void* handle)
573{
574	register Dict_item_t*	right;
575
576	do
577	{
578		right = item->right;
579		if (item->left && apply(dict, item->left, func, handle))
580			return -1;
581		if ((*func)(item, handle))
582			return -1;
583	} while (item = right);
584	return 0;
585}
586
587/*
588 * apply func to each dictionary item
589 */
590
591static int
592walk(Dict_t* dict, int (*func)(Dict_item_t*, void*), void* handle)
593{
594	return dict->root ? apply(dict, dict->root, func, handle) : 0;
595}
596
597/*
598 * return a rule pointer for name
599 */
600
601static Rule_t*
602rule(char* name)
603{
604	Rule_t*	r;
605
606	if (!(r = (Rule_t*)search(state.rules, name, NiL)))
607	{
608		if (!(r = newof(0, Rule_t, 1, 0)))
609			report(3, "out of space [rule]", name, (unsigned long)0);
610		r->name = (char*)search(state.rules, name, (void*)r);
611	}
612	return r;
613}
614
615/*
616 * prepend p onto rule r prereqs
617 */
618
619static void
620cons(Rule_t* r, Rule_t* p)
621{
622	register List_t*	x;
623
624	for (x = r->prereqs; x && x->rule != p; x = x->next);
625	if (!x)
626	{
627		if (!(x = newof(0, List_t, 1, 0)))
628			report(3, "out of space [list]", r->name, (unsigned long)0);
629		x->rule = p;
630		x->next = r->prereqs;
631		r->prereqs = x;
632	}
633}
634
635/*
636 * initialize the viewpath
637 */
638
639static void
640view(void)
641{
642	register char*		s;
643	register char*		t;
644	register char*		p;
645	register View_t*	vp;
646
647	View_t*			zp;
648	int			c;
649	int			n;
650
651	Stat_t			st;
652	Stat_t			ts;
653
654	char			buf[CHUNK];
655
656	if (stat(".", &st))
657		report(3, "cannot stat", ".", (unsigned long)0);
658	if ((s = (char*)search(state.vars, "PWD", NiL)) && !stat(s, &ts) &&
659	    ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
660		state.pwd = s;
661	if (!state.pwd)
662	{
663		if (!getcwd(buf, sizeof(buf) - 1))
664			report(3, "cannot determine PWD", NiL, (unsigned long)0);
665		state.pwd = duplicate(buf);
666		search(state.vars, "PWD", state.pwd);
667	}
668	if ((s = (char*)search(state.vars, "VPATH", NiL)) && *s)
669	{
670		zp = 0;
671		for (;;)
672		{
673			for (t = s; *t && *t != ':'; t++);
674			if (c = *t)
675				*t = 0;
676			if (!state.view)
677			{
678				/*
679				 * determine the viewpath offset
680				 */
681
682				if (stat(s, &st))
683					report(3, "cannot stat top view", s, (unsigned long)0);
684				if (stat(state.pwd, &ts))
685					report(3, "cannot stat", state.pwd, (unsigned long)0);
686				if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
687					p = ".";
688				else
689				{
690					p = state.pwd + strlen(state.pwd);
691					while (p > state.pwd)
692						if (*--p == '/')
693						{
694							if (p == state.pwd)
695								report(3, ". not under VPATH", s, (unsigned long)0);
696							*p = 0;
697							if (stat(state.pwd, &ts))
698								report(3, "cannot stat", state.pwd, (unsigned long)0);
699							*p = '/';
700							if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
701							{
702								p++;
703								break;
704							}
705						}
706					if (p <= state.pwd)
707						report(3, "cannot determine viewpath offset", s, (unsigned long)0);
708				}
709			}
710			n = strlen(s);
711			if (!(vp = newof(0, View_t, 1, strlen(p) + n + 1)))
712				report(3, "out of space [view]", s, (unsigned long)0);
713			vp->node = n + 1;
714			strcpy(vp->dir, s);
715			*(vp->dir + n) = '/';
716			strcpy(vp->dir + n + 1, p);
717			report(-4, vp->dir, "view", (unsigned long)0);
718			if (!state.view)
719				state.view = zp = vp;
720			else
721				zp = zp->next = vp;
722			if (!c)
723				break;
724			*t++ = c;
725			s = t;
726		}
727	}
728}
729
730/*
731 * return next '?' or '}' in nested '}'
732 */
733
734static char*
735cond(register char* s)
736{
737	register int	n;
738
739	if (*s == '?')
740		s++;
741	n = 0;
742	for (;;)
743	{
744		switch (*s++)
745		{
746		case 0:
747			break;
748		case '{':
749			n++;
750			continue;
751		case '}':
752			if (!n--)
753				break;
754			continue;
755		case '?':
756			if (!n)
757				break;
758			continue;
759		default:
760			continue;
761		}
762		break;
763	}
764	return s - 1;
765}
766
767/*
768 * expand var refs from s into buf
769 */
770
771static void
772substitute(Buf_t* buf, register char* s)
773{
774	register char*	t;
775	register char*	v;
776	register char*	q;
777	register char*	b;
778	register int	c;
779	register int	n;
780	int		a = 0;
781	int		i;
782
783	while (c = *s++)
784	{
785		if (c == '$' && *s == '{')
786		{
787			b = s - 1;
788			i = 1;
789			for (n = *(t = ++s) == '-' ? 0 : '-'; (c = *s) && c != '?' && c != '+' && c != n && c != ':' && c != '=' && c != '[' && c != '}'; s++)
790				if (!isalnum(c) && c != '_')
791					i = 0;
792			*s = 0;
793			if (c == '[')
794			{
795				append(buf, b);
796				*s = c;
797				continue;
798			}
799			v = (char*)search(state.vars, t, NiL);
800			if ((c == ':' || c == '=') && (!v || c == ':' && !*v))
801			{
802				append(buf, b);
803				*s = c;
804				continue;
805			}
806			if (t[0] == 'A' && t[1] == 'R' && t[2] == 0)
807				a = 1;
808			*s = c;
809			if (c && c != '}')
810			{
811				n = 1;
812				for (t = ++s; *s; s++)
813					if (*s == '{')
814						n++;
815					else if (*s == '}' && !--n)
816						break;
817			}
818			switch (c)
819			{
820			case '?':
821				q = cond(t - 1);
822				if (v)
823				{
824					if (((q - t) != 1 || *t != '*') && strncmp(v, t, q - t))
825						v = 0;
826				}
827				else if (q == t)
828					v = s;
829				t = cond(q);
830				if (v)
831				{
832					if (t > q)
833					{
834						c = *t;
835						*t = 0;
836						substitute(buf, q + 1);
837						*t = c;
838					}
839				}
840				else
841				{
842					q = cond(t);
843					if (q > t)
844					{
845						c = *q;
846						*q = 0;
847						substitute(buf, t + 1);
848						*q = c;
849					}
850				}
851				break;
852			case '+':
853			case '-':
854				if ((v == 0 || *v == 0) == (c == '-'))
855				{
856					c = *s;
857					*s = 0;
858					substitute(buf, t);
859					*s = c;
860					break;
861				}
862				if (c != '-')
863					break;
864				/*FALLTHROUGH*/
865			case 0:
866			case '=':
867			case '}':
868				if (v)
869				{
870					if (a && t[0] == 'm' && t[1] == 'a' && t[2] == 'm' && t[3] == '_' && t[4] == 'l' && t[5] == 'i' && t[6] == 'b')
871					{
872						for (t = v; *t == ' '; t++);
873						for (; *t && *t != ' '; t++);
874						if (*t)
875							*t = 0;
876						else
877							t = 0;
878						substitute(buf, v);
879						if (t)
880							*t = ' ';
881					}
882					else
883						substitute(buf, v);
884				}
885				else if (i)
886				{
887					c = *s;
888					*s = 0;
889					append(buf, b);
890					*s = c;
891					continue;
892				}
893				break;
894			}
895			if (*s)
896				s++;
897		}
898		else
899			add(buf, c);
900	}
901}
902
903/*
904 * expand var refs from s into buf and return buf base
905 */
906
907static char*
908expand(Buf_t* buf, char* s)
909{
910	substitute(buf, s);
911	return use(buf);
912}
913
914/*
915 * stat() with .exe check
916 */
917
918static char*
919status(Buf_t* buf, int off, char* path, struct stat* st)
920{
921	int		r;
922	char*		s;
923	Buf_t*		tmp;
924
925	if (!stat(path, st))
926		return path;
927	if (!(tmp = buf))
928	{
929		tmp = buffer();
930		off = 0;
931	}
932	if (off)
933		set(tmp, off);
934	else
935		append(tmp, path);
936	append(tmp, ".exe");
937	s = use(tmp);
938	r = stat(s, st);
939	if (!buf)
940	{
941		drop(tmp);
942		s = path;
943	}
944	if (r)
945	{
946		if (off)
947			s[off] = 0;
948		s = 0;
949	}
950	return s;
951}
952
953/*
954 * return path to file
955 */
956
957static char*
958find(Buf_t* buf, char* file, struct stat* st)
959{
960	char*		s;
961	View_t*		vp;
962	int		node;
963	int		c;
964	int		o;
965
966	if (s = status(buf, 0, file, st))
967	{
968		report(-3, s, "find", (unsigned long)0);
969		return s;
970	}
971	if (vp = state.view)
972	{
973		node = 0;
974		if (*file == '/')
975		{
976			do
977			{
978				if (!strncmp(file, vp->dir, vp->node))
979				{
980					file += vp->node;
981					node = 2;
982					break;
983				}
984			} while (vp = vp->next);
985		}
986		else
987			vp = vp->next;
988		if (vp)
989			do
990			{
991				if (node)
992				{
993					c = vp->dir[vp->node];
994					vp->dir[vp->node] = 0;
995					append(buf, vp->dir);
996					vp->dir[vp->node] = c;
997				}
998				else
999				{
1000					append(buf, vp->dir);
1001					append(buf, "/");
1002				}
1003				append(buf, file);
1004				o = get(buf);
1005				s = use(buf);
1006				if (s = status(buf, o, s, st))
1007				{
1008					report(-3, s, "find", (unsigned long)0);
1009					return s;
1010				}
1011			} while (vp = vp->next);
1012	}
1013	return 0;
1014}
1015
1016/*
1017 * bind r to a file and return the modify time
1018 */
1019
1020static unsigned long
1021bind(Rule_t* r)
1022{
1023	char*		s;
1024	Buf_t*		buf;
1025	struct stat	st;
1026
1027	buf = buffer();
1028	if (s = find(buf, r->name, &st))
1029	{
1030		if (s != r->name)
1031			r->path = duplicate(s);
1032		r->time = st.st_mtime;
1033		r->flags |= RULE_exists;
1034	}
1035	drop(buf);
1036	return r->time;
1037}
1038
1039/*
1040 * pop the current input file
1041 */
1042
1043static int
1044pop(void)
1045{
1046	int	r;
1047
1048	if (!state.sp)
1049		report(3, "input stack underflow", NiL, (unsigned long)0);
1050	if (!state.sp->fp || (state.sp->flags & STREAM_KEEP))
1051		r = 0;
1052	else if (state.sp->flags & STREAM_PIPE)
1053		r = pclose(state.sp->fp);
1054	else
1055		r = fclose(state.sp->fp);
1056	if (state.sp == state.streams)
1057		state.sp = 0;
1058	else
1059		state.sp--;
1060	return r;
1061}
1062
1063/*
1064 * push file onto the input stack
1065 */
1066
1067static int
1068push(char* file, Stdio_t* fp, int flags)
1069{
1070	char*		path;
1071	Buf_t*		buf;
1072	struct stat	st;
1073
1074	if (!state.sp)
1075		state.sp = state.streams;
1076	else if (++state.sp >= &state.streams[elementsof(state.streams)])
1077		report(3, "input stream stack overflow", NiL, (unsigned long)0);
1078	if (state.sp->fp = fp)
1079		state.sp->file = "pipeline";
1080	else if (flags & STREAM_PIPE)
1081		report(3, "pipe error", file, (unsigned long)0);
1082	else if (!file || !strcmp(file, "-") || !strcmp(file, "/dev/stdin"))
1083	{
1084		flags |= STREAM_KEEP;
1085		state.sp->file = "/dev/stdin";
1086		state.sp->fp = stdin;
1087	}
1088	else
1089	{
1090		buf = buffer();
1091		if (path = find(buf, file, &st))
1092		{
1093			if (!(state.sp->fp = fopen(path, "r")))
1094				report(3, "cannot read", path, (unsigned long)0);
1095			state.sp->file = duplicate(path);
1096			drop(buf);
1097		}
1098		else
1099		{
1100			drop(buf);
1101			pop();
1102			if (flags & STREAM_MUST)
1103				report(3, "not found", file, (unsigned long)0);
1104			return 0;
1105		}
1106	}
1107	state.sp->flags = flags;
1108	state.sp->line = 0;
1109	return 1;
1110}
1111
1112/*
1113 * return the next input line
1114 */
1115
1116static char*
1117input(void)
1118{
1119	char*	e;
1120
1121	if (!state.sp)
1122		report(3, "no input file stream", NiL, (unsigned long)0);
1123	if (state.peek)
1124		state.peek = 0;
1125	else if (!fgets(state.input, sizeof(state.input), state.sp->fp))
1126		return 0;
1127	else if (*state.input && *(e = state.input + strlen(state.input) - 1) == '\n')
1128		*e = 0;
1129	state.sp->line++;
1130	return state.input;
1131}
1132
1133/*
1134 * pass shell action s to ${SHELL:-/bin/sh}
1135 * the -c wrapper ensures that scripts are run in the selected shell
1136 * even on systems that otherwise demand #! magic (can you say cygwin)
1137 */
1138
1139static int
1140execute(register char* s)
1141{
1142	register int	c;
1143	Buf_t*		buf;
1144
1145	if (!state.shell && (!(state.shell = (char*)search(state.vars, "SHELL", NiL)) || !strcmp(state.shell, sh)))
1146		state.shell = sh;
1147	buf = buffer();
1148	append(buf, state.shell);
1149	append(buf, " -c '");
1150	while (c = *s++)
1151	{
1152		if (c == '\'')
1153		{
1154			add(buf, c);
1155			for (s--; *s == c; s++)
1156			{
1157				add(buf, '\\');
1158				add(buf, c);
1159			}
1160		}
1161		add(buf, c);
1162	}
1163	add(buf, '\'');
1164	s = use(buf);
1165	report(-5, s, "exec", (unsigned long)0);
1166	if ((c = system(s)) > 255)
1167		c >>= 8;
1168	drop(buf);
1169	return c;
1170}
1171
1172/*
1173 * run action s to update r
1174 */
1175
1176static unsigned long
1177run(Rule_t* r, register char* s)
1178{
1179	register Rule_t*	q;
1180	register char*		t;
1181	register int		c;
1182	register View_t*	v;
1183	int			i;
1184	int			j;
1185	int			x;
1186	Stat_t			st;
1187	Buf_t*			buf;
1188
1189	if (r->flags & RULE_error)
1190		return r->time;
1191	buf = buffer();
1192	if (!strncmp(s, "mamake -r ", 10))
1193	{
1194		state.verified = 1;
1195		x = !state.never;
1196	}
1197	else
1198		x = state.exec;
1199	if (x)
1200		append(buf, "trap - 1 2 3 15\nPATH=.:$PATH\nset -x\n");
1201	if (state.view)
1202	{
1203		do
1204		{
1205			for (; delimiter(*s); s++)
1206				add(buf, *s);
1207			for (t = s; *s && !delimiter(*s); s++);
1208			c = *s;
1209			*s = 0;
1210			if (c == '=')
1211			{
1212				append(buf, t);
1213				continue;
1214			}
1215			if ((q = (Rule_t*)search(state.rules, t, NiL)) && q->path && !(q->flags & RULE_generated))
1216				append(buf, q->path);
1217			else
1218			{
1219				append(buf, t);
1220				if (*t == '-' && *(t + 1) == 'I' && (*(t + 2) || c))
1221				{
1222					if (*(t + 2))
1223						i = 2;
1224					else
1225					{
1226						for (i = 3; *(t + i) == ' ' || *(t + i) == '\t'; i++);
1227						*s = c;
1228						for (s = t + i; *s && *s != ' ' && *s != '\t' && *s != '\n'; s++);
1229						c = *s;
1230						*s = 0;
1231						append(buf, t + 2);
1232					}
1233					if (*(t + i) && *(t + i) != '/')
1234					{
1235						v = state.view;
1236						while (v = v->next)
1237						{
1238							add(buf, ' ');
1239							for (j = 0; j < i; j++)
1240								add(buf, *(t + j));
1241							append(buf, v->dir);
1242							if (*(t + i) != '.' || *(t + i + 1))
1243							{
1244								add(buf, '/');
1245								append(buf, t + i);
1246							}
1247						}
1248					}
1249				}
1250			}
1251		} while (*s = c);
1252		s = use(buf);
1253	}
1254	else if (x)
1255	{
1256		append(buf, s);
1257		s = use(buf);
1258	}
1259	if (x)
1260	{
1261		if (c = execute(s))
1262			dont(r, c, state.keepgoing);
1263		if (status((Buf_t*)0, 0, r->name, &st))
1264		{
1265			r->time = st.st_mtime;
1266			r->flags |= RULE_exists;
1267		}
1268		else
1269			r->time = NOW;
1270	}
1271	else
1272	{
1273		fprintf(stdout, "%s\n", s);
1274		if (state.debug)
1275			fflush(stdout);
1276		r->time = NOW;
1277		r->flags |= RULE_exists;
1278	}
1279	drop(buf);
1280	return r->time;
1281}
1282
1283/*
1284 * return the full path for s using buf workspace
1285 */
1286
1287static char*
1288path(Buf_t* buf, char* s, int must)
1289{
1290	register char*	p;
1291	register char*	d;
1292	register char*	x;
1293	char*		e;
1294	register int	c;
1295	int		t;
1296	int		o;
1297	Stat_t		st;
1298
1299	for (e = s; *e && *e != ' ' && *e != '\t'; e++);
1300	t = *e;
1301	if ((x = status(buf, 0, s, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
1302		return x;
1303	if (!(p = (char*)search(state.vars, "PATH", NiL)))
1304		report(3, "variable not defined", "PATH", (unsigned long)0);
1305	do
1306	{
1307		for (d = p; *p && *p != ':'; p++);
1308		c = *p;
1309		*p = 0;
1310		if (*d && (*d != '.' || *(d + 1)))
1311		{
1312			append(buf, d);
1313			add(buf, '/');
1314		}
1315		*p = c;
1316		if (t)
1317			*e = 0;
1318		append(buf, s);
1319		if (t)
1320			*e = t;
1321		o = get(buf);
1322		x = use(buf);
1323		if ((x = status(buf, o, x, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
1324			return x;
1325	} while (*p++);
1326	if (must)
1327		report(3, "command not found", s, (unsigned long)0);
1328	return 0;
1329}
1330
1331/*
1332 * generate (if necessary) and read the MAM probe information
1333 * done on the first `setv CC ...'
1334 */
1335
1336static void
1337probe(void)
1338{
1339	register char*	cc;
1340	register char*	s;
1341	unsigned long	h;
1342	unsigned long	q;
1343	Buf_t*		buf;
1344	Buf_t*		pro;
1345	Buf_t*		tmp;
1346	struct stat	st;
1347
1348	static char	let[] = "ABCDEFGHIJKLMNOP";
1349	static char	cmd[] = "mamprobe";
1350
1351	if (!(cc = (char*)search(state.vars, "CC", NiL)))
1352		cc = "cc";
1353	buf = buffer();
1354	s = path(buf, cmd, 1);
1355	q = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime;
1356	pro = buffer();
1357	s = cc = path(pro, cc, 1);
1358	for (h = 0; *s; s++)
1359		h = h * 0x63c63cd9L + *s + 0x9c39c33dL;
1360	if (!(s = (char*)search(state.vars, "INSTALLROOT", NiL)))
1361		report(3, "variable must be defined", "INSTALLROOT", (unsigned long)0);
1362	append(buf, s);
1363	append(buf, "/lib/probe/C/mam/");
1364	for (h &= 0xffffffffL; h; h >>= 4)
1365		add(buf, let[h & 0xf]);
1366	s = use(buf);
1367	h = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime;
1368	if (h < q || !push(s, (Stdio_t*)0, 0))
1369	{
1370		tmp = buffer();
1371		append(tmp, cmd);
1372		add(tmp, ' ');
1373		append(tmp, s);
1374		add(tmp, ' ');
1375		append(tmp, cc);
1376		if (execute(use(tmp)))
1377			report(3, "cannot generate probe info", s, (unsigned long)0);
1378		drop(tmp);
1379		if (!push(s, (Stdio_t*)0, 0))
1380			report(3, "cannot read probe info", s, (unsigned long)0);
1381	}
1382	drop(pro);
1383	drop(buf);
1384	make(rule(""));
1385	pop();
1386}
1387
1388/*
1389 * add attributes in s to r
1390 */
1391
1392static void
1393attributes(register Rule_t* r, register char* s)
1394{
1395	register char*	t;
1396	register int	n;
1397
1398	for (;;)
1399	{
1400		for (; *s == ' '; s++);
1401		for (t = s; *s && *s != ' '; s++);
1402		if (!(n = s - t))
1403			break;
1404		switch (*t)
1405		{
1406		case 'd':
1407			if (n == 8 && !strncmp(t, "dontcare", n))
1408				r->flags |= RULE_dontcare;
1409			break;
1410		case 'g':
1411			if (n == 9 && !strncmp(t, "generated", n))
1412				r->flags |= RULE_generated;
1413			break;
1414		case 'i':
1415			if (n == 6 && !strncmp(t, "ignore", n))
1416				r->flags |= RULE_ignore;
1417			else if (n == 8 && !strncmp(t, "implicit", n))
1418				r->flags |= RULE_implicit;
1419			break;
1420		case 'v':
1421			if (n == 7 && !strncmp(t, "virtual", n))
1422				r->flags |= RULE_virtual;
1423			break;
1424		}
1425	}
1426}
1427
1428/*
1429 * define ${mam_libX} for library reference lib
1430 */
1431
1432static char*
1433require(char* lib, int dontcare)
1434{
1435	register int	c;
1436	char*		s;
1437	char*		r;
1438	FILE*		f;
1439	Buf_t*		buf;
1440	Buf_t*		tmp;
1441	struct stat	st;
1442
1443	static int	dynamic = -1;
1444
1445	if (dynamic < 0)
1446		dynamic = (s = search(state.vars, "mam_cc_L", NiL)) ? atoi(s) : 0;
1447	if (!(r = search(state.vars, lib, NiL)))
1448	{
1449		buf = buffer();
1450		tmp = buffer();
1451		s = 0;
1452		for (;;)
1453		{
1454			if (s)
1455				append(buf, s);
1456			if (r = search(state.vars, "mam_cc_PREFIX_ARCHIVE", NiL))
1457				append(buf, r);
1458			append(buf, lib + 2);
1459			if (r = search(state.vars, "mam_cc_SUFFIX_ARCHIVE", NiL))
1460				append(buf, r);
1461			r = expand(tmp, use(buf));
1462			if (!stat(r, &st))
1463				break;
1464			if (s)
1465			{
1466				r = lib;
1467				break;
1468			}
1469			s = "${INSTALLROOT}/lib/";
1470			if (dynamic)
1471			{
1472				append(buf, s);
1473				if (r = search(state.vars, "mam_cc_PREFIX_SHARED", NiL))
1474					append(buf, r);
1475				append(buf, lib + 2);
1476				if (r = search(state.vars, "mam_cc_SUFFIX_SHARED", NiL))
1477					append(buf, r);
1478				r = expand(tmp, use(buf));
1479				if (!stat(r, &st))
1480				{
1481					r = lib;
1482					break;
1483				}
1484			}
1485		}
1486		if (r != lib)
1487			r = duplicate(r);
1488		search(state.vars, lib, r);
1489		append(tmp, lib + 2);
1490		append(tmp, ".req");
1491		if (!(f = fopen(use(tmp), "r")))
1492		{
1493			append(tmp, "${INSTALLROOT}/lib/lib/");
1494			append(tmp, lib + 2);
1495			f = fopen(expand(buf, use(tmp)), "r");
1496		}
1497		if (f)
1498		{
1499			for (;;)
1500			{
1501				while ((c = fgetc(f)) == ' ' || c == '\t' || c == '\n');
1502				if (c == EOF)
1503					break;
1504				do
1505				{
1506					add(tmp, c);
1507				} while ((c = fgetc(f)) != EOF && c != ' ' && c != '\t' && c != '\n');
1508				s = use(tmp);
1509				if (s[0] && (s[0] != '-' || s[1]))
1510				{
1511					add(buf, ' ');
1512					append(buf, require(s, 0));
1513				}
1514			}
1515			fclose(f);
1516			r = use(buf);
1517		}
1518		else if (dontcare)
1519		{
1520			append(tmp, "set -\n");
1521			append(tmp, "cd /tmp\n");
1522			append(tmp, "echo 'int main(){return 0;}' > x.${!-$$}.c\n");
1523			append(tmp, "${CC} ${CCFLAGS} -o x.${!-$$}.x x.${!-$$}.c ");
1524			append(tmp, r);
1525			append(tmp, " >/dev/null 2>&1\n");
1526			append(tmp, "c=$?\n");
1527			append(tmp, "rm -f x.${!-$$}.[cox]\n");
1528			append(tmp, "exit $c\n");
1529			if (execute(expand(buf, use(tmp))))
1530				r = "";
1531		}
1532		r = duplicate(r);
1533		search(state.vars, lib, r);
1534		append(tmp, "mam_lib");
1535		append(tmp, lib + 2);
1536		search(state.vars, use(tmp), r);
1537		drop(tmp);
1538		drop(buf);
1539	}
1540	return r;
1541}
1542
1543/*
1544 * input() until `done r'
1545 */
1546
1547static unsigned long
1548make(Rule_t* r)
1549{
1550	register char*		s;
1551	register char*		t;
1552	register char*		u;
1553	register char*		v;
1554	register Rule_t*	q;
1555	unsigned long		z;
1556	unsigned long		x;
1557	Buf_t*			buf;
1558	Buf_t*			cmd;
1559
1560	r->making++;
1561	if (r->flags & RULE_active)
1562		state.active++;
1563	if (*r->name)
1564	{
1565		z = bind(r);
1566		state.indent++;
1567		report(-1, r->name, "make", r->time);
1568	}
1569	else
1570		z = 0;
1571	buf = buffer();
1572	cmd = 0;
1573	while (s = input())
1574	{
1575		for (; *s == ' '; s++);
1576		for (; isdigit(*s); s++);
1577		for (; *s == ' '; s++);
1578		for (u = s; *s && *s != ' '; s++);
1579		if (*s)
1580		{
1581			for (*s++ = 0; *s == ' '; s++);
1582			for (t = s; *s && *s != ' '; s++);
1583			if (*s)
1584				for (*s++ = 0; *s == ' '; s++);
1585			v = s;
1586		}
1587		else
1588			t = v = s;
1589		switch (KEY(u[0], u[1], u[2], u[3]))
1590		{
1591		case KEY('b','i','n','d'):
1592			if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && (s = require(t, !strcmp(v, "dontcare"))) && strncmp(r->name, "FEATURE/", 8) && strcmp(r->name, "configure.h"))
1593				for (;;)
1594				{
1595					for (t = s; *s && *s != ' '; s++);
1596					if (*s)
1597						*s = 0;
1598					else
1599						s = 0;
1600					if (*t)
1601					{
1602						q = rule(expand(buf, t));
1603						attributes(q, v);
1604						x = bind(q);
1605						if (z < x)
1606							z = x;
1607						if (q->flags & RULE_error)
1608							r->flags |= RULE_error;
1609					}
1610					if (!s)
1611						break;
1612					for (*s++ = ' '; *s == ' '; s++);
1613				}
1614			continue;
1615		case KEY('d','o','n','e'):
1616			q = rule(expand(buf, t));
1617			if (q != r)
1618				report(2, "improper done statement", t, (unsigned long)0);
1619			attributes(r, v);
1620			if (cmd && state.active && (state.force || r->time < z || !r->time && !z))
1621			{
1622				if (state.explain && !state.force)
1623				{
1624					if (!r->time)
1625						fprintf(stderr, "%s [not found]\n", r->name);
1626					else
1627						fprintf(stderr, "%s [%lu] older than prerequisites [%lu]\n", r->name, r->time, z);
1628				}
1629				substitute(buf, use(cmd));
1630				x = run(r, use(buf));
1631				if (z < x)
1632					z = x;
1633			}
1634			r->flags |= RULE_made;
1635			if (!(r->flags & (RULE_dontcare|RULE_error|RULE_exists|RULE_generated|RULE_implicit|RULE_virtual)))
1636				dont(r, 0, state.keepgoing);
1637			break;
1638		case KEY('e','x','e','c'):
1639			r->flags |= RULE_generated;
1640			if (r->path)
1641			{
1642				free(r->path);
1643				r->path = 0;
1644				r->time = 0;
1645			}
1646			if (state.active)
1647			{
1648				if (cmd)
1649					add(cmd, '\n');
1650				else
1651					cmd = buffer();
1652				append(cmd, v);
1653			}
1654			continue;
1655		case KEY('m','a','k','e'):
1656			q = rule(expand(buf, t));
1657			if (!q->making)
1658			{
1659				attributes(q, v);
1660				x = make(q);
1661				if (!(q->flags & RULE_ignore) && z < x)
1662					z = x;
1663				if (q->flags & RULE_error)
1664					r->flags |= RULE_error;
1665			}
1666			continue;
1667		case KEY('p','r','e','v'):
1668			q = rule(expand(buf, t));
1669			if (!q->making)
1670			{
1671				if (!(q->flags & RULE_ignore) && z < q->time)
1672					z = q->time;
1673				if (q->flags & RULE_error)
1674					r->flags |= RULE_error;
1675				state.indent++;
1676				report(-2, q->name, "prev", q->time);
1677				state.indent--;
1678			}
1679			continue;
1680		case KEY('s','e','t','v'):
1681			if (!search(state.vars, t, NiL))
1682			{
1683				if (*v == '"')
1684				{
1685					s = v + strlen(v) - 1;
1686					if (*s == '"')
1687					{
1688						*s = 0;
1689						v++;
1690					}
1691				}
1692				search(state.vars, t, duplicate(expand(buf, v)));
1693			}
1694			if (!state.probed && t[0] == 'C' && t[1] == 'C' && !t[2])
1695			{
1696				state.probed = 1;
1697				probe();
1698			}
1699			continue;
1700		default:
1701			continue;
1702		}
1703		break;
1704	}
1705	drop(buf);
1706	if (cmd)
1707		drop(cmd);
1708	if (*r->name)
1709	{
1710		report(-1, r->name, "done", z);
1711		state.indent--;
1712	}
1713	if (r->flags & RULE_active)
1714		state.active--;
1715	r->making--;
1716	return r->time = z;
1717}
1718
1719/*
1720 * verify that active targets were made
1721 */
1722
1723static int
1724verify(Dict_item_t* item, void* handle)
1725{
1726	Rule_t*	r = (Rule_t*)item->value;
1727
1728	if ((r->flags & (RULE_active|RULE_error|RULE_made)) == RULE_active)
1729		dont(r, 0, 1);
1730	return 0;
1731}
1732
1733/*
1734 * return 1 if name is an initializer
1735 */
1736
1737static int
1738initializer(char* name)
1739{
1740	register char*	s;
1741
1742	if (s = last(name, '/'))
1743		s++;
1744	else
1745		s = name;
1746	return s[0] == 'I' && s[1] == 'N' && s[2] == 'I' && s[3] == 'T';
1747}
1748
1749/*
1750 * update recursion leaf r and its prerequisites
1751 */
1752
1753static int
1754update(register Rule_t* r)
1755{
1756	register List_t*	x;
1757	Buf_t*			buf;
1758
1759	static char		cmd[] = "${MAMAKE} -C ";
1760	static char		arg[] = " ${MAMAKEARGS}";
1761
1762	r->flags |= RULE_made;
1763	if (r->leaf)
1764		r->leaf->flags |= RULE_made;
1765	for (x = r->prereqs; x; x = x->next)
1766		if (x->rule->leaf && !(x->rule->flags & RULE_made))
1767			update(x->rule);
1768	buf = buffer();
1769	substitute(buf, cmd);
1770	append(buf, r->name);
1771	substitute(buf, arg);
1772	run(r, use(buf));
1773	drop(buf);
1774	return 0;
1775}
1776
1777/*
1778 * scan makefile prereqs
1779 */
1780
1781static int
1782scan(Dict_item_t* item, void* handle)
1783{
1784	register Rule_t*	r = (Rule_t*)item->value;
1785	register char*		s;
1786	register char*		t;
1787	register char*		u;
1788	register char*		w;
1789	Rule_t*			q;
1790	int			i;
1791	int			j;
1792	int			k;
1793	int			p;
1794	Buf_t*			buf;
1795
1796	static char*		files[] =
1797				{
1798					"Nmakefile",
1799					"nmakefile",
1800					"Makefile",
1801					"makefile"
1802				};
1803
1804	/*
1805	 * drop non-leaf rules
1806	 */
1807
1808	if (!r->leaf)
1809		return 0;
1810
1811	/*
1812	 * always make initializers
1813	 */
1814
1815	if (initializer(r->name))
1816	{
1817		if (!(r->flags & RULE_made))
1818			update(r);
1819		return 0;
1820	}
1821	buf = buffer();
1822	for (i = 0; i < elementsof(files); i++)
1823	{
1824		append(buf, r->name);
1825		add(buf, '/');
1826		append(buf, files[i]);
1827		if (push(use(buf), (Stdio_t*)0, 0))
1828		{
1829			while (s = input())
1830			{
1831				j = p = 0;
1832				while (*s)
1833				{
1834					for (k = 1; (i = *s) == ' ' || i == '\t' || i == '"' || i == '\''; s++);
1835					for (t = s; (i = *s) && i != ' ' && i != '\t' && i != '"' && i != '\'' && i != '\\' && i != ':'; s++)
1836						if (i == '/')
1837							t = s + 1;
1838						else if (i == '.' && *(s + 1) != 'c' && *(s + 1) != 'C' && *(s + 1) != 'h' && *(s + 1) != 'H' && t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
1839							*s = 0;
1840					if (*s)
1841						*s++ = 0;
1842					if (!t[0])
1843						k = 0;
1844					else if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && t[2])
1845					{
1846						append(buf, "lib");
1847						append(buf, t + 2);
1848						t = use(buf);
1849					}
1850					else if (p)
1851					{
1852						if (t[0] == '+' && !t[1])
1853							p = 2;
1854						else if (p == 1)
1855						{
1856							if (i != ':' || strncmp(s, "command", 7))
1857							{
1858								append(buf, "lib");
1859								append(buf, t);
1860								t = use(buf);
1861							}
1862							if (i == ':')
1863								while (*s && (*s == ' ' || *s == '\t'))
1864									s++;
1865						}
1866					}
1867					else if (i == ':')
1868					{
1869						if (j != ':' || !isupper(*t))
1870							k = 0;
1871						else if (!strcmp(t, "PACKAGE"))
1872						{
1873							p = 1;
1874							k = 0;
1875						}
1876						else
1877							for (u = t; *u; u++)
1878								if (isupper(*u))
1879									*u = tolower(*u);
1880								else if (!isalnum(*u))
1881								{
1882									k = 0;
1883									break;
1884								}
1885					}
1886					else if (t[0] != 'l' || t[1] != 'i' || t[2] != 'b')
1887						k = 0;
1888					else
1889						for (u = t + 3; *u; u++)
1890							if (!isalnum(*u))
1891							{
1892								k = 0;
1893								break;
1894							}
1895					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))
1896					{
1897						for (t = w = r->name; *w; w++)
1898							if (*w == '/')
1899								t = w + 1;
1900						if (t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
1901							t += 3;
1902						for (u = w = q->name; *w; w++)
1903							if (*w == '/')
1904								u = w + 1;
1905						if (strcmp(t, u))
1906							cons(r, q);
1907					}
1908					j = i;
1909				}
1910			}
1911			pop();
1912			for (s = 0, w = r->name; *w; w++)
1913				if (*w == '/')
1914					s = w;
1915			if (s)
1916			{
1917				if ((s - r->name) > 3 && *(s - 1) == 'b' && *(s - 2) == 'i' && *(s - 3) == 'l' && *(s - 4) != '/')
1918				{
1919					/*
1920					 * foolib : foo : libfoo
1921					 */
1922
1923					*(s - 3) = 0;
1924					q = (Rule_t*)search(state.leaf, r->name, NiL);
1925					if (q && q != r)
1926						cons(r, q);
1927					for (t = w = r->name; *w; w++)
1928						if (*w == '/')
1929							t = w + 1;
1930					append(buf, "lib");
1931					append(buf, t);
1932					q = (Rule_t*)search(state.leaf, use(buf), NiL);
1933					if (q && q != r)
1934						cons(r, q);
1935					*(s - 3) = 'l';
1936				}
1937				else if (((s - r->name) != 3 || *(s - 1) != 'b' || *(s - 2) != 'i' || *(s - 3) != 'l') && (*(s + 1) != 'l' || *(s + 2) != 'i' || *(s + 3) != 'b'))
1938				{
1939					/*
1940					 * huh/foobar : lib/libfoo
1941					 */
1942
1943					s++;
1944					t = s + strlen(s);
1945					while (--t > s)
1946					{
1947						append(buf, "lib/lib");
1948						appendn(buf, s, t - s);
1949						q = (Rule_t*)search(state.leaf, use(buf), NiL);
1950						if (q && q != r)
1951							cons(r, q);
1952					}
1953				}
1954			}
1955			break;
1956		}
1957	}
1958	drop(buf);
1959	return 0;
1960}
1961
1962/*
1963 * descend into op and its prereqs
1964 */
1965
1966static int
1967descend(Dict_item_t* item, void* handle)
1968{
1969	Rule_t*	r = (Rule_t*)item->value;
1970
1971	if (!state.active && (!(r->flags & RULE_active) || !(r = (Rule_t*)search(state.leaf, r->name, NiL))))
1972		return 0;
1973	return r->leaf && !(r->flags & RULE_made) ? update(r) : 0;
1974}
1975
1976/*
1977 * append the non-leaf active targets to state.opt
1978 */
1979
1980static int
1981active(Dict_item_t* item, void* handle)
1982{
1983	Rule_t*	r = (Rule_t*)item->value;
1984
1985	if (r->flags & RULE_active)
1986	{
1987		if (r->leaf || search(state.leaf, r->name, NiL))
1988			state.active = 0;
1989		else
1990		{
1991			add(state.opt, ' ');
1992			append(state.opt, r->name);
1993		}
1994	}
1995	return 0;
1996}
1997
1998/*
1999 * recurse on mamfiles in subdirs matching pattern
2000 */
2001
2002static int
2003recurse(char* pattern)
2004{
2005	register char*	s;
2006	register char*	t;
2007	Rule_t*		r;
2008	Buf_t*		buf;
2009	Buf_t*		tmp;
2010	struct stat	st;
2011
2012	/*
2013	 * first determine the MAM subdirs
2014	 */
2015
2016	tmp = buffer();
2017	buf = buffer();
2018	state.exec = !state.never;
2019	state.leaf = dictionary();
2020	append(buf, "ls -d ");
2021	append(buf, pattern);
2022	s = use(buf);
2023	push("recurse", popen(s, "r"), STREAM_PIPE);
2024	while (s = input())
2025	{
2026		append(buf, s);
2027		add(buf, '/');
2028		append(buf, mamfile);
2029		if (find(tmp, use(buf), &st))
2030		{
2031			r = rule(s);
2032			if (t = last(r->name, '/'))
2033				t++;
2034			else
2035				t = r->name;
2036			r->leaf = rule(t);
2037			search(state.leaf, t, r);
2038		}
2039	}
2040	pop();
2041	drop(buf);
2042	drop(tmp);
2043
2044	/*
2045	 * grab the non-leaf active targets
2046	 */
2047
2048	if (!state.active)
2049	{
2050		state.active = 1;
2051		walk(state.rules, active, NiL);
2052	}
2053	search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1));
2054
2055	/*
2056	 * scan the makefile and descend
2057	 */
2058
2059	walk(state.rules, scan, NiL);
2060	state.view = 0;
2061	walk(state.rules, descend, NiL);
2062	return 0;
2063}
2064
2065int
2066main(int argc, char** argv)
2067{
2068	register char**		e;
2069	register char*		s;
2070	register char*		t;
2071	register char*		v;
2072	Buf_t*			tmp;
2073	int			c;
2074
2075	/*
2076	 * initialize the state
2077	 */
2078
2079	state.id = "mamake";
2080	state.active = 1;
2081	state.exec = 1;
2082	state.file = mamfile;
2083	state.opt = buffer();
2084	state.rules = dictionary();
2085	state.vars = dictionary();
2086	search(state.vars, "MAMAKE", *argv);
2087
2088	/*
2089	 * parse the options
2090	 */
2091
2092#if _PACKAGE_ast
2093	error_info.id = state.id;
2094	for (;;)
2095	{
2096		switch (optget(argv, usage))
2097		{
2098		case 'e':
2099			append(state.opt, " -e");
2100			state.explain = 1;
2101			continue;
2102		case 'i':
2103			append(state.opt, " -i");
2104			state.ignore = 1;
2105			continue;
2106		case 'k':
2107			append(state.opt, " -k");
2108			state.keepgoing = 1;
2109			continue;
2110		case 'N':
2111			state.never = 1;
2112			/*FALLTHROUGH*/
2113		case 'n':
2114			append(state.opt, " -n");
2115			state.exec = 0;
2116			continue;
2117		case 'F':
2118			append(state.opt, " -F");
2119			state.force = 1;
2120			continue;
2121		case 'K':
2122			continue;
2123		case 'V':
2124			fprintf(stdout, "%s\n", id + 10);
2125			exit(0);
2126		case 'f':
2127			append(state.opt, " -f ");
2128			append(state.opt, opt_info.arg);
2129			state.file = opt_info.arg;
2130			continue;
2131		case 'r':
2132			state.recurse = opt_info.arg;
2133			continue;
2134		case 'C':
2135			state.directory = opt_info.arg;
2136			continue;
2137		case 'D':
2138			append(state.opt, " -D");
2139			append(state.opt, opt_info.arg);
2140			state.debug = -opt_info.num;
2141			continue;
2142		case 'G':
2143			append(state.opt, " -G");
2144			search(state.vars, "-debug-symbols", "1");
2145			continue;
2146		case 'S':
2147			append(state.opt, " -S");
2148			search(state.vars, "-strip-symbols", "1");
2149			continue;
2150		case '?':
2151			error(ERROR_USAGE|4, "%s", opt_info.arg);
2152			continue;
2153		case ':':
2154			error(2, "%s", opt_info.arg);
2155			continue;
2156		}
2157		break;
2158	}
2159	if (error_info.errors)
2160		error(ERROR_USAGE|4, "%s", optusage(NiL));
2161	argv += opt_info.index;
2162#else
2163	while ((s = *++argv) && *s == '-')
2164	{
2165		if (*(s + 1) == '-')
2166		{
2167			if (!*(s + 2))
2168			{
2169				append(state.opt, " --");
2170				argv++;
2171				break;
2172			}
2173			for (t = s += 2; *t && *t != '='; t++);
2174			if (!strncmp(s, "debug-symbols", t - s) && append(state.opt, " -G") || !strncmp(s, "strip-symbols", t - s) && append(state.opt, " -S"))
2175			{
2176				if (*t)
2177				{
2178					v = t + 1;
2179					if (t > s && *(t - 1) == '+')
2180						t--;
2181					c = *t;
2182					*t = 0;
2183				}
2184				else
2185				{
2186					c = 0;
2187					v = "1";
2188				}
2189				search(state.vars, s - 1, v);
2190				if (c)
2191					*t = c;
2192				continue;
2193			}
2194			usage();
2195			break;
2196		}
2197		for (;;)
2198		{
2199			switch (*++s)
2200			{
2201			case 0:
2202				break;
2203			case 'e':
2204				append(state.opt, " -e");
2205				state.explain = 1;
2206				continue;
2207			case 'i':
2208				append(state.opt, " -i");
2209				state.ignore = 1;
2210				continue;
2211			case 'k':
2212				append(state.opt, " -k");
2213				state.keepgoing = 1;
2214				continue;
2215			case 'N':
2216				state.never = 1;
2217				/*FALLTHROUGH*/
2218			case 'n':
2219				append(state.opt, " -n");
2220				state.exec = 0;
2221				continue;
2222			case 'F':
2223				append(state.opt, " -F");
2224				state.force = 1;
2225				continue;
2226			case 'G':
2227				append(state.opt, " -G");
2228				search(state.vars, "-debug-symbols", "1");
2229				continue;
2230			case 'K':
2231				continue;
2232			case 'S':
2233				append(state.opt, " -S");
2234				search(state.vars, "-strip-symbols", "1");
2235				continue;
2236			case 'V':
2237				fprintf(stdout, "%s\n", id + 10);
2238				exit(0);
2239			case 'f':
2240			case 'r':
2241			case 'C':
2242			case 'D':
2243				t = s;
2244				if (!*++s && !(s = *++argv))
2245				{
2246					report(2, "option value expected", t, (unsigned long)0);
2247					usage();
2248				}
2249				else
2250					switch (*t)
2251					{
2252					case 'f':
2253						append(state.opt, " -f ");
2254						append(state.opt, s);
2255						state.file = s;
2256						break;
2257					case 'r':
2258						state.recurse = s;
2259						break;
2260					case 'C':
2261						state.directory = s;
2262						break;
2263					case 'D':
2264						append(state.opt, " -D");
2265						append(state.opt, s);
2266						state.debug = -atoi(s);
2267						break;
2268					}
2269				break;
2270			default:
2271				report(2, "unknown option", s, (unsigned long)0);
2272			case '?':
2273				usage();
2274				break;
2275			}
2276			break;
2277		}
2278	}
2279#endif
2280
2281	/*
2282	 * load the environment
2283	 */
2284
2285	for (e = environ; s = *e; e++)
2286		for (t = s; *t; t++)
2287			if (*t == '=')
2288			{
2289				*t = 0;
2290				search(state.vars, s, t + 1);
2291				*t = '=';
2292				break;
2293			}
2294
2295	/*
2296	 * grab the command line targets and variable definitions
2297	 */
2298
2299	while (s = *argv++)
2300	{
2301		for (t = s; *t; t++)
2302			if (*t == '=')
2303			{
2304				v = t + 1;
2305				if (t > s && *(t - 1) == '+')
2306					t--;
2307				c = *t;
2308				*t = 0;
2309				search(state.vars, s, v);
2310				tmp = buffer();
2311				append(tmp, s);
2312				append(tmp, ".FORCE");
2313				search(state.vars, use(tmp), v);
2314				drop(tmp);
2315				*t = c;
2316				break;
2317			}
2318		if (!*t)
2319		{
2320			/*
2321			 * handle a few targets for nmake compatibility
2322			 */
2323
2324			if (*s == 'e' && !strncmp(s, "error 0 $(MAKEVERSION:", 22))
2325				exit(1);
2326			if (*s == 'r' && !strcmp(s, "recurse") || *s == 'c' && !strncmp(s, "cc-", 3))
2327				continue;
2328			rule(s)->flags |= RULE_active;
2329			state.active = 0;
2330			if (state.recurse)
2331				continue;
2332		}
2333		add(state.opt, ' ');
2334		add(state.opt, '\'');
2335		append(state.opt, s);
2336		add(state.opt, '\'');
2337	}
2338
2339	/*
2340	 * initialize the views
2341	 */
2342
2343	if (state.directory && chdir(state.directory))
2344		report(3, "cannot change working directory", NiL, (unsigned long)0);
2345	view();
2346
2347	/*
2348	 * recursion drops out here
2349	 */
2350
2351	if (state.recurse)
2352		return recurse(state.recurse);
2353
2354	/*
2355	 * read the mamfile(s) and bring the targets up to date
2356	 */
2357
2358	search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1));
2359	push(state.file, (Stdio_t*)0, STREAM_MUST);
2360	make(rule(""));
2361	pop();
2362
2363	/*
2364	 * verify that active targets were made
2365	 */
2366
2367	if (!state.active && !state.verified)
2368		walk(state.rules, verify, NiL);
2369
2370	/*
2371	 * done
2372	 */
2373
2374	return state.errors != 0;
2375}
2376