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