commit 8d5eed2
shrub
·
2026-04-13 16:59:07 +0000 UTC
parent fb0930a
handle words functions and inline rule
4 files changed,
+178,
-4
+21,
-1
1@@ -315,6 +315,11 @@ static const struct func funcs[] = {
2 {"subst", 3, {.f3 = fnsubst}},
3 {"patsubst", 3, {.f3 = fnpatsubst}},
4 {"if", 3, {.f3 = fnif}},
5+ {"words", 1, {.f1 = fnwords}},
6+ {"word", 2, {.f2 = fnword}},
7+ {"wordlist", 3, {.f3 = fnwordlist}},
8+ {"firstword", 1, {.f1 = fnfirstword}},
9+ {"lastword", 1, {.f1 = fnlastword}},
10 {0, 0, {.f1 = 0}},
11 };
12
13@@ -440,10 +445,25 @@ expandstr(struct Env *env, const char *s)
14 i++;
15 continue;
16 }
17- if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
18+ if (s[i] != '$' || i + 1 >= n) {
19 bufappendc(&out, s[i]);
20 continue;
21 }
22+ if (s[i + 1] != '(' && s[i + 1] != '{') {
23+ /* leave automatic variables for expandauto and translateauto */
24+ if (s[i + 1] == '@' || s[i + 1] == '<' || s[i + 1] == '^' ||
25+ s[i + 1] == '+' || s[i + 1] == '?' || s[i + 1] == '*' ||
26+ s[i + 1] == '%') {
27+ bufappendc(&out, s[i]);
28+ continue;
29+ }
30+ /* some single char variable like $x */
31+ val = expandvarref(env, s + i + 1, 1);
32+ bufappend(&out, val);
33+ free(val);
34+ i++;
35+ continue;
36+ }
37 close = s[i + 1] == '(' ? ')' : '}';
38 j = findclose(s, i, n, close);
39 if (!j) {
+145,
-0
1@@ -690,3 +690,148 @@ fnif(const char *cond, const char *then, const char *otherwise)
2 ;
3 return xstrdup(*p ? then : otherwise);
4 }
5+
6+char *
7+fnwords(const char *text)
8+{
9+ size_t n;
10+ char buf[32];
11+
12+ n = 0;
13+ while (*text) {
14+ while (*text && isspace((unsigned char)*text))
15+ text++;
16+ if (!*text)
17+ break;
18+ n++;
19+ while (*text && !isspace((unsigned char)*text))
20+ text++;
21+ }
22+ snprintf(buf, sizeof(buf), "%zu", n);
23+ return xstrdup(buf);
24+}
25+
26+char *
27+fnword(const char *n, const char *list)
28+{
29+ size_t idx, i, wstart;
30+ char *end;
31+
32+ while (isspace((unsigned char)*n))
33+ n++;
34+ if (!*n)
35+ return xstrdup("");
36+ idx = (size_t)strtoul(n, &end, 10);
37+ if (end == n || idx == 0)
38+ return xstrdup("");
39+
40+ i = 0;
41+ while (list[i]) {
42+ while (list[i] && isspace((unsigned char)list[i]))
43+ i++;
44+ if (!list[i])
45+ break;
46+ wstart = i;
47+ while (list[i] && !isspace((unsigned char)list[i]))
48+ i++;
49+ if (--idx == 0)
50+ return xstrndup(list + wstart, i - wstart);
51+ }
52+ return xstrdup("");
53+}
54+
55+char *
56+fnwordlist(const char *s, const char *e, const char *list)
57+{
58+ size_t start, end, n, i, wstart, wlen, len, cap, need;
59+ char *out, *ep;
60+
61+ while (isspace((unsigned char)*s))
62+ s++;
63+ while (isspace((unsigned char)*e))
64+ e++;
65+ if (!*s || !*e)
66+ return xstrdup("");
67+ start = (size_t)strtoul(s, &ep, 10);
68+ if (ep == s || start == 0)
69+ return xstrdup("");
70+ end = (size_t)strtoul(e, &ep, 10);
71+ if (ep == e)
72+ return xstrdup("");
73+ if (start > end)
74+ return xstrdup("");
75+
76+ cap = strlen(list) + 1;
77+ if (cap < 16)
78+ cap = 16;
79+ len = 0;
80+ out = xmalloc(cap);
81+ out[0] = 0;
82+
83+ n = 0;
84+ i = 0;
85+ while (list[i]) {
86+ while (list[i] && isspace((unsigned char)list[i]))
87+ i++;
88+ if (!list[i])
89+ break;
90+ wstart = i;
91+ while (list[i] && !isspace((unsigned char)list[i]))
92+ i++;
93+ wlen = i - wstart;
94+ n++;
95+ if (n < start)
96+ continue;
97+ if (n > end)
98+ break;
99+ need = len + wlen + 2;
100+ if (need > cap) {
101+ while (cap < need)
102+ cap *= 2;
103+ out = xrealloc(out, cap);
104+ }
105+ if (len)
106+ out[len++] = ' ';
107+ memcpy(out + len, list + wstart, wlen);
108+ len += wlen;
109+ out[len] = 0;
110+ }
111+ return out;
112+}
113+
114+char *
115+fnfirstword(const char *list)
116+{
117+ size_t i, j;
118+
119+ for (i = 0; list[i] && isspace((unsigned char)list[i]); i++)
120+ ;
121+ if (!list[i])
122+ return xstrdup("");
123+ for (j = i; list[j] && !isspace((unsigned char)list[j]); j++)
124+ ;
125+ return xstrndup(list + i, j - i);
126+}
127+
128+char *
129+fnlastword(const char *list)
130+{
131+ const char *last;
132+ size_t lastlen;
133+ const char *p;
134+
135+ last = 0;
136+ lastlen = 0;
137+ p = list;
138+ while (*p) {
139+ while (*p && isspace((unsigned char)*p))
140+ p++;
141+ if (!*p)
142+ break;
143+ last = p;
144+ while (*p && !isspace((unsigned char)*p))
145+ p++;
146+ lastlen = (size_t)(p - last);
147+ }
148+ return last ? xstrndup(last, lastlen) : xstrdup("");
149+}
+5,
-0
1@@ -52,5 +52,10 @@ char *fnbasename(const char *names);
2 char *fnsubst(const char *from, const char *to, const char *text);
3 char *fnpatsubst(const char *pattern, const char *replacement, const char *text);
4 char *fnif(const char *cond, const char *then, const char *otherwise);
5+char *fnwords(const char *text);
6+char *fnword(const char *n, const char *list);
7+char *fnwordlist(const char *s, const char *e, const char *list);
8+char *fnfirstword(const char *list);
9+char *fnlastword(const char *list);
10
11 #endif
+7,
-3
1@@ -733,9 +733,13 @@ parseline(const struct PreLine *line)
2 colon = findtop(trim, n, ':');
3 as = findassign(trim, n, 0);
4 if (colon >= 0 && as.ok && (size_t)colon < as.pos) {
5- state = parseassign(line, trim, n, (size_t)colon + 1, as, 1, (size_t)colon);
6- free(trim);
7- return state;
8+ /* some inline rule like 'all: ; @echo hi' */
9+ ptrdiff_t semi = findtop(trim + colon + 1, as.pos - (size_t)colon - 1, ';');
10+ if (semi < 0) {
11+ state = parseassign(line, trim, n, (size_t)colon + 1, as, 1, (size_t)colon);
12+ free(trim);
13+ return state;
14+ }
15 }
16 if (as.ok && (colon < 0 || as.pos <= (size_t)colon)) {
17 state = parseassign(line, trim, n, 0, as, 0, 0);