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&LT) 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