commit 3997951
shrub
·
2026-04-12 12:38:14 +0000 UTC
parent 3ff2026
split eval into two files and make expandstr not be a huge monolithic function, handle addsuffix and sort, check viability of pattern rule
8 files changed,
+836,
-718
+0,
-715
1@@ -1,715 +0,0 @@
2-#include "shinobi.h"
3-#include "internal.h"
4-
5-#include <ctype.h>
6-#include <stddef.h>
7-#include <stdio.h>
8-#include <stdlib.h>
9-#include <string.h>
10-
11-/*
12- * eval does the second pass over the built ast
13- * seed builtins like CC and so on
14- * apply assignment semantics
15- * expand plain var refs and simple substitution refs
16- * execute conditionals and flatten the chosen branch
17- *
18- * right now only a small subset of gmake syntax is handled.
19- */
20-
21-static int evalerrs;
22-
23-static void
24-evalerr(const char *msg, const char *detail)
25-{
26- if (detail)
27- fprintf(stderr, "eval: unsupported: %s: %s\n", msg, detail);
28- else
29- fprintf(stderr, "eval: unsupported: %s\n", msg);
30- evalerrs++;
31-}
32-
33-struct Var *
34-findvar(struct Env *env, const char *name)
35-{
36- size_t i;
37-
38- for (i = 0; i < env->n; i++) {
39- if (strcmp(env->v[i].name, name) == 0)
40- return &env->v[i];
41- }
42- return 0;
43-}
44-
45-static int
46-isplainvar(const char *s, size_t n)
47-{
48- size_t i;
49-
50- if (!n)
51- return 0;
52- for (i = 0; i < n; i++) {
53- if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
54- return 0;
55- }
56- return 1;
57-}
58-
59-static char *
60-substword(const char *word, size_t n, const char *from, const char *to)
61-{
62- size_t nfrom, nto;
63- const char *pct;
64- char *out;
65-
66- nfrom = strlen(from);
67- nto = strlen(to);
68- pct = strchr(from, '%');
69- if (!pct) {
70- if (n < nfrom || memcmp(word + n - nfrom, from, nfrom) != 0)
71- return xstrndup(word, n);
72- out = xmalloc(n - nfrom + nto + 1);
73- memcpy(out, word, n - nfrom);
74- memcpy(out + n - nfrom, to, nto);
75- out[n - nfrom + nto] = 0;
76- return out;
77- }
78- {
79- size_t pre, suf, stem;
80- const char *tpct;
81-
82- pre = (size_t)(pct - from);
83- suf = nfrom - pre - 1;
84- if (n < pre + suf)
85- return xstrndup(word, n);
86- if (memcmp(word, from, pre) != 0 || memcmp(word + n - suf, pct + 1, suf) != 0)
87- return xstrndup(word, n);
88- stem = n - pre - suf;
89- tpct = strchr(to, '%');
90- if (!tpct) {
91- out = xstrdup(to);
92- return out;
93- }
94- {
95- size_t tpre, tsuf;
96-
97- tpre = (size_t)(tpct - to);
98- tsuf = nto - tpre - 1;
99- out = xmalloc(tpre + stem + tsuf + 1);
100- memcpy(out, to, tpre);
101- memcpy(out + tpre, word + pre, stem);
102- memcpy(out + tpre + stem, tpct + 1, tsuf);
103- out[tpre + stem + tsuf] = 0;
104- return out;
105- }
106- }
107-}
108-
109-static char *
110-substval(const char *val, const char *from, const char *to)
111-{
112- size_t i, j, n, cap, len, wn;
113- char *out, *part;
114-
115- n = strlen(val);
116- cap = n + 1;
117- len = 0;
118- out = xmalloc(cap);
119- for (i = 0; i < n;) {
120- if (isspace((unsigned char)val[i])) {
121- if (len + 2 > cap) {
122- cap *= 2;
123- out = xrealloc(out, cap);
124- }
125- out[len++] = val[i++];
126- continue;
127- }
128- j = i;
129- while (j < n && !isspace((unsigned char)val[j]))
130- j++;
131- part = substword(val + i, j - i, from, to);
132- wn = strlen(part);
133- if (len + wn + 1 > cap) {
134- cap = len + wn + 1;
135- out = xrealloc(out, cap);
136- }
137- memcpy(out + len, part, wn);
138- len += wn;
139- free(part);
140- i = j;
141- }
142- out[len] = 0;
143- return out;
144-}
145-
146-static int
147-issubstref(const char *s, size_t n, size_t *colon, size_t *eq)
148-{
149- size_t i;
150-
151- for (i = 0; i < n; i++) {
152- if (s[i] == ':') {
153- *colon = i;
154- break;
155- }
156- if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
157- return 0;
158- }
159- if (i == 0 || i >= n)
160- return 0;
161- for (i = *colon + 1; i < n; i++) {
162- if (s[i] == '=') {
163- *eq = i;
164- return i > *colon + 1;
165- }
166- }
167- return 0;
168-}
169-
170-static ptrdiff_t
171-findargcomma(const char *s, size_t n)
172-{
173- size_t i, depth;
174-
175- depth = 0;
176- for (i = 0; i < n; i++) {
177- if (s[i] == '$' && i + 1 < n && (s[i + 1] == '(' || s[i + 1] == '{')) {
178- depth++;
179- i++;
180- continue;
181- }
182- if ((s[i] == ')' || s[i] == '}') && depth > 0) {
183- depth--;
184- continue;
185- }
186- if (s[i] == ',' && depth == 0)
187- return (ptrdiff_t)i;
188- }
189- return -1;
190-}
191-
192-char *
193-expandstr(struct Env *env, const char *s)
194-{
195- size_t i, j, k, n, cap, len, inner, start, colon, eq;
196- char close;
197- char *out, *name, *val, *from, *to;
198- struct Var *v;
199-
200- n = strlen(s);
201- cap = n + 1;
202- len = 0;
203- out = xmalloc(cap);
204- for (i = 0; i < n; i++) {
205- if (s[i] == '$' && i + 1 < n && s[i + 1] == '$') {
206- if (len + 3 > cap) {
207- cap *= 2;
208- out = xrealloc(out, cap);
209- }
210- out[len++] = '$';
211- out[len++] = '$';
212- i++;
213- continue;
214- }
215- if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
216- if (len + 2 > cap) {
217- cap *= 2;
218- out = xrealloc(out, cap);
219- }
220- out[len++] = s[i];
221- continue;
222- }
223- close = s[i + 1] == '(' ? ')' : '}';
224- start = i;
225- j = i + 2;
226- inner = 1;
227- while (j < n && inner) {
228- if (s[j] == '$' && j + 1 < n && (s[j + 1] == '(' || s[j + 1] == '{')) {
229- inner++;
230- j += 2;
231- continue;
232- }
233- if (s[j] == close)
234- inner--;
235- j++;
236- }
237- if (inner) {
238- if (len + (n - start) + 1 > cap) {
239- cap = len + (n - start) + 1;
240- out = xrealloc(out, cap);
241- }
242- memcpy(out + len, s + start, n - start);
243- len += n - start;
244- break;
245- }
246- if (isplainvar(s + i + 2, j - i - 3)) {
247- name = xstrndup(s + i + 2, j - i - 3);
248- v = findvar(env, name);
249- free(name);
250- val = v ? expandstr(env, v->val) : xstrdup("");
251- k = strlen(val);
252- if (len + k + 1 > cap) {
253- cap = len + k + 1;
254- out = xrealloc(out, cap);
255- }
256- memcpy(out + len, val, k);
257- len += k;
258- free(val);
259- } else if (issubstref(s + i + 2, j - i - 3, &colon, &eq)) {
260- name = xstrndup(s + i + 2, colon);
261- from = xstrndup(s + i + 2 + colon + 1, eq - colon - 1);
262- to = xstrndup(s + i + 2 + eq + 1, j - i - 4 - eq);
263- v = findvar(env, name);
264- free(name);
265- if (v) {
266- char *base;
267-
268- base = expandstr(env, v->val);
269- val = substval(base, from, to);
270- free(base);
271- } else {
272- val = xstrdup("");
273- }
274- free(from);
275- free(to);
276- k = strlen(val);
277- if (len + k + 1 > cap) {
278- cap = len + k + 1;
279- out = xrealloc(out, cap);
280- }
281- memcpy(out + len, val, k);
282- len += k;
283- free(val);
284- } else if (j - i - 3 >= 10 &&
285- memcmp(s + i + 2, "wildcard", 8) == 0 &&
286- isspace((unsigned char)s[i + 10])) {
287- char *pat_raw, *pat_exp, *val;
288- size_t pstart;
289-
290- pstart = i + 11;
291- pat_raw = xstrndup(s + pstart, j - 1 - pstart);
292- pat_exp = expandstr(env, pat_raw);
293- free(pat_raw);
294- val = fnwildcard(pat_exp);
295- free(pat_exp);
296- k = strlen(val);
297- if (len + k + 1 > cap) {
298- cap = len + k + 1;
299- out = xrealloc(out, cap);
300- }
301- memcpy(out + len, val, k);
302- len += k;
303- free(val);
304- } else if (j - i - 3 >= 7 &&
305- memcmp(s + i + 2, "shell", 5) == 0 &&
306- isspace((unsigned char)s[i + 7])) {
307- char *cmd_raw, *cmd_exp, *val;
308- size_t cstart;
309-
310- cstart = i + 8;
311- cmd_raw = xstrndup(s + cstart, j - 1 - cstart);
312- cmd_exp = expandstr(env, cmd_raw);
313- free(cmd_raw);
314- val = fnshell(cmd_exp);
315- free(cmd_exp);
316- k = strlen(val);
317- if (len + k + 1 > cap) {
318- cap = len + k + 1;
319- out = xrealloc(out, cap);
320- }
321- memcpy(out + len, val, k);
322- len += k;
323- free(val);
324- } else if (j - i - 3 >= 10 &&
325- memcmp(s + i + 2, "filter-out", 10) == 0) {
326- char *args_raw, *pat_raw, *text_raw;
327- char *pat_exp, *text_exp, *val;
328- size_t astart;
329- ptrdiff_t comma;
330-
331- astart = i + 12;
332- while (astart < j - 1 && isspace((unsigned char)s[astart]))
333- astart++;
334- args_raw = xstrndup(s + astart, j - 1 - astart);
335- comma = findargcomma(args_raw, strlen(args_raw));
336- if (comma < 0) {
337- evalerr("malformed function arguments", "$(filter-out)");
338- free(args_raw);
339- val = xstrdup("");
340- } else {
341- pat_raw = xstrndup(args_raw, (size_t)comma);
342- text_raw = xstrdup(args_raw + comma + 1);
343- free(args_raw);
344- pat_exp = expandstr(env, pat_raw);
345- text_exp = expandstr(env, text_raw);
346- free(pat_raw);
347- free(text_raw);
348- val = fnfilterout(pat_exp, text_exp);
349- free(pat_exp);
350- free(text_exp);
351- }
352- k = strlen(val);
353- if (len + k + 1 > cap) {
354- cap = len + k + 1;
355- out = xrealloc(out, cap);
356- }
357- memcpy(out + len, val, k);
358- len += k;
359- free(val);
360- } else if (j - i - 3 >= 6 &&
361- memcmp(s + i + 2, "filter", 6) == 0) {
362- char *args_raw, *pat_raw, *text_raw;
363- char *pat_exp, *text_exp, *val;
364- size_t astart;
365- ptrdiff_t comma;
366-
367- astart = i + 8;
368- while (astart < j - 1 && isspace((unsigned char)s[astart]))
369- astart++;
370- args_raw = xstrndup(s + astart, j - 1 - astart);
371- comma = findargcomma(args_raw, strlen(args_raw));
372- if (comma < 0) {
373- evalerr("malformed function arguments", "$(filter)");
374- free(args_raw);
375- val = xstrdup("");
376- } else {
377- pat_raw = xstrndup(args_raw, (size_t)comma);
378- text_raw = xstrdup(args_raw + comma + 1);
379- free(args_raw);
380- pat_exp = expandstr(env, pat_raw);
381- text_exp = expandstr(env, text_raw);
382- free(pat_raw);
383- free(text_raw);
384- val = fnfilter(pat_exp, text_exp);
385- free(pat_exp);
386- free(text_exp);
387- }
388- k = strlen(val);
389- if (len + k + 1 > cap) {
390- cap = len + k + 1;
391- out = xrealloc(out, cap);
392- }
393- memcpy(out + len, val, k);
394- len += k;
395- free(val);
396- } else if (j - i - 3 >= 9 &&
397- memcmp(s + i + 2, "addprefix", 9) == 0) {
398- char *args_raw, *pre_raw, *names_raw;
399- char *pre_exp, *names_exp, *val;
400- size_t astart;
401- ptrdiff_t comma;
402-
403- astart = i + 11;
404- while (astart < j - 1 && isspace((unsigned char)s[astart]))
405- astart++;
406- args_raw = xstrndup(s + astart, j - 1 - astart);
407- comma = findargcomma(args_raw, strlen(args_raw));
408- if (comma < 0) {
409- evalerr("malformed function arguments", "$(addprefix)");
410- free(args_raw);
411- val = xstrdup("");
412- } else {
413- pre_raw = xstrndup(args_raw, (size_t)comma);
414- names_raw = xstrdup(args_raw + comma + 1);
415- free(args_raw);
416- pre_exp = expandstr(env, pre_raw);
417- names_exp = expandstr(env, names_raw);
418- free(pre_raw);
419- free(names_raw);
420- val = fnaddprefix(pre_exp, names_exp);
421- free(pre_exp);
422- free(names_exp);
423- }
424- k = strlen(val);
425- if (len + k + 1 > cap) {
426- cap = len + k + 1;
427- out = xrealloc(out, cap);
428- }
429- memcpy(out + len, val, k);
430- len += k;
431- free(val);
432- } else if (j - i - 3 >= 4 &&
433- memcmp(s + i + 2, "info", 4) == 0) {
434- char *text_raw, *text_exp, *val;
435- size_t tstart;
436-
437- tstart = i + 6;
438- while (tstart < j - 1 && isspace((unsigned char)s[tstart]))
439- tstart++;
440- text_raw = xstrndup(s + tstart, j - 1 - tstart);
441- text_exp = expandstr(env, text_raw);
442- free(text_raw);
443- val = fninfo(text_exp);
444- free(text_exp);
445- k = strlen(val);
446- if (len + k + 1 > cap) {
447- cap = len + k + 1;
448- out = xrealloc(out, cap);
449- }
450- memcpy(out + len, val, k);
451- len += k;
452- free(val);
453- } else {
454- char *unsup;
455-
456- unsup = xstrndup(s + start, j - start);
457- evalerr("variable reference or function", unsup);
458- free(unsup);
459- k = j - start;
460- if (len + k + 1 > cap) {
461- cap = len + k + 1;
462- out = xrealloc(out, cap);
463- }
464- memcpy(out + len, s + start, k);
465- len += k;
466- }
467- i = j - 1;
468- }
469- out[len] = 0;
470- return out;
471-}
472-
473-void
474-freeenv(struct Env *env)
475-{
476- size_t i;
477-
478- for (i = 0; i < env->n; i++) {
479- free(env->v[i].name);
480- free(env->v[i].val);
481- }
482- free(env->v);
483- env->v = 0;
484- env->n = 0;
485-}
486-
487-void
488-copyenv(struct Env *dst, const struct Env *src)
489-{
490- size_t i;
491-
492- memset(dst, 0, sizeof(*dst));
493- if (src->n)
494- dst->v = xrealloc(0, src->n * sizeof(dst->v[0]));
495- for (i = 0; i < src->n; i++) {
496- dst->v[i].name = xstrdup(src->v[i].name);
497- dst->v[i].val = xstrdup(src->v[i].val);
498- dst->v[i].simple = src->v[i].simple;
499- }
500- dst->n = src->n;
501-}
502-
503-void
504-evalassign(struct Env *env, const struct AssignNode *in)
505-{
506- struct Var *v;
507- char *rhs, *joined;
508-
509- switch (in->op) {
510- case ASSIGN_EQ:
511- envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
512- break;
513- case ASSIGN_COLON_EQ:
514- envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
515- break;
516- case ASSIGN_QMARK_EQ:
517- if (!findvar(env, in->lhs))
518- envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
519- break;
520- case ASSIGN_PLUS_EQ:
521- v = findvar(env, in->lhs);
522- if (!v) {
523- envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
524- break;
525- }
526- rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
527- joined = cat3(v->val, " ", rhs);
528- free(rhs);
529- free(v->val);
530- v->val = joined;
531- break;
532- case ASSIGN_BANG_EQ:
533- envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
534- break;
535- }
536-}
537-
538-static int
539-testcond(struct Env *env, const struct CondNode *cond)
540-{
541- char *a, *b, *name;
542- struct Var *v;
543- int ok;
544-
545- if (cond->kind == COND_IFDEF || cond->kind == COND_IFNDEF) {
546- name = expandstr(env, cond->arg1);
547- v = findvar(env, name);
548- ok = v != 0;
549- free(name);
550- if (cond->kind == COND_IFNDEF)
551- ok = !ok;
552- return ok;
553- }
554-
555- a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
556- b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
557- ok = strcmp(a, b) == 0;
558- free(a);
559- free(b);
560- if (cond->kind == COND_IFNEQ)
561- ok = !ok;
562- return ok;
563-}
564-
565-static void
566-copywords(struct StrList *out, const struct StrList *in, struct Env *env)
567-{
568- size_t i;
569-
570- memset(out, 0, sizeof(*out));
571- for (i = 0; i < in->n; i++) {
572- char *s = expandstr(env, in->v[i]);
573- splitwords(out, s, strlen(s));
574- free(s);
575- }
576-}
577-
578-static void
579-copyrecipes(struct RecipeList *out, const struct RecipeList *in)
580-{
581- size_t i;
582-
583- memset(out, 0, sizeof(*out));
584- for (i = 0; i < in->n; i++) {
585- out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
586- out->v[out->n++] = xstrdup(in->v[i]);
587- }
588-}
589-
590-static int
591-evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
592-{
593- size_t i;
594-
595- memset(out, 0, sizeof(*out));
596- for (i = 0; i < in->n; i++) {
597- const struct Node *src;
598- struct Node node;
599-
600- src = &in->v[i];
601- memset(&node, 0, sizeof(node));
602- node.kind = src->kind;
603- node.loc = src->loc;
604- switch (src->kind) {
605- case NODE_BLANK:
606- break;
607- case NODE_COMMENT:
608- node.data.raw.text = xstrdup(src->data.raw.text);
609- break;
610- case NODE_RAW: {
611- char *exp;
612-
613- exp = expandstr(env, src->data.raw.text);
614- free(exp);
615- node.kind = NODE_BLANK;
616- break;
617- }
618- case NODE_INCLUDE:
619- node.data.include.optional = src->data.include.optional;
620- node.data.include.path = expandstr(env, src->data.include.path);
621- break;
622- case NODE_COND:
623- if (testcond(env, &src->data.cond)) {
624- if (evalnodes(&src->data.cond.thenpart, out, env) < 0)
625- return -1;
626- } else {
627- if (evalnodes(&src->data.cond.elsepart, out, env) < 0)
628- return -1;
629- }
630- continue;
631- case NODE_ASSIGN:
632- node.data.assign.lhs = xstrdup(src->data.assign.lhs);
633- node.data.assign.op = src->data.assign.op;
634- node.data.assign.tspec = src->data.assign.tspec;
635- if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
636- node.data.assign.rhs = expandstr(env, src->data.assign.rhs);
637- else
638- node.data.assign.rhs = xstrdup(src->data.assign.rhs);
639- copywords(&node.data.assign.targets, &src->data.assign.targets, env);
640- if (!src->data.assign.tspec)
641- evalassign(env, &src->data.assign);
642- break;
643- case NODE_RULE:
644- copywords(&node.data.rule.targets, &src->data.rule.targets, env);
645- copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
646- copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
647- copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes);
648- break;
649- }
650- addnode(out, node);
651- }
652- return 0;
653-}
654-
655-int
656-eval(const struct Ast *ast, struct Ast *out)
657-{
658- struct Env env;
659- int rc;
660-
661- memset(&env, 0, sizeof(env));
662- seedenv(&env);
663- evalerrs = 0;
664- rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
665- freeenv(&env);
666- if (rc == 0 && evalerrs)
667- return -1;
668- return rc;
669-}
670-
671-static void
672-freenodes(struct NodeList *list)
673-{
674- size_t i;
675-
676- for (i = 0; i < list->n; i++) {
677- switch (list->v[i].kind) {
678- case NODE_COMMENT:
679- case NODE_RAW:
680- free(list->v[i].data.raw.text);
681- break;
682- case NODE_ASSIGN:
683- free(list->v[i].data.assign.lhs);
684- free(list->v[i].data.assign.rhs);
685- freestrs(&list->v[i].data.assign.targets);
686- break;
687- case NODE_RULE:
688- freestrs(&list->v[i].data.rule.targets);
689- freestrs(&list->v[i].data.rule.prereqs);
690- freestrs(&list->v[i].data.rule.order_only);
691- freerecipes(&list->v[i].data.rule.recipes);
692- break;
693- case NODE_INCLUDE:
694- free(list->v[i].data.include.path);
695- break;
696- case NODE_COND:
697- free(list->v[i].data.cond.arg1);
698- free(list->v[i].data.cond.arg2);
699- free(list->v[i].data.cond.raw);
700- freenodes(&list->v[i].data.cond.thenpart);
701- freenodes(&list->v[i].data.cond.elsepart);
702- break;
703- case NODE_BLANK:
704- break;
705- }
706- }
707- free(list->v);
708- list->v = 0;
709- list->n = 0;
710-}
711-
712-void
713-freeast(struct Ast *ast)
714-{
715- freenodes((struct NodeList *)ast);
716-}
+408,
-0
1@@ -0,0 +1,408 @@
2+#include "internal.h"
3+
4+#include <ctype.h>
5+#include <stddef.h>
6+#include <stdio.h>
7+#include <stdlib.h>
8+#include <string.h>
9+
10+/* $() ${} expansion is handled here */
11+
12+static int evalerrs;
13+
14+struct Buf {
15+ char *s;
16+ size_t len;
17+ size_t cap;
18+};
19+
20+static void
21+evalerr(const char *msg, const char *detail)
22+{
23+ if (detail)
24+ fprintf(stderr, "eval: unsupported: %s: %s\n", msg, detail);
25+ else
26+ fprintf(stderr, "eval: unsupported: %s\n", msg);
27+ evalerrs++;
28+}
29+
30+void
31+resetevalerrs(void)
32+{
33+ evalerrs = 0;
34+}
35+
36+int
37+getevalerrs(void)
38+{
39+ return evalerrs;
40+}
41+
42+static void
43+bufinit(struct Buf *buf, size_t cap)
44+{
45+ buf->cap = cap ? cap : 1;
46+ buf->len = 0;
47+ buf->s = xmalloc(buf->cap);
48+ buf->s[0] = 0;
49+}
50+
51+static void
52+bufgrow(struct Buf *buf, size_t need)
53+{
54+ while (buf->cap < need)
55+ buf->cap *= 2;
56+ buf->s = xrealloc(buf->s, buf->cap);
57+}
58+
59+static void
60+bufappendn(struct Buf *buf, const char *s, size_t n)
61+{
62+ if (buf->len + n + 1 > buf->cap)
63+ bufgrow(buf, buf->len + n + 1);
64+ memcpy(buf->s + buf->len, s, n);
65+ buf->len += n;
66+ buf->s[buf->len] = 0;
67+}
68+
69+static void
70+bufappend(struct Buf *buf, const char *s)
71+{
72+ bufappendn(buf, s, strlen(s));
73+}
74+
75+static void
76+bufappendc(struct Buf *buf, char c)
77+{
78+ if (buf->len + 2 > buf->cap)
79+ bufgrow(buf, buf->len + 2);
80+ buf->s[buf->len++] = c;
81+ buf->s[buf->len] = 0;
82+}
83+
84+static char *
85+bufdone(struct Buf *buf)
86+{
87+ return buf->s;
88+}
89+
90+static int
91+isplainvar(const char *s, size_t n)
92+{
93+ size_t i;
94+
95+ if (!n)
96+ return 0;
97+ for (i = 0; i < n; i++) {
98+ if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
99+ return 0;
100+ }
101+ return 1;
102+}
103+
104+static char *
105+substword(const char *word, size_t n, const char *from, const char *to)
106+{
107+ size_t nfrom, nto;
108+ const char *pct;
109+ char *out;
110+
111+ nfrom = strlen(from);
112+ nto = strlen(to);
113+ pct = strchr(from, '%');
114+ if (!pct) {
115+ if (n < nfrom || memcmp(word + n - nfrom, from, nfrom) != 0)
116+ return xstrndup(word, n);
117+ out = xmalloc(n - nfrom + nto + 1);
118+ memcpy(out, word, n - nfrom);
119+ memcpy(out + n - nfrom, to, nto);
120+ out[n - nfrom + nto] = 0;
121+ return out;
122+ }
123+ {
124+ size_t pre, suf, stem;
125+ const char *tpct;
126+
127+ pre = (size_t)(pct - from);
128+ suf = nfrom - pre - 1;
129+ if (n < pre + suf)
130+ return xstrndup(word, n);
131+ if (memcmp(word, from, pre) != 0 || memcmp(word + n - suf, pct + 1, suf) != 0)
132+ return xstrndup(word, n);
133+ stem = n - pre - suf;
134+ tpct = strchr(to, '%');
135+ if (!tpct)
136+ return xstrdup(to);
137+ {
138+ size_t tpre, tsuf;
139+
140+ tpre = (size_t)(tpct - to);
141+ tsuf = nto - tpre - 1;
142+ out = xmalloc(tpre + stem + tsuf + 1);
143+ memcpy(out, to, tpre);
144+ memcpy(out + tpre, word + pre, stem);
145+ memcpy(out + tpre + stem, tpct + 1, tsuf);
146+ out[tpre + stem + tsuf] = 0;
147+ return out;
148+ }
149+ }
150+}
151+
152+static char *
153+substval(const char *val, const char *from, const char *to)
154+{
155+ size_t i, j, n;
156+ char *part;
157+ struct Buf out;
158+
159+ n = strlen(val);
160+ bufinit(&out, n + 1);
161+ for (i = 0; i < n;) {
162+ if (isspace((unsigned char)val[i])) {
163+ bufappendc(&out, val[i]);
164+ i++;
165+ continue;
166+ }
167+ j = i;
168+ while (j < n && !isspace((unsigned char)val[j]))
169+ j++;
170+ part = substword(val + i, j - i, from, to);
171+ bufappend(&out, part);
172+ free(part);
173+ i = j;
174+ }
175+ return bufdone(&out);
176+}
177+
178+static int
179+issubstref(const char *s, size_t n, size_t *colon, size_t *eq)
180+{
181+ size_t i;
182+
183+ for (i = 0; i < n; i++) {
184+ if (s[i] == ':') {
185+ *colon = i;
186+ break;
187+ }
188+ if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
189+ return 0;
190+ }
191+ if (i == 0 || i >= n)
192+ return 0;
193+ for (i = *colon + 1; i < n; i++) {
194+ if (s[i] == '=') {
195+ *eq = i;
196+ return i > *colon + 1;
197+ }
198+ }
199+ return 0;
200+}
201+
202+static ptrdiff_t
203+findargcomma(const char *s, size_t n)
204+{
205+ size_t i, depth;
206+
207+ depth = 0;
208+ for (i = 0; i < n; i++) {
209+ if (s[i] == '$' && i + 1 < n && (s[i + 1] == '(' || s[i + 1] == '{')) {
210+ depth++;
211+ i++;
212+ continue;
213+ }
214+ if ((s[i] == ')' || s[i] == '}') && depth > 0) {
215+ depth--;
216+ continue;
217+ }
218+ if (s[i] == ',' && depth == 0)
219+ return (ptrdiff_t)i;
220+ }
221+ return -1;
222+}
223+
224+static size_t
225+findclose(const char *s, size_t i, size_t n, char close)
226+{
227+ size_t j, inner;
228+
229+ j = i + 2;
230+ inner = 1;
231+ while (j < n && inner) {
232+ if (s[j] == '$' && j + 1 < n && (s[j + 1] == '(' || s[j + 1] == '{')) {
233+ inner++;
234+ j += 2;
235+ continue;
236+ }
237+ if (s[j] == close)
238+ inner--;
239+ j++;
240+ }
241+ return inner ? 0 : j;
242+}
243+
244+static char *
245+expandvarref(struct Env *env, const char *s, size_t n)
246+{
247+ char *name, *val;
248+ struct Var *v;
249+
250+ name = xstrndup(s, n);
251+ v = findvar(env, name);
252+ free(name);
253+ val = v ? expandstr(env, v->val) : xstrdup("");
254+ return val;
255+}
256+
257+static char *
258+expandsubstref(struct Env *env, const char *s, size_t colon, size_t eq, size_t n)
259+{
260+ char *name, *from, *to, *val;
261+ struct Var *v;
262+
263+ name = xstrndup(s, colon);
264+ from = xstrndup(s + colon + 1, eq - colon - 1);
265+ to = xstrndup(s + eq + 1, n - eq - 1);
266+ v = findvar(env, name);
267+ free(name);
268+ if (v) {
269+ char *base;
270+
271+ base = expandstr(env, v->val);
272+ val = substval(base, from, to);
273+ free(base);
274+ } else {
275+ val = xstrdup("");
276+ }
277+ free(from);
278+ free(to);
279+ return val;
280+}
281+
282+typedef char *(*fn1_t)(const char *);
283+typedef char *(*fn2_t)(const char *, const char *);
284+
285+struct func {
286+ const char *name;
287+ int arity;
288+ union {
289+ fn1_t f1;
290+ fn2_t f2;
291+ } fn;
292+};
293+
294+static const struct func funcs[] = {
295+ {"wildcard", 1, {.f1 = fnwildcard}},
296+ {"shell", 1, {.f1 = fnshell}},
297+ {"sort", 1, {.f1 = fnsort}},
298+ {"info", 1, {.f1 = fninfo}},
299+ {"filter-out", 2, {.f2 = fnfilterout}},
300+ {"filter", 2, {.f2 = fnfilter}},
301+ {"addprefix", 2, {.f2 = fnaddprefix}},
302+ {"addsuffix", 2, {.f2 = fnaddsuffix}},
303+ {0, 0, {.f1 = 0}},
304+};
305+
306+static char *
307+funcref(struct Env *env, const char *s, size_t n)
308+{
309+ size_t i, namelen, start;
310+ const struct func *f;
311+ char *val;
312+
313+ for (i = 0; funcs[i].name; i++) {
314+ f = &funcs[i];
315+ namelen = strlen(f->name);
316+ if (n < namelen || memcmp(s, f->name, namelen) != 0)
317+ continue;
318+ start = namelen;
319+ while (start < n && isspace((unsigned char)s[start]))
320+ start++;
321+ if (f->arity == 1) {
322+ char *raw, *exp;
323+
324+ raw = xstrndup(s + start, n - start);
325+ exp = expandstr(env, raw);
326+ free(raw);
327+ val = f->fn.f1(exp);
328+ free(exp);
329+ } else {
330+ char *args, *lhs_raw, *rhs_raw, *lhs_exp, *rhs_exp, *detail;
331+ ptrdiff_t comma;
332+
333+ args = xstrndup(s + start, n - start);
334+ comma = findargcomma(args, strlen(args));
335+ if (comma < 0) {
336+ detail = cat3("$(", f->name, ")");
337+ evalerr("malformed function arguments", detail);
338+ free(detail);
339+ free(args);
340+ return xstrdup("");
341+ }
342+ lhs_raw = xstrndup(args, (size_t)comma);
343+ rhs_raw = xstrdup(args + comma + 1);
344+ free(args);
345+ lhs_exp = expandstr(env, lhs_raw);
346+ rhs_exp = expandstr(env, rhs_raw);
347+ free(lhs_raw);
348+ free(rhs_raw);
349+ val = f->fn.f2(lhs_exp, rhs_exp);
350+ free(lhs_exp);
351+ free(rhs_exp);
352+ }
353+ return val;
354+ }
355+ return 0;
356+}
357+
358+static char *
359+expandref(struct Env *env, const char *s, size_t n)
360+{
361+ size_t colon, eq;
362+ char *val, *unsup;
363+
364+ if (isplainvar(s, n))
365+ return expandvarref(env, s, n);
366+ if (issubstref(s, n, &colon, &eq))
367+ return expandsubstref(env, s, colon, eq, n);
368+ val = funcref(env, s, n);
369+ if (val)
370+ return val;
371+ unsup = xstrndup(s - 2, n + 3);
372+ evalerr("variable reference or function", unsup);
373+ free(unsup);
374+ return xstrndup(s - 2, n + 3);
375+}
376+
377+char *
378+expandstr(struct Env *env, const char *s)
379+{
380+ size_t i, j, n;
381+ char close;
382+ char *val;
383+ struct Buf out;
384+
385+ n = strlen(s);
386+ bufinit(&out, n + 1);
387+ for (i = 0; i < n; i++) {
388+ if (s[i] == '$' && i + 1 < n && s[i + 1] == '$') {
389+ bufappend(&out, "$$");
390+ i++;
391+ continue;
392+ }
393+ if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
394+ bufappendc(&out, s[i]);
395+ continue;
396+ }
397+ close = s[i + 1] == '(' ? ')' : '}';
398+ j = findclose(s, i, n, close);
399+ if (!j) {
400+ bufappendn(&out, s + i, n - i);
401+ break;
402+ }
403+ val = expandref(env, s + i + 2, j - i - 3);
404+ bufappend(&out, val);
405+ free(val);
406+ i = j - 1;
407+ }
408+ return bufdone(&out);
409+}
+271,
-0
1@@ -0,0 +1,271 @@
2+#include "shinobi.h"
3+#include "internal.h"
4+
5+#include <stdlib.h>
6+#include <string.h>
7+
8+/*
9+ * eval does the second pass over the built ast
10+ * seed builtins like CC and so on
11+ * apply assignment semantics
12+ * execute conditionals and flatten the chosen branch
13+ */
14+
15+struct Var *
16+findvar(struct Env *env, const char *name)
17+{
18+ size_t i;
19+
20+ for (i = 0; i < env->n; i++) {
21+ if (strcmp(env->v[i].name, name) == 0)
22+ return &env->v[i];
23+ }
24+ return 0;
25+}
26+
27+void
28+freeenv(struct Env *env)
29+{
30+ size_t i;
31+
32+ for (i = 0; i < env->n; i++) {
33+ free(env->v[i].name);
34+ free(env->v[i].val);
35+ }
36+ free(env->v);
37+ env->v = 0;
38+ env->n = 0;
39+}
40+
41+void
42+copyenv(struct Env *dst, const struct Env *src)
43+{
44+ size_t i;
45+
46+ memset(dst, 0, sizeof(*dst));
47+ if (src->n)
48+ dst->v = xrealloc(0, src->n * sizeof(dst->v[0]));
49+ for (i = 0; i < src->n; i++) {
50+ dst->v[i].name = xstrdup(src->v[i].name);
51+ dst->v[i].val = xstrdup(src->v[i].val);
52+ dst->v[i].simple = src->v[i].simple;
53+ }
54+ dst->n = src->n;
55+}
56+
57+void
58+evalassign(struct Env *env, const struct AssignNode *in)
59+{
60+ struct Var *v;
61+ char *rhs, *joined;
62+
63+ switch (in->op) {
64+ case ASSIGN_EQ:
65+ envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
66+ break;
67+ case ASSIGN_COLON_EQ:
68+ envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
69+ break;
70+ case ASSIGN_QMARK_EQ:
71+ if (!findvar(env, in->lhs))
72+ envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
73+ break;
74+ case ASSIGN_PLUS_EQ:
75+ v = findvar(env, in->lhs);
76+ if (!v) {
77+ envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
78+ break;
79+ }
80+ rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
81+ joined = cat3(v->val, " ", rhs);
82+ free(rhs);
83+ free(v->val);
84+ v->val = joined;
85+ break;
86+ case ASSIGN_BANG_EQ:
87+ envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
88+ break;
89+ }
90+}
91+
92+static int
93+testcond(struct Env *env, const struct CondNode *cond)
94+{
95+ char *a, *b, *name;
96+ struct Var *v;
97+ int ok;
98+
99+ if (cond->kind == COND_IFDEF || cond->kind == COND_IFNDEF) {
100+ name = expandstr(env, cond->arg1);
101+ v = findvar(env, name);
102+ ok = v != 0;
103+ free(name);
104+ if (cond->kind == COND_IFNDEF)
105+ ok = !ok;
106+ return ok;
107+ }
108+
109+ a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
110+ b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
111+ ok = strcmp(a, b) == 0;
112+ free(a);
113+ free(b);
114+ if (cond->kind == COND_IFNEQ)
115+ ok = !ok;
116+ return ok;
117+}
118+
119+static void
120+copywords(struct StrList *out, const struct StrList *in, struct Env *env)
121+{
122+ size_t i;
123+
124+ memset(out, 0, sizeof(*out));
125+ for (i = 0; i < in->n; i++) {
126+ char *s;
127+
128+ s = expandstr(env, in->v[i]);
129+ splitwords(out, s, strlen(s));
130+ free(s);
131+ }
132+}
133+
134+static void
135+copyrecipes(struct RecipeList *out, const struct RecipeList *in)
136+{
137+ size_t i;
138+
139+ memset(out, 0, sizeof(*out));
140+ for (i = 0; i < in->n; i++) {
141+ out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
142+ out->v[out->n++] = xstrdup(in->v[i]);
143+ }
144+}
145+
146+static int
147+evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
148+{
149+ size_t i;
150+
151+ memset(out, 0, sizeof(*out));
152+ for (i = 0; i < in->n; i++) {
153+ const struct Node *src;
154+ struct Node node;
155+
156+ src = &in->v[i];
157+ memset(&node, 0, sizeof(node));
158+ node.kind = src->kind;
159+ node.loc = src->loc;
160+ switch (src->kind) {
161+ case NODE_BLANK:
162+ break;
163+ case NODE_COMMENT:
164+ node.data.raw.text = xstrdup(src->data.raw.text);
165+ break;
166+ case NODE_RAW: {
167+ char *exp;
168+
169+ exp = expandstr(env, src->data.raw.text);
170+ free(exp);
171+ node.kind = NODE_BLANK;
172+ break;
173+ }
174+ case NODE_INCLUDE:
175+ node.data.include.optional = src->data.include.optional;
176+ node.data.include.path = expandstr(env, src->data.include.path);
177+ break;
178+ case NODE_COND:
179+ if (testcond(env, &src->data.cond)) {
180+ if (evalnodes(&src->data.cond.thenpart, out, env) < 0)
181+ return -1;
182+ } else {
183+ if (evalnodes(&src->data.cond.elsepart, out, env) < 0)
184+ return -1;
185+ }
186+ continue;
187+ case NODE_ASSIGN:
188+ node.data.assign.lhs = xstrdup(src->data.assign.lhs);
189+ node.data.assign.op = src->data.assign.op;
190+ node.data.assign.tspec = src->data.assign.tspec;
191+ if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
192+ node.data.assign.rhs = expandstr(env, src->data.assign.rhs);
193+ else
194+ node.data.assign.rhs = xstrdup(src->data.assign.rhs);
195+ copywords(&node.data.assign.targets, &src->data.assign.targets, env);
196+ if (!src->data.assign.tspec)
197+ evalassign(env, &src->data.assign);
198+ break;
199+ case NODE_RULE:
200+ copywords(&node.data.rule.targets, &src->data.rule.targets, env);
201+ copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
202+ copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
203+ copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes);
204+ break;
205+ }
206+ addnode(out, node);
207+ }
208+ return 0;
209+}
210+
211+int
212+eval(const struct Ast *ast, struct Ast *out)
213+{
214+ struct Env env;
215+ int rc;
216+
217+ memset(&env, 0, sizeof(env));
218+ seedenv(&env);
219+ resetevalerrs();
220+ rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
221+ freeenv(&env);
222+ if (rc == 0 && getevalerrs())
223+ return -1;
224+ return rc;
225+}
226+
227+static void
228+freenodes(struct NodeList *list)
229+{
230+ size_t i;
231+
232+ for (i = 0; i < list->n; i++) {
233+ switch (list->v[i].kind) {
234+ case NODE_COMMENT:
235+ case NODE_RAW:
236+ free(list->v[i].data.raw.text);
237+ break;
238+ case NODE_ASSIGN:
239+ free(list->v[i].data.assign.lhs);
240+ free(list->v[i].data.assign.rhs);
241+ freestrs(&list->v[i].data.assign.targets);
242+ break;
243+ case NODE_RULE:
244+ freestrs(&list->v[i].data.rule.targets);
245+ freestrs(&list->v[i].data.rule.prereqs);
246+ freestrs(&list->v[i].data.rule.order_only);
247+ freerecipes(&list->v[i].data.rule.recipes);
248+ break;
249+ case NODE_INCLUDE:
250+ free(list->v[i].data.include.path);
251+ break;
252+ case NODE_COND:
253+ free(list->v[i].data.cond.arg1);
254+ free(list->v[i].data.cond.arg2);
255+ free(list->v[i].data.cond.raw);
256+ freenodes(&list->v[i].data.cond.thenpart);
257+ freenodes(&list->v[i].data.cond.elsepart);
258+ break;
259+ case NODE_BLANK:
260+ break;
261+ }
262+ }
263+ free(list->v);
264+ list->v = 0;
265+ list->n = 0;
266+}
267+
268+void
269+freeast(struct Ast *ast)
270+{
271+ freenodes((struct NodeList *)ast);
272+}
+114,
-0
1@@ -306,6 +306,120 @@ fnaddprefix(const char *prefix, const char *names)
2 return out;
3 }
4
5+char *
6+fnaddsuffix(const char *suffix, const char *names)
7+{
8+ char *out;
9+ size_t cap, len, i, j, nsuffix;
10+
11+ nsuffix = strlen(suffix);
12+ cap = strlen(names) + nsuffix + 1;
13+ if (cap < 64)
14+ cap = 64;
15+ len = 0;
16+ out = xmalloc(cap);
17+ out[0] = 0;
18+
19+ for (i = 0; names[i];) {
20+ size_t wn, need;
21+
22+ while (names[i] && isspace((unsigned char)names[i]))
23+ i++;
24+ if (!names[i])
25+ break;
26+ j = i;
27+ while (names[j] && !isspace((unsigned char)names[j]))
28+ j++;
29+ wn = j - i;
30+ need = len + wn + nsuffix + 2;
31+ if (need > cap) {
32+ while (cap < need)
33+ cap *= 2;
34+ out = xrealloc(out, cap);
35+ }
36+ if (len)
37+ out[len++] = ' ';
38+ memcpy(out + len, names + i, wn);
39+ len += wn;
40+ memcpy(out + len, suffix, nsuffix);
41+ len += nsuffix;
42+ out[len] = 0;
43+ i = j;
44+ }
45+
46+ return out;
47+}
48+
49+static int
50+cmpstr(const void *a, const void *b)
51+{
52+ const char *const *sa;
53+ const char *const *sb;
54+
55+ sa = a;
56+ sb = b;
57+ return strcmp(*sa, *sb);
58+}
59+
60+char *
61+fnsort(const char *text)
62+{
63+ char **words;
64+ char *out;
65+ size_t i, j, n, cap, len;
66+
67+ words = 0;
68+ n = 0;
69+ for (i = 0; text[i];) {
70+ size_t start;
71+
72+ while (text[i] && isspace((unsigned char)text[i]))
73+ i++;
74+ if (!text[i])
75+ break;
76+ start = i;
77+ while (text[i] && !isspace((unsigned char)text[i]))
78+ i++;
79+ words = xrealloc(words, (n + 1) * sizeof(words[0]));
80+ words[n++] = xstrndup(text + start, i - start);
81+ }
82+
83+ if (n == 0)
84+ return xstrdup("");
85+
86+ qsort(words, n, sizeof(words[0]), cmpstr);
87+
88+ cap = strlen(text) + 1;
89+ if (cap < 64)
90+ cap = 64;
91+ len = 0;
92+ out = xmalloc(cap);
93+ out[0] = 0;
94+ for (i = 0; i < n; i++) {
95+ size_t wn, need;
96+
97+ if (i > 0 && strcmp(words[i - 1], words[i]) == 0)
98+ continue;
99+ wn = strlen(words[i]);
100+ need = len + wn + 2;
101+ if (need > cap) {
102+ while (cap < need)
103+ cap *= 2;
104+ out = xrealloc(out, cap);
105+ }
106+ if (len)
107+ out[len++] = ' ';
108+ memcpy(out + len, words[i], wn);
109+ len += wn;
110+ out[len] = 0;
111+ }
112+
113+ for (j = 0; j < n; j++)
114+ free(words[j]);
115+ free(words);
116+ return out;
117+}
118+
119 char *
120 fninfo(const char *text)
121 {
+37,
-1
1@@ -1,5 +1,6 @@
2 #include "gnu/pattern.h"
3
4+#include <unistd.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8@@ -124,8 +125,39 @@ collectpat(struct PatRules *rules, const struct RuleNode *rule)
9 }
10 }
11
12+static int
13+pattargetexists(const struct Graph *graph, const char *name)
14+{
15+ const struct Target *t;
16+
17+ if (access(name, F_OK) == 0)
18+ return 1;
19+ t = findctarget(graph, name);
20+ if (!t)
21+ return 0;
22+ return t->recipes.n > 0 || t->prereqs.n > 0 || t->order_only.n > 0;
23+}
24+
25+static int
26+patruleviable(const struct PatRule *rule, const struct Graph *graph, const char *stem)
27+{
28+ size_t i;
29+
30+ for (i = 0; i < rule->prereqs.n; i++) {
31+ char *s;
32+ int ok;
33+
34+ s = applystem(rule->prereqs.v[i], stem);
35+ ok = pattargetexists(graph, s);
36+ free(s);
37+ if (!ok)
38+ return 0;
39+ }
40+ return 1;
41+}
42+
43 int
44-instpatrule(const struct PatRules *rules, struct Target *t, struct Env *env)
45+instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct Env *env)
46 {
47 size_t i, j;
48
49@@ -135,6 +167,10 @@ instpatrule(const struct PatRules *rules, struct Target *t, struct Env *env)
50 stem = matchpat(rules->v[i].target, t->name);
51 if (!stem)
52 continue;
53+ if (!patruleviable(&rules->v[i], graph, stem)) {
54+ free(stem);
55+ continue;
56+ }
57 for (j = 0; j < rules->v[i].prereqs.n; j++) {
58 char *s;
59
+1,
-1
1@@ -18,7 +18,7 @@ struct PatRules {
2 int ispat(const char *s);
3 int patmatches(const char *pat, const char *name);
4 void collectpat(struct PatRules *rules, const struct RuleNode *rule);
5-int instpatrule(const struct PatRules *rules, struct Target *t, struct Env *env);
6+int instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct Env *env);
7 void freepatrule(struct PatRules *rules);
8
9 #endif
+1,
-1
1@@ -192,7 +192,7 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
2 struct Env env;
3
4 targetenv(&gs, &env, t->name);
5- instpatrule(&gs.patterns, t, &env);
6+ instpatrule(&gs.patterns, graph, t, &env);
7 matched = t->recipes.n > 0;
8 if (!matched) {
9 instsufrule(&gs.sufs, graph, t, &env);
+4,
-0
1@@ -31,6 +31,8 @@ const struct Target *findctarget(const struct Graph *graph, const char *name);
2
3 struct Var *findvar(struct Env *env, const char *name);
4 char *expandstr(struct Env *env, const char *s);
5+void resetevalerrs(void);
6+int getevalerrs(void);
7 void seedenv(struct Env *env);
8 void freeenv(struct Env *env);
9 void copyenv(struct Env *dst, const struct Env *src);
10@@ -41,6 +43,8 @@ char *fnshell(const char *cmd);
11 char *fnfilter(const char *patterns, const char *text);
12 char *fnfilterout(const char *patterns, const char *text);
13 char *fnaddprefix(const char *prefix, const char *names);
14+char *fnaddsuffix(const char *suffix, const char *names);
15+char *fnsort(const char *text);
16 char *fninfo(const char *text);
17
18 #endif