commit de8d436

shrub  ·  2026-04-07 22:06:53 +0000 UTC
parent bcdf49f
split files
5 files changed,  +621, -573
A eval.c
A util.c
+1, -1
1@@ -1,5 +1,5 @@
2 BIN = shin
3-SRCS = main.c shinobi.c
4+SRCS = main.c parse.c eval.c util.c
5 FMTSRCS = $(SRCS) shinobi.h
6 OBJS = $(SRCS:.c=.o)
7 
A eval.c
+540, -0
  1@@ -0,0 +1,540 @@
  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+struct Var {
 22+	char *name;
 23+	char *val;
 24+	int simple;
 25+};
 26+
 27+struct Env {
 28+	struct Var *v;
 29+	size_t n;
 30+};
 31+
 32+static struct Var *
 33+findvar(struct Env *env, const char *name)
 34+{
 35+	size_t i;
 36+
 37+	for (i = 0; i < env->n; i++) {
 38+		if (strcmp(env->v[i].name, name) == 0)
 39+			return &env->v[i];
 40+	}
 41+	return 0;
 42+}
 43+
 44+static char *
 45+cat2(const char *a, const char *b)
 46+{
 47+	size_t na, nb;
 48+	char *s;
 49+
 50+	na = strlen(a);
 51+	nb = strlen(b);
 52+	s = xmalloc(na + nb + 1);
 53+	memcpy(s, a, na);
 54+	memcpy(s + na, b, nb);
 55+	s[na + nb] = 0;
 56+	return s;
 57+}
 58+
 59+static int
 60+isplainvar(const char *s, size_t n)
 61+{
 62+	size_t i;
 63+
 64+	if (!n)
 65+		return 0;
 66+	for (i = 0; i < n; i++) {
 67+		if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
 68+			return 0;
 69+	}
 70+	return 1;
 71+}
 72+
 73+static char *
 74+substword(const char *word, size_t n, const char *from, const char *to)
 75+{
 76+	size_t nfrom, nto;
 77+	const char *pct;
 78+	char *out;
 79+
 80+	nfrom = strlen(from);
 81+	nto = strlen(to);
 82+	pct = strchr(from, '%');
 83+	if (!pct) {
 84+		if (n < nfrom || memcmp(word + n - nfrom, from, nfrom) != 0)
 85+			return xstrndup(word, n);
 86+		out = xmalloc(n - nfrom + nto + 1);
 87+		memcpy(out, word, n - nfrom);
 88+		memcpy(out + n - nfrom, to, nto);
 89+		out[n - nfrom + nto] = 0;
 90+		return out;
 91+	}
 92+	{
 93+		size_t pre, suf, stem;
 94+		const char *tpct;
 95+
 96+		pre = (size_t)(pct - from);
 97+		suf = nfrom - pre - 1;
 98+		if (n < pre + suf)
 99+			return xstrndup(word, n);
100+		if (memcmp(word, from, pre) != 0 || memcmp(word + n - suf, pct + 1, suf) != 0)
101+			return xstrndup(word, n);
102+		stem = n - pre - suf;
103+		tpct = strchr(to, '%');
104+		if (!tpct) {
105+			out = xstrdup(to);
106+			return out;
107+		}
108+		{
109+			size_t tpre, tsuf;
110+
111+			tpre = (size_t)(tpct - to);
112+			tsuf = nto - tpre - 1;
113+			out = xmalloc(tpre + stem + tsuf + 1);
114+			memcpy(out, to, tpre);
115+			memcpy(out + tpre, word + pre, stem);
116+			memcpy(out + tpre + stem, tpct + 1, tsuf);
117+			out[tpre + stem + tsuf] = 0;
118+			return out;
119+		}
120+	}
121+}
122+
123+static char *
124+substval(const char *val, const char *from, const char *to)
125+{
126+	size_t i, j, n, cap, len, wn;
127+	char *out, *part;
128+
129+	n = strlen(val);
130+	cap = n + 1;
131+	len = 0;
132+	out = xmalloc(cap);
133+	for (i = 0; i < n;) {
134+		if (isspace((unsigned char)val[i])) {
135+			if (len + 2 > cap) {
136+				cap *= 2;
137+				out = xrealloc(out, cap);
138+			}
139+			out[len++] = val[i++];
140+			continue;
141+		}
142+		j = i;
143+		while (j < n && !isspace((unsigned char)val[j]))
144+			j++;
145+		part = substword(val + i, j - i, from, to);
146+		wn = strlen(part);
147+		if (len + wn + 1 > cap) {
148+			cap = len + wn + 1;
149+			out = xrealloc(out, cap);
150+		}
151+		memcpy(out + len, part, wn);
152+		len += wn;
153+		free(part);
154+		i = j;
155+	}
156+	out[len] = 0;
157+	return out;
158+}
159+
160+static int
161+issubstref(const char *s, size_t n, size_t *colon, size_t *eq)
162+{
163+	size_t i;
164+
165+	for (i = 0; i < n; i++) {
166+		if (s[i] == ':') {
167+			*colon = i;
168+			break;
169+		}
170+		if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
171+			return 0;
172+	}
173+	if (i == 0 || i >= n)
174+		return 0;
175+	for (i = *colon + 1; i < n; i++) {
176+		if (s[i] == '=') {
177+			*eq = i;
178+			return i > *colon + 1;
179+		}
180+	}
181+	return 0;
182+}
183+
184+static char *
185+expandstr(struct Env *env, const char *s)
186+{
187+	size_t i, j, k, n, cap, len, inner, start, colon, eq;
188+	char close;
189+	char *out, *name, *val, *from, *to;
190+	struct Var *v;
191+
192+	n = strlen(s);
193+	cap = n + 1;
194+	len = 0;
195+	out = xmalloc(cap);
196+	for (i = 0; i < n; i++) {
197+		if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
198+			if (len + 2 > cap) {
199+				cap *= 2;
200+				out = xrealloc(out, cap);
201+			}
202+			out[len++] = s[i];
203+			continue;
204+		}
205+		close = s[i + 1] == '(' ? ')' : '}';
206+		start = i;
207+		j = i + 2;
208+		inner = 1;
209+		while (j < n && inner) {
210+			if (s[j] == '$' && j + 1 < n && (s[j + 1] == '(' || s[j + 1] == '{')) {
211+				inner++;
212+				j += 2;
213+				continue;
214+			}
215+			if (s[j] == close)
216+				inner--;
217+			j++;
218+		}
219+		if (inner) {
220+			if (len + (n - start) + 1 > cap) {
221+				cap = len + (n - start) + 1;
222+				out = xrealloc(out, cap);
223+			}
224+			memcpy(out + len, s + start, n - start);
225+			len += n - start;
226+			break;
227+		}
228+		if (isplainvar(s + i + 2, j - i - 3)) {
229+			name = xstrndup(s + i + 2, j - i - 3);
230+			v = findvar(env, name);
231+			free(name);
232+			val = v ? expandstr(env, v->val) : xstrdup("");
233+			k = strlen(val);
234+			if (len + k + 1 > cap) {
235+				cap = len + k + 1;
236+				out = xrealloc(out, cap);
237+			}
238+			memcpy(out + len, val, k);
239+			len += k;
240+			free(val);
241+		} else if (issubstref(s + i + 2, j - i - 3, &colon, &eq)) {
242+			name = xstrndup(s + i + 2, colon);
243+			from = xstrndup(s + i + 2 + colon + 1, eq - colon - 1);
244+			to = xstrndup(s + i + 2 + eq + 1, j - i - 4 - eq);
245+			v = findvar(env, name);
246+			free(name);
247+			if (v) {
248+				char *base;
249+
250+				base = expandstr(env, v->val);
251+				val = substval(base, from, to);
252+				free(base);
253+			} else {
254+				val = xstrdup("");
255+			}
256+			free(from);
257+			free(to);
258+			k = strlen(val);
259+			if (len + k + 1 > cap) {
260+				cap = len + k + 1;
261+				out = xrealloc(out, cap);
262+			}
263+			memcpy(out + len, val, k);
264+			len += k;
265+			free(val);
266+		} else {
267+			k = j - start;
268+			if (len + k + 1 > cap) {
269+				cap = len + k + 1;
270+				out = xrealloc(out, cap);
271+			}
272+			memcpy(out + len, s + start, k);
273+			len += k;
274+		}
275+		i = j - 1;
276+	}
277+	out[len] = 0;
278+	return out;
279+}
280+
281+static void
282+setvar(struct Env *env, const char *name, char *val, int simple)
283+{
284+	struct Var *v;
285+
286+	v = findvar(env, name);
287+	if (v) {
288+		free(v->val);
289+		v->val = val;
290+		v->simple = simple;
291+		return;
292+	}
293+	env->v = xrealloc(env->v, (env->n + 1) * sizeof(env->v[0]));
294+	env->v[env->n].name = xstrdup(name);
295+	env->v[env->n].val = val;
296+	env->v[env->n].simple = simple;
297+	env->n++;
298+}
299+
300+static void
301+seedenv(struct Env *env)
302+{
303+	setvar(env, "CC", xstrdup("cc"), 1);
304+	setvar(env, "CXX", xstrdup("c++"), 1);
305+	setvar(env, "CPP", xstrdup("$(CC) -E"), 0);
306+	setvar(env, "AR", xstrdup("ar"), 1);
307+	setvar(env, "ARFLAGS", xstrdup("-rv"), 1);
308+	setvar(env, "AS", xstrdup("as"), 1);
309+	setvar(env, "LD", xstrdup("ld"), 1);
310+	setvar(env, "LEX", xstrdup("lex"), 1);
311+	setvar(env, "YACC", xstrdup("yacc"), 1);
312+	setvar(env, "SHELL", xstrdup("/bin/sh"), 1);
313+}
314+
315+static void
316+freeenv(struct Env *env)
317+{
318+	size_t i;
319+
320+	for (i = 0; i < env->n; i++) {
321+		free(env->v[i].name);
322+		free(env->v[i].val);
323+	}
324+	free(env->v);
325+	env->v = 0;
326+	env->n = 0;
327+}
328+
329+static void
330+evalassign(struct Env *env, const struct AssignNode *in)
331+{
332+	struct Var *v;
333+	char *rhs, *joined;
334+
335+	if (in->tspec)
336+		return;
337+	switch (in->op) {
338+	case ASSIGN_EQ:
339+		setvar(env, in->lhs, xstrdup(in->rhs), 0);
340+		break;
341+	case ASSIGN_COLON_EQ:
342+		setvar(env, in->lhs, expandstr(env, in->rhs), 1);
343+		break;
344+	case ASSIGN_QMARK_EQ:
345+		if (!findvar(env, in->lhs))
346+			setvar(env, in->lhs, xstrdup(in->rhs), 0);
347+		break;
348+	case ASSIGN_PLUS_EQ:
349+		v = findvar(env, in->lhs);
350+		if (!v) {
351+			setvar(env, in->lhs, xstrdup(in->rhs), 0);
352+			break;
353+		}
354+		rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
355+		joined = cat2(v->val, rhs);
356+		free(rhs);
357+		free(v->val);
358+		v->val = joined;
359+		break;
360+	case ASSIGN_BANG_EQ:
361+		setvar(env, in->lhs, expandstr(env, in->rhs), 1);
362+		break;
363+	}
364+}
365+
366+static int
367+testcond(struct Env *env, const struct CondNode *cond)
368+{
369+	char *a, *b;
370+	int ok;
371+
372+	a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
373+	b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
374+	ok = strcmp(a, b) == 0;
375+	free(a);
376+	free(b);
377+	if (cond->kind == COND_IFNEQ)
378+		ok = !ok;
379+	return ok;
380+}
381+
382+static void
383+copywords(struct StrList *out, const struct StrList *in, struct Env *env)
384+{
385+	size_t i;
386+
387+	memset(out, 0, sizeof(*out));
388+	for (i = 0; i < in->n; i++) {
389+		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
390+		out->v[out->n++] = expandstr(env, in->v[i]);
391+	}
392+}
393+
394+static void
395+copyrecipes(struct RecipeList *out, const struct RecipeList *in, struct Env *env)
396+{
397+	size_t i;
398+
399+	memset(out, 0, sizeof(*out));
400+	for (i = 0; i < in->n; i++) {
401+		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
402+		out->v[out->n++] = expandstr(env, in->v[i]);
403+	}
404+}
405+
406+static int
407+evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
408+{
409+	size_t i;
410+
411+	memset(out, 0, sizeof(*out));
412+	for (i = 0; i < in->n; i++) {
413+		const struct Node *src;
414+		struct Node node;
415+
416+		src = &in->v[i];
417+		memset(&node, 0, sizeof(node));
418+		node.kind = src->kind;
419+		node.loc = src->loc;
420+		switch (src->kind) {
421+		case NODE_BLANK:
422+			break;
423+		case NODE_COMMENT:
424+		case NODE_RAW:
425+			node.data.raw.text = xstrdup(src->data.raw.text);
426+			break;
427+		case NODE_INCLUDE:
428+			node.data.include.optional = src->data.include.optional;
429+			node.data.include.path = expandstr(env, src->data.include.path);
430+			break;
431+		case NODE_COND:
432+			if (testcond(env, &src->data.cond)) {
433+				if (evalnodes(&src->data.cond.thenpart, out, env) < 0)
434+					return -1;
435+			} else {
436+				if (evalnodes(&src->data.cond.elsepart, out, env) < 0)
437+					return -1;
438+			}
439+			continue;
440+		case NODE_ASSIGN:
441+			node.data.assign.lhs = xstrdup(src->data.assign.lhs);
442+			node.data.assign.op = src->data.assign.op;
443+			node.data.assign.tspec = src->data.assign.tspec;
444+			if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
445+				node.data.assign.rhs = expandstr(env, src->data.assign.rhs);
446+			else
447+				node.data.assign.rhs = xstrdup(src->data.assign.rhs);
448+			copywords(&node.data.assign.targets, &src->data.assign.targets, env);
449+			evalassign(env, &src->data.assign);
450+			break;
451+		case NODE_RULE:
452+			copywords(&node.data.rule.targets, &src->data.rule.targets, env);
453+			copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
454+			copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
455+			copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes, env);
456+			break;
457+		}
458+		addnode(out, node);
459+	}
460+	return 0;
461+}
462+
463+int
464+eval(const struct Ast *ast, struct Ast *out)
465+{
466+	struct Env env;
467+	int rc;
468+
469+	memset(&env, 0, sizeof(env));
470+	seedenv(&env);
471+	rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
472+	freeenv(&env);
473+	return rc;
474+}
475+
476+static void
477+freestrs(struct StrList *list)
478+{
479+	size_t i;
480+
481+	for (i = 0; i < list->n; i++)
482+		free(list->v[i]);
483+	free(list->v);
484+}
485+
486+static void
487+freerec(struct RecipeList *list)
488+{
489+	size_t i;
490+
491+	for (i = 0; i < list->n; i++)
492+		free(list->v[i]);
493+	free(list->v);
494+}
495+
496+static void
497+freenodes(struct NodeList *list)
498+{
499+	size_t i;
500+
501+	for (i = 0; i < list->n; i++) {
502+		switch (list->v[i].kind) {
503+		case NODE_COMMENT:
504+		case NODE_RAW:
505+			free(list->v[i].data.raw.text);
506+			break;
507+		case NODE_ASSIGN:
508+			free(list->v[i].data.assign.lhs);
509+			free(list->v[i].data.assign.rhs);
510+			freestrs(&list->v[i].data.assign.targets);
511+			break;
512+		case NODE_RULE:
513+			freestrs(&list->v[i].data.rule.targets);
514+			freestrs(&list->v[i].data.rule.prereqs);
515+			freestrs(&list->v[i].data.rule.order_only);
516+			freerec(&list->v[i].data.rule.recipes);
517+			break;
518+		case NODE_INCLUDE:
519+			free(list->v[i].data.include.path);
520+			break;
521+		case NODE_COND:
522+			free(list->v[i].data.cond.arg1);
523+			free(list->v[i].data.cond.arg2);
524+			free(list->v[i].data.cond.raw);
525+			freenodes(&list->v[i].data.cond.thenpart);
526+			freenodes(&list->v[i].data.cond.elsepart);
527+			break;
528+		case NODE_BLANK:
529+			break;
530+		}
531+	}
532+	free(list->v);
533+	list->v = 0;
534+	list->n = 0;
535+}
536+
537+void
538+freeast(struct Ast *ast)
539+{
540+	freenodes((struct NodeList *)ast);
541+}
+12, -0
 1@@ -0,0 +1,12 @@
 2+#ifndef INTERNAL_H
 3+#define INTERNAL_H
 4+
 5+#include "shinobi.h"
 6+
 7+void *xmalloc(size_t n);
 8+void *xrealloc(void *p, size_t n);
 9+char *xstrndup(const char *s, size_t n);
