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
usage()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
identify(Stdio_t * sp)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
report(int level,char * text,char * item,unsigned long stamp)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
dont(Rule_t * r,int code,int keepgoing)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*
last(register char * s,register int c)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*
buffer(void)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
drop(Buf_t * buf)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*
appendn(Buf_t * buf,char * str,int n)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*
append(Buf_t * buf,char * str)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*
duplicate(char * s)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*
dictionary(void)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*
search(register Dict_t * dict,char * name,void * value)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
apply(Dict_t * dict,Dict_item_t * item,int (* func)(Dict_item_t *,void *),void * handle)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
walk(Dict_t * dict,int (* func)(Dict_item_t *,void *),void * handle)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*
rule(char * name)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
cons(Rule_t * r,Rule_t * p)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
view(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*
cond(register char * s)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
substitute(Buf_t * buf,register char * s)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*
expand(Buf_t * buf,char * s)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*
status(Buf_t * buf,int off,char * path,struct stat * st)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*
find(Buf_t * buf,char * file,struct stat * st)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
bind(Rule_t * r)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
pop(void)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
push(char * file,Stdio_t * fp,int flags)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*
input(void)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
execute(register char * s)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
run(Rule_t * r,register char * s)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*
path(Buf_t * buf,char * s,int must)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
probe(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
attributes(register Rule_t * r,register char * s)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*
require(char * lib,int dontcare)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
make(Rule_t * r)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
verify(Dict_item_t * item,void * handle)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
initializer(char * name)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
update(register Rule_t * r)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
scan(Dict_item_t * item,void * handle)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
descend(Dict_item_t * item,void * handle)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
active(Dict_item_t * item,void * handle)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
recurse(char * pattern)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
main(int argc,char ** argv)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