commit 44ed1ac
shrub
·
2026-04-08 20:45:01 +0000 UTC
parent b1a8a94
shell and filter support
3 files changed,
+205,
-0
+77,
-0
1@@ -166,6 +166,28 @@ issubstref(const char *s, size_t n, size_t *colon, size_t *eq)
2 return 0;
3 }
4
5+static ptrdiff_t
6+findargcomma(const char *s, size_t n)
7+{
8+ size_t i, depth;
9+
10+ depth = 0;
11+ for (i = 0; i < n; i++) {
12+ if (s[i] == '$' && i + 1 < n && (s[i + 1] == '(' || s[i + 1] == '{')) {
13+ depth++;
14+ i++;
15+ continue;
16+ }
17+ if ((s[i] == ')' || s[i] == '}') && depth > 0) {
18+ depth--;
19+ continue;
20+ }
21+ if (s[i] == ',' && depth == 0)
22+ return (ptrdiff_t)i;
23+ }
24+ return -1;
25+}
26+
27 char *
28 expandstr(struct Env *env, const char *s)
29 {
30@@ -278,6 +300,61 @@ expandstr(struct Env *env, const char *s)
31 memcpy(out + len, val, k);
32 len += k;
33 free(val);
34+ } else if (j - i - 3 >= 7 &&
35+ memcmp(s + i + 2, "shell", 5) == 0 &&
36+ isspace((unsigned char)s[i + 7])) {
37+ char *cmd_raw, *cmd_exp, *val;
38+ size_t cstart;
39+
40+ cstart = i + 8;
41+ cmd_raw = xstrndup(s + cstart, j - 1 - cstart);
42+ cmd_exp = expandstr(env, cmd_raw);
43+ free(cmd_raw);
44+ val = fnshell(cmd_exp);
45+ free(cmd_exp);
46+ k = strlen(val);
47+ if (len + k + 1 > cap) {
48+ cap = len + k + 1;
49+ out = xrealloc(out, cap);
50+ }
51+ memcpy(out + len, val, k);
52+ len += k;
53+ free(val);
54+ } else if (j - i - 3 >= 8 &&
55+ memcmp(s + i + 2, "filter", 6) == 0 &&
56+ isspace((unsigned char)s[i + 8])) {
57+ char *args_raw, *pat_raw, *text_raw;
58+ char *pat_exp, *text_exp, *val;
59+ size_t astart;
60+ ptrdiff_t comma;
61+
62+ astart = i + 9;
63+ args_raw = xstrndup(s + astart, j - 1 - astart);
64+ comma = findargcomma(args_raw, strlen(args_raw));
65+ if (comma < 0) {
66+ evalerr("malformed function arguments", "$(filter)");
67+ free(args_raw);
68+ val = xstrdup("");
69+ } else {
70+ pat_raw = xstrndup(args_raw, (size_t)comma);
71+ text_raw = xstrdup(args_raw + comma + 1);
72+ free(args_raw);
73+ pat_exp = expandstr(env, pat_raw);
74+ text_exp = expandstr(env, text_raw);
75+ free(pat_raw);
76+ free(text_raw);
77+ val = fnfilter(pat_exp, text_exp);
78+ free(pat_exp);
79+ free(text_exp);
80+ }
81+ k = strlen(val);
82+ if (len + k + 1 > cap) {
83+ cap = len + k + 1;
84+ out = xrealloc(out, cap);
85+ }
86+ memcpy(out + len, val, k);
87+ len += k;
88+ free(val);
89 } else {
90 char *unsup;
91
+126,
-0
1@@ -3,11 +3,32 @@
2 #include <ctype.h>
3 #include <glob.h>
4 #include <stddef.h>
5+#include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 /* implementations of gnu make builtin functions*/
10
11+static int
12+matchword(const char *pat, size_t npat, const char *word, size_t nword)
13+{
14+ const char *pct;
15+ size_t pre, suf;
16+
17+ pct = memchr(pat, '%', npat);
18+ if (!pct)
19+ return npat == nword && memcmp(pat, word, npat) == 0;
20+ pre = (size_t)(pct - pat);
21+ suf = npat - pre - 1;
22+ if (nword < pre + suf)
23+ return 0;
24+ if (memcmp(pat, word, pre) != 0)
25+ return 0;
26+ if (memcmp(pct + 1, word + nword - suf, suf) != 0)
27+ return 0;
28+ return 1;
29+}
30+
31 char *
32 fnwildcard(const char *patterns)
33 {
34@@ -62,3 +83,108 @@ fnwildcard(const char *patterns)
35 }
36 return out;
37 }
38+
39+char *
40+fnshell(const char *cmd)
41+{
42+ FILE *fp;
43+ char buf[4096];
44+ char *out;
45+ size_t len, cap, nread, i, j;
46+
47+ fp = popen(cmd, "r");
48+ if (!fp)
49+ return xstrdup("");
50+
51+ cap = 64;
52+ len = 0;
53+ out = xmalloc(cap);
54+ out[0] = 0;
55+
56+ while ((nread = fread(buf, 1, sizeof(buf), fp)) > 0) {
57+ if (len + nread + 1 > cap) {
58+ while (cap < len + nread + 1)
59+ cap *= 2;
60+ out = xrealloc(out, cap);
61+ }
62+ memcpy(out + len, buf, nread);
63+ len += nread;
64+ }
65+ out[len] = 0;
66+ pclose(fp);
67+
68+ for (i = 0, j = 0; i < len; i++) {
69+ unsigned char ch;
70+
71+ ch = (unsigned char)out[i];
72+ if (ch == '\n' || ch == '\r') {
73+ out[j++] = ' ';
74+ continue;
75+ }
76+ out[j++] = out[i];
77+ }
78+ while (j > 0 && isspace((unsigned char)out[j - 1]))
79+ j--;
80+ out[j] = 0;
81+ return out;
82+}
83+
84+char *
85+fnfilter(const char *patterns, const char *text)
86+{
87+ char *out;
88+ size_t cap, len, i, j, p0, p1;
89+
90+ cap = strlen(text) + 1;
91+ if (cap < 64)
92+ cap = 64;
93+ len = 0;
94+ out = xmalloc(cap);
95+ out[0] = 0;
96+
97+ for (i = 0; text[i];) {
98+ int keep;
99+
100+ while (text[i] && isspace((unsigned char)text[i]))
101+ i++;
102+ if (!text[i])
103+ break;
104+ j = i;
105+ while (text[j] && !isspace((unsigned char)text[j]))
106+ j++;
107+
108+ keep = 0;
109+ for (p0 = 0; patterns[p0] && !keep;) {
110+ while (patterns[p0] && isspace((unsigned char)patterns[p0]))
111+ p0++;
112+ if (!patterns[p0])
113+ break;
114+ p1 = p0;
115+ while (patterns[p1] && !isspace((unsigned char)patterns[p1]))
116+ p1++;
117+ if (matchword(patterns + p0, p1 - p0, text + i, j - i))
118+ keep = 1;
119+ p0 = p1;
120+ }
121+
122+ if (keep) {
123+ size_t wn, need;
124+
125+ wn = j - i;
126+ need = len + wn + 2;
127+ if (need > cap) {
128+ while (cap < need)
129+ cap *= 2;
130+ out = xrealloc(out, cap);
131+ }
132+ if (len)
133+ out[len++] = ' ';
134+ memcpy(out + len, text + i, wn);
135+ len += wn;
136+ out[len] = 0;
137+ }
138+ i = j;
139+ }
140+
141+ return out;
142+}
+2,
-0
1@@ -32,5 +32,7 @@ void copyenv(struct Env *dst, const struct Env *src);
2 void evalassign(struct Env *env, const struct AssignNode *in);
3
4 char *fnwildcard(const char *patterns);
5+char *fnshell(const char *cmd);
6+char *fnfilter(const char *patterns, const char *text);
7
8 #endif