xref: /illumos-gate/usr/src/contrib/ast/src/cmd/INIT/mamake.c (revision 8226594fdd4479be135127f43632f1f995074654)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1990-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 
28 static 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 
35 static const char usage[] =
36 "[-?\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\n]"
37 USAGE_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 
150 struct Rule_s;
151 
152 typedef struct stat Stat_t;
153 typedef FILE Stdio_t;
154 
155 typedef 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 
163 typedef 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 
171 typedef struct Dict_s			/* dictionary handle		*/
172 {
173 	Dict_item_t*	root;		/* root item			*/
174 } Dict_t;
175 
176 typedef 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 
182 typedef 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 
193 typedef 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 
201 typedef 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 
208 static 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 
248 static unsigned long	make(Rule_t*);
249 
250 static char		mamfile[] = "Mamfile";
251 static char		sh[] = "/bin/sh";
252 
253 extern 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 
265 static void
266 usage()
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 
278 static void
279 identify(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 
297 static void
298 report(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 
340 static void
341 dont(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 
363 static char*
364 last(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 
378 static Buf_t*
379 buffer(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 
396 static void
397 drop(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 
407 static char*
408 appendn(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 
432 static char*
433 append(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 
445 static char*
446 duplicate(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 
462 static Dict_t*
463 dictionary(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 
478 static void*
479 search(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 
571 static int
572 apply(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 
591 static int
592 walk(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 
601 static Rule_t*
602 rule(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 
619 static void
620 cons(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 
639 static void
640 view(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 
734 static char*
735 cond(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 
771 static void
772 substitute(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 
907 static char*
908 expand(Buf_t* buf, char* s)
909 {
910 	substitute(buf, s);
911 	return use(buf);
912 }
913 
914 /*
915  * stat() with .exe check
916  */
917 
918 static char*
919 status(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 
957 static char*
958 find(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 
1020 static unsigned long
1021 bind(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 
1043 static int
1044 pop(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 
1067 static int
1068 push(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 
1116 static char*
1117 input(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 
1139 static int
1140 execute(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 
1176 static unsigned long
1177 run(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 
1287 static char*
1288 path(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 
1336 static void
1337 probe(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 
1392 static void
1393 attributes(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 
1432 static char*
1433 require(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 
1547 static unsigned long
1548 make(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 
1723 static int
1724 verify(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 
1737 static int
1738 initializer(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 
1753 static int
1754 update(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 
1781 static int
1782 scan(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 
1966 static int
1967 descend(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 
1980 static int
1981 active(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 
2002 static int
2003 recurse(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 
2065 int
2066 main(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