1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1984-2013 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 <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * make variable expansion routines
26 */
27
28 #include "make.h"
29 #include "expand.h"
30
31 #include <magic.h>
32 #include <regex.h>
33
34 #define BREAKARGS 100
35 #define BREAKLINE (BREAKARGS*16)
36 #define EDITCONTEXT 20
37
38 #define SORT_posix 0x0000
39 #define SORT_collate 0x0002
40 #define SORT_numeric 0x0004
41 #define SORT_prefix 0x0006
42 #define SORT_version 0x0008
43
44 #define SORT_invert 0x0001
45 #define SORT_MASK 0x000f
46
47 #define SORT_first ((SORT_MASK+1)<<0)
48 #define SORT_force ((SORT_MASK+1)<<1)
49 #define SORT_qualified ((SORT_MASK+1)<<2)
50 #define SORT_reverse ((SORT_MASK+1)<<3)
51 #define SORT_sort ((SORT_MASK+1)<<4)
52 #define SORT_uniq ((SORT_MASK+1)<<5)
53
54 static const regflags_t submap[] =
55 {
56 'g', REG_SUB_ALL,
57 'l', REG_SUB_LOWER,
58 'u', REG_SUB_UPPER,
59 'G', REG_SUB_ALL,
60 'L', REG_SUB_LOWER,
61 'U', REG_SUB_UPPER,
62 0, 0
63 };
64
65 /*
66 * inverted strcmp(3)
67 */
68
69 static int
istrcmp(const char * a,const char * b)70 istrcmp(const char* a, const char* b)
71 {
72 return strcmp(b, a);
73 }
74
75 /*
76 * inverted strcoll(3)
77 */
78
79 static int
istrcoll(const char * a,const char * b)80 istrcoll(const char* a, const char* b)
81 {
82 return strcoll(b, a);
83 }
84
85 /*
86 * numeric strcmp(3)
87 */
88
89 static int
numcmp(const char * a,const char * b)90 numcmp(const char* a, const char* b)
91 {
92 char* ea;
93 char* eb;
94 long na = strton(a, NiL, NiL, 0);
95 long nb = strton(b, NiL, NiL, 0);
96
97 na = strton(a, &ea, NiL, 0);
98 nb = strton(b, &eb, NiL, 0);
99 if (na < nb)
100 return -1;
101 if (na > nb)
102 return 1;
103 if (*ea || *eb)
104 return strcmp(ea, eb);
105 return 0;
106 }
107
108 /*
109 * inverted numcmp(3)
110 */
111
112 static int
inumcmp(const char * a,const char * b)113 inumcmp(const char* a, const char* b)
114 {
115 return numcmp(b, a);
116 }
117
118 /*
119 * inverted strpcmp(3)
120 */
121
122 static int
istrpcmp(const char * a,const char * b)123 istrpcmp(const char* a, const char* b)
124 {
125 return strpcmp(b, a);
126 }
127
128 /*
129 * inverted strvcmp(3)
130 */
131
132 static int
istrvcmp(const char * a,const char * b)133 istrvcmp(const char* a, const char* b)
134 {
135 return strvcmp(b, a);
136 }
137
138 static Strcmp_f sort_cmp[] =
139 {
140 strcmp,
141 istrcmp,
142 /* strcoll is an ast macro */ 0,
143 istrcoll,
144 numcmp,
145 inumcmp,
146 strpcmp,
147 istrpcmp,
148 istrvcmp,
149 strvcmp,
150 };
151
152 /*
153 * return sort comparison function for SORT_* flags
154 */
155
156 static Strcmp_f
sortcmpf(int flags)157 sortcmpf(int flags)
158 {
159 Strcmp_f f;
160
161 if ((flags &= SORT_MASK) >= elementsof(sort_cmp))
162 flags &= SORT_invert;
163 if (!(f = sort_cmp[flags & SORT_MASK]))
164 f = strcoll;
165 return f;
166 }
167
168 /*
169 * `$(...)' expansion
170 *
171 * each <var> is expanded before the value is determined
172 * each <op> is applied to blank separated tokens in the variable value
173 * $$(...) expands to $(...) -- one $ gobbled each time
174 *
175 * general syntax:
176 *
177 * $(<var>[|<var>][:[<pfx>]<op>[<sep><val>]])
178 *
179 * top level alternate syntax:
180 *
181 * $(<var>[|<var>]`[<del>[<pfx>]<op>[<sep><val>]]<del>)
182 *
183 * variable name syntax:
184 *
185 * $(<var>) value of <var>
186 * $(<var1>|<var2>) value of <var1> if non-null else value of <var2>
187 * $("<string>") <string> is used as the variable value
188 *
189 * edit operator syntax:
190 *
191 * :<op><sep><value>:
192 * :/<old>/<new>/<glb>:
193 * :C<del><old><del><new><del><glb>:
194 * :?<if-non-null>?<if-null>?:
195 * :Y<del><if-non-null><del><if-null><del>:
196 *
197 *
198 * edit operator forms:
199 *
200 * $(s) simple expansion (multiple character name)
201 * $(s:@<op>...) don't tokenize list elements for <op>
202 * $(s:A=a[|b]) list of rules with attribute a [or b ...]
203 * $(s:F=<fmt>) format components according to <fmt>
204 * $(s:G=<pat>) select components that build (generate) <pat> files
205 * $(s:H[>]) sort [hi to lo]
206 * $(s:I=<lst>) directory intersection of s with list <lst>
207 * $(s:K=<pfx>) break into `<pfx> s ...' ~ BREAK(ARGS|LINE) line chunks
208 * $(s:L[=<pat>) list all (viewed) files matching <pat>
209 * $(s:M[!]=<pat>) select components [not] matching RE pattern <pat>
210 * $(s:N[!]=<pat>) select components [not] matching shell pattern <pat>
211 * $(s:O<sep><n>) select components <|<=|==|!=|>=|> <n> starting at 1
212 * $(s:P=<op>) path name operations (see below)
213 * $(s:Q) quote shell chars in value
214 * $(s:R) read value as makefile
215 * $(s:T=<op>[r]) select components matching rule token <op> (see below)
216 * $(s:V) do not expand value (prefix)
217 * $(s:W?[=<arg>]) ops on entire value
218 * $(s:X=<lst>) directory cross product of components of s and <lst>
219 * $(s:Z) expand in parent reference frame (prefix cumulative)
220 * $(s:f) include or modify file name components (see below)
221 *
222 * token op components:
223 *
224 * A archive (returns archive symbol table update command)
225 * D definition of state variable: -Dx[=y]
226 * E alternate definition of state variable: x=[y]
227 * F file
228 * G built (generated) file
229 * I[-] value is [non]expanded contents of bound file
230 * Q select defined atoms
231 * QV select defined variables
232 * P physically a file (not a symbolic link ...)
233 * R relative time since epoch
234 * Sc return staterule name given non-state rule
235 * U return variable or non-state name given state rule
236 * W component prefix to not wait for bind
237 * X component prefix to skip binding
238 * * any
239 *
240 * token op forms:
241 *
242 * $(s:T=t?ret) ret if true else null
243 *
244 * format forms:
245 *
246 * L convert to lower case
247 * U convert to upper case
248 * %n.nc printf(3) format
249 *
250 * path name operations:
251 *
252 * A absolute (rooted) path name
253 * B physical binding
254 * C canonicalize path name
255 * D generate directory where s was bound
256 * I=n files identical to n
257 * L[=n] return s if bound in view level 0 [n]
258 * P=l probe info file for language l processor (in token)
259 * R[=d] relative dir path to d [pwd]
260 * S atoms bound in subdirectory of view
261 * U unbound name
262 * V generate view directory path where s was bound
263 * X return s if it is an existing file
264 *
265 * file name components (any or all may be null):
266 *
267 * D directory up to and including the last '/'
268 * B base after D up to but not including the last '.'
269 * S suffix after B
270 *
271 * a component (DBS) is deleted if the corresponding char is omitted
272 * a component (DBS) is retained if the corresponding char is specified
273 * a component (DBS) is changed if the corresponding char is followed
274 * by '=' and a replacement value
275 *
276 * if '=' is specified then a ':' separates the value from the next
277 * file name component specification
278 */
279
280 /*
281 * return edit map for *p delimited by space or del
282 * 0 for no match otherwise *p points to del or op arg
283 */
284
285 static Edit_map_t*
getedit(char ** p,int del)286 getedit(char** p, int del)
287 {
288 register int v;
289 register unsigned char* s;
290 register unsigned char* t;
291 register Edit_map_t* mid = (Edit_map_t*)editmap;
292 register Edit_map_t* lo = mid;
293 register Edit_map_t* hi = mid + elementsof(editmap) - 1;
294
295 while (lo <= hi)
296 {
297 mid = lo + (hi - lo) / 2;
298 s = (unsigned char*)*p;
299 t = (unsigned char*)mid->name;
300 for (;;)
301 {
302 if (!*t && (*s == del || isspace(*s) || !*s))
303 {
304 while (isspace(*s)) s++;
305 *p = (char*)s;
306 return mid;
307 }
308 if ((v = *s++ - *t++) > 0)
309 {
310 lo = mid + 1;
311 break;
312 }
313 else if (v < 0)
314 {
315 hi = mid - 1;
316 break;
317 }
318 }
319 }
320 return 0;
321 }
322
323 /*
324 * expand one instance of v not in w into xp
325 * (sep & NOT) for file equality
326 */
327
328 static void
uniq(Sfio_t * xp,char * v,char * w,int sep)329 uniq(Sfio_t* xp, char* v, char* w, int sep)
330 {
331 register char* s;
332 char* tok;
333 Hash_table_t* tab;
334 Fileid_t id;
335 Stat_t st;
336
337 tok = tokopen(w, 1);
338 if (sep & NOT)
339 {
340 tab = hashalloc(table.dir, 0);
341 while (s = tokread(tok))
342 if (!stat(s, &st))
343 {
344 id.dev = st.st_dev;
345 id.ino = st.st_ino;
346 hashput(tab, (char*)&id, (char*)tab);
347 }
348 tokclose(tok);
349 sep = 0;
350 tok = tokopen(v, 1);
351 while (s = tokread(tok))
352 if (!stat(s, &st))
353 {
354 id.dev = st.st_dev;
355 id.ino = st.st_ino;
356 if (!hashget(tab, (char*)&id))
357 {
358 hashput(tab, (char*)0, (char*)tab);
359 if (sep)
360 sfputc(xp, ' ');
361 else
362 sep = 1;
363 sfputr(xp, s, -1);
364 }
365 }
366 }
367 else
368 {
369 tab = hashalloc(table.rule, 0);
370 while (s = tokread(tok))
371 hashput(tab, s, (char*)tab);
372 tokclose(tok);
373 sep = 0;
374 tok = tokopen(v, 1);
375 while (s = tokread(tok))
376 if (!hashget(tab, s))
377 {
378 hashput(tab, (char*)0, (char*)tab);
379 if (sep)
380 sfputc(xp, ' ');
381 else
382 sep = 1;
383 sfputr(xp, s, -1);
384 }
385 }
386 hashfree(tab);
387 tokclose(tok);
388 }
389
390 /*
391 * mark r and recursively all prerequisites with m
392 * prereqs already marked with m are also marked with c
393 * if c!=0 then c marked prereqs listed in xp
394 * otherwise m marked prereqs listed in xp
395 * xp==0 removes marks
396 */
397
398 static int
mark(Sfio_t * xp,Rule_t * r,int m,int c)399 mark(Sfio_t* xp, Rule_t* r, int m, int c)
400 {
401 register List_t* p;
402
403 if (xp)
404 {
405 if (r->mark & c)
406 {
407 sfputr(xp, r->name, ' ');
408 return 1;
409 }
410 if (!(r->mark & m))
411 {
412 r->mark |= m|c;
413 for (p = r->prereqs; p; p = p->next)
414 if (mark(xp, p->rule, m, c))
415 {
416 sfputr(xp, r->name, ' ');
417 return 1;
418 }
419 if (c)
420 r->mark &= ~c;
421 else
422 sfputr(xp, r->name, ' ');
423 }
424 }
425 else if (r->mark & m)
426 {
427 r->mark &= ~(m|c);
428 for (p = r->prereqs; p; p = p->next)
429 mark(xp, p->rule, m, c);
430 }
431 return 0;
432 }
433
434 /*
435 * expand closure of v into xp
436 */
437
438 static void
closure(Sfio_t * xp,char * v,char * w)439 closure(Sfio_t* xp, char* v, char* w)
440 {
441 register char* s;
442 char* tok;
443 long pos;
444 int cycle;
445
446 cycle = (w && (*w == 'C' || *w == 'c')) ? M_scan : 0;
447 pos = sfstrtell(xp);
448 tok = tokopen(v, 1);
449 while (s = tokread(tok))
450 mark(xp, makerule(s), M_mark, cycle);
451 tokclose(tok);
452 tok = tokopen(v, 1);
453 while (s = tokread(tok))
454 mark(NiL, makerule(s), M_mark, cycle);
455 tokclose(tok);
456 if (sfstrtell(xp) > pos)
457 sfstrseek(xp, -1, SEEK_CUR);
458 }
459
460 /*
461 * expand the directory cross product of v and w into xp
462 *
463 * memorize your multiplication table:
464 *
465 * . unit multiplication operand
466 * A absolute path rooted at /
467 * R path relative to .
468 *
469 * lhs rhs cross-product
470 * ---- ----- -------------
471 * . . . *note (1)*
472 * . A A *note (2)*
473 * . R R
474 * A . A
475 * A R A/R
476 * A A A *note (2)*
477 * R . R
478 * R A A *note (2)*
479 * R R R/R
480 *
481 * (1) the first . lhs operand produces a . in the product
482 *
483 * (2) the first A rhs operand is placed in the product
484 */
485
486 static void
cross(Sfio_t * xp,char * v,char * w)487 cross(Sfio_t* xp, char* v, char* w)
488 {
489 register char* s;
490 register char* t;
491 char* x;
492 int dot;
493 int sep;
494 long pos;
495 char* tok0;
496 char* tok1;
497 Sfio_t* tmp;
498
499 tmp = sfstropen();
500 expand(tmp, w);
501 w = sfstruse(tmp);
502 sep = 0;
503 tok0 = tokopen(w, 0);
504 while (t = tokread(tok0))
505 {
506 if (*t == '/')
507 {
508 if (sep) sfputc(xp, ' ');
509 else sep = 1;
510 pos = sfstrtell(xp);
511 sfputr(xp, t, 0);
512 x = sfstrseek(xp, pos, SEEK_SET);
513 pos += canon(x) - x;
514 sfstrseek(xp, pos, SEEK_SET);
515 }
516 else
517 {
518 dot = (*t == '.' && !*(t + 1));
519 tok1 = tokopen(v, 1);
520 while (s = tokread(tok1))
521 {
522 if (sep) sfputc(xp, ' ');
523 else sep = 1;
524 pos = sfstrtell(xp);
525 x = t;
526 if (dot)
527 x = s;
528 else if (s[strlen(s) - 1] == '/')
529 sfprintf(xp, "%s", s);
530 else if (*s != '.' || *(s + 1))
531 sfprintf(xp, "%s/", s);
532 sfputr(xp, x, 0);
533 x = sfstrseek(xp, pos, SEEK_SET);
534 pos += canon(x) - x;
535 sfstrseek(xp, pos, SEEK_SET);
536 }
537 tokclose(tok1);
538 }
539 }
540 tokclose(tok0);
541 sfstrclose(tmp);
542 }
543
544 /*
545 * expand the pathname intersection of v with w into xp
546 * sep!=EQ for literal intersection
547 */
548
549 static void
intersect(Sfio_t * xp,char * v,char * w,int sep)550 intersect(Sfio_t* xp, char* v, char* w, int sep)
551 {
552 register List_t* p;
553 register Rule_t* r;
554 register char* s;
555 register int n;
556 List_t* q;
557 List_t* x;
558 char* tok;
559 Sfio_t* tmp;
560
561 tmp = sfstropen();
562 p = q = 0;
563 tok = tokopen(v, 1);
564 while (s = tokread(tok))
565 {
566 canon(s);
567 r = makerule(s);
568 if (p) p = p->next = cons(r, NiL);
569 else p = q = cons(r, NiL);
570 s = r->name;
571 if (sep == EQ)
572 {
573 if ((r->dynamic & D_alias) && (!state.context || !iscontext(r->uname)) && (r = makerule(s)))
574 p = p->next = cons(r, NiL);
575 if (state.expandview && state.fsview)
576 {
577 if (s[0] == '.' && !s[1]) sfputr(tmp, "...", -1);
578 else sfprintf(tmp, "%s/...", s);
579 s = sfstruse(tmp);
580 while (!mount(s, s, FS3D_GET|FS3D_VIEW|FS3D_SIZE(MAXNAME), NiL))
581 {
582 r = makerule(s);
583 p = p->next = cons(r, NiL);
584 strcpy(s + strlen(s), "/...");
585 }
586 }
587 }
588 }
589 tokclose(tok);
590 expand(tmp, w);
591 for (x = q; x; x = x->next)
592 x->rule->mark |= M_mark|M_metarule;
593 tok = tokopen(sfstruse(tmp), 0);
594 while (s = tokread(tok))
595 {
596 canon(s);
597 if (r = getrule(s))
598 {
599 r->mark &= ~M_mark;
600 if (sep == EQ)
601 {
602 if (!(r->mark & M_metarule) && r->name[0] == '.' && r->name[1] == '.' && (!r->name[2] || r->name[2] == '/'))
603 {
604 r->mark |= M_metarule;
605 if (p) p = p->next = cons(r, NiL);
606 else p = q = cons(r, NiL);
607 internal.dot->mark &= ~M_mark;
608 }
609 if ((r->dynamic & D_alias) && (r = makerule(r->name)))
610 {
611 r->mark &= ~M_mark;
612 if (!(r->mark & M_metarule) && r->name[0] == '.' && r->name[1] == '.' && (!r->name[2] || r->name[2] == '/'))
613 {
614 r->mark |= M_metarule;
615 p = p->next = cons(r, NiL);
616 internal.dot->mark &= ~M_mark;
617 }
618 }
619 }
620 }
621 }
622 n = 0;
623 for (p = q; p; p = p->next)
624 if (!(p->rule->mark & M_mark))
625 {
626 if (n) sfputc(xp, ' ');
627 else n = 1;
628 sfputr(xp, state.localview ? localview(p->rule) : p->rule->name, -1);
629 p->rule->mark |= M_mark;
630 }
631 tokclose(tok);
632 sfstrclose(tmp);
633 for (p = q; p; p = p->next)
634 p->rule->mark &= ~(M_mark|M_metarule);
635 freelist(q);
636 }
637
638
639 /*
640 * expand the rules in v that have any of the prereqs in w
641 */
642
643 static void
hasprereq(Sfio_t * xp,char * v,char * w)644 hasprereq(Sfio_t* xp, char* v, char* w)
645 {
646 register List_t* p;
647 register List_t* q;
648 register Rule_t* r;
649 register char* s;
650 int sep;
651 List_t* x;
652 char* tok;
653
654 x = 0;
655 tok = tokopen(w, 1);
656 while (s = tokread(tok))
657 x = cons(makerule(s), x);
658 tokclose(tok);
659 sep = 0;
660 tok = tokopen(v, 1);
661 while (s = tokread(tok))
662 {
663 if (r = getrule(s))
664 for (p = r->prereqs; p; p = p->next)
665 for (q = x; q; q = q->next)
666 if (p->rule == q->rule)
667 {
668 if (sep) sfputc(xp, ' ');
669 else sep = 1;
670 sfputr(xp, r->name, -1);
671 goto next;
672 }
673 next:;
674 }
675 tokclose(tok);
676 freelist(x);
677 }
678
679 /*
680 * break into ~ BREAK(ARGS|LINE) `<pfx> s ...' line chunks
681 */
682
683 static void
linebreak(Sfio_t * xp,register char * s,char * pfx)684 linebreak(Sfio_t* xp, register char* s, char* pfx)
685 {
686 register int a;
687 char* tok;
688 long rew;
689 long pre;
690 long pos;
691 long brk;
692
693 rew = sfstrtell(xp);
694 sfputr(xp, pfx, -1);
695 pre = pos = sfstrtell(xp);
696 brk = pos + BREAKLINE;
697 a = state.mam.out ? 30 : BREAKARGS;
698 tok = tokopen(s, 1);
699 while (s = tokread(tok))
700 {
701 if (pos >= brk || !a--)
702 {
703 pos += sfprintf(xp, "\n%s", pfx);
704 brk = pos + BREAKLINE;
705 a = BREAKARGS;
706 }
707 pos += sfprintf(xp, " %s", s);
708 }
709 tokclose(tok);
710 if (pos == pre) sfstrseek(xp, rew, SEEK_SET);
711 }
712
713 /*
714 * generate list of hash table names matching pat
715 */
716
717 static void
listtab(Sfio_t * xp,Hash_table_t * tab,char * pat,int flags)718 listtab(Sfio_t* xp, Hash_table_t* tab, char* pat, int flags)
719 {
720 register char** v;
721 Hash_position_t* pos;
722 int n;
723 Sfio_t* hit;
724 regex_t sre;
725
726 if (*pat && (n = regcomp(&sre, pat, REG_SHELL|REG_AUGMENTED|REG_LENIENT|REG_LEFT|REG_RIGHT)))
727 {
728 regfatalpat(&sre, 2, n, pat);
729 return;
730 }
731 hit = sfstropen();
732 if (pos = hashscan(tab, 0))
733 {
734 while (hashnext(pos))
735 {
736 if (*pat && (n = regexec(&sre, pos->bucket->name, 0, NiL, 0)))
737 {
738 if (n != REG_NOMATCH)
739 {
740 regfatal(&sre, 2, n);
741 break;
742 }
743 continue;
744 }
745 putptr(hit, pos->bucket->name);
746 }
747 hashdone(pos);
748 }
749
750 /*
751 * sort and fill the output buffer
752 */
753
754 if (sfstrtell(hit) > 0)
755 {
756 n = sfstrtell(hit);
757 putptr(hit, 0);
758 v = (char**)sfstrbase(hit);
759 if (flags & SORT_sort)
760 strsort(v, n / sizeof(v), sortcmpf(flags));
761 sfputr(xp, *v, -1);
762 while (*++v)
763 sfprintf(xp, " %s", *v);
764 }
765 sfstrclose(hit);
766 if (*pat)
767 regfree(&sre);
768 }
769
770 /*
771 * generate list of file base names matching pat from all dirs in s
772 */
773
774 static void
list(Sfio_t * xp,register char * s,char * pat,int flags)775 list(Sfio_t* xp, register char* s, char* pat, int flags)
776 {
777 register Rule_t* r;
778 register char** v;
779 char** w;
780 File_t* f;
781 Hash_position_t* pos;
782 char* tok;
783 int n;
784 int ignorecase;
785 Sfio_t* vec;
786 Sfio_t* hit;
787 regex_t* re;
788 regex_t sre;
789 regex_t ire;
790
791 if (*pat && (n = regcomp(&sre, pat, REG_SHELL|REG_AUGMENTED|REG_LENIENT|REG_LEFT|REG_RIGHT)))
792 {
793 regfatalpat(&sre, 2, n, pat);
794 return;
795 }
796 ignorecase = 0;
797 hit = sfstropen();
798 vec = sfstropen();
799
800 /*
801 * generate and bind (scan) the ordered dir list
802 */
803
804 tok = tokopen(s, 1);
805 while (s = tokread(tok))
806 {
807 r = makerule(s);
808 if (!(r->mark & M_mark))
809 {
810 r->mark |= M_mark;
811 if (flags & SORT_force)
812 r->dynamic &= ~D_scanned;
813 if (!(r->dynamic & D_scanned))
814 dirscan(r);
815 putptr(vec, r);
816 }
817 }
818 putptr(vec, 0);
819 tokclose(tok);
820 for (v = (char**)sfstrbase(vec); r = (Rule_t*)*v;)
821 {
822 r->mark &= ~M_mark;
823 *v++ = r->name;
824 }
825
826 /*
827 * scan the file hash for pattern and dir matches
828 */
829
830 if (pos = hashscan(table.file, 0))
831 {
832 while (hashnext(pos))
833 {
834 if ((s = pos->bucket->name)[0] != '.' || s[1] && (s[1] != '.' || s[2]))
835 {
836 f = (File_t*)pos->bucket->value;
837 if (*pat)
838 {
839 if (f->dir->ignorecase)
840 {
841 re = &ire;
842 if (!ignorecase)
843 {
844 if (n = regcomp(re, pat, REG_SHELL|REG_AUGMENTED|REG_LENIENT|REG_ICASE|REG_LEFT|REG_RIGHT))
845 {
846 regfatalpat(re, 2, n, pat);
847 break;
848 }
849 ignorecase = 1;
850 }
851 }
852 else
853 re = &sre;
854 if (n = regexec(re, s, 0, NiL, 0))
855 {
856 if (n != REG_NOMATCH)
857 {
858 regfatal(re, 2, n);
859 break;
860 }
861 continue;
862 }
863 }
864 for (; f; f = f->next)
865 for (v = (char**)sfstrbase(vec); s = *v++;)
866 if (f->dir->name == s)
867 {
868 putptr(hit, pos->bucket->name);
869 goto next;
870 }
871 }
872 next: ;
873 }
874 hashdone(pos);
875 }
876
877 /*
878 * sort and fill the output buffer
879 */
880
881 if (sfstrtell(hit) > 0)
882 {
883 n = sfstrtell(hit);
884 putptr(hit, 0);
885 v = (char**)sfstrbase(hit);
886 if (flags & SORT_sort)
887 strsort(v, n / sizeof(v), sortcmpf(flags));
888 if (flags & (SORT_first|SORT_qualified))
889 {
890 if (flags & SORT_version)
891 {
892 for (v = (char**)sfstrbase(hit); *v; v++)
893 for (w = (char**)sfstrbase(vec); s = *w++;)
894 for (f = getfile(*v); f; f = f->next)
895 if (f->dir->name == s)
896 {
897 if (flags & SORT_qualified)
898 sfprintf(xp, " %s/%s", s, *v);
899 else
900 sfputr(xp, *v, -1);
901 if (flags & SORT_first)
902 goto first;
903 }
904 }
905 else
906 {
907 for (w = (char**)sfstrbase(vec); s = *w++;)
908 for (v = (char**)sfstrbase(hit); *v; v++)
909 for (f = getfile(*v); f; f = f->next)
910 if (f->dir->name == s)
911 {
912 if (flags & SORT_qualified)
913 sfprintf(xp, " %s/%s", s, *v);
914 else
915 sfputr(xp, *v, -1);
916 if (flags & SORT_first)
917 goto first;
918 }
919 }
920 first: ;
921 }
922 else
923 {
924 sfputr(xp, *v, -1);
925 while (*++v)
926 sfprintf(xp, " %s", *v);
927 }
928 }
929 sfstrclose(vec);
930 sfstrclose(hit);
931 if (*pat)
932 {
933 regfree(&sre);
934 if (ignorecase)
935 regfree(&ire);
936 }
937 }
938
939 /*
940 * sort list s into xp
941 *
942 * NOTE: s modified in place and not restored
943 */
944
945 static void
sort(Sfio_t * xp,register char * s,int flags)946 sort(Sfio_t* xp, register char* s, int flags)
947 {
948 register char** p;
949 register char** r;
950 char* tok;
951 long n;
952 Sfio_t* vec;
953 Strcmp_f cmp;
954
955 vec = sfstropen();
956 tok = tokopen(s, 0);
957 while (s = tokread(tok))
958 putptr(vec, s);
959 tokclose(tok);
960 if (n = sfstrtell(vec) / sizeof(s))
961 {
962 putptr(vec, 0);
963 p = (char**)sfstrbase(vec);
964 if (flags & SORT_reverse)
965 {
966 r = p + n - 1;
967 sfputr(xp, *r, -1);
968 while (--r >= p)
969 sfprintf(xp, " %s", *r);
970 }
971 else
972 {
973 cmp = sortcmpf(flags);
974 strsort((char**)sfstrbase(vec), n, cmp);
975 sfputr(xp, *p, -1);
976 if (!(flags & SORT_first))
977 {
978 flags &= SORT_uniq;
979 while (s = *++p)
980 if (!flags || (*cmp)(s, *(p - 1)))
981 sfprintf(xp, " %s", s);
982 }
983 }
984 }
985 sfstrclose(vec);
986 }
987
988 /*
989 * construct relative path from dir s to t in xp
990 */
991
992 static void
relative(Sfio_t * xp,register char * s,register char * t)993 relative(Sfio_t* xp, register char* s, register char* t)
994 {
995 register char* u;
996 register char* v;
997 long pos;
998 Sfio_t* tmp;
999
1000 tmp = sfstropen();
1001 if (*s != '/') sfprintf(tmp, "%s/", internal.pwd);
1002 sfprintf(tmp, "%s/", s);
1003 s = sfstruse(tmp);
1004 pos = pathcanon(s, 0, 0) - s + 1;
1005 sfstrseek(tmp, pos, SEEK_SET);
1006 if (*t != '/') sfprintf(tmp, "%s/", internal.pwd);
1007 sfprintf(tmp, "%s/%c", t, 0);
1008 pathcanon(v = t = sfstrseek(tmp, pos, SEEK_SET), 0, 0);
1009 u = s = sfstrbase(tmp);
1010 while (*s && *s == *t)
1011 if (*s++ == '/')
1012 {
1013 u = s;
1014 v = ++t;
1015 }
1016 else t++;
1017 pos = sfstrtell(xp);
1018 while (*u)
1019 if (*u++ == '/')
1020 sfputr(xp, "../", -1);
1021 sfputr(xp, v, -1);
1022 if (sfstrtell(xp) > pos + 1) sfstrseek(xp, -1, SEEK_CUR);
1023 else sfputc(xp, '.');
1024 sfstrclose(tmp);
1025 }
1026
1027 /*
1028 * apply binary separator operator to a and b
1029 */
1030
1031 static int
sepcmp(int sep,unsigned long a,unsigned long b)1032 sepcmp(int sep, unsigned long a, unsigned long b)
1033 {
1034 switch (sep)
1035 {
1036 case LT:
1037 return a < b;
1038 case LE:
1039 return a <= b;
1040 case EQ:
1041 return a == b;
1042 case NE:
1043 case NOT:
1044 return a != b;
1045 case GE:
1046 return a >= b;
1047 case GT:
1048 return a > b;
1049 default:
1050 #if DEBUG
1051 error(PANIC, "invalid separator operator %d", sep);
1052 #endif
1053 return 0;
1054 }
1055 }
1056
1057 /*
1058 * apply binary separator operator to times a and b
1059 */
1060
1061 static int
septimecmp(int sep,Time_t a,Time_t b)1062 septimecmp(int sep, Time_t a, Time_t b)
1063 {
1064 switch (sep)
1065 {
1066 case LT:
1067 return a < b;
1068 case LE:
1069 return a <= b;
1070 case EQ:
1071 return a == b;
1072 case NE:
1073 case NOT:
1074 return a != b;
1075 case GE:
1076 return a >= b;
1077 case GT:
1078 return a > b;
1079 default:
1080 #if DEBUG
1081 error(PANIC, "invalid separator operator %d", sep);
1082 #endif
1083 return 0;
1084 }
1085 }
1086
1087 /*
1088 * convert path to native representation with '..' quotes if needed
1089 */
1090
1091 static void
native(Sfio_t * xp,const char * s)1092 native(Sfio_t* xp, const char* s)
1093 {
1094 size_t m;
1095 size_t n;
1096
1097 if (*s)
1098 {
1099 sfputc(xp, '\'');
1100 n = PATH_MAX;
1101 do
1102 {
1103 m = n;
1104 n = pathnative(s, sfstrrsrv(xp, m), m);
1105 } while (n > m);
1106 sfstrseek(xp, n, SEEK_CUR);
1107 sfputc(xp, '\'');
1108 }
1109 }
1110
1111 #define ORDER_COMMAND "" /* command assertion operator */
1112 #define ORDER_INIT "INIT" /* no prereq dir prefix */
1113 #define ORDER_LIBRARY "LIBRARY" /* library assertion operator */
1114 #define ORDER_PACKAGE "PACKAGE" /* package assertion operator */
1115 #define ORDER_RECURSE "MAKE" /* recursion assertion operator */
1116 #define ORDER_REQUIRE "REQUIRE" /* library prerequisite operator */
1117
1118 #define ORDER_all 0x01
1119 #define ORDER_directory 0x02
1120 #define ORDER_force 0x04
1121 #define ORDER_implicit 0x08
1122 #define ORDER_paths 0x10
1123 #define ORDER_prereqs 0x20
1124
1125 #define M_INIT M_metarule
1126 #define M_MUST M_mark
1127 #define M_LHS M_compile
1128 #define M_RHS M_scan
1129 #define M_SKIP M_generate
1130
1131 /*
1132 * order_recurse() partial order traversal support
1133 */
1134
1135 static unsigned long
order_descend(Sfio_t * xp,Hash_table_t * tab,Rule_t * r,unsigned long mark,unsigned int flags)1136 order_descend(Sfio_t* xp, Hash_table_t* tab, Rule_t* r, unsigned long mark, unsigned int flags)
1137 {
1138 register List_t* p;
1139 register Rule_t* a;
1140 unsigned long here;
1141 unsigned long need;
1142
1143 need = 0;
1144 here = sfstrtell(xp);
1145 r->mark &= ~M_MUST;
1146 r->complink = mark;
1147 if (r->prereqs)
1148 {
1149 if ((flags & (ORDER_all|ORDER_prereqs)) == (ORDER_all|ORDER_prereqs))
1150 {
1151 for (p = r->prereqs; p; p = p->next)
1152 {
1153 if (!(a = (Rule_t*)hashget(tab, p->rule->name)))
1154 a = p->rule;
1155 else if (a == r)
1156 continue;
1157 if (!need)
1158 {
1159 need = 1;
1160 r->mark |= M_LHS;
1161 sfprintf(xp, "%s :", r->name);
1162 }
1163 sfprintf(xp, " %s", a->name);
1164 a->mark |= M_RHS;
1165 }
1166 if (need)
1167 {
1168 need = 0;
1169 sfprintf(xp, "\n");
1170 }
1171 }
1172 for (p = r->prereqs; p; p = p->next)
1173 {
1174 if (!(a = (Rule_t*)hashget(tab, p->rule->name)))
1175 a = p->rule;
1176 if (a->mark & M_MUST)
1177 mark = order_descend(xp, tab, a, mark, flags);
1178 else if (need < a->complink)
1179 need = a->complink;
1180 }
1181 freelist(r->prereqs);
1182 r->prereqs = 0;
1183 }
1184 else if (flags & ORDER_prereqs)
1185 {
1186 if (!(flags & ORDER_all) || (r->mark & M_RHS))
1187 return mark;
1188 r->mark |= M_LHS;
1189 }
1190 if (!(flags & ORDER_prereqs))
1191 {
1192 if (!(flags & ORDER_all) || (r->mark & M_RHS))
1193 return mark;
1194 if (sfstrtell(xp) != here || mark == need)
1195 {
1196 if (sfstrtell(xp))
1197 sfputr(xp, "-", ' ');
1198 mark++;
1199 }
1200 sfputr(xp, r->name, ' ');
1201 r->complink = mark;
1202 }
1203 return mark;
1204 }
1205
1206 static void order_find(Sfio_t*, Sfio_t*, Sfio_t*, Hash_table_t*, char*, char*, char*, char*, unsigned int);
1207
1208 static void
order_all(Sfio_t * xp,Sfio_t * tmp,Sfio_t * vec,Hash_table_t * tab,Rule_t * d,char * makefiles,char * skip,unsigned int flags)1209 order_all(Sfio_t* xp, Sfio_t* tmp, Sfio_t* vec, Hash_table_t* tab, Rule_t* d, char* makefiles, char* skip, unsigned int flags)
1210 {
1211 register char* t;
1212 register char** v;
1213 int n;
1214 glob_t gl;
1215
1216 d->mark |= M_RHS;
1217 n = strlen(d->name) + 1;
1218 sfprintf(internal.tmp, "%s/*/", d->name);
1219 v = globv(&gl, sfstruse(internal.tmp));
1220 flags |= ORDER_directory|ORDER_force;
1221 while (t = *v++)
1222 {
1223 t[strlen(t) - 1] = 0;
1224 order_find(xp, tmp, vec, tab, d->name, t + n, makefiles, skip, flags);
1225 }
1226 globfree(&gl);
1227 }
1228
1229 /*
1230 * scan makefile r for ORDER_RECURSE assertion operators and add to vec
1231 */
1232
1233 static void
order_scan(Sfio_t * xp,Sfio_t * tmp,Sfio_t * vec,Hash_table_t * tab,Rule_t * d,Rule_t * r,char * makefiles,char * skip,unsigned int flags)1234 order_scan(Sfio_t* xp, Sfio_t* tmp, Sfio_t* vec, Hash_table_t* tab, Rule_t* d, Rule_t* r, char* makefiles, char* skip, unsigned int flags)
1235 {
1236 register char* s;
1237 Sfio_t* sp;
1238
1239 if (d == internal.dot)
1240 d->mark |= M_MUST|M_SKIP;
1241 else
1242 {
1243 if (s = strrchr(d->name, '/'))
1244 s++;
1245 else
1246 s = d->name;
1247 if (!strneq(s, ORDER_INIT, sizeof(ORDER_INIT) - 1))
1248 d->mark |= M_MUST;
1249 else if (flags & ORDER_prereqs)
1250 d->mark |= M_INIT|M_SKIP;
1251 else
1252 {
1253 if (sfstrtell(xp))
1254 sfputr(xp, "-", ' ');
1255 sfputr(xp, d->name, ' ');
1256 d->mark |= M_MUST|M_RHS|M_SKIP;
1257 }
1258 hashput(tab, s, d);
1259 }
1260 putptr(vec, r);
1261 putptr(vec, d);
1262 if (makefiles && (d->mark & M_MUST) && (sp = rsfopen(r->name)))
1263 {
1264 if (flags & ORDER_implicit)
1265 order_all(xp, tmp, vec, tab, d, makefiles, skip, flags);
1266 else
1267 while (s = sfgetr(sp, '\n', 1))
1268 while (*s)
1269 if (*s++ == ':')
1270 {
1271 if (strneq(s, ORDER_RECURSE, sizeof(ORDER_RECURSE) - 1) && *(s += sizeof(ORDER_RECURSE) - 1) == ':')
1272 {
1273 while (*++s && (*s == ' ' || *s == '\t'));
1274 if (*s)
1275 order_find(xp, tmp, vec, tab, NiL, s, makefiles, skip, flags|ORDER_force);
1276 else
1277 order_all(xp, tmp, vec, tab, d, makefiles, skip, flags);
1278 }
1279 break;
1280 }
1281 sfclose(sp);
1282 }
1283 }
1284
1285 /*
1286 * find first makefile in dir/files/makefiles and scan
1287 */
1288
1289 static void
order_find(Sfio_t * xp,Sfio_t * tmp,Sfio_t * vec,Hash_table_t * tab,char * dir,char * files,char * makefiles,char * skip,unsigned int flags)1290 order_find(Sfio_t* xp, Sfio_t* tmp, Sfio_t* vec, Hash_table_t* tab, char* dir, char* files, char* makefiles, char* skip, unsigned int flags)
1291 {
1292 Rule_t* r;
1293 Rule_t* d;
1294 char* s;
1295 char* t;
1296 char* e;
1297 char* tok;
1298 Stat_t st;
1299
1300 if (dir && streq(dir, internal.dot->name))
1301 dir = 0;
1302 tok = tokopen(files, 1);
1303 while (s = tokread(tok))
1304 {
1305 if (skip)
1306 {
1307 if (t = strrchr(s, '/'))
1308 t++;
1309 else
1310 t = s;
1311 if ((*t != '.' || *(t + 1)) && strmatch(t, skip))
1312 continue;
1313 }
1314 if (dir)
1315 {
1316 sfprintf(tmp, "%s/%s", dir, s);
1317 t = sfstruse(tmp);
1318 }
1319 else if (!(state.questionable & 0x20000000) && !strchr(s, '/') && (t = strrchr(internal.pwd, '/')) && streq(s, t + 1))
1320 continue;
1321 else
1322 t = s;
1323 if ((flags & ORDER_directory) || (flags & (ORDER_force|ORDER_paths)) == ORDER_force && (d = bindfile(NiL, t, 0)) && !stat(d->name, &st) && S_ISDIR(st.st_mode))
1324 {
1325 d = makerule(t);
1326 t = makefiles;
1327 while (t)
1328 {
1329 if (dir)
1330 sfprintf(tmp, "%s/", dir);
1331 if (e = strchr(t, ':'))
1332 {
1333 sfprintf(tmp, "%s/%-.*s", s, e - t, t);
1334 t = e + 1;
1335 }
1336 else
1337 {
1338 sfprintf(tmp, "%s/%s", s, t);
1339 t = 0;
1340 }
1341 if (r = bindfile(NiL, e = sfstruse(tmp), 0))
1342 {
1343 order_scan(xp, tmp, vec, tab, d, r, makefiles, skip, flags);
1344 break;
1345 }
1346 }
1347 }
1348 else if ((flags & ORDER_force) && (r = bindfile(NiL, t, 0)))
1349 {
1350 if (s = strrchr(t, '/'))
1351 {
1352 *s = 0;
1353 d = makerule(t);
1354 *s = '/';
1355 }
1356 else
1357 d = internal.dot;
1358 order_scan(xp, tmp, vec, tab, d, r, makefiles, skip, flags);
1359 }
1360 }
1361 tokclose(tok);
1362 }
1363
1364 /*
1365 * order strsort comparison function
1366 */
1367
1368 static int
order_cmp(const void * a,const void * b)1369 order_cmp(const void* a, const void* b)
1370 {
1371 return strcoll((*((Rule_t**)a + 1))->name, (*((Rule_t**)b + 1))->name);
1372 }
1373
1374 /*
1375 * generate an ordered list of directories in xp based on
1376 * (recursive) makefile prereqs; if targets!=0 then only
1377 * those targets and prerequisites are considered
1378 * directories and targets are ' ' separated
1379 * ORDER_paths for old :W=O: where directories are makefile paths
1380 */
1381
1382 static void
order_recurse(Sfio_t * xp,char * directories,char * makefiles,char * skip,char * targets,unsigned int flags)1383 order_recurse(Sfio_t* xp, char* directories, char* makefiles, char* skip, char* targets, unsigned int flags)
1384 {
1385 char* s;
1386 char* t;
1387 char* u;
1388 char* b;
1389 char* a;
1390 char* z;
1391 char* tok;
1392 char* lib;
1393 Rule_t* r;
1394 Rule_t* d;
1395 Rule_t* order;
1396 Rule_t** v;
1397 Rule_t** e;
1398 List_t* q;
1399 Sfio_t* vec;
1400 Sfio_t* tmp;
1401 Sfio_t* sp;
1402 Hash_table_t* tab;
1403 unsigned long mark;
1404 int i;
1405 int j;
1406 int k;
1407 int m;
1408 int p;
1409 int var;
1410
1411 order = targets ? (Rule_t*)0 : getrule(external.order);
1412 tab = hashalloc(table.rule, 0);
1413 tmp = sfstropen();
1414 vec = sfstropen();
1415 getop(tmp, "recurse", 0);
1416 if (strmatch(sfstruse(tmp), "*implicit*"))
1417 flags |= ORDER_implicit;
1418 order_find(xp, tmp, vec, tab, NiL, directories, makefiles, skip, flags);
1419 mark = sfstrtell(vec);
1420 putptr(vec, 0);
1421 v = (Rule_t**)sfstrbase(vec);
1422 qsort(v, mark / sizeof(v) / 2, sizeof(v) * 2, order_cmp);
1423 while (r = *v++)
1424 {
1425 d = *v++;
1426 if ((d->mark & M_MUST) && (sp = rsfopen(bind(r)->name)))
1427 {
1428 z = 0;
1429 while (s = sfgetr(sp, '\n', 1))
1430 {
1431 j = p = 0;
1432 b = s;
1433 while (*s)
1434 {
1435 var = 0;
1436 lib = 0;
1437 for (k = 1; (i = *s) == ' ' || i == '\t' || i == '\r' || i == '"' || i == '\''; s++);
1438 m = *s != '-' && *s != '+' || *(s + 1) != 'l';
1439 for (t = s; (i = *s) && i != ' ' && i != '\t' && i != '\r' && i != '"' && i != '\'' && i != '\\' && i != ':'; s++)
1440 if (i == '/' && m)
1441 t = s + 1;
1442 else if (i == '.' && *(s + 1) != 'c' && *(s + 1) != 'C' && *(s + 1) != 'h' && *(s + 1) != 'H' && t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
1443 *s = 0;
1444 else if (i == '$')
1445 var = 1;
1446 if (*s)
1447 *s++ = 0;
1448 if (var)
1449 continue;
1450 if (!t[0])
1451 k = 0;
1452 else if ((t[0] == '-' || t[0] == '+') && t[1] == 'l')
1453 {
1454 a = 0;
1455 for (u = t += 2; istype(*u, C_ID1|C_ID2) || *u == '-' || *u == '/' && (a = u); u++);
1456 *u = 0;
1457 if (!*t)
1458 continue;
1459 if (a)
1460 {
1461 if (!z)
1462 for (m = 0, z = d->name + strlen(d->name); z > d->name; z--)
1463 if (*z == '/' && ++m == 2)
1464 {
1465 z++;
1466 break;
1467 }
1468 if (z > d->name)
1469 sfprintf(internal.nam, "%-.*s%-.*slib/%s", z - d->name, d->name, a - t, t, a + 1);
1470 else
1471 sfprintf(internal.nam, "%s", a + 1);
1472 }
1473 else
1474 sfprintf(internal.nam, "lib%s", t);
1475 lib = t = sfstruse(internal.nam);
1476 }
1477 else if (p)
1478 {
1479 if (t[0] == '+' && !t[1])
1480 p = 2;
1481 else if (p == 1)
1482 {
1483 if (i == ':' && strneq(s, "order", 5))
1484 {
1485 if (!order)
1486 order = makerule(external.order);
1487 order->prereqs = append(order->prereqs, cons(makerule(t), NiL));
1488 }
1489 else if (i != ':' || !strneq(s, "command", 7))
1490 {
1491 sfprintf(internal.nam, "lib%s", t);
1492 t = sfstruse(internal.nam);
1493 }
1494 if (i == ':')
1495 while (*s && !isspace(*s))
1496 s++;
1497 }
1498 }
1499 else if (i == ':')
1500 {
1501 if (j != ':' || !isupper(*t))
1502 k = 0;
1503 else if ((i = streq(t, ORDER_COMMAND)) || streq(t, ORDER_LIBRARY) || streq(t, ORDER_REQUIRE))
1504 {
1505 for (; *b == ' ' || *b == '\t' || *b == '\r'; b++);
1506 for (u = b; istype(*b, C_ID1|C_ID2) || *b == '-'; b++);
1507 if (!*b || *b == ':' || *b == ' ' || *b == '\t' || *b == '\r')
1508 {
1509 *b = 0;
1510 if (!i)
1511 {
1512 sfprintf(internal.nam, "lib%s", u);
1513 u = sfstruse(internal.nam);
1514 }
1515 if (!hashget(tab, u))
1516 hashput(tab, u, d);
1517 }
1518 }
1519 else if (streq(t, ORDER_PACKAGE))
1520 {
1521 p = 1;
1522 k = 0;
1523 }
1524 else if (streq(t, ORDER_RECURSE))
1525 {
1526 p = -1;
1527 k = 0;
1528 }
1529 else
1530 for (u = t; *u; u++)
1531 if (isupper(*u))
1532 *u = tolower(*u);
1533 else if (!isalnum(*u))
1534 {
1535 k = 0;
1536 break;
1537 }
1538 }
1539 else if (t[0] != 'l' || t[1] != 'i' || t[2] != 'b')
1540 k = 0;
1541 else
1542 for (u = t + 3; *u; u++)
1543 if (!isalnum(*u))
1544 {
1545 k = 0;
1546 break;
1547 }
1548 if (k && ((r = (Rule_t*)hashget(tab, t)) && (r->mark & M_MUST) && r != d || *t++ == 'l' && *t++ == 'i' && *t++ == 'b' && *t && (r = (Rule_t*)hashget(tab, t)) && (r->mark & M_MUST) && r != d))
1549 {
1550 if (t = strrchr(d->name, '/'))
1551 t++;
1552 else
1553 t = d->name;
1554 if (t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
1555 t += 3;
1556 if (u = strrchr(r->name, '/'))
1557 u++;
1558 else
1559 u = r->name;
1560 if (!streq(t, u) && (u[0] != 'l' || u[1] != 'i' || u[2] != 'b' || u[3]))
1561 addprereq(d, r, PREREQ_APPEND);
1562 }
1563 else if (lib && (r = makerule(lib)) != d)
1564 addprereq(d, r, PREREQ_APPEND);
1565 j = i;
1566 }
1567 }
1568 sfclose(sp);
1569 if (s = strrchr(d->name, '/'))
1570 {
1571 if ((s - d->name) > 3 && *(s - 1) == 'b' && *(s - 2) == 'i' && *(s - 3) == 'l' && *(s - 4) != '/')
1572 {
1573 /*
1574 * foolib : foo : libfoo
1575 */
1576
1577 *(s - 3) = 0;
1578 r = makerule(d->name);
1579 if (r != d)
1580 addprereq(d, r, PREREQ_APPEND);
1581 if (t = strrchr(d->name, '/'))
1582 t++;
1583 else
1584 t = d->name;
1585 sfprintf(internal.nam, "lib/lib%s", t);
1586 r = makerule(sfstruse(internal.nam));
1587 if (r != d)
1588 addprereq(d, r, PREREQ_APPEND);
1589 *(s - 3) = 'l';
1590 }
1591 else if (((s - d->name) != 3 || *(s - 1) != 'b' || *(s - 2) != 'i' || *(s - 3) != 'l') && (*(s + 1) != 'l' || *(s + 2) != 'i' || *(s + 3) != 'b'))
1592 {
1593 /*
1594 * huh/foobar : lib/libfoo
1595 */
1596
1597 s++;
1598 t = s + strlen(s);
1599 while (--t > s)
1600 {
1601 sfprintf(internal.nam, "lib/lib%-.*s", t - s, s);
1602 if ((r = getrule(sfstruse(internal.nam))) && r != d)
1603 addprereq(d, r, PREREQ_APPEND);
1604 }
1605 }
1606 }
1607 }
1608 }
1609 mark = 0;
1610 if (targets)
1611 {
1612 tok = tokopen(targets, 1);
1613 while (s = tokread(tok))
1614 if ((r = (Rule_t*)hashget(tab, s)) && (r->mark & M_MUST))
1615 mark = order_descend(xp, tab, r, mark, flags|ORDER_all);
1616 tokclose(tok);
1617 }
1618 else
1619 {
1620 /*
1621 * favor external.order prereqs if they are in the mix
1622 */
1623
1624 flags |= ORDER_all;
1625 if (order)
1626 for (q = order->prereqs; q; q = q->next)
1627 if ((r = (Rule_t*)hashget(tab, unbound(q->rule))) && (r->mark & M_MUST))
1628 {
1629 mark = order_descend(xp, tab, r, mark, flags);
1630 if (!(flags & ORDER_prereqs))
1631 sfputr(xp, "-", ' ');
1632 }
1633 }
1634 v = (Rule_t**)sfstrbase(vec);
1635 while (*v++)
1636 {
1637 r = *v++;
1638 if (r->mark & M_MUST)
1639 mark = order_descend(xp, tab, r, mark, flags);
1640 }
1641 e = v - 1;
1642 if (flags & ORDER_prereqs)
1643 {
1644 sfprintf(xp, "all :");
1645 v = (Rule_t**)sfstrbase(vec);
1646 while (*v++)
1647 {
1648 r = *v++;
1649 if (r->mark & M_INIT)
1650 sfprintf(xp, " %s", r->name);
1651 }
1652 }
1653 k = 1;
1654 v = (Rule_t**)sfstrbase(vec);
1655 while (--e >= v)
1656 {
1657 r = *e;
1658 if ((flags & ORDER_prereqs) && (r->mark & (M_INIT|M_LHS|M_MUST|M_RHS|M_SKIP)) == M_LHS)
1659 sfprintf(xp, " %s", r->name);
1660 else if (!(state.questionable & 0x40000000) && !(flags & ORDER_prereqs) && (r->mark & (M_INIT|M_LHS|M_MUST|M_RHS|M_SKIP)) == M_RHS)
1661 {
1662 if (k)
1663 {
1664 k = 0;
1665 sfputr(xp, "+", ' ');
1666 }
1667 sfputr(xp, r->name, ' ');
1668 }
1669 r->mark &= ~(M_INIT|M_LHS|M_MUST|M_RHS|M_SKIP);
1670 r->complink = 0;
1671 }
1672 sfstrclose(vec);
1673 sfstrclose(tmp);
1674 hashfree(tab);
1675 }
1676
1677 /*
1678 * path name operations from (rule) s into xp using op
1679 *
1680 * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1681 * A B C D E F G H I L N P R S U V W X Z
1682 */
1683
1684 static void
pathop(Sfio_t * xp,register char * s,char * op,int sep)1685 pathop(Sfio_t* xp, register char* s, char* op, int sep)
1686 {
1687 register char* t;
1688 register int n;
1689 register Rule_t* r;
1690 register char** p;
1691 char* e;
1692 Rule_t* x;
1693 int c;
1694 int i;
1695 int chop;
1696 int root;
1697 Stat_t st;
1698 long pos;
1699 Sfio_t* tmp;
1700
1701 n = islower(*op) ? toupper(*op) : *op;
1702 if (r = getrule(s))
1703 {
1704 if (r->dynamic & D_alias)
1705 switch (n)
1706 {
1707 case 'B':
1708 case 'D':
1709 case 'P':
1710 case 'Z':
1711 break;
1712 default:
1713 r = makerule(r->name);
1714 break;
1715 }
1716 s = r->name;
1717 }
1718 switch (n)
1719 {
1720 case 'A':
1721 absolute:
1722 /*
1723 * construct absolute pathname for s
1724 */
1725
1726 if (*s)
1727 {
1728 pos = sfstrtell(xp);
1729 if (*s == '/')
1730 sfputr(xp, s, 0);
1731 else if (r && (r->dynamic & D_bound) && r->time)
1732 {
1733 op = "A";
1734 goto view;
1735 }
1736 else
1737 sfprintf(xp, "%s/%s%c", state.mam.statix ? internal.dot->name : internal.pwd, s, 0);
1738 s = sfstrseek(xp, pos, SEEK_SET);
1739 pos += canon(s) - s;
1740 sfstrseek(xp, pos, SEEK_SET);
1741 }
1742 return;
1743 case 'B':
1744 /*
1745 * return physical binding for name
1746 */
1747
1748 if (r && (r->dynamic & D_bound))
1749 {
1750 if ((t = getbound(s)) && (r = getrule(t)) && (r->dynamic & D_regular))
1751 s = state.localview ? localview(makerule(t)) : t;
1752 sfputr(xp, s, -1);
1753 }
1754 return;
1755 case 'C':
1756 /*
1757 * canonicalize path name
1758 */
1759
1760 if (*s)
1761 {
1762 pos = sfstrtell(xp);
1763 sfputr(xp, s, 0);
1764 s = sfstrseek(xp, pos, SEEK_SET);
1765 pos += canon(s) - s;
1766 sfstrseek(xp, pos, SEEK_SET);
1767 }
1768 return;
1769 case 'D':
1770 /*
1771 * generate directory where s was bound
1772 */
1773
1774 sep = 0;
1775 if (!r || !r->time || (r->property & P_state) || r->status == IGNORE)
1776 break;
1777 if (((state.questionable & 0x10000000) || !(s = r->uname) || !(t = strrchr(r->name, '/')) || !streq(t+1, s)) && ((t = getbound(r->name)) || (s = r->uname) && (t = getbound(s))))
1778 {
1779 if ((x = getrule(t)) && (x->dynamic & (D_entries|D_scanned)) == (D_entries|D_scanned) || *t == '/' && !*(t + 1))
1780 s = 0;
1781 else if (s = strrchr(t, '/'))
1782 *s = 0;
1783 else
1784 t = ".";
1785 x = makerule(t);
1786 if (s)
1787 *s = '/';
1788 s = (!(state.questionable & 0x00008000) && *r->name == '/') ? r->uname : (char*)0;
1789 t = state.localview ? localview(x) : x->name;
1790 if ((r->dynamic & D_alias) && !(state.questionable & 0x10000000))
1791 {
1792 sfprintf(internal.nam, "%s/%s", t, r->uname);
1793 if (!getrule(sfstruse(internal.nam)) && (e = strrchr(r->name, '/')))
1794 {
1795 *e = 0;
1796 x = getrule(r->name);
1797 *e = '/';
1798 if (x && (x->dynamic & (D_entries|D_scanned)) == (D_entries|D_scanned))
1799 t = state.localview ? localview(x) : x->name;
1800 }
1801 }
1802 sfputr(xp, state.localview ? localview(x) : x->name, -1);
1803 if (!s)
1804 return;
1805 sep = 1;
1806 }
1807 if (s)
1808 {
1809 if ((n = strlen(r->name)) > (c = strlen(s))) for (;;)
1810 {
1811 if (*(t = r->name + n - c - 1) == '/' && streq(s, t + 1))
1812 {
1813 *t = 0;
1814 r = makerule(r->name);
1815 *t = '/';
1816 if (sep)
1817 sfputc(xp, ' ');
1818 sfputr(xp, state.localview ? localview(r) : r->name, -1);
1819 return;
1820 }
1821 if (!(t = strchr(s, '/')))
1822 {
1823 #if DEBUG
1824 message((-2, "pathop('%c',%s==%s): cannot find '/'", *op, r->name, r->uname));
1825 #endif
1826 break;
1827 }
1828 c -= ++t - s;
1829 s = t;
1830 }
1831 #if DEBUG
1832 else
1833 message((-2, "pathop('%c',%s==%s): bound shorter than unbound", *op, r->name, r->uname));
1834 #endif
1835 }
1836 if (*r->name != '/')
1837 {
1838 if (sep)
1839 sfputc(xp, ' ');
1840 sfputc(xp, '.');
1841 }
1842 return;
1843 case 'E':
1844 /*
1845 * construct a PATH independent executable pathname for s
1846 */
1847
1848 if (*s)
1849 {
1850 pos = sfstrtell(xp);
1851 if (state.mam.statix && r && !(r->property & P_state) && !(r->dynamic & D_alias))
1852 s = unbound(r);
1853 if (*s != '/' && (*s != '.' || *(s + 1) != '/' && (*(s + 1) != '.' || *(s + 2) != '/')))
1854 {
1855 sfputc(xp, '.');
1856 sfputc(xp, '/');
1857 }
1858 sfputr(xp, s, -1);
1859 }
1860 return;
1861 case 'F':
1862 sep = 0;
1863 if (!r)
1864 r = makerule(s);
1865 r = bind(r);
1866 if (!(chop = streq(r->name, ".")))
1867 sfputr(xp, r->name, -1);
1868 if (!(r->dynamic & D_regular))
1869 {
1870 tmp = sfstropen();
1871 if (chop)
1872 sfprintf(tmp, "**");
1873 else
1874 sfprintf(tmp, "%s/**", s);
1875 for (p = globv(NiL, sfstruse(tmp)); *p; p++)
1876 {
1877 sfputc(xp, ' ');
1878 sfputr(xp, *p, -1);
1879 }
1880 sfstrclose(tmp);
1881 }
1882 return;
1883 case 'G':
1884 sep = 0;
1885 for (p = globv(NiL, s); *p; p++)
1886 {
1887 if (sep)
1888 sfputc(xp, ' ');
1889 else
1890 sep = 1;
1891 sfputr(xp, *p, -1);
1892 }
1893 return;
1894 case 'H':
1895 /*
1896 * generate 14 char hash of s with op suffix
1897 */
1898
1899 sfprintf(xp, "M%08lX", strhash(s));
1900 if (*++op == '=')
1901 op++;
1902 if ((n = strlen(op)) > 5)
1903 n = 5;
1904 while (c = *s++)
1905 if (istype(c, C_VARIABLE1|C_VARIABLE2))
1906 {
1907 if (n++ >= 5)
1908 break;
1909 sfputc(xp, c);
1910 }
1911 n = 0;
1912 while (n++ < 5 && (c = *op++))
1913 sfputc(xp, c);
1914 return;
1915 case 'I':
1916 /*
1917 * return bound name if identical to op
1918 * or return inode number
1919 */
1920
1921 if (*s)
1922 {
1923 Stat_t st1;
1924
1925 if (*++op == '=')
1926 op++;
1927 if (!*op)
1928 sfprintf(xp, "%lu", stat(s, &st) ? 0L : st.st_ino);
1929 else if ((!stat(s, &st) && !stat(op, &st1) && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) == (sep == EQ))
1930 sfputr(xp, s, -1);
1931 }
1932 return;
1933 case 'L':
1934 if (!r || !(r->dynamic & D_bound) || !r->time)
1935 /* ignore */;
1936 else if (*++op == '*' || *op == '!' || *op == '=' && (*(op + 1) == '*' || *(op + 1) == '!'))
1937 {
1938 view:
1939 /*
1940 * if bound then return top and covered view names
1941 */
1942
1943 if (*op == '=')
1944 op++;
1945 sep = 0;
1946 if (!state.maxview)
1947 {
1948 r = 0;
1949 goto absolute;
1950 }
1951 else if (state.fsview)
1952 {
1953 sfstrrsrv(internal.nam, MAXNAME + 5);
1954 t = sfstruse(internal.nam);
1955 strcpy(t, r->name);
1956 for (n = *op == 'A';;)
1957 {
1958 if (mount(t, t, FS3D_GET|FS3D_VIEW|FS3D_SIZE(MAXNAME), NiL))
1959 break;
1960 if (sep)
1961 sfputc(xp, ' ');
1962 else
1963 sep = 1;
1964 sfputr(xp, t + ((!n++ && !strncmp(t, internal.pwd, internal.pwdlen) && t[internal.pwdlen] == '/') ? (internal.pwdlen + 1) : 0), -1);
1965 if (*op != '*')
1966 break;
1967 if (n > 64)
1968 {
1969 error(1, "%s: view loop", t);
1970 break;
1971 }
1972 strcpy(t + strlen(t), "/...");
1973 }
1974 }
1975 else
1976 {
1977 root = 0;
1978 s = r->name;
1979 if (n = r->view)
1980 {
1981 c = state.view[n].rootlen;
1982 if (!strncmp(s, state.view[n].root, c) && (!s[c] || s[c] == '/') && *(s += c))
1983 {
1984 s++;
1985 root = 1;
1986 }
1987 }
1988 else if (*s == '/')
1989 {
1990 for (i = 0;; i++)
1991 {
1992 if (i > state.maxview)
1993 break;
1994 if (!strncmp(s, state.view[i].root, n = state.view[i].rootlen) && (!*(s + n) || *(s + n) == '/'))
1995 {
1996 if (!*(s += n) || !*++s)
1997 s = internal.dot->name;
1998 n = i;
1999 root = 1;
2000 break;
2001 }
2002 }
2003 }
2004 if (*s == '/')
2005 {
2006 r = 0;
2007 goto absolute;
2008 }
2009 for (; n <= state.maxview; n++)
2010 {
2011 if (root)
2012 sfprintf(internal.nam, "%s/%s", state.view[n].root, s);
2013 else
2014 {
2015 if (*state.view[n].path != '/')
2016 sfprintf(internal.nam, "%s/", internal.pwd);
2017 sfprintf(internal.nam, "%s", state.view[n].path);
2018 if (*s)
2019 sfprintf(internal.nam, "/%s", s);
2020 }
2021 t = sfstruse(internal.nam);
2022 pathcanon(t, 0, 0);
2023 if (!stat(t, &st))
2024 {
2025 if (sep)
2026 sfputc(xp, ' ');
2027 else
2028 sep = 1;
2029 sfputr(xp, t, -1);
2030 if (*op != '*')
2031 break;
2032 }
2033 }
2034 }
2035 if (*op == 'A' && !sep)
2036 {
2037 r = 0;
2038 goto absolute;
2039 }
2040 }
2041 else
2042 {
2043 /*
2044 * return bound name if bound in view level 0 [n]
2045 */
2046
2047 n = (*op == '=') ? (int)strtol(op + 1, NiL, 0) : 0;
2048 if (sepcmp(sep, (unsigned long)((r->dynamic & D_global) ? state.maxview : r->view), (unsigned long)n))
2049 sfputr(xp, s, -1);
2050 }
2051 return;
2052 case 'N':
2053 native(xp, s);
2054 return;
2055 case 'P':
2056 if (*++op == '=')
2057 op++;
2058 if ((t = strchr(op, ',')) || (t = strchr(op, ' ')))
2059 *t++ = 0;
2060 if (s = pathprobe(op, t ? t : idname, s, 0, sfstrrsrv(xp, MAXNAME), MAXNAME, NiL, 0))
2061 {
2062 sfstrseek(xp, strlen(s), SEEK_CUR);
2063 makerule(s)->dynamic |= D_built|D_global;
2064 }
2065 if (t)
2066 *--t = 0;
2067 return;
2068 case 'R':
2069 if (*++op == '=')
2070 op++;
2071 relative(xp, s, op);
2072 return;
2073 case 'S':
2074 /*
2075 * return bound name if bound in subdirectory in view
2076 */
2077
2078 if (r && (r->dynamic & D_bound))
2079 {
2080 c = 0;
2081 if (*++op == '=')
2082 op++;
2083 if (r->view && (!state.fsview || state.expandview))
2084 {
2085 n = state.view[r->view].pathlen;
2086 if (*op)
2087 {
2088 for (n = 1; op = strchr(op, '/'); n++, op++);
2089 for (t = state.view[r->view].path + n; t > state.view[r->view].path && (*t != '/' || --n > 0); t--);
2090 n = t - state.view[r->view].path;
2091 }
2092 if (!strncmp(s, state.view[r->view].path, n) && *(s + n) == '/')
2093 c = 1;
2094 }
2095 else if (!(r->dynamic & D_global))
2096 {
2097 if (*op)
2098 {
2099 sfprintf(internal.nam, "%s/%s", op, s);
2100 s = sfstruse(internal.nam);
2101 pathcanon(s, 0, 0);
2102 }
2103 if (*s++ != '.' || *s++ != '.' || *s && *s != '/')
2104 c = 1;
2105 }
2106 if (c == !(sep & NOT))
2107 sfputr(xp, r->name, -1);
2108 }
2109 return;
2110 case 'U':
2111 /*
2112 * return unbound name
2113 */
2114
2115 sfputr(xp, (r && !(r->property & P_state) && !(r->dynamic & D_alias)) ? unbound(r) : s, -1);
2116 return;
2117 case 'V':
2118 if (!r || !(r->dynamic & D_bound) || !r->time)
2119 return;
2120 if (*++op)
2121 {
2122 /*
2123 * return the top view logical name of s
2124 */
2125
2126 s = r->name;
2127 if (*op == '=')
2128 op++;
2129 if (strtol(op, &e, 0) || *e)
2130 {
2131 error(2, "%s: view %s not supported", s, op);
2132 return;
2133 }
2134 else if (state.maxview && !state.fsview && r->view)
2135 {
2136 c = state.view[r->view].pathlen;
2137 if (!strncmp(s, state.view[r->view].path, c) && (!s[c] || s[c] == '/') && *(s += c))
2138 s++;
2139 }
2140 }
2141 else
2142 {
2143 /*
2144 * return view directory path of s
2145 */
2146
2147 s = state.view[r->view].path;
2148 }
2149 sfputr(xp, s, -1);
2150 return;
2151 case 'W':
2152 /*
2153 * return license info
2154 */
2155
2156 if (*++op == '=')
2157 op++;
2158 n = 8 * 1024;
2159 if ((n = astlicense(sfstrrsrv(xp, n), n, s, op, '/', '*', '/')) < 0)
2160 error(2, "license: %s", sfstrseek(xp, 0, SEEK_CUR));
2161 else if (n > 0 && *(sfstrseek(xp, n, SEEK_CUR) - 1) == '\n')
2162 sfstrseek(xp, -1, SEEK_CUR);
2163 return;
2164 case 'X':
2165 /*
2166 * return s if it is an existing file
2167 * op[1] == 'P' does physical test
2168 */
2169
2170 if ((*(op + 1) == 'P' ? !lstat(s, &st) : !stat(s, &st)) == (sep == EQ))
2171 sfputr(xp, s, -1);
2172 return;
2173 case 'Z':
2174 /*
2175 * return the longer of the bound name and the alias name
2176 */
2177
2178 if (r)
2179 sfputr(xp, !(r->dynamic & D_alias) || !r->uname || strlen(r->name) >= strlen(r->uname) ? r->name : r->uname, -1);
2180 return;
2181 default:
2182 error(1, "invalid path name operator `%c'", *op);
2183 return;
2184 }
2185 }
2186
2187 /*
2188 * edit a single (expanded) file name s into xp
2189 *
2190 * each file component (described above) is modified as follows:
2191 *
2192 * KEEP component is kept unchanged
2193 * DELETE component is deleted
2194 * <string> component is changed to <string>
2195 */
2196
2197 void
edit(Sfio_t * xp,register char * s,char * dir,char * bas,char * suf)2198 edit(Sfio_t* xp, register char* s, char* dir, char* bas, char* suf)
2199 {
2200 register char* p;
2201 register char* q;
2202 long pos;
2203
2204 if (!*s)
2205 return;
2206 pos = sfstrtell(xp);
2207
2208 /*
2209 * directory
2210 */
2211
2212 q = dir;
2213 if (q != DELETE && q != KEEP && (!*q || *q == '.' && !*(q + 1)))
2214 q = DELETE;
2215 if (p = strrchr(s, '/'))
2216 {
2217 if (q == KEEP)
2218 while (s <= p)
2219 sfputc(xp, *s++);
2220 else
2221 s = ++p;
2222 }
2223 if (q != DELETE && q != KEEP)
2224 {
2225 sfputr(xp, q, -1);
2226 if (*q && *(sfstrseek(xp, 0, SEEK_CUR) - 1) != '/')
2227 sfputc(xp, '/');
2228 }
2229
2230 /*
2231 * base
2232 */
2233
2234 q = bas;
2235 if (!(p = strrchr(s, '.')) || p == s)
2236 p = s + strlen(s);
2237 else
2238 while (p > s && *(p - 1) == '.')
2239 p--;
2240 if (q == KEEP)
2241 while (s < p)
2242 sfputc(xp, *s++);
2243 else
2244 s = p;
2245 if (q != DELETE && q != KEEP)
2246 sfputr(xp, q, -1);
2247
2248 /*
2249 * suffix
2250 */
2251
2252 q = suf;
2253 if (*p && q == KEEP)
2254 sfputr(xp, s, -1);
2255 else if (q != DELETE && q != KEEP)
2256 sfputr(xp, q, -1);
2257
2258 /*
2259 * cleanup
2260 */
2261
2262 p = sfstrbase(xp) + pos + 1;
2263 q = sfstrseek(xp, 0, SEEK_CUR);
2264 while (q > p && *(q - 1) == '/')
2265 q--;
2266 pos = q - sfstrbase(xp);
2267 sfstrseek(xp, pos, SEEK_SET);
2268 }
2269
2270 /*
2271 * substitute a single (expanded) name s into xp
2272 */
2273
2274 static void
substitute(Sfio_t * xp,regex_t * re,register char * s)2275 substitute(Sfio_t* xp, regex_t* re, register char* s)
2276 {
2277 int n;
2278 regmatch_t match[10];
2279
2280 if (*s)
2281 {
2282 if (!(n = regexec(re, s, elementsof(match), match, 0)) && !(n = regsubexec(re, s, elementsof(match), match)))
2283 s = re->re_sub->re_buf;
2284 else if (n != REG_NOMATCH)
2285 regfatal(re, 2, n);
2286 sfputr(xp, s, -1);
2287 }
2288 }
2289
2290 static void
mimetype(Sfio_t * xp,char * file)2291 mimetype(Sfio_t* xp, char* file)
2292 {
2293 Sfio_t* sp;
2294 char* mime;
2295 Stat_t st;
2296
2297 static Magic_t* magic;
2298 static Magicdisc_t disc;
2299
2300 if (!magic)
2301 {
2302 disc.version = MAGIC_VERSION;
2303 disc.flags = MAGIC_MIME;
2304 disc.errorf = errorf;
2305 if (!(magic = magicopen(&disc)))
2306 error(3, "out of space [magic]");
2307 magicload(magic, NiL, 0);
2308 }
2309 if (rstat(file, &st, 0) || !(sp = rsfopen(file)))
2310 mime = "error";
2311 else
2312 {
2313 mime = magictype(magic, sp, file, &st);
2314 sfclose(sp);
2315 }
2316 sfputr(xp, mime, -1);
2317 }
2318
2319 /*
2320 * apply token op p on (possibly bound) s with result in xp
2321 *
2322 * NOTE: this and :D:B:S: were the first edit operators
2323 *
2324 * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
2325 * A B D E F G I M N O P Q R S T U V W X Y Z
2326 */
2327
2328 static void
token(Sfio_t * xp,char * s,register char * p,int sep)2329 token(Sfio_t* xp, char* s, register char* p, int sep)
2330 {
2331 register Rule_t* r;
2332 register Rule_t* x;
2333 register Var_t* v;
2334 register int op;
2335 char* ops;
2336 int dobind;
2337 int dounbind;
2338 int dowait;
2339 int force;
2340 int matched;
2341 int tst;
2342 int f;
2343 Time_t tm;
2344 List_t* q;
2345 List_t* z;
2346 Stat_t st;
2347 Sfio_t* sp;
2348 Sfio_t* tmp = 0;
2349
2350 dobind = 1;
2351 dounbind = 0;
2352 dowait = 1;
2353 while (op = *p)
2354 {
2355 p++;
2356 if (islower(op))
2357 op = toupper(op);
2358 switch (op)
2359 {
2360 case 'B':
2361 dounbind = 1;
2362 continue;
2363 case 'W':
2364 dowait = 0;
2365 continue;
2366 case 'X':
2367 dobind = 0;
2368 continue;
2369 }
2370 break;
2371 }
2372 ops = p;
2373 while (*p && *p++ != '?');
2374 switch (op)
2375 {
2376 case 'N':
2377 case 'V':
2378 if ((*s == 0) == ((op == 'V') == !(sep & NOT)))
2379 /*NOP*/;
2380 else if (*p)
2381 expand(xp, p);
2382 else
2383 sfputc(xp, '1');
2384 return;
2385 case 'Q':
2386 switch (*ops)
2387 {
2388 case 0:
2389 tst = !!getrule(s);
2390 break;
2391 case 'O':
2392 tst = isoption(s);
2393 break;
2394 case 'R':
2395 tst = (!dobind || getrule(s)) && !(nametype(s, NiL) & (NAME_altstate|NAME_staterule|NAME_statevar));
2396 break;
2397 case 'S':
2398 switch (*(ops + 1))
2399 {
2400 case 0:
2401 tst = NAME_altstate|NAME_staterule|NAME_statevar;
2402 break;
2403 case 'A':
2404 tst = NAME_altstate;
2405 break;
2406 case 'R':
2407 tst = NAME_staterule;
2408 break;
2409 case 'V':
2410 tst = NAME_statevar;
2411 break;
2412 default:
2413 tst = 0;
2414 break;
2415 }
2416 tst = (!dobind || getrule(s)) && (nametype(s, NiL) & tst);
2417 break;
2418 case 'V':
2419 switch (*(ops + 1))
2420 {
2421 case 0:
2422 tst = !!getvar(s);
2423 break;
2424 case 'I':
2425 tst = (!dobind || getvar(s)) && isintvar(s);
2426 break;
2427 case 'V':
2428 tst = (!dobind || getvar(s)) && !isintvar(s);
2429 break;
2430 default:
2431 tst = 0;
2432 break;
2433 }
2434 break;
2435 case 'Z':
2436 if (*++ops == '=')
2437 ops++;
2438 sfputr(xp, timefmt(ops, tmxdate(s, NiL, CURTIME)), -1);
2439 return;
2440 default:
2441 tst = 0;
2442 break;
2443 }
2444 if (tst == !(sep & NOT))
2445 sfputr(xp, s, -1);
2446 return;
2447 }
2448 r = makerule(s);
2449 if (dounbind)
2450 unbind(NiL, (char*)r, NiL);
2451 if (dobind)
2452 {
2453 tst = state.mam.regress && state.user > 1 && !(r->dynamic & D_bound);
2454 r = bind(r);
2455 if (tst && !(r->dynamic & D_built))
2456 sfprintf(state.mam.out, "%sbind %s\n", state.mam.label, mamname(r));
2457 if (op == 'F' && r->status == MAKING && dowait)
2458 {
2459 Frame_t* fp;
2460
2461 /*
2462 * don't wait for targets in the active frames
2463 */
2464
2465 for (fp = state.frame; fp->target != r; fp = fp->parent)
2466 if (fp == fp->parent)
2467 {
2468 complete(r, NiL, NiL, 0);
2469 break;
2470 }
2471 }
2472 }
2473 if (*ops == '=')
2474 ops++;
2475 if (!*r->name)
2476 {
2477 switch (op)
2478 {
2479 case 'Z':
2480 if (*ops != 'W')
2481 break;
2482 if (*++ops == '=')
2483 ops++;
2484 /*FALLTHROUGH*/
2485 case 'R':
2486 sfputr(xp, timefmt(ops, CURTIME), -1);
2487 break;
2488 }
2489 return;
2490 }
2491 tst = (notfile(r) || !r->time && ((state.questionable & 0x04000000) || !(r->dynamic & D_triggered)) || r->status == IGNORE || state.exec && r->status != NOTYET && (x = staterule(RULE, r, NiL, 0)) && !x->time || (r->dynamic & (D_member|D_membertoo)) == D_member) ? 0 : 'F';
2492 switch (op)
2493 {
2494 case 0:
2495 case '*':
2496 matched = 1;
2497 break;
2498 case 'A':
2499 if ((r->scan != SCAN_IGNORE || *ops == 'F' || *ops == 'f') && (s = arupdate(r->name)))
2500 {
2501 Frame_t* oframe;
2502 Frame_t frame;
2503
2504 oframe = state.frame;
2505 if (!(state.frame = r->active))
2506 {
2507 zero(frame);
2508 frame.target = r;
2509 state.frame = frame.parent = &frame;
2510 }
2511 expand(xp, s);
2512 state.frame = oframe;
2513 }
2514 return;
2515 case 'D':
2516 case 'E':
2517 if ((r->property & (P_parameter|P_statevar)) == P_statevar && (v = varstate(r, 0)))
2518 {
2519 if (*(p = v->value))
2520 {
2521 tmp = sfstropen();
2522 expand(tmp, p);
2523 p = sfstruse(tmp);
2524 }
2525 if (state.localview > 1)
2526 localvar(xp, v, p, op == 'D' ? V_local_D : V_local_E);
2527 else if (*p)
2528 {
2529 if (op == 'D')
2530 {
2531 if (*p != '-' || isdigit(*(p + 1)))
2532 sfprintf(xp, "-D%s", v->name);
2533 else
2534 op = 0;
2535 if (*p != '1' || *(p + 1))
2536 {
2537 if (!op)
2538 sfputr(xp, p, -1);
2539 else
2540 {
2541 sfputc(xp, '=');
2542 shquote(xp, p);
2543 }
2544 }
2545 }
2546 else
2547 {
2548 sfprintf(xp, "%s=", v->name);
2549 if (*p)
2550 {
2551 if (*p == '"' || *p == '\'')
2552 sfputr(xp, p, -1);
2553 else
2554 shquote(xp, p);
2555 }
2556 }
2557 }
2558 if (tmp)
2559 sfstrclose(tmp);
2560 }
2561 return;
2562 case 'F':
2563 if (!(op = *ops))
2564 {
2565 matched = (tst == 'F');
2566 break;
2567 }
2568 if (islower(op))
2569 op = toupper(op);
2570 if (tst == 'F' && op == 'R' && (r->dynamic & (D_bound|D_regular)) == (D_bound|D_regular))
2571 {
2572 matched = 1;
2573 break;
2574 }
2575 if (tst != 'F' || ((sep & GT) ? pathstat(r->name, &st) : lstat(r->name, &st)))
2576 {
2577 matched = 0;
2578 break;
2579 }
2580 switch (op)
2581 {
2582 case 'B':
2583 matched = S_ISBLK(st.st_mode);
2584 break;
2585 case 'C':
2586 matched = S_ISCHR(st.st_mode);
2587 break;
2588 case 'D':
2589 matched = S_ISDIR(st.st_mode);
2590 break;
2591 case 'F':
2592 case 'R':
2593 case '-':
2594 matched = S_ISREG(st.st_mode);
2595 break;
2596 case 'L':
2597 matched = S_ISLNK(st.st_mode);
2598 break;
2599 case 'P':
2600 matched = S_ISFIFO(st.st_mode);
2601 break;
2602 case 'X':
2603 matched = 1;
2604 break;
2605 default:
2606 error(2, "%c: unknown file type op", op);
2607 break;
2608 }
2609 break;
2610 case 'G':
2611 if (tst != 'F' && dobind || (r->property & (P_target|P_terminal)) == P_terminal)
2612 {
2613 matched = 0;
2614 break;
2615 }
2616 matched = 1;
2617 if ((r->dynamic & D_built) || (x = staterule(RULE, r, NiL, 0)) && (x->dynamic & D_built))
2618 break;
2619
2620 /*
2621 * the remaining checks are necessary because of
2622 * state.accept | state.ignorestate
2623 * the tests are conservative, allowing some built
2624 * files to go undetected
2625 */
2626
2627 if (r->property & P_target)
2628 {
2629 if (r->action || (r->property & (P_archive|P_command)) || !(state.questionable & 0x00004000) && (r->dynamic & D_dynamic))
2630 break;
2631 for (z = r->prereqs; z; z = z->next)
2632 if (z->rule->property & P_use) break;
2633 if (z)
2634 break;
2635 }
2636 tmp = sfstropen();
2637 matched = 0;
2638 f = x ? (x->property & P_implicit) : 0;
2639 for (z = internal.metarule->prereqs; z; z = z->next)
2640 {
2641 char stem[MAXNAME];
2642
2643 if (metamatch(stem, unbound(r), z->rule->name) && (!(r->property & P_terminal) || (z->rule->property & P_terminal)) && !(z->rule->property & f) && (x = metainfo('I', z->rule->name, NiL, 0)))
2644 for (q = x->prereqs; q; q = q->next)
2645 if ((x = metarule(q->rule->name, z->rule->name, 0)) && (!(r->property & P_terminal) || (x->property & P_terminal)) && !(x->property & f))
2646 {
2647 metaexpand(tmp, stem, q->rule->name);
2648 if ((x = bindfile(NiL, sfstruse(tmp), 0)) && (x->time || (x->property & P_target)))
2649 {
2650 matched = 1;
2651 break;
2652 }
2653 }
2654 }
2655 sfstrclose(tmp);
2656 break;
2657 case 'I':
2658 if (!(sp = sfopen(NiL, r->name, "r")))
2659 error(2, "%s: cannot read", r->name);
2660 else
2661 {
2662 switch (*ops)
2663 {
2664 case '-':
2665 case 'X':
2666 case 'x':
2667 tmp = xp;
2668 break;
2669 default:
2670 tmp = sfstropen();
2671 break;
2672 }
2673 sfmove(sp, tmp, SF_UNBOUND, -1);
2674 sfclose(sp);
2675 s = sfstrbase(tmp);
2676 p = s + sfstrtell(tmp);
2677 while (p > s && *(p - 1) == '\n')
2678 p--;
2679 sfstrseek(tmp, p - s, SEEK_SET);
2680 if (tmp != xp)
2681 {
2682 expand(xp, sfstruse(tmp));
2683 sfstrclose(tmp);
2684 }
2685 }
2686 return;
2687 case 'M':
2688 if (!*ops)
2689 ops = *(ops - 1) == '=' ? " : " : " ";
2690 parentage(xp, r, ops);
2691 return;
2692 case 'O':
2693 p = "w";
2694 sep = '\n';
2695 tst = 1;
2696 for (;; ops++)
2697 {
2698 switch (*ops)
2699 {
2700 case '+':
2701 case 'A':
2702 case 'a':
2703 p = "a";
2704 continue;
2705 case '-':
2706 case 'N':
2707 case 'n':
2708 sep = -1;
2709 continue;
2710 case 'X':
2711 case 'x':
2712 tst = 0;
2713 continue;
2714 }
2715 break;
2716 }
2717 if (*ops == '=')
2718 ops++;
2719 if (!(sp = sfopen(NiL, r->name, p)))
2720 error(2, "%s: cannot write", r->name);
2721 else
2722 {
2723 if (tst)
2724 sfputr(sp, ops, sep);
2725 else
2726 {
2727 tmp = sfstropen();
2728 expand(tmp, ops);
2729 if (sep != -1) sfputc(tmp, sep);
2730 sep = sfstrtell(tmp);
2731 sfwrite(sp, sfstrbase(tmp), sep);
2732 sfstrclose(tmp);
2733 }
2734 if (sferror(sp))
2735 error(ERROR_SYSTEM|2, "%s: write error", r->name);
2736 sfclose(sp);
2737 }
2738 return;
2739 case 'P':
2740 matched = (tst == 'F' && (lstat(r->name, &st) || !S_ISLNK(st.st_mode)));
2741 break;
2742 case 'R':
2743 sfputr(xp, timefmt(ops, r->time), -1);
2744 return;
2745 case 'S':
2746 op = *ops++;
2747 if (*ops == '=')
2748 ops++;
2749 if (islower(op))
2750 op = toupper(op);
2751 if (force = op == 'F')
2752 {
2753 op = *ops++;
2754 if (*ops == '=')
2755 ops++;
2756 if (islower(op))
2757 op = toupper(op);
2758 }
2759 if (!op)
2760 {
2761 matched = (r->property & P_state) != 0;
2762 break;
2763 }
2764 x = 0;
2765 switch (op)
2766 {
2767 case 'A':
2768 if (r->property & P_staterule)
2769 x = rulestate(r, force);
2770 break;
2771 case 'M':
2772 x = metarule(r->name, ops, force);
2773 break;
2774 case 'P':
2775 x = staterule(PREREQS, r, NiL, force);
2776 break;
2777 case 'R':
2778 x = staterule(RULE, r, NiL, force);
2779 break;
2780 case 'V':
2781 if (!(r->property & P_state))
2782 x = staterule(VAR, NiL, r->name, force);
2783 break;
2784 default:
2785 error(2, "%c: unknown state op", op);
2786 break;
2787 }
2788 if (x)
2789 sfputr(xp, x->name, -1);
2790 return;
2791 case 'T':
2792 if (!*ops)
2793 tm = state.frame->target->time;
2794 else
2795 {
2796 tm = strtoull(ops, &s, 10);
2797 if (*s == '.')
2798 tm = tmxsns(tm, strtoull(s, &s, 10));
2799 if (*s)
2800 {
2801 if (x = getrule(ops))
2802 {
2803 if (dobind)
2804 x = bind(x);
2805 tm = x->time;
2806 }
2807 else
2808 tm = 0;
2809 }
2810 }
2811 if (septimecmp(sep, r->time, tm))
2812 sfputr(xp, r->name, -1);
2813 return;
2814 case 'U':
2815 if (r->property & P_staterule)
2816 {
2817 if (r = rulestate(r, *ops != 'Q'))
2818 sfputr(xp, r->name, -1);
2819 }
2820 else if (r->property & P_statevar)
2821 {
2822 if (v = varstate(r, *ops != 'Q'))
2823 sfputr(xp, v->name, -1);
2824 }
2825 else
2826 sfputr(xp, r->name, -1);
2827 return;
2828 case 'Y':
2829 mimetype(xp, r->name);
2830 return;
2831 case 'Z':
2832 switch (*ops++)
2833 {
2834 case 'C':
2835 tm = (x = staterule(PREREQS, r, NiL, 0)) ? x->time : 0;
2836 break;
2837 case 'E':
2838 tm = (x = staterule(RULE, r, NiL, 0)) ? x->event : 0;
2839 break;
2840 case 'R':
2841 tm = r->time;
2842 break;
2843 case 0:
2844 ops--;
2845 /*FALLTHROUGH*/
2846 case 'S':
2847 tm = (x = staterule(RULE, r, NiL, 0)) ? x->time : 0;
2848 break;
2849 case 'W':
2850 tm = CURTIME;
2851 break;
2852 default:
2853 tm = 0;
2854 error(1, "%s: unknown time component", ops);
2855 ops = null;
2856 break;
2857 }
2858 if (*ops == '=')
2859 ops++;
2860 sfputr(xp, timefmt(ops, tm), -1);
2861 return;
2862 default:
2863 matched = (op == tst);
2864 break;
2865 }
2866 if (matched == !(sep & NOT))
2867 {
2868 if (*p)
2869 expand(xp, p);
2870 else
2871 sfputr(xp, r->name, -1);
2872 }
2873 }
2874
2875 /*
2876 * return 1 if fp is an active frame
2877 */
2878
2879 static int
active(Rule_t * r,register Frame_t * fp)2880 active(Rule_t* r, register Frame_t* fp)
2881 {
2882 register Frame_t* ap;
2883 register Frame_t* pp;
2884
2885 for (pp = 0, ap = state.frame; ap && ap != pp; pp = ap, ap = ap->parent)
2886 if (fp == ap)
2887 return 1;
2888 if (!(r->property & P_joint))
2889 error(1, "%s: parentage not in active frame", r->name);
2890 return 0;
2891 }
2892
2893 /*
2894 * construct the parentage of r in xp, starting with r
2895 * sep placed between names
2896 */
2897
2898 void
parentage(Sfio_t * xp,register Rule_t * r,char * sep)2899 parentage(Sfio_t* xp, register Rule_t* r, char* sep)
2900 {
2901 if (r->active && active(r, r->active) && r->active->parent && !(r->active->parent->target->mark & M_mark) && r->active->parent->parent != r->active->parent)
2902 {
2903 r->mark |= M_mark;
2904 parentage(xp, r->active->parent->target, sep);
2905 r->mark &= ~M_mark;
2906 sfputr(xp, sep, -1);
2907 }
2908 sfputr(xp, (r->property & P_operator) && r->statedata ? r->statedata : r->name, -1);
2909 }
2910
2911 /*
2912 * copy s into xp if rule s has any attribute in att or
2913 * if att is 0 then copy the named attributes of rule s into xp
2914 * attribute pattern propagation is taken into account
2915 */
2916
2917 static void
attribute(Sfio_t * xp,char * s,register char * att,int sep)2918 attribute(Sfio_t* xp, char* s, register char* att, int sep)
2919 {
2920 register char* t;
2921 register Rule_t* r;
2922 register Rule_t* a;
2923 register List_t* p;
2924 long n;
2925 int c;
2926 int i;
2927 Rule_t* x;
2928 Rule_t* y;
2929 Rule_t* z;
2930
2931 i = 0;
2932 r = getrule(s);
2933 do
2934 {
2935 if (!r) z = 0;
2936 else if (!(r->dynamic & D_alias))
2937 {
2938 s = r->name;
2939 z = 0;
2940 }
2941 else if (!(z = getrule(r->name))) break;
2942 x = associate(internal.attribute_p, r, s, NiL);
2943 if (r || (x || (sep & LT)) && (r = makerule(s)))
2944 {
2945 n = r->attribute;
2946 if (x) n |= x->attribute;
2947 if (att)
2948 {
2949 for (;;)
2950 {
2951 while (isspace(*att)) att++;
2952 if ((t = strchr(att, c = '|')) || (t = strchr(att, c = ' '))) *t = 0;
2953 if (sep & GT)
2954 {
2955 for (p = r->prereqs; p; p = p->next)
2956 if (strmatch(p->rule->name, att))
2957 {
2958 if (t) *t = c;
2959 if (i) sfputc(xp, ' ');
2960 else i = 1;
2961 sfputr(xp, s, -1);
2962 goto next;
2963 }
2964 }
2965 else if (a = getrule(att))
2966 {
2967 if (sep & LT)
2968 {
2969 if ((y = associate(a, r, s, NiL)) || x && (y = associate(a, x, NiL, NiL)))
2970 {
2971 if (t) *t = c;
2972 if (i) sfputc(xp, ' ');
2973 else i = 1;
2974 sfputr(xp, y->name, -1);
2975 goto next;
2976 }
2977 }
2978 else if (hasattribute(r, a, x))
2979 {
2980 if (t) *t = c;
2981 if (sep == EQ)
2982 {
2983 if (i) sfputc(xp, ' ');
2984 else i = 1;
2985 sfputr(xp, s, -1);
2986 }
2987 goto next;
2988 }
2989 }
2990 if (!t) break;
2991 *t++ = c;
2992 att = t;
2993 }
2994 if (sep & NOT)
2995 {
2996 if (i) sfputc(xp, ' ');
2997 else i = 1;
2998 sfputr(xp, s, -1);
2999 }
3000 }
3001 else
3002 {
3003 if (n && r != internal.attribute)
3004 for (p = internal.attribute->prereqs; p; p = p->next)
3005 if (n & p->rule->attribute)
3006 {
3007 if (i) sfputc(xp, ' ');
3008 else i = 1;
3009 sfputr(xp, p->rule->name, -1);
3010 }
3011 if (r->scan && r != internal.scan)
3012 for (p = internal.scan->prereqs; p; p = p->next)
3013 if (p->rule->scan == r->scan)
3014 {
3015 if (i) sfputc(xp, ' ');
3016 else i = 1;
3017 sfputr(xp, p->rule->name, -1);
3018 break;
3019 }
3020 }
3021 }
3022 else if (att && (sep & NOT))
3023 {
3024 if (i) sfputc(xp, ' ');
3025 else i = 1;
3026 sfputr(xp, s, -1);
3027 }
3028 next: ;
3029 } while (r = z);
3030 }
3031
3032 /*
3033 * check if name generates a target matching the metarule pattern pat
3034 * (sep<) lists the primary and secondary targets for name
3035 */
3036
3037 static void
generate(Sfio_t * xp,char * name,char * pat,int sep)3038 generate(Sfio_t* xp, char* name, char* pat, int sep)
3039 {
3040 register Rule_t* x;
3041 register List_t* p;
3042 register List_t* q;
3043 char* b;
3044 char stem[MAXNAME];
3045
3046 if (pat[0] != '%' || pat[1])
3047 {
3048 if (metamatch(NiL, name, pat))
3049 {
3050 if (!(sep & NOT))
3051 sfputr(xp, name, -1);
3052 return;
3053 }
3054 if (!strchr(pat, '%'))
3055 {
3056 for (p = internal.metarule->prereqs; p; p = p->next)
3057 if (metamatch(stem, pat, p->rule->name) && (x = metainfo('I', p->rule->name, NiL, 0)))
3058 for (q = x->prereqs; q; q = q->next)
3059 if (metamatch(tmpname, name, q->rule->name) && streq(stem, tmpname))
3060 {
3061 if (!(sep & NOT))
3062 sfputr(xp, name, -1);
3063 return;
3064 }
3065 if (x = metainfo('N', NiL, NiL, 0))
3066 for (p = x->prereqs; p; p = p->next)
3067 if (metamatch(tmpname, name, p->rule->name) && (streq(tmpname, pat) || (b = strrchr(pat, '/')) && streq(tmpname, b + 1)))
3068 {
3069 if (!(sep & NOT))
3070 sfputr(xp, name, -1);
3071 return;
3072 }
3073 }
3074 else
3075 {
3076 Sfio_t* tp;
3077 long n;
3078
3079 tp = sfstropen();
3080 for (p = internal.metarule->prereqs; p; p = p->next)
3081 if (metamatch(NiL, p->rule->name, pat) && (x = metainfo('I', p->rule->name, NiL, 0)))
3082 {
3083 for (q = x->prereqs; q; q = q->next)
3084 if (metamatch(stem, name, q->rule->name))
3085 {
3086 if (!(sep & NOT))
3087 {
3088 /*UNDENT...*/
3089
3090 Rule_t* y;
3091 Rule_t* z;
3092 List_t* u;
3093 long b;
3094
3095 if (z = metarule(q->rule->name, p->rule->name, 0))
3096 {
3097 if (!z->uname)
3098 {
3099 if (y = metainfo('S', z->name, NiL, 0))
3100 {
3101 b = sfstrtell(xp);
3102 for (u = y->prereqs; u; u = u->next)
3103 {
3104 n = sfstrtell(xp);
3105 metaexpand(tp, stem, u->rule->name);
3106 generate(xp, sfstruse(tp), pat, sep);
3107 if (sfstrtell(xp) != n)
3108 sfputc(xp, ' ');
3109 if ((state.questionable & 0x00040000) && !(sep & LT))
3110 break;
3111 }
3112 if (sfstrtell(xp) != b)
3113 sfstrseek(xp, -1, SEEK_CUR);
3114 sfstrclose(tp);
3115 return;
3116 }
3117 }
3118 else if (y = metainfo('O', q->rule->name, NiL, 0))
3119 {
3120 for (u = y->prereqs; u; u = u->next)
3121 if (metamatch(NiL, u->rule->name, z->uname) && (y = metainfo('S', q->rule->name, u->rule->name, 0)))
3122 {
3123 b = sfstrtell(xp);
3124 for (u = y->prereqs; u; u = u->next)
3125 {
3126 n = sfstrtell(xp);
3127 metaexpand(tp, stem, u->rule->name);
3128 generate(xp, sfstruse(tp), pat, sep);
3129 if (sfstrtell(xp) != n)
3130 sfputc(xp, ' ');
3131 }
3132 if (sfstrtell(xp) != b)
3133 sfstrseek(xp, -1, SEEK_CUR);
3134 sfstrclose(tp);
3135 return;
3136 }
3137 }
3138 }
3139 metaexpand(xp, stem, z && z->uname && (y = metarule(z->uname, p->rule->name, 0)) && y->action && !*y->action ? z->uname : p->rule->name);
3140
3141 /*...INDENT*/
3142 }
3143 sfstrclose(tp);
3144 return;
3145 }
3146 if (!(state.questionable & 0x00000200))
3147 {
3148 char* t;
3149
3150 n = sfstrtell(xp);
3151 for (q = x->prereqs; q; q = q->next)
3152 if (q->rule->name != pat)
3153 {
3154 generate(tp, name, q->rule->name, sep);
3155 if (sfstrtell(tp))
3156 {
3157 t = sfstruse(tp);
3158 if (!streq(t, name))
3159 {
3160 if (sfstrtell(xp) != n)
3161 sfputc(xp, ' ');
3162 generate(xp, sfstruse(tp), pat, sep);
3163 }
3164 }
3165 }
3166 if (sfstrtell(xp) != n)
3167 {
3168 sfstrclose(tp);
3169 return;
3170 }
3171 }
3172 }
3173 sfstrclose(tp);
3174 }
3175 }
3176 else if (x = metainfo('N', NiL, NiL, 0))
3177 {
3178 for (p = x->prereqs; p; p = p->next)
3179 if (metamatch(tmpname, name, p->rule->name))
3180 {
3181 if (!(sep & NOT))
3182 metaexpand(xp, tmpname, p->rule->name);
3183 return;
3184 }
3185 }
3186 if (sep & NOT) sfputr(xp, name, -1);
3187 }
3188
3189 /*
3190 * quote s into xp according to sh syntax
3191 */
3192
3193 void
shquote(register Sfio_t * xp,char * s)3194 shquote(register Sfio_t* xp, char* s)
3195 {
3196 register char* t;
3197 register char* b;
3198 register int c;
3199 register int q;
3200
3201 if (*s == '"' && *(s + strlen(s) - 1) == '"')
3202 {
3203 sfprintf(xp, "\\\"%s\\\"", s);
3204 return;
3205 }
3206 q = 0;
3207 b = 0;
3208 for (t = s;;)
3209 {
3210 switch (c = *t++)
3211 {
3212 case 0:
3213 break;
3214 case '\n':
3215 case ';':
3216 case '&':
3217 case '|':
3218 case '<':
3219 case '>':
3220 case '(':
3221 case ')':
3222 case '[':
3223 case ']':
3224 case '{':
3225 case '}':
3226 case '*':
3227 case '?':
3228 case ' ':
3229 case '\t':
3230 case '\\':
3231 q |= 4;
3232 #if _HUH_2000_06_01
3233 if (!b)
3234 b = t - 1;
3235 #endif
3236 continue;
3237 case '\'':
3238 q |= 1;
3239 if (q & 2)
3240 break;
3241 continue;
3242 case '"':
3243 case '$':
3244 q |= 2;
3245 if (q & 1)
3246 break;
3247 continue;
3248 case '=':
3249 if (!q && !b && *(b = t) == '=')
3250 b++;
3251 continue;
3252 default:
3253 continue;
3254 }
3255 break;
3256 }
3257 if (!q)
3258 sfputr(xp, s, -1);
3259 else if (!(q & 1))
3260 {
3261 if (b)
3262 sfprintf(xp, "%-.*s'%s'", b - s, s, b);
3263 else
3264 sfprintf(xp, "'%s'", s);
3265 }
3266 else if (!(q & 2))
3267 {
3268 if (b)
3269 sfprintf(xp, "%-.*s\"%s\"", b - s, s, b);
3270 else
3271 sfprintf(xp, "\"%s\"", s);
3272 }
3273 else
3274 for (t = s;;)
3275 switch (c = *t++)
3276 {
3277 case 0:
3278 return;
3279 case '\n':
3280 sfputc(xp, '"');
3281 sfputc(xp, c);
3282 sfputc(xp, '"');
3283 break;
3284 case ';':
3285 case '&':
3286 case '|':
3287 case '<':
3288 case '>':
3289 case '(':
3290 case ')':
3291 case '[':
3292 case ']':
3293 case '{':
3294 case '}':
3295 case '$':
3296 case '*':
3297 case '?':
3298 case ' ':
3299 case '\t':
3300 case '\\':
3301 case '\'':
3302 case '"':
3303 sfputc(xp, '\\');
3304 /*FALLTHROUGH*/
3305 default:
3306 sfputc(xp, c);
3307 break;
3308 }
3309 }
3310
3311 /*
3312 * generate edit context in xp at cur from beg
3313 */
3314
3315 static char*
editcontext(register char * beg,register char * cur)3316 editcontext(register char* beg, register char* cur)
3317 {
3318 register int n;
3319
3320 sfstrseek(internal.tmp, 0, SEEK_SET);
3321 if ((n = cur - beg) > (EDITCONTEXT / 2)) beg = cur - (n = (EDITCONTEXT / 2));
3322 if (n > 0) sfprintf(internal.tmp, "%-*.*s", n, n, beg);
3323 if (*cur) sfprintf(internal.tmp, ">>>%c", *cur);
3324 sfprintf(internal.tmp, "<<<");
3325 if (*cur && *(cur + 1))
3326 {
3327 n = EDITCONTEXT - n;
3328 if (n > 0) sfprintf(internal.tmp, "%-*.*s", n, n, cur + 1);
3329 }
3330 return sfstruse(internal.tmp);
3331 }
3332
3333 /*
3334 * expand rules selected by $(...) into xp
3335 */
3336
3337 static void
expandall(register Sfio_t * xp,register unsigned long all)3338 expandall(register Sfio_t* xp, register unsigned long all)
3339 {
3340 register int sep;
3341 register Rule_t* r;
3342 Hash_position_t* pos;
3343
3344 sep = 0;
3345 if (pos = hashscan(table.rule, 0))
3346 {
3347 while (hashnext(pos))
3348 {
3349 r = (Rule_t*)pos->bucket->value;
3350 if (pos->bucket->name == r->name && !(r->dynamic & D_alias) && (!all || (r->dynamic & all)))
3351 {
3352 if (sep) sfputc(xp, ' ');
3353 else sep = 1;
3354 sfputr(xp, r->name, -1);
3355 }
3356 }
3357 hashdone(pos);
3358 }
3359 }
3360
3361 /*
3362 * apply edit operators ed on value v into xp
3363 *
3364 * :C:, :/:, :Y: and :?: have a different syntax from the other ops
3365 * :D:, :B: and :S:, when contiguous, are collected and applied as a group
3366 * a single `:' must separate each op, the trailing `:' is optional
3367 * =, !, !=, <>, <, <=, > and >= may separate an op from its value
3368 *
3369 * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
3370 * A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
3371 */
3372
3373 static void
expandops(Sfio_t * xp,char * v,char * ed,int del,int exp)3374 expandops(Sfio_t* xp, char* v, char* ed, int del, int exp)
3375 {
3376 register char* s;
3377 register int op;
3378 char* dir;
3379 char* bas;
3380 char* suf;
3381 char* val;
3382 char* oldp;
3383 char* newp;
3384 char* eb;
3385 int zer;
3386 int old;
3387 int qual;
3388 int cnt;
3389 int cntlim;
3390 int ctx;
3391 int expall;
3392 int n;
3393 int m;
3394 int sep;
3395 int tokenize;
3396 long beg;
3397 long ctx_beg;
3398 long cur;
3399 long arg;
3400 unsigned long all;
3401 char* ctx_end;
3402 char* tok;
3403 char* x;
3404 Rule_t* r;
3405 Edit_map_t* map;
3406 Hash_position_t* pos;
3407 int out;
3408 regex_t re;
3409 char flags[8];
3410 long top[2];
3411 Sfio_t* buf[2];
3412
3413 static unsigned long lla = D_select0;
3414
3415 /*
3416 * apply the operators from left to right
3417 */
3418
3419 out = 0;
3420 buf[0] = xp;
3421 top[0] = beg = sfstrtell(xp);
3422 buf[1] = 0;
3423 top[1] = 0;
3424 dir = bas = suf = DELETE;
3425 if (exp < 0)
3426 {
3427 if (state.expandall)
3428 {
3429 error(1, "$(...) recursion disabled");
3430 return;
3431 }
3432 state.expandall = 1;
3433 all = lla == D_select0 ? D_select1 : D_select0;
3434 }
3435 else
3436 {
3437 all = 0;
3438 if (exp)
3439 expand(xp, v);
3440 else
3441 {
3442 out = !out;
3443 xp = buf[out];
3444 beg = top[out];
3445 }
3446 }
3447 eb = ed;
3448 qual = 0;
3449 while (op = *ed++)
3450 {
3451 if (op == del || isspace(op))
3452 continue;
3453 if (op == '!')
3454 {
3455 while (isspace(*ed))
3456 ed++;
3457 if ((op = *ed++) == del)
3458 continue;
3459 qual |= NE;
3460 }
3461 if (islower(op))
3462 {
3463 qual |= ED_LONG;
3464 ed--;
3465 #if DEBUG
3466 if (state.test & 0x00000010)
3467 error(2, "edit +++ %-.32s", ed);
3468 #endif
3469 if (map = getedit(&ed, del))
3470 {
3471 switch (map->cmd.type)
3472 {
3473 case ED_COPY:
3474 goto copy;
3475 case ED_EDIT:
3476 qual |= ED_PARTS;
3477 if (!map->cmd.arg || *ed && *ed != del)
3478 {
3479 dir = bas = suf = KEEP;
3480 *--ed = '=';
3481 }
3482 goto copy;
3483 case ED_OP:
3484 if (map->options)
3485 {
3486 const Edit_opt_t* opt;
3487
3488 /*UNDENT...*/
3489 val = flags;
3490 if (!*(opt = map->options)->name)
3491 {
3492 if (opt->cmd.type == ED_QUAL)
3493 qual |= opt->cmd.op;
3494 else if (*ed != '-' || *(ed + 1) == '-' && (!*(ed + 2) || isspace(*(ed + 2))))
3495 {
3496 if (opt->cmd.op)
3497 op = opt->cmd.op;
3498 if (opt->cmd.arg && val < &flags[sizeof(flags)])
3499 {
3500 *val++ = opt->cmd.arg;
3501 if (opt->cmd.aux && val < &flags[sizeof(flags)])
3502 *val++ = opt->cmd.aux;
3503 }
3504 }
3505 }
3506 while (*ed == '-')
3507 {
3508 if ((n = *++ed) == '-' || n == 'o')
3509 {
3510 if (!*++ed || *ed == del)
3511 break;
3512 if (isspace(*ed))
3513 {
3514 if (n == '-')
3515 break;
3516 while (isspace(*++ed));
3517 }
3518 for (s = ed; *ed && *ed != del && !isspace(*ed); ed++);
3519 m = *ed;
3520 *ed = 0;
3521 for (opt = map->options;; opt++)
3522 {
3523 if (!opt->name)
3524 {
3525 error(1, "%s: --%s: unknown edit operator option", map->name, s);
3526 break;
3527 }
3528 if (streq(s, opt->name))
3529 {
3530 if (opt->cmd.type == ED_QUAL)
3531 qual ^= opt->cmd.op;
3532 else
3533 {
3534 if (opt->cmd.op)
3535 op = opt->cmd.op;
3536 if (opt->cmd.arg && val < &flags[sizeof(flags)])
3537 {
3538 *val++ = opt->cmd.arg;
3539 if (opt->cmd.aux && val < &flags[sizeof(flags)])
3540 *val++ = opt->cmd.aux;
3541 }
3542 }
3543 break;
3544 }
3545 }
3546 *ed = m;
3547 }
3548 else
3549 {
3550 while (n && n != del)
3551 {
3552 if (isspace(n))
3553 break;
3554 for (opt = map->options;; opt++)
3555 {
3556 if (!opt->name)
3557 {
3558 error(1, "%s: -%c: unknown edit operator option", map->name, n);
3559 break;
3560 }
3561 if (*opt->name == n)
3562 {
3563 if (opt->cmd.type == ED_QUAL)
3564 qual ^= opt->cmd.op;
3565 else
3566 {
3567 if (opt->cmd.op)
3568 op = opt->cmd.op;
3569 if (opt->cmd.arg && val < &flags[sizeof(flags)])
3570 {
3571 *val++ = opt->cmd.arg;
3572 if (opt->cmd.aux && val < &flags[sizeof(flags)])
3573 *val++ = opt->cmd.aux;
3574 }
3575 }
3576 break;
3577 }
3578 }
3579 n = *++ed;
3580 }
3581 }
3582 while (isspace(*ed))
3583 ed++;
3584 }
3585 while (val > flags)
3586 *--ed = *--val;
3587 /*..INDENT*/
3588 }
3589 break;
3590 case ED_QUAL:
3591 qual |= map->cmd.op;
3592 continue;
3593 }
3594 s = (*ed && *ed != del || (qual & (NOT|EQ|LT|GT))) ? null : ed;
3595 if (op != 'T')
3596 qual &= ~(ED_NOBIND|ED_NOWAIT);
3597 else if (map->cmd.arg != 'S')
3598 qual &= ~(ED_FORCE);
3599 if (map->cmd.arg)
3600 {
3601 if (map->cmd.aux)
3602 *--ed = map->cmd.aux;
3603 if (qual & ED_FORCE)
3604 *--ed = 'F';
3605 *--ed = map->cmd.arg;
3606 }
3607 if (qual & ED_NOBIND)
3608 *--ed = 'X';
3609 if (qual & ED_NOWAIT)
3610 *--ed = 'W';
3611 if (ed != s)
3612 {
3613 if (qual & (NOT|EQ|LT|GT))
3614 {
3615 if (qual & EQ)
3616 *--ed = '=';
3617 if (qual & GT)
3618 *--ed = '>';
3619 if (qual & LT)
3620 *--ed = '<';
3621 if ((qual & (NOT|GT|LT)) == NOT)
3622 *--ed = '!';
3623 }
3624 else
3625 *--ed = '=';
3626 }
3627 copy:
3628 *--ed = map->cmd.op;
3629 }
3630 else
3631 {
3632 for (eb = ed; islower(*ed); ed++);
3633 error(3, "unknown edit operator: %-.*s", ed - eb, eb);
3634 }
3635 if (qual & ED_JOIN)
3636 *--ed = '@';
3637 #if DEBUG
3638 if (state.test & 0x00000010)
3639 error(2, "edit --- %-.32s", ed);
3640 #endif
3641 op = *ed++;
3642 }
3643
3644 /*
3645 * check for tokenization
3646 */
3647
3648 expall = op == 'O' && (!*ed || *ed == del);
3649 if (op == '@')
3650 {
3651 tokenize = 0;
3652 if (!(op = *ed++) || op == del)
3653 error(3, "prefix edit operator only: %s", editcontext(eb, ed));
3654 if (islower(op))
3655 op = toupper(op);
3656 }
3657 else
3658 tokenize = 1;
3659 sep = 0;
3660
3661 /*
3662 * collect the operands
3663 */
3664
3665 if (op == 'C' || op == '/')
3666 {
3667 /*
3668 * substitute: <delim><old><delim><new><delim>[flags]
3669 */
3670
3671 switch (op)
3672 {
3673 case 'C':
3674 break;
3675 case '/':
3676 op = 'C';
3677 ed--;
3678 break;
3679 }
3680 s = ed;
3681 if (!(n = regcomp(&re, ed, REG_DELIMITED|REG_LENIENT|REG_NULL)))
3682 {
3683 ed += re.re_npat;
3684 if (!(n = regsubcomp(&re, ed, submap, 0, 0)))
3685 ed += re.re_npat;
3686 }
3687 if (n)
3688 {
3689 regfatalpat(&re, 2, n, s);
3690 while (*ed && *ed++ != del);
3691 continue;
3692 }
3693 if (*ed)
3694 {
3695 if (*ed != del)
3696 error(1, "invalid character after substitution: %s", editcontext(eb, ed));
3697 while (*ed && *ed++ != del);
3698 }
3699 if (*++s == ' ')
3700 tokenize = 0;
3701 }
3702 else if (op == 'Y' || op == '?')
3703 {
3704 /*
3705 * conditional: <delim><non-null><delim><null><delim>
3706 */
3707
3708 n = op;
3709 switch (op)
3710 {
3711 case 'Y':
3712 if (n = *ed)
3713 ed++;
3714 break;
3715 case '?':
3716 op = 'Y';
3717 break;
3718 }
3719 oldp = ed;
3720 while (*ed && *ed != n)
3721 if (*ed++ == '\\' && !*ed++)
3722 error(3, "unterminated lhs of conditional: %s", editcontext(eb, ed));
3723 s = ed;
3724 if (*ed == n)
3725 ed++;
3726 *s = 0;
3727 newp = ed;
3728 while (*ed && *ed != n)
3729 if (*ed++ == '\\' && !*ed++)
3730 error(3, "unterminated rhs of conditional: %s", editcontext(eb, ed));
3731 s = ed;
3732 if (*ed)
3733 ed++;
3734 *s = 0;
3735 old = zer = 0;
3736 while (*ed && *ed != del && !isspace(*ed))
3737 switch (*ed++)
3738 {
3739 case 'O':
3740 case 'o':
3741 old = 1;
3742 break;
3743 case 'Z':
3744 case 'z':
3745 case '+':
3746 case '-':
3747 zer = 1;
3748 break;
3749 default:
3750 error(1, "invalid character after conditional: %s", editcontext(eb, ed));
3751 break;
3752 }
3753 if (*ed)
3754 ed++;
3755 }
3756 else if (*ed == del || (qual & ED_LONG) && isspace(*ed))
3757 {
3758 ed++;
3759 val = KEEP;
3760 }
3761 else if (*ed)
3762 {
3763 /*
3764 * value: [~!<>][=][<val>]
3765 */
3766
3767 if (isupper(op))
3768 {
3769 for (;; ed++)
3770 {
3771 switch (*ed)
3772 {
3773 case '=':
3774 sep |= EQ;
3775 ed++;
3776 break;
3777 case '-':
3778 case '+':
3779 if (!sep)
3780 {
3781 sep |= EQ;
3782 ed++;
3783 }
3784 break;
3785 case '~':
3786 if (sep & MAT)
3787 break;
3788 sep |= MAT;
3789 continue;
3790 case '^':
3791 if (sep & HAT)
3792 break;
3793 sep |= HAT;
3794 continue;
3795 case '!':
3796 if (sep & NOT)
3797 break;
3798 sep |= NOT;
3799 continue;
3800 case '<':
3801 if (sep & LT)
3802 break;
3803 sep |= LT;
3804 continue;
3805 case '>':
3806 if (sep & GT)
3807 break;
3808 sep |= GT;
3809 continue;
3810 }
3811 break;
3812 }
3813 if (!sep)
3814 {
3815 error(3, "edit operator delimiter omitted: %s", editcontext(eb, ed + 1));
3816 break;
3817 }
3818 }
3819 val = ed;
3820 for (cnt = n = 0; *ed; ed++)
3821 {
3822 if (cnt)
3823 {
3824 if (*ed == cnt)
3825 n++;
3826 else if (*ed == cntlim && !--n)
3827 cnt = 0;
3828 }
3829 else if (*ed == '(')
3830 {
3831 cnt = '(';
3832 cntlim = ')';
3833 n++;
3834 }
3835 else if (*ed == '[')
3836 {
3837 cnt = '[';
3838 cntlim = ']';
3839 n++;
3840 }
3841 else if (n <= 0)
3842 {
3843 if (*ed == del)
3844 break;
3845 if ((qual & ED_LONG) && isspace(*ed))
3846 {
3847 s = ed;
3848 while (isspace(*++s));
3849 if (*s == del)
3850 break;
3851 }
3852 }
3853 }
3854 if (*ed)
3855 *ed++ = 0;
3856 if (!*val)
3857 val = DELETE;
3858 }
3859 else
3860 val = KEEP;
3861 switch (op)
3862 {
3863 case 'B':
3864 bas = val;
3865 parts:
3866 if (!(qual & ED_PARTS))
3867 {
3868 /*
3869 * B, D and S are grouped before application
3870 */
3871
3872 switch (*ed)
3873 {
3874 case 'B':
3875 if (bas == DELETE)
3876 continue;
3877 break;
3878 case 'D':
3879 if (dir == DELETE)
3880 continue;
3881 break;
3882 case 'S':
3883 if (suf == DELETE)
3884 continue;
3885 break;
3886 }
3887 }
3888 break;
3889 case 'D':
3890 dir = val;
3891 goto parts;
3892 case 'S':
3893 suf = val;
3894 goto parts;
3895 case 'E':
3896 case 'H':
3897 case 'I':
3898 case 'J':
3899 case 'K':
3900 case 'L':
3901 case 'R':
3902 case 'U':
3903 case 'W':
3904 case 'X':
3905 case 'Z':
3906 case '-':
3907 case '+':
3908 case '~':
3909 tokenize = 0;
3910 break;
3911 }
3912 qual = 0;
3913
3914 /*
3915 * validate the operator and apply non-tokenizing operators
3916 */
3917
3918 if (all && (expall || !tokenize))
3919 {
3920 expandall(xp, exp < 0 ? 0 : all);
3921 all = 0;
3922 state.expandall = 0;
3923 }
3924 if (!all)
3925 {
3926 if (xp)
3927 {
3928 sfputc(xp, 0);
3929 x = sfstrseek(xp, beg, SEEK_SET);
3930 }
3931 else
3932 x = v;
3933 out = !out;
3934 if (!(xp = buf[out]))
3935 xp = buf[out] = sfstropen();
3936 beg = top[out];
3937 }
3938 ctx = !!state.context;
3939 switch (op)
3940 {
3941 case 'A':
3942 case 'Q':
3943 if (val == KEEP || val == DELETE)
3944 val = 0;
3945 break;
3946 case 'B':
3947 case 'D':
3948 case 'S':
3949 if (!dir)
3950 ctx = 0;
3951 break;
3952 case 'C':
3953 case 'Y':
3954 ctx = 0;
3955 break;
3956 case 'E':
3957 sfprintf(xp, "%ld", expr(xp, x));
3958 continue;
3959 case 'F':
3960 case 'X':
3961 ctx = 0;
3962 /*FALLTHROUGH*/
3963 case 'P':
3964 case 'T':
3965 if (val == KEEP || val == DELETE)
3966 error(3, "edit operator value omitted: %s", editcontext(eb, ed));
3967 switch (op)
3968 {
3969 case 'X':
3970 cross(xp, x, val);
3971 continue;
3972 }
3973 break;
3974 case 'G':
3975 case 'I':
3976 case 'J':
3977 case 'K':
3978 case 'L':
3979 case 'W':
3980 case 'Z':
3981 ctx = 0;
3982 /*FALLTHROUGH*/
3983 case 'M':
3984 case 'N':
3985 case 'O':
3986 case 'U':
3987 if (!sep)
3988 sep = EQ;
3989 if (val == KEEP || val == DELETE)
3990 val = null;
3991 switch (op)
3992 {
3993 case 'I':
3994 intersect(xp, x, val, sep);
3995 continue;
3996 case 'J':
3997 hasprereq(xp, x, val);
3998 continue;
3999 case 'K':
4000 linebreak(xp, x, val);
4001 continue;
4002 case 'L':
4003 n = SORT_first;
4004 if (sep & GT)
4005 n |= SORT_sort|SORT_invert;
4006 if (sep & LT)
4007 n |= SORT_sort;
4008 if (sep & EQ)
4009 n &= ~SORT_first;
4010 if (sep & NOT)
4011 n |= SORT_qualified;
4012 if (sep & MAT)
4013 n |= SORT_sort|SORT_version;
4014 if (sep & HAT)
4015 n |= SORT_force;
4016 if (x[0] == '<' && x[strlen(x)-1] == '>')
4017 switch (x[1])
4018 {
4019 case 'F':
4020 case 'f':
4021 listtab(xp, table.file, val, n);
4022 break;
4023 case 'R':
4024 case 'r':
4025 listtab(xp, table.rule, val, n);
4026 break;
4027 case 'V':
4028 case 'v':
4029 listtab(xp, table.var, val, n);
4030 break;
4031 default:
4032 error(2, "%s: unknown table", x);
4033 return;
4034 }
4035 else
4036 list(xp, x, val, n);
4037 continue;
4038 case 'M':
4039 if (n = regcomp(&re, val, REG_AUGMENTED|REG_LENIENT|REG_NOSUB|REG_NULL))
4040 {
4041 regfatalpat(&re, 2, n, val);
4042 continue;
4043 }
4044 break;
4045 case 'N':
4046 if (n = regcomp(&re, val, REG_SHELL|REG_AUGMENTED|REG_LEFT|REG_RIGHT|REG_LENIENT|REG_NOSUB|REG_NULL))
4047 {
4048 regfatalpat(&re, 2, n, val);
4049 continue;
4050 }
4051 break;
4052 case 'O':
4053 cntlim = (*val == 'N' || *val == 'n' || *val == '*') ? -1 : (int)strtol(val, NiL, 0);
4054 break;
4055 case 'U':
4056 uniq(xp, x, val, sep);
4057 continue;
4058 case 'W':
4059 op = *val++;
4060 if (!*val || *val == '=' && !*++val)
4061 val = 0;
4062 switch (op)
4063 {
4064 case 'O':
4065 order_recurse(xp, x, NiL, NiL, val, ORDER_force|ORDER_paths);
4066 break;
4067 case 'P':
4068 order_recurse(xp, x, getval(external.files, VAL_PRIMARY|VAL_AUXILIARY), getval(external.skip, VAL_PRIMARY|VAL_AUXILIARY), val, ORDER_force|ORDER_prereqs);
4069 break;
4070 case 'R':
4071 order_recurse(xp, x, getval(external.files, VAL_PRIMARY|VAL_AUXILIARY), getval(external.skip, VAL_PRIMARY|VAL_AUXILIARY), val, ORDER_force);
4072 break;
4073 default:
4074 error(1, "unknown edit operator `W=%c'", op);
4075 break;
4076 }
4077 continue;
4078 case 'Z':
4079 closure(xp, x, val);
4080 continue;
4081 }
4082 break;
4083 case 'H':
4084 if (x == v)
4085 {
4086 buf[1] = sfstropen();
4087 sfputr(buf[1], x, 0);
4088 x = sfstrseek(buf[1], 0, SEEK_SET);
4089 }
4090 n = SORT_sort;
4091 if (val == DELETE || val == KEEP)
4092 {
4093 if (sep & GT)
4094 n |= SORT_invert;
4095 if (sep & EQ)
4096 n |= SORT_numeric;
4097 if (sep & NOT)
4098 n |= SORT_uniq;
4099 if (sep & MAT)
4100 n |= SORT_version;
4101 }
4102 else
4103 for (;;)
4104 {
4105 switch (*val++)
4106 {
4107 case 0:
4108 break;
4109 case 'C':
4110 case 'c':
4111 n |= SORT_collate;
4112 continue;
4113 case 'F':
4114 case 'f':
4115 n |= SORT_first;
4116 continue;
4117 case 'I':
4118 case 'i':
4119 case 'R':
4120 case 'r':
4121 n |= SORT_invert;
4122 continue;
4123 case 'N':
4124 case 'n':
4125 n |= SORT_numeric;
4126 continue;
4127 case 'O':
4128 case 'o':
4129 n |= SORT_reverse;
4130 continue;
4131 case 'P':
4132 case 'p':
4133 n |= SORT_prefix;
4134 continue;
4135 case 'U':
4136 case 'u':
4137 n |= SORT_uniq;
4138 continue;
4139 case 'V':
4140 case 'v':
4141 n |= SORT_version;
4142 continue;
4143 }
4144 break;
4145 }
4146 sort(xp, x, n);
4147 continue;
4148 case 'R':
4149 parse(NiL, x, "expand", NiL);
4150 continue;
4151 case 'V':
4152 error(1, "edit operator `%c' must appear first", op);
4153 continue;
4154 case '-':
4155 case '+':
4156 case '~':
4157 if (val != KEEP && val != DELETE && (*x && (*x != '0' || *(x + 1))) == (op == '+'))
4158 expand(xp, val);
4159 else if (op != '~')
4160 expand(xp, x);
4161 continue;
4162 default:
4163 error(1, "unknown edit operator `%c'", op);
4164 continue;
4165 }
4166
4167 /*
4168 * the operator is applied to each token if tokenize!=0
4169 */
4170
4171 arg = beg;
4172 if (!all)
4173 tok = tokopen(x, 1);
4174 else if (!(pos = hashscan(table.rule, 0)))
4175 goto breakloop;
4176 for (cnt = 1, ctx_end = 0;; cnt++, ctx_end = 0)
4177 {
4178 if (!tokenize)
4179 {
4180 if (cnt > 1)
4181 break;
4182 s = x;
4183 }
4184 else if (all)
4185 {
4186 for (;;)
4187 {
4188 if (!hashnext(pos))
4189 goto breakloop;
4190 r = (Rule_t*)pos->bucket->value;
4191 if (pos->bucket->name == r->name && !(r->dynamic & D_alias) && (exp < 0 || (r->dynamic & all)))
4192 {
4193 r->dynamic &= ~all;
4194 break;
4195 }
4196 }
4197 s = r->name;
4198 }
4199 else
4200 {
4201 if (!(s = tokread(tok)))
4202 {
4203 if (cnt > 1)
4204 break;
4205 s = null;
4206 }
4207 if (*s != '\n' && (cur = sfstrtell(xp)) > arg)
4208 {
4209 if (!isspace(*(sfstrbase(xp) + cur - 1)))
4210 sfputc(xp, ' ');
4211 arg = sfstrtell(xp);
4212 }
4213 }
4214 if (ctx && iscontextp(s, &ctx_end))
4215 {
4216 s++;
4217 *(ctx_end - 1) = 0;
4218 sfputc(xp, MARK_CONTEXT);
4219 ctx_beg = sfstrtell(xp);
4220 }
4221 switch (op)
4222 {
4223 case 'A':
4224 attribute(xp, s, val, sep);
4225 break;
4226 case 'B':
4227 case 'D':
4228 case 'S':
4229 if (*s)
4230 {
4231 if (dir == KEEP)
4232 cur = sfstrtell(xp);
4233 edit(xp, s, dir, bas, suf);
4234 if (dir == KEEP && cur == sfstrtell(xp))
4235 sfputc(xp, '.');
4236 }
4237 break;
4238 case 'C':
4239 substitute(xp, &re, s);
4240 break;
4241 case 'F':
4242 #if !_drop_this_in_3_2
4243 switch (*val)
4244 {
4245 case 'L':
4246 val = "%(lower)s";
4247 message((-3, ":F=L: is obsolete -- use :F=%s: instead", val));
4248 break;
4249 case 'U':
4250 val = "%(upper)s";
4251 message((-3, ":F=U: is obsolete -- use :F=%s: instead", val));
4252 break;
4253 case 'V':
4254 val = "%(variable)s";
4255 message((-3, ":F=V: is obsolete -- use :F=%s: instead", val));
4256 break;
4257 }
4258 #endif
4259 strprintf(xp, val, s, 1, -1);
4260 break;
4261 case 'G':
4262 if (*val)
4263 {
4264 char* t;
4265 char* v;
4266
4267 t = tokopen(val, 1);
4268 while (v = tokread(t))
4269 generate(xp, s, v, sep);
4270 tokclose(t);
4271 }
4272 else
4273 generate(xp, s, val, sep);
4274 break;
4275 case 'M':
4276 case 'N':
4277 if ((n = regexec(&re, s, 0, NiL, 0)) && n != REG_NOMATCH)
4278 regfatal(&re, 2, n);
4279 else if ((n == 0) == (sep == EQ))
4280 sfputr(xp, s, -1);
4281 break;
4282 case 'O':
4283 if (cntlim < 0)
4284 sfstrseek(xp, arg = beg, SEEK_SET);
4285 else
4286 switch (sep)
4287 {
4288 case EQ:
4289 if (!cntlim)
4290 {
4291 if (s == null)
4292 goto breakloop;
4293 goto nextloop;
4294 }
4295 else if (cnt < cntlim)
4296 goto nextloop;
4297 else if (cnt > cntlim)
4298 goto breakloop;
4299 break;
4300 case NOT:
4301 if (!cntlim)
4302 {
4303 sfprintf(xp, "%d", strlen(s));
4304 goto nextloop;
4305 }
4306 /*FALLTHROUGH*/
4307 case NE:
4308 if (cnt == cntlim)
4309 goto nextloop;
4310 break;
4311 case LT:
4312 if (cnt >= cntlim)
4313 goto breakloop;
4314 break;
4315 case LE:
4316 if (cnt > cntlim)
4317 goto breakloop;
4318 break;
4319 case GE:
4320 if (cnt < cntlim)
4321 goto nextloop;
4322 break;
4323 case GT:
4324 if (cnt <= cntlim)
4325 goto nextloop;
4326 break;
4327 }
4328 sfputr(xp, s, -1);
4329 break;
4330 case 'P':
4331 pathop(xp, s, val, sep);
4332 break;
4333 case 'Q':
4334 shquote(xp, s);
4335 break;
4336 case 'T':
4337 token(xp, s, val, sep);
4338 break;
4339 case 'Y':
4340 expand(xp, (*s && (zer ? (*s != '0' || *(s + 1)) : 1)) ? (old ? s : oldp) : newp);
4341 break;
4342 #if DEBUG
4343 default:
4344 error(PANIC, "edit operator `%c' not applied to each token", op);
4345 #endif
4346 }
4347 nextloop:
4348 if (ctx_end)
4349 {
4350 *ctx_end = MARK_CONTEXT;
4351 n = sfstrtell(xp) - ctx_beg;
4352 if (!n)
4353 sfstrseek(xp, -1, SEEK_CUR);
4354 else if (n > 1 && *(s = sfstrbase(xp) + ctx_beg) == MARK_CONTEXT)
4355 *(s - 1) = ' ';
4356 else
4357 {
4358 sfputc(xp, MARK_CONTEXT);
4359 s = sfstrbase(xp) + ctx_beg;
4360 x = s + n + 1;
4361 m = 0;
4362 while (s < x)
4363 if (*s++ == ' ')
4364 for (m++; s < x && *s == ' '; s++);
4365 if (m)
4366 {
4367 sfprintf(xp, "%*s", m * 2, null);
4368 x = sfstrbase(xp) + ctx_beg + n + 1;
4369 s = x + m * 2;
4370 for (;;)
4371 {
4372 if ((*--s = *--x) == ' ')
4373 {
4374 *s = MARK_CONTEXT;
4375 for (x++; *(x - 1) == ' '; *--s = *--x);
4376 *--s = MARK_CONTEXT;
4377 if (--m <= 0)
4378 break;
4379 }
4380 }
4381 }
4382 }
4383 }
4384 if (all && sfstrtell(xp) > beg)
4385 {
4386 sfputc(xp, 0);
4387 makerule(sfstrseek(xp, beg, SEEK_SET))->dynamic |= lla;
4388 }
4389 }
4390 breakloop:
4391 if (ctx_end)
4392 {
4393 *ctx_end = MARK_CONTEXT;
4394 if (sfstrtell(xp) == ctx_beg)
4395 sfstrseek(xp, -1, SEEK_CUR);
4396 }
4397 if (all)
4398 {
4399 if (pos)
4400 {
4401 hashdone(pos);
4402 if (pos = hashscan(table.rule, 0))
4403 {
4404 /*
4405 * a poorly understood interaction
4406 * between binding/aliasing lets
4407 * some unwanted rules slip through
4408 * this loop catches them
4409 */
4410
4411 while (hashnext(pos))
4412 {
4413 r = (Rule_t*)pos->bucket->value;
4414 r->dynamic &= ~all;
4415 }
4416 hashdone(pos);
4417 }
4418 }
4419 n = all;
4420 all = lla;
4421 lla = n;
4422 exp++;
4423 }
4424 else
4425 {
4426 tokclose(tok);
4427 if (sfstrtell(xp) == arg)
4428 {
4429 if (op == 'O' && !cntlim)
4430 sfprintf(xp, "%d", cnt - 1);
4431 else if (arg > beg)
4432 sfstrseek(xp, -1, SEEK_CUR);
4433 }
4434 }
4435
4436 /*
4437 * operator cleanup
4438 */
4439
4440 switch (op)
4441 {
4442 case 'B':
4443 case 'D':
4444 case 'S':
4445 dir = bas = suf = DELETE;
4446 break;
4447 case 'C':
4448 case 'M':
4449 case 'N':
4450 regfree(&re);
4451 break;
4452 }
4453 }
4454 if (all)
4455 {
4456 expandall(buf[0], exp < 0 ? 0 : all);
4457 state.expandall = 0;
4458 }
4459 else
4460 {
4461 if (out)
4462 sfputr(buf[0], xp ? sfstruse(xp) : v, -1);
4463 if (buf[1])
4464 sfstrclose(buf[1]);
4465 }
4466 }
4467
4468 /*
4469 * expand first non-null of nvars variables in s with edit ops ed into xp
4470 */
4471
4472 static void
expandvars(register Sfio_t * xp,register char * s,char * ed,int del,int nvars)4473 expandvars(register Sfio_t* xp, register char* s, char* ed, int del, int nvars)
4474 {
4475 register char* v;
4476 char* t;
4477 int exp;
4478 int op;
4479 int aux;
4480 int ign;
4481 long pos;
4482 Edit_map_t* map;
4483 Sfio_t* cvt = 0;
4484 Sfio_t* val = 0;
4485 Sfio_t* tmp;
4486 #if DEBUG
4487 long beg;
4488 Sfio_t* msg = 0;
4489 #endif
4490
4491 static int level;
4492
4493 if (level++ > 64)
4494 {
4495 level = 0;
4496 error(3, "%s: recursive variable definition", s);
4497 }
4498 #if DEBUG
4499 if (error_info.trace <= -10)
4500 {
4501 beg = sfstrtell(xp);
4502 msg = sfstropen();
4503 }
4504 #endif
4505
4506 /*
4507 * some operators must appear first (and before expansion)
4508 */
4509
4510 exp = 1;
4511 aux = 0;
4512 if (ed)
4513 {
4514 while (op = *ed)
4515 {
4516 if (op == del || isspace(op))
4517 ed++;
4518 else if (islower(op))
4519 {
4520 t = ed;
4521 if ((map = getedit(&ed, del)) && map->cmd.type == ED_QUAL)
4522 switch (map->cmd.op)
4523 {
4524 case ED_AUXILLIARY:
4525 exp = 0;
4526 aux |= VAL_AUXILIARY;
4527 continue;
4528 case ED_LITERAL:
4529 exp = 0;
4530 continue;
4531 case ED_PRIMARY:
4532 exp = 0;
4533 aux |= VAL_PRIMARY;
4534 continue;
4535 }
4536 ed = t;
4537 break;
4538 }
4539 else if (op != 'V')
4540 break;
4541 else
4542 {
4543 exp = ign = 0;
4544 for (;;)
4545 {
4546 switch (*++ed)
4547 {
4548 case 0:
4549 break;
4550 case 'A':
4551 aux |= VAL_AUXILIARY;
4552 continue;
4553 case 'B':
4554 aux |= VAL_BRACE;
4555 continue;
4556 case 'F':
4557 aux |= VAL_FILE;
4558 continue;
4559 case 'I':
4560 ign = 1;
4561 continue;
4562 case 'P':
4563 aux |= VAL_PRIMARY;
4564 continue;
4565 case 'U':
4566 aux |= VAL_UNBOUND;
4567 continue;
4568 case 'X':
4569 exp = 1;
4570 continue;
4571 default:
4572 if (*ed == del)
4573 {
4574 ed++;
4575 break;
4576 }
4577 if (!ign)
4578 error(1, "edit operator `%c' operand `%c' ignored", op, *ed);
4579 continue;
4580 }
4581 break;
4582 }
4583 }
4584 }
4585 }
4586 if (!(aux & (VAL_PRIMARY|VAL_AUXILIARY)))
4587 aux |= VAL_PRIMARY|VAL_AUXILIARY;
4588 for (;;)
4589 {
4590 #if DEBUG
4591 if (msg)
4592 {
4593 sfprintf(msg, "var=%s,ops=%s", s, ed);
4594 sfstruse(msg);
4595 }
4596 #endif
4597 if ((*s == '"' || *s == MARK_QUOTE) && *(s + 1) && *(t = s + strlen(s) - 1) == *s)
4598 {
4599 if (!cvt)
4600 cvt = sfstropen();
4601 *t = 0;
4602 sfputr(cvt, s + 1, 0);
4603 *t = *s;
4604 stresc(v = sfstrbase(cvt));
4605 }
4606 else
4607 {
4608 if (strchr(s, '$'))
4609 {
4610 if (!cvt)
4611 cvt = sfstropen();
4612 expand(cvt, s);
4613 v = sfstruse(cvt);
4614 }
4615 else v = s;
4616 if (nvars == 1 && streq(v, "..."))
4617 {
4618 exp = -1;
4619 if (!ed)
4620 ed = null;
4621 }
4622 else
4623 v = getval(v, aux);
4624 }
4625 if (state.mam.statix && nvars > 1 && strmatch(v, "${mam_*}") && (!ed || !*ed))
4626 {
4627 if (!cvt)
4628 cvt = sfstropen();
4629 exp = 0;
4630 for (;;)
4631 {
4632 if (nvars > 1 && strmatch(v, "${mam_*}"))
4633 {
4634 sfwrite(cvt, v, strlen(v) - 1);
4635 sfputc(cvt, '-');
4636 exp++;
4637 }
4638 else
4639 sfputr(cvt, v, -1);
4640 if (--nvars <= 0)
4641 break;
4642 while (*s++);
4643 v = getval(s, aux);
4644 }
4645 while (exp--)
4646 sfputc(cvt, '}');
4647 sfputr(xp, sfstruse(cvt), -1);
4648 break;
4649 }
4650 if (*v || --nvars <= 0)
4651 {
4652 if (ed)
4653 {
4654 if (!cvt)
4655 cvt = sfstropen();
4656 if (v == sfstrbase(internal.val))
4657 {
4658 tmp = internal.val;
4659 if (!val)
4660 val = sfstropen();
4661 internal.val = val;
4662 }
4663 else
4664 {
4665 tmp = 0;
4666 if (v == sfstrbase(cvt))
4667 v = 0;
4668 }
4669 pos = sfstrtell(cvt);
4670 expand(cvt, ed);
4671 sfputc(cvt, 0);
4672 expandops(xp, v ? v : sfstrbase(cvt), sfstrbase(cvt) + pos, del, exp);
4673 if (tmp)
4674 internal.val = tmp;
4675 }
4676 else if (*v)
4677 expand(xp, v);
4678 break;
4679 } while (*s++);
4680 }
4681 #if DEBUG
4682 if (msg)
4683 {
4684 pos = sfstrtell(xp);
4685 sfputc(xp, 0);
4686 error(-10, "expand(%s,lev=%d): `%s'", sfstrbase(msg), level, sfstrseek(xp, beg, SEEK_SET));
4687 sfstrseek(xp, pos, SEEK_SET);
4688 sfstrclose(msg);
4689 }
4690 #endif
4691 if (cvt)
4692 sfstrclose(cvt);
4693 if (val)
4694 sfstrclose(val);
4695 level--;
4696 }
4697
4698 /*
4699 * expand `$(...)' from a into xp
4700 */
4701
4702 void
expand(register Sfio_t * xp,register char * a)4703 expand(register Sfio_t* xp, register char* a)
4704 {
4705 register int c;
4706 register char* s;
4707 int alt;
4708 int del;
4709 int p;
4710 int q;
4711 int nvars;
4712 long ed;
4713 long var;
4714 Sfio_t* val = 0;
4715
4716 if (!*a) return;
4717 if (!(s = strchr(a, '$')))
4718 {
4719 sfputr(xp, a, -1);
4720 return;
4721 }
4722 if (a == sfstrbase(internal.val) || state.val)
4723 {
4724 val = internal.val;
4725 internal.val = sfstropen();
4726 }
4727 sfwrite(xp, a, s - a);
4728 a = s;
4729 while (*a)
4730 {
4731 if (*a != '$')
4732 sfputc(xp, *a++);
4733 else if (*++a == '(')
4734 {
4735 if (isspace(*++a))
4736 {
4737 sfputc(xp, '$');
4738 sfputc(xp, '(');
4739 sfputc(xp, *a++);
4740 }
4741 else
4742 {
4743 var = sfstrtell(xp);
4744 ed = 0;
4745 nvars = 1;
4746 alt = '|';
4747 del = ':';
4748 q = 0;
4749 p = 1;
4750 while (c = *a++)
4751 {
4752 if (c == '\\' && *a)
4753 {
4754 sfputc(xp, c);
4755 c = *a++;
4756 }
4757 else if (c == '"')
4758 q = !q;
4759 else if (q)
4760 /* quoted */;
4761 else if (c == '(')
4762 p++;
4763 else if (c == ')')
4764 {
4765 if (!--p)
4766 break;
4767 }
4768 else if (!ed && p == 1)
4769 {
4770 if (c == alt)
4771 {
4772 c = 0;
4773 nvars++;
4774 }
4775 else if (c == del)
4776 {
4777 alt = c = 0;
4778 ed = sfstrtell(xp);
4779 }
4780 else if (c == '`')
4781 {
4782 alt = c = 0;
4783 ed = sfstrtell(xp);
4784 if (!(del = *a++))
4785 {
4786 ed--;
4787 break;
4788 }
4789 }
4790 else if (isspace(c))
4791 {
4792 for (alt = 0; isspace(*a); a++);
4793 if (*a == del || *a == '`')
4794 continue;
4795 }
4796 }
4797 sfputc(xp, c);
4798 }
4799 sfputc(xp, 0);
4800 s = sfstrseek(xp, var, SEEK_SET);
4801 if (q || !c)
4802 {
4803 a--;
4804 error(1, "missing %c in %s variable expansion", q ? '"' : ')', s);
4805 }
4806 expandvars(xp, s, ed ? sfstrbase(xp) + ed + 1 : (char*)0, del, nvars);
4807 }
4808 }
4809 else if (*a == '$')
4810 {
4811 for (s = a; *s == '$'; s++);
4812 if (*s != '(')
4813 sfputc(xp, '$');
4814 while (a < s)
4815 sfputc(xp, *a++);
4816 }
4817 else
4818 sfputc(xp, '$');
4819 }
4820 if (val)
4821 {
4822 sfstrclose(internal.val);
4823 internal.val = val;
4824 }
4825 }
4826