commit 81a73c9
xplshn
·
2026-04-12 21:50:53 +0000 UTC
parent cb7ea0c
fix evalnodes, cleanrecipe & expandsubstref, they weren't parsing backslashes properly and were dropping args, added gnu/functions: patsubst, if, subst, basename, dir, notdir Signed-off-by: xplshn <anto@xplshn.com.ar>
8 files changed,
+360,
-21
+15,
-3
1@@ -22,13 +22,25 @@ emitrule(FILE *fp, const char *cmd, int id)
2 static char *
3 cleanrecipe(const char *s)
4 {
5+ size_t n;
6+
7 while (*s == ' ' || *s == '\t')
8 s++;
9- while (*s == '@' || *s == '+' || *s == '-')
10- s++;
11+ while (*s == '@' || *s == '+' || *s == '-') {
12+ char next = s[1];
13+
14+ if (next == '@' || next == '+' || next == '-' ||
15+ next == ' ' || next == '\t' || next == '\0')
16+ s++;
17+ else
18+ break;
19+ }
20 while (*s == ' ' || *s == '\t')
21 s++;
22- return xstrdup(s);
23+ n = strlen(s);
24+ while (n > 0 && (s[n - 1] == ' ' || s[n - 1] == '\t'))
25+ n--;
26+ return xstrndup(s, n);
27 }
28
29 static char *
+57,
-6
1@@ -259,11 +259,19 @@ expandsubstref(struct Env *env, const char *s, size_t colon, size_t eq, size_t n
2 char *name, *from, *to, *val;
3 struct Var *v;
4
5- name = xstrndup(s, colon);
6- from = xstrndup(s + colon + 1, eq - colon - 1);
7- to = xstrndup(s + eq + 1, n - eq - 1);
8- v = findvar(env, name);
9- free(name);
10+ {
11+ char *toraw, *fromraw;
12+
13+ name = xstrndup(s, colon);
14+ fromraw = xstrndup(s + colon + 1, eq - colon - 1);
15+ toraw = xstrndup(s + eq + 1, n - eq - 1);
16+ from = expandstr(env, fromraw);
17+ to = expandstr(env, toraw);
18+ free(fromraw);
19+ free(toraw);
20+ v = findvar(env, name);
21+ free(name);
22+ }
23 if (v) {
24 char *base;
25
26@@ -280,6 +288,7 @@ expandsubstref(struct Env *env, const char *s, size_t colon, size_t eq, size_t n
27
28 typedef char *(*fn1_t)(const char *);
29 typedef char *(*fn2_t)(const char *, const char *);
30+typedef char *(*fn3_t)(const char *, const char *, const char *);
31
32 struct func {
33 const char *name;
34@@ -287,6 +296,7 @@ struct func {
35 union {
36 fn1_t f1;
37 fn2_t f2;
38+ fn3_t f3;
39 } fn;
40 };
41
42@@ -295,10 +305,16 @@ static const struct func funcs[] = {
43 {"shell", 1, {.f1 = fnshell}},
44 {"sort", 1, {.f1 = fnsort}},
45 {"info", 1, {.f1 = fninfo}},
46+ {"notdir", 1, {.f1 = fnnotdir}},
47+ {"dir", 1, {.f1 = fndir}},
48+ {"basename", 1, {.f1 = fnbasename}},
49 {"filter-out", 2, {.f2 = fnfilterout}},
50 {"filter", 2, {.f2 = fnfilter}},
51 {"addprefix", 2, {.f2 = fnaddprefix}},
52 {"addsuffix", 2, {.f2 = fnaddsuffix}},
53+ {"subst", 3, {.f3 = fnsubst}},
54+ {"patsubst", 3, {.f3 = fnpatsubst}},
55+ {"if", 3, {.f3 = fnif}},
56 {0, 0, {.f1 = 0}},
57 };
58
59@@ -325,7 +341,7 @@ funcref(struct Env *env, const char *s, size_t n)
60 free(raw);
61 val = f->fn.f1(exp);
62 free(exp);
63- } else {
64+ } else if (f->arity == 2) {
65 char *args, *lhs_raw, *rhs_raw, *lhs_exp, *rhs_exp, *detail;
66 ptrdiff_t comma;
67
68@@ -348,6 +364,41 @@ funcref(struct Env *env, const char *s, size_t n)
69 val = f->fn.f2(lhs_exp, rhs_exp);
70 free(lhs_exp);
71 free(rhs_exp);
72+ } else {
73+ char *args, *a1r, *a2r, *a3r, *a1e, *a2e, *a3e, *detail;
74+ ptrdiff_t c1, c2;
75+ size_t rest;
76+
77+ args = xstrndup(s + start, n - start);
78+ c1 = findargcomma(args, strlen(args));
79+ if (c1 < 0) {
80+ detail = cat3("$(", f->name, ")");
81+ evalerr("malformed function arguments", detail);
82+ free(detail);
83+ free(args);
84+ return xstrdup("");
85+ }
86+ a1r = xstrndup(args, (size_t)c1);
87+ rest = (size_t)c1 + 1;
88+ c2 = findargcomma(args + rest, strlen(args + rest));
89+ if (c2 < 0) {
90+ a2r = xstrdup(args + rest);
91+ a3r = xstrdup("");
92+ } else {
93+ a2r = xstrndup(args + rest, (size_t)c2);
94+ a3r = xstrdup(args + rest + (size_t)c2 + 1);
95+ }
96+ free(args);
97+ a1e = expandstr(env, a1r);
98+ a2e = expandstr(env, a2r);
99+ a3e = expandstr(env, a3r);
100+ free(a1r);
101+ free(a2r);
102+ free(a3r);
103+ val = f->fn.f3(a1e, a2e, a3e);
104+ free(a1e);
105+ free(a2e);
106+ free(a3e);
107 }
108 return val;
109 }
+1,
-1
1@@ -147,7 +147,6 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
2 {
3 size_t i;
4
5- memset(out, 0, sizeof(*out));
6 for (i = 0; i < in->n; i++) {
7 const struct Node *src;
8 struct Node node;
9@@ -213,6 +212,7 @@ eval(const struct Ast *ast, struct Ast *out)
10 struct Env env;
11 int rc;
12
13+ memset(out, 0, sizeof(*out));
14 memset(&env, 0, sizeof(env));
15 seedenv(&env);
16 resetevalerrs();
+262,
-0
1@@ -427,3 +427,265 @@ fninfo(const char *text)
2 fputc('\n', stderr);
3 return xstrdup("");
4 }
5+
6+char *
7+fnnotdir(const char *names)
8+{
9+ size_t i, j, k, len, cap, need, wn;
10+ const char *slash;
11+ char *out;
12+
13+ cap = strlen(names) + 1;
14+ if (cap < 16)
15+ cap = 16;
16+ len = 0;
17+ out = xmalloc(cap);
18+ out[0] = 0;
19+ for (i = 0; names[i];) {
20+ while (names[i] && isspace((unsigned char)names[i]))
21+ i++;
22+ if (!names[i])
23+ break;
24+ j = i;
25+ while (names[j] && !isspace((unsigned char)names[j]))
26+ j++;
27+ slash = 0;
28+ for (k = i; k < j; k++) {
29+ if (names[k] == '/')
30+ slash = names + k;
31+ }
32+ wn = slash ? (size_t)(names + j - slash - 1) : j - i;
33+ need = len + wn + 2;
34+ if (need > cap) {
35+ while (cap < need)
36+ cap *= 2;
37+ out = xrealloc(out, cap);
38+ }
39+ if (len)
40+ out[len++] = ' ';
41+ memcpy(out + len, slash ? slash + 1 : names + i, wn);
42+ len += wn;
43+ out[len] = 0;
44+ i = j;
45+ }
46+ return out;
47+}
48+
49+char *
50+fndir(const char *names)
51+{
52+ size_t i, j, k, len, cap, need, dlen;
53+ const char *slash;
54+ char *out;
55+
56+ cap = strlen(names) + 1;
57+ if (cap < 16)
58+ cap = 16;
59+ len = 0;
60+ out = xmalloc(cap);
61+ out[0] = 0;
62+ for (i = 0; names[i];) {
63+ while (names[i] && isspace((unsigned char)names[i]))
64+ i++;
65+ if (!names[i])
66+ break;
67+ j = i;
68+ while (names[j] && !isspace((unsigned char)names[j]))
69+ j++;
70+ slash = 0;
71+ for (k = i; k < j; k++) {
72+ if (names[k] == '/')
73+ slash = names + k;
74+ }
75+ dlen = slash ? (size_t)(slash - (names + i)) + 1 : 2;
76+ need = len + dlen + 2;
77+ if (need > cap) {
78+ while (cap < need)
79+ cap *= 2;
80+ out = xrealloc(out, cap);
81+ }
82+ if (len)
83+ out[len++] = ' ';
84+ if (slash) {
85+ memcpy(out + len, names + i, dlen);
86+ } else {
87+ memcpy(out + len, "./", 2);
88+ }
89+ len += dlen;
90+ out[len] = 0;
91+ i = j;
92+ }
93+ return out;
94+}
95+
96+char *
97+fnbasename(const char *names)
98+{
99+ size_t i, j, k, len, cap, need, wn;
100+ const char *fnstart, *dot;
101+ char *out;
102+
103+ cap = strlen(names) + 1;
104+ if (cap < 16)
105+ cap = 16;
106+ len = 0;
107+ out = xmalloc(cap);
108+ out[0] = 0;
109+ for (i = 0; names[i];) {
110+ while (names[i] && isspace((unsigned char)names[i]))
111+ i++;
112+ if (!names[i])
113+ break;
114+ j = i;
115+ while (names[j] && !isspace((unsigned char)names[j]))
116+ j++;
117+ wn = j - i;
118+ fnstart = names + i;
119+ dot = 0;
120+ for (k = i; k < j; k++) {
121+ if (names[k] == '/')
122+ fnstart = names + k + 1;
123+ else if (names[k] == '.' && names + k > fnstart)
124+ dot = names + k;
125+ }
126+ if (dot)
127+ wn = (size_t)(dot - (names + i));
128+ need = len + wn + 2;
129+ if (need > cap) {
130+ while (cap < need)
131+ cap *= 2;
132+ out = xrealloc(out, cap);
133+ }
134+ if (len)
135+ out[len++] = ' ';
136+ memcpy(out + len, names + i, wn);
137+ len += wn;
138+ out[len] = 0;
139+ i = j;
140+ }
141+ return out;
142+}
143+
144+char *
145+fnsubst(const char *from, const char *to, const char *text)
146+{
147+ size_t nfrom, nto, i, len, cap, need;
148+ char *out;
149+
150+ nfrom = strlen(from);
151+ nto = strlen(to);
152+ if (!nfrom)
153+ return xstrdup(text);
154+ cap = strlen(text) + 1;
155+ if (cap < 16)
156+ cap = 16;
157+ len = 0;
158+ out = xmalloc(cap);
159+ out[0] = 0;
160+ for (i = 0; text[i];) {
161+ if (strncmp(text + i, from, nfrom) == 0) {
162+ need = len + nto + 1;
163+ if (need > cap) {
164+ while (cap < need)
165+ cap *= 2;
166+ out = xrealloc(out, cap);
167+ }
168+ memcpy(out + len, to, nto);
169+ len += nto;
170+ i += nfrom;
171+ } else {
172+ need = len + 2;
173+ if (need > cap) {
174+ cap *= 2;
175+ out = xrealloc(out, cap);
176+ }
177+ out[len++] = text[i++];
178+ }
179+ }
180+ out[len] = 0;
181+ return out;
182+}
183+
184+static char *
185+patsubstword(const char *word, size_t wn, const char *pattern, const char *replacement)
186+{
187+ size_t npat, nrep, pre, suf, stem, rpre, rsuf;
188+ const char *pct, *rpct;
189+ char *out;
190+
191+ npat = strlen(pattern);
192+ nrep = strlen(replacement);
193+ pct = strchr(pattern, '%');
194+ if (!pct) {
195+ if (wn == npat && memcmp(word, pattern, wn) == 0)
196+ return xstrdup(replacement);
197+ return xstrndup(word, wn);
198+ }
199+ pre = (size_t)(pct - pattern);
200+ suf = npat - pre - 1;
201+ if (wn < pre + suf)
202+ return xstrndup(word, wn);
203+ if (memcmp(word, pattern, pre) != 0 || memcmp(word + wn - suf, pct + 1, suf) != 0)
204+ return xstrndup(word, wn);
205+ stem = wn - pre - suf;
206+ rpct = strchr(replacement, '%');
207+ if (!rpct)
208+ return xstrdup(replacement);
209+ rpre = (size_t)(rpct - replacement);
210+ rsuf = nrep - rpre - 1;
211+ out = xmalloc(rpre + stem + rsuf + 1);
212+ memcpy(out, replacement, rpre);
213+ memcpy(out + rpre, word + pre, stem);
214+ memcpy(out + rpre + stem, rpct + 1, rsuf);
215+ out[rpre + stem + rsuf] = 0;
216+ return out;
217+}
218+
219+char *
220+fnpatsubst(const char *pattern, const char *replacement, const char *text)
221+{
222+ size_t i, j, wlen, len, cap, need;
223+ char *w, *out;
224+
225+ cap = strlen(text) + 1;
226+ if (cap < 16)
227+ cap = 16;
228+ len = 0;
229+ out = xmalloc(cap);
230+ out[0] = 0;
231+ for (i = 0; text[i];) {
232+ while (text[i] && isspace((unsigned char)text[i]))
233+ i++;
234+ if (!text[i])
235+ break;
236+ j = i;
237+ while (text[j] && !isspace((unsigned char)text[j]))
238+ j++;
239+ w = patsubstword(text + i, j - i, pattern, replacement);
240+ wlen = strlen(w);
241+ need = len + wlen + 2;
242+ if (need > cap) {
243+ while (cap < need)
244+ cap *= 2;
245+ out = xrealloc(out, cap);
246+ }
247+ if (len)
248+ out[len++] = ' ';
249+ memcpy(out + len, w, wlen);
250+ len += wlen;
251+ out[len] = 0;
252+ free(w);
253+ i = j;
254+ }
255+ return out;
256+}
257+
258+char *
259+fnif(const char *cond, const char *then, const char *otherwise)
260+{
261+ const char *p;
262+
263+ for (p = cond; *p && isspace((unsigned char)*p); p++)
264+ ;
265+ return xstrdup(*p ? then : otherwise);
266+}
+12,
-7
1@@ -135,7 +135,7 @@ pattargetexists(const struct Graph *graph, const char *name)
2 t = findctarget(graph, name);
3 if (!t)
4 return 0;
5- return t->recipes.n > 0 || t->prereqs.n > 0 || t->order_only.n > 0;
6+ return t->recipes.n > 0;
7 }
8
9 static int
10@@ -171,12 +171,17 @@ instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Targ
11 free(stem);
12 continue;
13 }
14- for (j = 0; j < rules->v[i].prereqs.n; j++) {
15- char *s;
16-
17- s = applystem(rules->v[i].prereqs.v[j], stem);
18- t->prereqs.v = xrealloc(t->prereqs.v, (t->prereqs.n + 1) * sizeof(t->prereqs.v[0]));
19- t->prereqs.v[t->prereqs.n++] = s;
20+ /* prepend so $< is the pattern source, not an extra dep
21+ * added by an explicit rule for the same target */
22+ if (rules->v[i].prereqs.n > 0) {
23+ size_t np = rules->v[i].prereqs.n;
24+ t->prereqs.v = xrealloc(t->prereqs.v,
25+ (t->prereqs.n + np) * sizeof(t->prereqs.v[0]));
26+ memmove(t->prereqs.v + np, t->prereqs.v,
27+ t->prereqs.n * sizeof(t->prereqs.v[0]));
28+ for (j = 0; j < np; j++)
29+ t->prereqs.v[j] = applystem(rules->v[i].prereqs.v[j], stem);
30+ t->prereqs.n += np;
31 }
32 for (j = 0; j < rules->v[i].order_only.n; j++) {
33 char *s;
+4,
-3
1@@ -185,9 +185,10 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
2 for (i = start; i < current_n; i++) {
3 struct Target *t = &graph->v[i];
4 size_t nprereqs;
5- /* for placeholder targets with no recipe or prereqs,
6- * try a pattern rule or suffix rule */
7- if (t->recipes.n == 0 && t->prereqs.n == 0 && t->order_only.n == 0) {
8+ /* for targets with no recipe, try a pattern rule or
9+ * suffix rule. existing prereqs dont block this,
10+ * the rule may still supply the recipe */
11+ if (t->recipes.n == 0) {
12 int matched;
13 struct Env env;
14
+6,
-0
1@@ -46,5 +46,11 @@ char *fnaddprefix(const char *prefix, const char *names);
2 char *fnaddsuffix(const char *suffix, const char *names);
3 char *fnsort(const char *text);
4 char *fninfo(const char *text);
5+char *fnnotdir(const char *names);
6+char *fndir(const char *names);
7+char *fnbasename(const char *names);
8+char *fnsubst(const char *from, const char *to, const char *text);
9+char *fnpatsubst(const char *pattern, const char *replacement, const char *text);
10+char *fnif(const char *cond, const char *then, const char *otherwise);
11
12 #endif
+3,
-1
1@@ -280,10 +280,12 @@ readline(const char **src, int *lineno, struct PreLine *line)
2 len += chunk;
3 buf[len] = 0;
4
5- if (line->isrecipe || !hascont(buf, len))
6+ if (!hascont(buf, len))
7 break;
8 len--;
9 buf[len++] = ' ';
10+ while (*p == ' ' || *p == '\t')
11+ p++;
12 line1++;
13 first = 0;
14 if (!*p)