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