10+char *xstrdup(const char *s);
11+void addnode(struct NodeList *list, struct Node node);
12+
13+#endif
R shinobi.c => parse.c
+11, -572
  1@@ -1,4 +1,5 @@
  2 #include "shinobi.h"
  3+#include "internal.h"
  4 
  5 #include <ctype.h>
  6 #include <stddef.h>
  7@@ -6,6 +7,16 @@
  8 #include <stdlib.h>
  9 #include <string.h>
 10 
 11+/*
 12+ * parse owns the first bit of the pipeline:
 13+ *   preproc reads file, joins \ newlines, strips comments,
 14+ *   and resolves includes, passes that to buildast.
 15+ *   buildast parses that gnu make syntax into nodes.
 16+ *
 17+ * this is only abt syntax. we dont eval variables or
 18+ * execute conditional branches.
 19+ */
 20+
 21 struct AssignScan {
 22 	size_t pos;
 23 	size_t len;
 24@@ -18,67 +29,6 @@ struct Inc {
 25 	size_t n;
 26 };
 27 
 28-struct Var {
 29-	char *name;
 30-	char *val;
 31-	int simple;
 32-};
 33-
 34-struct Env {
 35-	struct Var *v;
 36-	size_t n;
 37-};
 38-
 39-static void *
 40-xmalloc(size_t n)
 41-{
 42-	void *p;
 43-
 44-	p = malloc(n ? n : 1);
 45-	if (!p) {
 46-		fprintf(stderr, "out of memory\n");
 47-		exit(1);
 48-	}
 49-	return p;
 50-}
 51-
 52-static void *
 53-xrealloc(void *p, size_t n)
 54-{
 55-	void *q;
 56-
 57-	q = realloc(p, n ? n : 1);
 58-	if (!q) {
 59-		fprintf(stderr, "out of memory\n");
 60-		exit(1);
 61-	}
 62-	return q;
 63-}
 64-
 65-static char *
 66-xstrndup(const char *s, size_t n)
 67-{
 68-	char *p;
 69-
 70-	p = xmalloc(n + 1);
 71-	memcpy(p, s, n);
 72-	p[n] = 0;
 73-	return p;
 74-}
 75-
 76-static char *
 77-xstrdup(const char *s)
 78-{
 79-	return xstrndup(s, strlen(s));
 80-}
 81-
 82-static void
 83-addnode(struct NodeList *list, struct Node node)
 84-{
 85-	list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
 86-	list->v[list->n++] = node;
 87-}
 88-
 89 static char *
 90 trimdup(const char *s, size_t n)
 91 {
 92@@ -816,514 +766,3 @@ parse(const char *path, const char *src, struct Ast *ast)
 93 	freepre(&pre);
 94 	return rc;
 95 }
 96-
 97-static struct Var *
 98-findvar(struct Env *env, const char *name)
 99-{
100-	size_t i;
101-
102-	for (i = 0; i < env->n; i++) {
103-		if (strcmp(env->v[i].name, name) == 0)
104-			return &env->v[i];
105-	}
106-	return 0;
107-}
108-
109-static char *
110-cat2(const char *a, const char *b)
111-{
112-	size_t na, nb;
113-	char *s;
114-
115-	na = strlen(a);
116-	nb = strlen(b);
117-	s = xmalloc(na + nb + 1);
118-	memcpy(s, a, na);
119-	memcpy(s + na, b, nb);
120-	s[na + nb] = 0;
121-	return s;
122-}
123-
124-static int
125-isplainvar(const char *s, size_t n)
126-{
127-	size_t i;
128-
129-	if (!n)
130-		return 0;
131-	for (i = 0; i < n; i++) {
132-		if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
133-			return 0;
134-	}
135-	return 1;
136-}
137-
138-static char *
139-substword(const char *word, size_t n, const char *from, const char *to)
140-{
141-	size_t nfrom, nto;
142-	const char *pct;
143-	char *out;
144-
145-	nfrom = strlen(from);
146-	nto = strlen(to);
147-	pct = strchr(from, '%');
148-	if (!pct) {
149-		if (n < nfrom || memcmp(word + n - nfrom, from, nfrom) != 0)
150-			return xstrndup(word, n);
151-		out = xmalloc(n - nfrom + nto + 1);
152-		memcpy(out, word, n - nfrom);
153-		memcpy(out + n - nfrom, to, nto);
154-		out[n - nfrom + nto] = 0;
155-		return out;
156-	}
157-	{
158-		size_t pre, suf, stem;
159-		const char *tpct;
160-
161-		pre = (size_t)(pct - from);
162-		suf = nfrom - pre - 1;
163-		if (n < pre + suf)
164-			return xstrndup(word, n);
165-		if (memcmp(word, from, pre) != 0 || memcmp(word + n - suf, pct + 1, suf) != 0)
166-			return xstrndup(word, n);
167-		stem = n - pre - suf;
168-		tpct = strchr(to, '%');
169-		if (!tpct) {
170-			out = xstrdup(to);
171-			return out;
172-		}
173-		{
174-			size_t tpre, tsuf;
175-
176-			tpre = (size_t)(tpct - to);
177-			tsuf = nto - tpre - 1;
178-			out = xmalloc(tpre + stem + tsuf + 1);
179-			memcpy(out, to, tpre);
180-			memcpy(out + tpre, word + pre, stem);
181-			memcpy(out + tpre + stem, tpct + 1, tsuf);
182-			out[tpre + stem + tsuf] = 0;
183-			return out;
184-		}
185-	}
186-}
187-
188-static char *
189-substval(const char *val, const char *from, const char *to)
190-{
191-	size_t i, j, n, cap, len, wn;
192-	char *out, *part;
193-
194-	n = strlen(val);
195-	cap = n + 1;
196-	len = 0;
197-	out = xmalloc(cap);
198-	for (i = 0; i < n;) {
199-		if (isspace((unsigned char)val[i])) {
200-			if (len + 2 > cap) {
201-				cap *= 2;
202-				out = xrealloc(out, cap);
203-			}
204-			out[len++] = val[i++];
205-			continue;
206-		}
207-		j = i;
208-		while (j < n && !isspace((unsigned char)val[j]))
209-			j++;
210-		part = substword(val + i, j - i, from, to);
211-		wn = strlen(part);
212-		if (len + wn + 1 > cap) {
213-			cap = len + wn + 1;
214-			out = xrealloc(out, cap);
215-		}
216-		memcpy(out + len, part, wn);
217-		len += wn;
218-		free(part);
219-		i = j;
220-	}
221-	out[len] = 0;
222-	return out;
223-}
224-
225-static int
226-issubstref(const char *s, size_t n, size_t *colon, size_t *eq)
227-{
228-	size_t i;
229-
230-	for (i = 0; i < n; i++) {
231-		if (s[i] == ':') {
232-			*colon = i;
233-			break;
234-		}
235-		if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
236-			return 0;
237-	}
238-	if (i == 0 || i >= n)
239-		return 0;
240-	for (i = *colon + 1; i < n; i++) {
241-		if (s[i] == '=') {
242-			*eq = i;
243-			return i > *colon + 1;
244-		}
245-	}
246-	return 0;
247-}
248-
249-static char *
250-expandstr(struct Env *env, const char *s)
251-{
252-	size_t i, j, k, n, cap, len, inner, start, colon, eq;
253-	char close;
254-	char *out, *name, *val, *from, *to;
255-	struct Var *v;
256-
257-	n = strlen(s);
258-	cap = n + 1;
259-	len = 0;
260-	out = xmalloc(cap);
261-	for (i = 0; i < n; i++) {
262-		if (s[i] != '$' || i + 1 >= n || (s[i + 1] != '(' && s[i + 1] != '{')) {
263-			if (len + 2 > cap) {
264-				cap *= 2;
265-				out = xrealloc(out, cap);
266-			}
267-			out[len++] = s[i];
268-			continue;
269-		}
270-		close = s[i + 1] == '(' ? ')' : '}';
271-		start = i;
272-		j = i + 2;
273-		inner = 1;
274-		while (j < n && inner) {
275-			if (s[j] == '$' && j + 1 < n && (s[j + 1] == '(' || s[j + 1] == '{')) {
276-				inner++;
277-				j += 2;
278-				continue;
279-			}
280-			if (s[j] == close)
281-				inner--;
282-			j++;
283-		}
284-		if (inner) {
285-			if (len + (n - start) + 1 > cap) {
286-				cap = len + (n - start) + 1;
287-				out = xrealloc(out, cap);
288-			}
289-			memcpy(out + len, s + start, n - start);
290-			len += n - start;
291-			break;
292-		}
293-		if (isplainvar(s + i + 2, j - i - 3)) {
294-			name = xstrndup(s + i + 2, j - i - 3);
295-			v = findvar(env, name);
296-			free(name);
297-			val = v ? expandstr(env, v->val) : xstrdup("");
298-			k = strlen(val);
299-			if (len + k + 1 > cap) {
300-				cap = len + k + 1;
301-				out = xrealloc(out, cap);
302-			}
303-			memcpy(out + len, val, k);
304-			len += k;
305-			free(val);
306-		} else if (issubstref(s + i + 2, j - i - 3, &colon, &eq)) {
307-			name = xstrndup(s + i + 2, colon);
308-			from = xstrndup(s + i + 2 + colon + 1, eq - colon - 1);
309-			to = xstrndup(s + i + 2 + eq + 1, j - i - 4 - eq);
310-			v = findvar(env, name);
311-			free(name);
312-			if (v) {
313-				char *base;
314-
315-				base = expandstr(env, v->val);
316-				val = substval(base, from, to);
317-				free(base);
318-			} else {
319-				val = xstrdup("");
320-			}
321-			free(from);
322-			free(to);
323-			k = strlen(val);
324-			if (len + k + 1 > cap) {
325-				cap = len + k + 1;
326-				out = xrealloc(out, cap);
327-			}
328-			memcpy(out + len, val, k);
329-			len += k;
330-			free(val);
331-		} else {
332-			k = j - start;
333-			if (len + k + 1 > cap) {
334-				cap = len + k + 1;
335-				out = xrealloc(out, cap);
336-			}
337-			memcpy(out + len, s + start, k);
338-			len += k;
339-		}
340-		i = j - 1;
341-	}
342-	out[len] = 0;
343-	return out;
344-}
345-
346-static void
347-setvar(struct Env *env, const char *name, char *val, int simple)
348-{
349-	struct Var *v;
350-
351-	v = findvar(env, name);
352-	if (v) {
353-		free(v->val);
354-		v->val = val;
355-		v->simple = simple;
356-		return;
357-	}
358-	env->v = xrealloc(env->v, (env->n + 1) * sizeof(env->v[0]));
359-	env->v[env->n].name = xstrdup(name);
360-	env->v[env->n].val = val;
361-	env->v[env->n].simple = simple;
362-	env->n++;
363-}
364-
365-static void
366-seedenv(struct Env *env)
367-{
368-	setvar(env, "CC", xstrdup("cc"), 1);
369-	setvar(env, "CXX", xstrdup("c++"), 1);
370-	setvar(env, "CPP", xstrdup("$(CC) -E"), 0);
371-	setvar(env, "AR", xstrdup("ar"), 1);
372-	setvar(env, "ARFLAGS", xstrdup("-rv"), 1);
373-	setvar(env, "AS", xstrdup("as"), 1);
374-	setvar(env, "LD", xstrdup("ld"), 1);
375-	setvar(env, "LEX", xstrdup("lex"), 1);
376-	setvar(env, "YACC", xstrdup("yacc"), 1);
377-	setvar(env, "SHELL", xstrdup("/bin/sh"), 1);
378-}
379-
380-static void
381-freeenv(struct Env *env)
382-{
383-	size_t i;
384-
385-	for (i = 0; i < env->n; i++) {
386-		free(env->v[i].name);
387-		free(env->v[i].val);
388-	}
389-	free(env->v);
390-	env->v = 0;
391-	env->n = 0;
392-}
393-
394-static void
395-evalassign(struct Env *env, const struct AssignNode *in)
396-{
397-	struct Var *v;
398-	char *rhs, *joined;
399-
400-	if (in->tspec)
401-		return;
402-	switch (in->op) {
403-	case ASSIGN_EQ:
404-		setvar(env, in->lhs, xstrdup(in->rhs), 0);
405-		break;
406-	case ASSIGN_COLON_EQ:
407-		setvar(env, in->lhs, expandstr(env, in->rhs), 1);
408-		break;
409-	case ASSIGN_QMARK_EQ:
410-		if (!findvar(env, in->lhs))
411-			setvar(env, in->lhs, xstrdup(in->rhs), 0);
412-		break;
413-	case ASSIGN_PLUS_EQ:
414-		v = findvar(env, in->lhs);
415-		if (!v) {
416-			setvar(env, in->lhs, xstrdup(in->rhs), 0);
417-			break;
418-		}
419-		rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
420-		joined = cat2(v->val, rhs);
421-		free(rhs);
422-		free(v->val);
423-		v->val = joined;
424-		break;
425-	case ASSIGN_BANG_EQ:
426-		setvar(env, in->lhs, expandstr(env, in->rhs), 1);
427-		break;
428-	}
429-}
430-
431-static int
432-testcond(struct Env *env, const struct CondNode *cond)
433-{
434-	char *a, *b;
435-	int ok;
436-
437-	a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
438-	b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
439-	ok = strcmp(a, b) == 0;
440-	free(a);
441-	free(b);
442-	if (cond->kind == COND_IFNEQ)
443-		ok = !ok;
444-	return ok;
445-}
446-
447-static void
448-copywords(struct StrList *out, const struct StrList *in, struct Env *env)
449-{
450-	size_t i;
451-
452-	memset(out, 0, sizeof(*out));
453-	for (i = 0; i < in->n; i++) {
454-		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
455-		out->v[out->n++] = expandstr(env, in->v[i]);
456-	}
457-}
458-
459-static void
460-copyrecipes(struct RecipeList *out, const struct RecipeList *in, struct Env *env)
461-{
462-	size_t i;
463-
464-	memset(out, 0, sizeof(*out));
465-	for (i = 0; i < in->n; i++) {
466-		out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
467-		out->v[out->n++] = expandstr(env, in->v[i]);
468-	}
469-}
470-
471-static int
472-evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
473-{
474-	size_t i;
475-
476-	memset(out, 0, sizeof(*out));
477-	for (i = 0; i < in->n; i++) {
478-		const struct Node *src;
479-		struct Node node;
480-
481-		src = &in->v[i];
482-		memset(&node, 0, sizeof(node));
483-		node.kind = src->kind;
484-		node.loc = src->loc;
485-		switch (src->kind) {
486-		case NODE_BLANK:
487-			break;
488-		case NODE_COMMENT:
489-		case NODE_RAW:
490-			node.data.raw.text = xstrdup(src->data.raw.text);
491-			break;
492-		case NODE_INCLUDE:
493-			node.data.include.optional = src->data.include.optional;
494-			node.data.include.path = expandstr(env, src->data.include.path);
495-			break;
496-		case NODE_COND:
497-			if (testcond(env, &src->data.cond)) {
498-				if (evalnodes(&src->data.cond.thenpart, out, env) < 0)
499-					return -1;
500-			} else {
501-				if (evalnodes(&src->data.cond.elsepart, out, env) < 0)
502-					return -1;
503-			}
504-			continue;
505-		case NODE_ASSIGN:
506-			node.data.assign.lhs = xstrdup(src->data.assign.lhs);
507-			node.data.assign.op = src->data.assign.op;
508-			node.data.assign.tspec = src->data.assign.tspec;
509-			if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
510-				node.data.assign.rhs = expandstr(env, src->data.assign.rhs);
511-			else
512-				node.data.assign.rhs = xstrdup(src->data.assign.rhs);
513-			copywords(&node.data.assign.targets, &src->data.assign.targets, env);
514-			evalassign(env, &src->data.assign);
515-			break;
516-		case NODE_RULE:
517-			copywords(&node.data.rule.targets, &src->data.rule.targets, env);
518-			copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
519-			copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
520-			copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes, env);
521-			break;
522-		}
523-		addnode(out, node);
524-	}
525-	return 0;
526-}
527-
528-int
529-eval(const struct Ast *ast, struct Ast *out)
530-{
531-	struct Env env;
532-	int rc;
533-
534-	memset(&env, 0, sizeof(env));
535-	seedenv(&env);
536-	rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
537-	freeenv(&env);
538-	return rc;
539-}
540-
541-static void
542-freestrs(struct StrList *list)
543-{
544-	size_t i;
545-
546-	for (i = 0; i < list->n; i++)
547-		free(list->v[i]);
548-	free(list->v);
549-}
550-
551-static void
552-freerec(struct RecipeList *list)
553-{
554-	size_t i;
555-
556-	for (i = 0; i < list->n; i++)
557-		free(list->v[i]);
558-	free(list->v);
559-}
560-
561-static void
562-freenodes(struct NodeList *list)
563-{
564-	size_t i;
565-
566-	for (i = 0; i < list->n; i++) {
567-		switch (list->v[i].kind) {
568-		case NODE_COMMENT:
569-		case NODE_RAW:
570-			free(list->v[i].data.raw.text);
571-			break;
572-		case NODE_ASSIGN:
573-			free(list->v[i].data.assign.lhs);
574-			free(list->v[i].data.assign.rhs);
575-			freestrs(&list->v[i].data.assign.targets);
576-			break;
577-		case NODE_RULE:
578-			freestrs(&list->v[i].data.rule.targets);
579-			freestrs(&list->v[i].data.rule.prereqs);
580-			freestrs(&list->v[i].data.rule.order_only);
581-			freerec(&list->v[i].data.rule.recipes);
582-			break;
583-		case NODE_INCLUDE:
584-			free(list->v[i].data.include.path);
585-			break;
586-		case NODE_COND:
587-			free(list->v[i].data.cond.arg1);
588-			free(list->v[i].data.cond.arg2);
589-			free(list->v[i].data.cond.raw);
590-			freenodes(&list->v[i].data.cond.thenpart);
591-			freenodes(&list->v[i].data.cond.elsepart);
592-			break;
593-		case NODE_BLANK:
594-			break;
595-		}
596-	}
597-	free(list->v);
598-	list->v = 0;
599-	list->n = 0;
600-}
601-
602-void
603-freeast(struct Ast *ast)
604-{
605-	freenodes((struct NodeList *)ast);
606-}
A util.c
+57, -0
 1@@ -0,0 +1,57 @@
 2+#include "internal.h"
 3+
 4+#include <stdio.h>
 5+#include <stdlib.h>
 6+#include <string.h>
 7+
 8+/* shared utility functions */
 9+
10+void *
11+xmalloc(size_t n)
12+{
13+	void *p;
14+
15+	p = malloc(n ? n : 1);
16+	if (!p) {
17+		fprintf(stderr, "out of memory\n");
18+		exit(1);
19+	}
20+	return p;
21+}
22+
23+void *
24+xrealloc(void *p, size_t n)
25+{
26+	void *q;
27+
28+	q = realloc(p, n ? n : 1);
29+	if (!q) {
30+		fprintf(stderr, "out of memory\n");
31+		exit(1);
32+	}
33+	return q;
34+}
35+
36+char *
37+xstrndup(const char *s, size_t n)
38+{
39+	char *p;
40+
41+	p = xmalloc(n + 1);
42+	memcpy(p, s, n);
43+	p[n] = 0;
44+	return p;
45+}
46+
47+char *
48+xstrdup(const char *s)
49+{
50+	return xstrndup(s, strlen(s));
51+}
52+
53+void
54+addnode(struct NodeList *list, struct Node node)
55+{
56+	list->v = xrealloc(list->v, (list->n + 1) * sizeof(list->v[0]));
57+	list->v[list->n++] = node;
58+}