commit d34ce1a

shrub  ·  2026-04-16 16:19:03 +0000 UTC
parent edddaa3
make eval state less shitty
9 files changed,  +155, -92
+58, -0
 1@@ -0,0 +1,58 @@
 2+digraph shin {
 3+  rankdir=LR;
 4+  node [fontname="monospace"];
 5+  n0 [label="all", style=dashed];
 6+  n1 [label="shin", shape=box];
 7+  n2 [label="cli/main.o", shape=box];
 8+  n3 [label="src/parse.o", shape=box];
 9+  n4 [label="src/eval/eval.o", shape=box];
10+  n5 [label="src/eval/dollar.o", shape=box];
11+  n6 [label="src/graph.o", shape=box];
12+  n7 [label="src/posix.o", shape=box];
13+  n8 [label="src/gnu/pattern.o", shape=box];
14+  n9 [label="backends/ninja.o", shape=box];
15+  n10 [label="backends/graphviz.o", shape=box];
16+  n11 [label="src/util.o", shape=box];
17+  n12 [label="src/gnu/functions.o", shape=box];
18+  n13 [label="fmt", shape=box];
19+  n14 [label="test", shape=box];
20+  n15 [label="install", shape=box];
21+  n16 [label="clean", shape=box];
22+  n17 [label="cli/main.c"];
23+  n18 [label="src/parse.c"];
24+  n19 [label="src/eval/eval.c"];
25+  n20 [label="src/eval/dollar.c"];
26+  n21 [label="src/graph.c"];
27+  n22 [label="src/posix.c"];
28+  n23 [label="src/gnu/pattern.c"];
29+  n24 [label="backends/ninja.c"];
30+  n25 [label="backends/graphviz.c"];
31+  n26 [label="src/util.c"];
32+  n27 [label="src/gnu/functions.c"];
33+
34+  n1 -> n0;
35+  n2 -> n1;
36+  n3 -> n1;
37+  n4 -> n1;
38+  n5 -> n1;
39+  n6 -> n1;
40+  n7 -> n1;
41+  n8 -> n1;
42+  n9 -> n1;
43+  n10 -> n1;
44+  n11 -> n1;
45+  n12 -> n1;
46+  n17 -> n2;
47+  n18 -> n3;
48+  n19 -> n4;
49+  n20 -> n5;
50+  n21 -> n6;
51+  n22 -> n7;
52+  n23 -> n8;
53+  n24 -> n9;
54+  n25 -> n10;
55+  n26 -> n11;
56+  n27 -> n12;
57+  n1 -> n14;
58+  n1 -> n15;
59+}
+27, -41
  1@@ -8,8 +8,6 @@
  2 
  3 /* $() ${} expansion is handled here */
  4 
  5-static int evalerrs;
  6-
  7 struct Buf {
  8 	char *s;
  9 	size_t len;
 10@@ -17,25 +15,13 @@ struct Buf {
 11 };
 12 
 13 static void
 14-evalerr(const char *msg, const char *detail)
 15+evalerr(struct EvalCtx *ctx, const char *msg, const char *detail)
 16 {
 17 	if (detail)
 18 		fprintf(stderr, "eval: unsupported: %s: %s\n", msg, detail);
 19 	else
 20 		fprintf(stderr, "eval: unsupported: %s\n", msg);
 21-	evalerrs++;
 22-}
 23-
 24-void
 25-resetevalerrs(void)
 26-{
 27-	evalerrs = 0;
 28-}
 29-
 30-int
 31-getevalerrs(void)
 32-{
 33-	return evalerrs;
 34+	ctx->errors++;
 35 }
 36 
 37 static void
 38@@ -241,20 +227,20 @@ findclose(const char *s, size_t i, size_t n, char close)
 39 }
 40 
 41 static char *
 42-expandvarref(struct Env *env, const char *s, size_t n)
 43+expandvarref(struct EvalCtx *ctx, const char *s, size_t n)
 44 {
 45 	char *name, *val;
 46 	struct Var *v;
 47 
 48 	name = xstrndup(s, n);
 49-	v = findvar(env, name);
 50+	v = findvar(ctx->env, name);
 51 	free(name);
 52-	val = v ? expandstr(env, v->val) : xstrdup("");
 53+	val = v ? expandstr(ctx, v->val) : xstrdup("");
 54 	return val;
 55 }
 56 
 57 static char *
 58-expandsubstref(struct Env *env, const char *s, size_t colon, size_t eq, size_t n)
 59+expandsubstref(struct EvalCtx *ctx, const char *s, size_t colon, size_t eq, size_t n)
 60 {
 61 	char *name, *from, *to, *val;
 62 	struct Var *v;
 63@@ -265,17 +251,17 @@ expandsubstref(struct Env *env, const char *s, size_t colon, size_t eq, size_t n
 64 		name = xstrndup(s, colon);
 65 		fromraw = xstrndup(s + colon + 1, eq - colon - 1);
 66 		toraw = xstrndup(s + eq + 1, n - eq - 1);
 67-		from = expandstr(env, fromraw);
 68-		to = expandstr(env, toraw);
 69+		from = expandstr(ctx, fromraw);
 70+		to = expandstr(ctx, toraw);
 71 		free(fromraw);
 72 		free(toraw);
 73-		v = findvar(env, name);
 74+		v = findvar(ctx->env, name);
 75 		free(name);
 76 	}
 77 	if (v) {
 78 		char *base;
 79 
 80-		base = expandstr(env, v->val);
 81+		base = expandstr(ctx, v->val);
 82 		val = substval(base, from, to);
 83 		free(base);
 84 	} else {
 85@@ -324,7 +310,7 @@ static const struct func funcs[] = {
 86 };
 87 
 88 static char *
 89-funcref(struct Env *env, const char *s, size_t n)
 90+funcref(struct EvalCtx *ctx, const char *s, size_t n)
 91 {
 92 	size_t i, namelen, start;
 93 	const struct func *f;
 94@@ -342,7 +328,7 @@ funcref(struct Env *env, const char *s, size_t n)
 95 			char *raw, *exp;
 96 
 97 			raw = xstrndup(s + start, n - start);
 98-			exp = expandstr(env, raw);
 99+			exp = expandstr(ctx, raw);
100 			free(raw);
101 			val = f->fn.f1(exp);
102 			free(exp);
103@@ -354,7 +340,7 @@ funcref(struct Env *env, const char *s, size_t n)
104 			comma = findargcomma(args, strlen(args));
105 			if (comma < 0) {
106 				detail = cat3("$(", f->name, ")");
107-				evalerr("malformed function arguments", detail);
108+				evalerr(ctx, "malformed function arguments", detail);
109 				free(detail);
110 				free(args);
111 				return xstrdup("");
112@@ -362,8 +348,8 @@ funcref(struct Env *env, const char *s, size_t n)
113 			lhs_raw = xstrndup(args, (size_t)comma);
114 			rhs_raw = xstrdup(args + comma + 1);
115 			free(args);
116-			lhs_exp = expandstr(env, lhs_raw);
117-			rhs_exp = expandstr(env, rhs_raw);
118+			lhs_exp = expandstr(ctx, lhs_raw);
119+			rhs_exp = expandstr(ctx, rhs_raw);
120 			free(lhs_raw);
121 			free(rhs_raw);
122 			val = f->fn.f2(lhs_exp, rhs_exp);
123@@ -378,7 +364,7 @@ funcref(struct Env *env, const char *s, size_t n)
124 			c1 = findargcomma(args, strlen(args));
125 			if (c1 < 0) {
126 				detail = cat3("$(", f->name, ")");
127-				evalerr("malformed function arguments", detail);
128+				evalerr(ctx, "malformed function arguments", detail);
129 				free(detail);
130 				free(args);
131 				return xstrdup("");
132@@ -394,9 +380,9 @@ funcref(struct Env *env, const char *s, size_t n)
133 				a3r = xstrdup(args + rest + (size_t)c2 + 1);
134 			}
135 			free(args);
136-			a1e = expandstr(env, a1r);
137-			a2e = expandstr(env, a2r);
138-			a3e = expandstr(env, a3r);
139+			a1e = expandstr(ctx, a1r);
140+			a2e = expandstr(ctx, a2r);
141+			a3e = expandstr(ctx, a3r);
142 			free(a1r);
143 			free(a2r);
144 			free(a3r);
145@@ -411,26 +397,26 @@ funcref(struct Env *env, const char *s, size_t n)
146 }
147 
148 static char *
149-expandref(struct Env *env, const char *s, size_t n)
150+expandref(struct EvalCtx *ctx, const char *s, size_t n)
151 {
152 	size_t colon, eq;
153 	char *val, *unsup;
154 
155 	if (isplainvar(s, n))
156-		return expandvarref(env, s, n);
157+		return expandvarref(ctx, s, n);
158 	if (issubstref(s, n, &colon, &eq))
159-		return expandsubstref(env, s, colon, eq, n);
160-	val = funcref(env, s, n);
161+		return expandsubstref(ctx, s, colon, eq, n);
162+	val = funcref(ctx, s, n);
163 	if (val)
164 		return val;
165 	unsup = xstrndup(s - 2, n + 3);
166-	evalerr("variable reference or function", unsup);
167+	evalerr(ctx, "variable reference or function", unsup);
168 	free(unsup);
169 	return xstrndup(s - 2, n + 3);
170 }
171 
172 char *
173-expandstr(struct Env *env, const char *s)
174+expandstr(struct EvalCtx *ctx, const char *s)
175 {
176 	size_t i, j, n;
177 	char close;
178@@ -458,7 +444,7 @@ expandstr(struct Env *env, const char *s)
179 				continue;
180 			}
181 			/* some single char variable like $x */
182-			val = expandvarref(env, s + i + 1, 1);
183+			val = expandvarref(ctx, s + i + 1, 1);
184 			bufappend(&out, val);
185 			free(val);
186 			i++;
187@@ -470,7 +456,7 @@ expandstr(struct Env *env, const char *s)
188 			bufappendn(&out, s + i, n - i);
189 			break;
190 		}
191-		val = expandref(env, s + i + 2, j - i - 3);
192+		val = expandref(ctx, s + i + 2, j - i - 3);
193 		bufappend(&out, val);
194 		free(val);
195 		i = j - 1;
+30, -26
  1@@ -54,17 +54,19 @@ copyenv(struct Env *dst, const struct Env *src)
  2 }
  3 
  4 void
  5-evalassign(struct Env *env, const struct AssignNode *in)
  6+evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
  7 {
  8+	struct Env *env;
  9 	struct Var *v;
 10 	char *rhs, *joined;
 11 
 12+	env = ctx->env;
 13 	switch (in->op) {
 14 	case ASSIGN_EQ:
 15 		envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
 16 		break;
 17 	case ASSIGN_COLON_EQ:
 18-		envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
 19+		envsetvar(env, in->lhs, expandstr(ctx, in->rhs), 1);
 20 		break;
 21 	case ASSIGN_QMARK_EQ:
 22 		if (!findvar(env, in->lhs))
 23@@ -76,28 +78,28 @@ evalassign(struct Env *env, const struct AssignNode *in)
 24 			envsetvar(env, in->lhs, xstrdup(in->rhs), 0);
 25 			break;
 26 		}
 27-		rhs = v->simple ? expandstr(env, in->rhs) : xstrdup(in->rhs);
 28+		rhs = v->simple ? expandstr(ctx, in->rhs) : xstrdup(in->rhs);
 29 		joined = cat3(v->val, " ", rhs);
 30 		free(rhs);
 31 		free(v->val);
 32 		v->val = joined;
 33 		break;
 34 	case ASSIGN_BANG_EQ:
 35-		envsetvar(env, in->lhs, expandstr(env, in->rhs), 1);
 36+		envsetvar(env, in->lhs, expandstr(ctx, in->rhs), 1);
 37 		break;
 38 	}
 39 }
 40 
 41 static int
 42-testcond(struct Env *env, const struct CondNode *cond)
 43+testcond(struct EvalCtx *ctx, const struct CondNode *cond)
 44 {
 45 	char *a, *b, *name;
 46 	struct Var *v;
 47 	int ok;
 48 
 49 	if (cond->kind == COND_IFDEF || cond->kind == COND_IFNDEF) {
 50-		name = expandstr(env, cond->arg1);
 51-		v = findvar(env, name);
 52+		name = expandstr(ctx, cond->arg1);
 53+		v = findvar(ctx->env, name);
 54 		ok = v != 0;
 55 		free(name);
 56 		if (cond->kind == COND_IFNDEF)
 57@@ -105,8 +107,8 @@ testcond(struct Env *env, const struct CondNode *cond)
 58 		return ok;
 59 	}
 60 
 61-	a = cond->arg1 ? expandstr(env, cond->arg1) : xstrdup("");
 62-	b = cond->arg2 ? expandstr(env, cond->arg2) : xstrdup("");
 63+	a = cond->arg1 ? expandstr(ctx, cond->arg1) : xstrdup("");
 64+	b = cond->arg2 ? expandstr(ctx, cond->arg2) : xstrdup("");
 65 	ok = strcmp(a, b) == 0;
 66 	free(a);
 67 	free(b);
 68@@ -116,7 +118,7 @@ testcond(struct Env *env, const struct CondNode *cond)
 69 }
 70 
 71 static void
 72-copywords(struct StrList *out, const struct StrList *in, struct Env *env)
 73+copywords(struct StrList *out, const struct StrList *in, struct EvalCtx *ctx)
 74 {
 75 	size_t i;
 76 
 77@@ -124,7 +126,7 @@ copywords(struct StrList *out, const struct StrList *in, struct Env *env)
 78 	for (i = 0; i < in->n; i++) {
 79 		char *s;
 80 
 81-		s = expandstr(env, in->v[i]);
 82+		s = expandstr(ctx, in->v[i]);
 83 		splitwords(out, s, strlen(s));
 84 		free(s);
 85 	}
 86@@ -143,7 +145,7 @@ copyrecipes(struct RecipeList *out, const struct RecipeList *in)
 87 }
 88 
 89 static int
 90-evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
 91+evalnodes(const struct NodeList *in, struct NodeList *out, struct EvalCtx *ctx)
 92 {
 93 	size_t i;
 94 
 95@@ -164,21 +166,21 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
 96 		case NODE_RAW: {
 97 			char *exp;
 98 
 99-			exp = expandstr(env, src->data.raw.text);
100+			exp = expandstr(ctx, src->data.raw.text);
101 			free(exp);
102 			node.kind = NODE_BLANK;
103 			break;
104 		}
105 		case NODE_INCLUDE:
106 			node.data.include.optional = src->data.include.optional;
107-			node.data.include.path = expandstr(env, src->data.include.path);
108+			node.data.include.path = expandstr(ctx, src->data.include.path);
109 			break;
110 		case NODE_COND:
111-			if (testcond(env, &src->data.cond)) {
112-				if (evalnodes(&src->data.cond.thenpart, out, env) < 0)
113+			if (testcond(ctx, &src->data.cond)) {
114+				if (evalnodes(&src->data.cond.thenpart, out, ctx) < 0)
115 					return -1;
116 			} else {
117-				if (evalnodes(&src->data.cond.elsepart, out, env) < 0)
118+				if (evalnodes(&src->data.cond.elsepart, out, ctx) < 0)
119 					return -1;
120 			}
121 			continue;
122@@ -187,18 +189,18 @@ evalnodes(const struct NodeList *in, struct NodeList *out, struct Env *env)
123 			node.data.assign.op = src->data.assign.op;
124 			node.data.assign.tspec = src->data.assign.tspec;
125 			if (src->data.assign.op == ASSIGN_COLON_EQ || src->data.assign.op == ASSIGN_BANG_EQ)
126-				node.data.assign.rhs = expandstr(env, src->data.assign.rhs);
127+				node.data.assign.rhs = expandstr(ctx, src->data.assign.rhs);
128 			else
129 				node.data.assign.rhs = xstrdup(src->data.assign.rhs);
130-			copywords(&node.data.assign.targets, &src->data.assign.targets, env);
131+			copywords(&node.data.assign.targets, &src->data.assign.targets, ctx);
132 			if (!src->data.assign.tspec)
133-				evalassign(env, &src->data.assign);
134+				evalassign(ctx, &src->data.assign);
135 			break;
136 		case NODE_RULE:
137 			node.data.rule.dcolon = src->data.rule.dcolon;
138-			copywords(&node.data.rule.targets, &src->data.rule.targets, env);
139-			copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, env);
140-			copywords(&node.data.rule.order_only, &src->data.rule.order_only, env);
141+			copywords(&node.data.rule.targets, &src->data.rule.targets, ctx);
142+			copywords(&node.data.rule.prereqs, &src->data.rule.prereqs, ctx);
143+			copywords(&node.data.rule.order_only, &src->data.rule.order_only, ctx);
144 			copyrecipes(&node.data.rule.recipes, &src->data.rule.recipes);
145 			break;
146 		}
147@@ -211,15 +213,17 @@ int
148 eval(const struct Ast *ast, struct Ast *out)
149 {
150 	struct Env env;
151+	struct EvalCtx ctx;
152 	int rc;
153 
154 	memset(out, 0, sizeof(*out));
155 	memset(&env, 0, sizeof(env));
156+	memset(&ctx, 0, sizeof(ctx));
157 	seedenv(&env);
158-	resetevalerrs();
159-	rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &env);
160+	ctx.env = &env;
161+	rc = evalnodes((const struct NodeList *)ast, (struct NodeList *)out, &ctx);
162 	freeenv(&env);
163-	if (rc == 0 && getevalerrs())
164+	if (rc == 0 && ctx.errors)
165 		return -1;
166 	return rc;
167 }
+2, -2
 1@@ -157,7 +157,7 @@ patruleviable(const struct PatRule *rule, const struct Graph *graph, const char
 2 }
 3 
 4 int
 5-instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct Env *env)
 6+instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx)
 7 {
 8 	size_t i, j;
 9 
10@@ -195,7 +195,7 @@ instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Targ
11 			char *s, *vars, *exp;
12 
13 			s = applystem(rules->v[i].recipes.v[j], stem);
14-			vars = expandstr(env, s);
15+			vars = expandstr(ctx, s);
16 			exp = expandstem(vars, stem);
17 			free(vars);
18 			free(s);
+1, -1
1@@ -18,7 +18,7 @@ struct PatRules {
2 int ispat(const char *s);
3 int patmatches(const char *pat, const char *name);
4 void collectpat(struct PatRules *rules, const struct RuleNode *rule);
5-int instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct Env *env);
6+int instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx);
7 void freepatrule(struct PatRules *rules);
8 
9 #endif
+26, -14
  1@@ -104,14 +104,14 @@ targetmatches(const char *pat, const char *name)
  2 }
  3 
  4 static void
  5-addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Env *env)
  6+addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct EvalCtx *ctx)
  7 {
  8 	size_t i;
  9 
 10 	for (i = 0; i < src->n; i++) {
 11 		char *exp;
 12 
 13-		exp = expandstr(env, src->v[i]);
 14+		exp = expandstr(ctx, src->v[i]);
 15 		if (!exp[0]) {
 16 			free(exp);
 17 			continue;
 18@@ -127,7 +127,7 @@ addexprecipes(struct RecipeList *dest, const struct RecipeList *src, struct Env
 19  * binary : LDFLAGS += -static
 20  */
 21 static void
 22-applytassigns(struct GraphState *gs, struct Env *env, const char *name)
 23+applytassigns(struct GraphState *gs, struct EvalCtx *ctx, const char *name)
 24 {
 25 	size_t i, j;
 26 
 27@@ -141,16 +141,16 @@ applytassigns(struct GraphState *gs, struct Env *env, const char *name)
 28 			in.lhs = gs->tas[i].lhs;
 29 			in.rhs = gs->tas[i].rhs;
 30 			in.op = gs->tas[i].op;
 31-			evalassign(env, &in);
 32+			evalassign(ctx, &in);
 33 		}
 34 	}
 35 }
 36 
 37 static void
 38-targetenv(struct GraphState *gs, struct Env *env, const char *name)
 39+targetenv(struct GraphState *gs, struct EvalCtx *ctx, const char *name)
 40 {
 41-	copyenv(env, &gs->env);
 42-	applytassigns(gs, env, name);
 43+	copyenv(ctx->env, &gs->env);
 44+	applytassigns(gs, ctx, name);
 45 }
 46 
 47 /* add an explicit target rule to the graph, and add all
 48@@ -162,6 +162,10 @@ addrule(struct GraphState *gs, const char *name, const struct RuleNode *rule)
 49 	size_t i;
 50 	struct Target *t;
 51 	struct Env env;
 52+	struct EvalCtx ctx;
 53+
 54+	memset(&ctx, 0, sizeof(ctx));
 55+	ctx.env = &env;
 56 
 57 	t = findtarget(gs->graph, name);
 58 	if (!t) {
 59@@ -178,8 +182,8 @@ addrule(struct GraphState *gs, const char *name, const struct RuleNode *rule)
 60 	t->dcolon = rule->dcolon;
 61 	addwords(&t->prereqs, &rule->prereqs);
 62 	addwords(&t->order_only, &rule->order_only);
 63-	targetenv(gs, &env, name);
 64-	addexprecipes(&t->recipes, &rule->recipes, &env);
 65+	targetenv(gs, &ctx, name);
 66+	addexprecipes(&t->recipes, &rule->recipes, &ctx);
 67 	freeenv(&env);
 68 
 69 	for (i = 0; i < rule->prereqs.n; i++) {
 70@@ -216,11 +220,14 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
 71 {
 72 	size_t i, j, start;
 73 	struct GraphState gs;
 74+	struct EvalCtx ctx;
 75 
 76 	memset(graph, 0, sizeof(*graph));
 77 	memset(&gs, 0, sizeof(gs));
 78+	memset(&ctx, 0, sizeof(ctx));
 79 	gs.graph = graph;
 80 	seedenv(&gs.env);
 81+	ctx.env = &gs.env;
 82 	imprules(&gs.sufs);
 83 
 84 	/* get the state, env vars in gs.env, target-specific var in gs.tas,
 85@@ -230,7 +237,7 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
 86 			if (ast->v[i].data.assign.tspec)
 87 				addtassign(&gs, &ast->v[i].data.assign);
 88 			else
 89-				evalassign(&gs.env, &ast->v[i].data.assign);
 90+				evalassign(&ctx, &ast->v[i].data.assign);
 91 		} else if (ast->v[i].kind == NODE_RULE) {
 92 			if (issufrule(&ast->v[i].data.rule)) {
 93 				gs.saw_suffixes = 1;
 94@@ -273,14 +280,19 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
 95 			if (t->recipes.n == 0) {
 96 				int matched;
 97 				struct Env env;
 98+				struct EvalCtx targetctx;
 99 
100-				targetenv(&gs, &env, t->name);
101-				instpatrule(&gs.patterns, graph, t, &env);
102+				memset(&targetctx, 0, sizeof(targetctx));
103+				targetctx.env = &env;
104+				targetenv(&gs, &targetctx, t->name);
105+				instpatrule(&gs.patterns, graph, t, &targetctx);
106 				matched = t->recipes.n > 0;
107 				if (!matched) {
108-					instsufrule(&gs.sufs, graph, t, &env);
109+					instsufrule(&gs.sufs, graph, t, &targetctx);
110 				}
111 				freeenv(&env);
112+				if (targetctx.errors)
113+					ctx.errors += targetctx.errors;
114 			}
115 			nprereqs = t->prereqs.n;
116 				for (j = 0; j < nprereqs; j++) {
117@@ -311,7 +323,7 @@ buildgraph(const struct Ast *ast, struct Graph *graph)
118 	free(gs.rules);
119 	freeenv(&gs.env);
120 
121-	return 0;
122+	return ctx.errors ? -1 : 0;
123 }
124 
125 void
+7, -4
 1@@ -14,6 +14,11 @@ struct Env {
 2 	size_t n;
 3 };
 4 
 5+struct EvalCtx {
 6+	struct Env *env;
 7+	int errors;
 8+};
 9+
10 void *xmalloc(size_t n);
11 void *xrealloc(void *p, size_t n);
12 char *xstrndup(const char *s, size_t n);
13@@ -30,13 +35,11 @@ struct Target *findtarget(struct Graph *graph, const char *name);
14 const struct Target *findctarget(const struct Graph *graph, const char *name);
15 
16 struct Var *findvar(struct Env *env, const char *name);
17-char *expandstr(struct Env *env, const char *s);
18-void resetevalerrs(void);
19-int getevalerrs(void);
20+char *expandstr(struct EvalCtx *ctx, const char *s);
21 void seedenv(struct Env *env);
22 void freeenv(struct Env *env);
23 void copyenv(struct Env *dst, const struct Env *src);
24-void evalassign(struct Env *env, const struct AssignNode *in);
25+void evalassign(struct EvalCtx *ctx, const struct AssignNode *in);
26 
27 char *fnwildcard(const char *patterns);
28 char *fnshell(const char *cmd);
+3, -3
 1@@ -406,7 +406,7 @@ int
 2 instsufrule(const struct SufRules *rules,
 3             const struct Graph *graph,
 4             struct Target *t,
 5-            struct Env *env)
 6+            struct EvalCtx *ctx)
 7 {
 8 	size_t i, k;
 9 
10@@ -436,7 +436,7 @@ instsufrule(const struct SufRules *rules,
11 				for (k = 0; k < rules->sufs[r].recipes.n; k++) {
12 					char *vars, *exp;
13 
14-					vars = expandstr(env, rules->sufs[r].recipes.v[k]);
15+					vars = expandstr(ctx, rules->sufs[r].recipes.v[k]);
16 					exp = expandauto(vars, t, stem);
17 					free(vars);
18 					if (!exp[0]) {
19@@ -474,7 +474,7 @@ instsufrule(const struct SufRules *rules,
20 			for (k = 0; k < rules->singlesuf[j].recipes.n; k++) {
21 				char *vars, *exp;
22 
23-				vars = expandstr(env, rules->singlesuf[j].recipes.v[k]);
24+				vars = expandstr(ctx, rules->singlesuf[j].recipes.v[k]);
25 				exp = expandauto(vars, t, t->name);
26 				free(vars);
27 				if (!exp[0]) {
+1, -1
1@@ -39,7 +39,7 @@ int collectsufrule(struct SufRules *rules, const struct RuleNode *rule);
2 int instsufrule(const struct SufRules *rules,
3             const struct Graph *graph,
4             struct Target *t,
5-            struct Env *env);
6+            struct EvalCtx *ctx);
7 int issufrule(const struct RuleNode *rule);
8 void addsufs(struct SuffixList *list, const struct StrList *sufs);
9 void clearsufs(struct SuffixList *list);