commit 1c4d5c7

shrub  ·  2026-05-19 19:39:17 +0000 UTC
parent c6bde0a
move include gating to preproc and parse time
6 files changed,  +84, -60
+1, -1
1@@ -382,7 +382,7 @@ main(int argc, char **argv)
2 		{
3 			int rc;
4 
5-			rc = parse(path, src, &ast);
6+			rc = parse(path, src, &ast, mode);
7 			if (rc < 0) {
8 				free(assigns);
9 				free(goals);
+1, -1
1@@ -79,7 +79,7 @@ loadimprules(struct SufRules *rules, const char *src)
2 	size_t i;
3 	int rc;
4 
5-	rc = parse("<built-in-rules>", src, &ast);
6+	rc = parse("<built-in-rules>", src, &ast, MODE_GNU);
7 	if (rc < 0)
8 		return -1;
9 	for (i = 0; i < ast.n; i++) {
+1, -26
 1@@ -440,31 +440,6 @@ evalinclude(struct EvalCtx *ctx, const struct IncludeNode *inc)
 2 	memset(&paths, 0, sizeof(paths));
 3 	splitwords(&paths, exp, strlen(exp));
 4 	free(exp);
 5-	/* posix 2008 only allows plain includes with one file, posix 2024 allows both 
 6-	 * include and -include with multiple files, but no sinclude. */
 7-	if (ctx->mode == MODE_POSIX_2008) {
 8-		if (inc->optional) {
 9-			dielikemake(ctx->cur_path, ctx->cur_line,
10-			            "optional includes are not valid in POSIX 2008", 0);
11-			ctx->errors++;
12-			freestrs(&paths);
13-			return 0;
14-		}
15-		if (paths.n != 1) {
16-			dielikemake(ctx->cur_path, ctx->cur_line,
17-			            "include in POSIX 2008 must specify exactly one file", 0);
18-			ctx->errors++;
19-			freestrs(&paths);
20-			return 0;
21-		}
22-	}
23-	if (ctx->mode == MODE_POSIX_2024 && inc->sinclude) {
24-		dielikemake(ctx->cur_path, ctx->cur_line,
25-		            "'sinclude' is not valid in POSIX 2024; use '-include'", 0);
26-		ctx->errors++;
27-		freestrs(&paths);
28-		return 0;
29-	}
30 	for (i = 0; i < paths.n; i++) {
31 		size_t j, nmatch;
32 		char *single;
33@@ -711,7 +686,7 @@ evalsnippet(struct EvalCtx *ctx, const char *path, const char *src)
34 	const char *saved_path;
35 	int rc;
36 
37-	if (parse(path, src, &ast) < 0)
38+	if (parse(path, src, &ast, ctx->mode) < 0)
39 		return -1;
40 	saved_path = ctx->cur_path;
41 	ctx->cur_path = path;
+2, -2
 1@@ -649,7 +649,7 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
 2 		char *assignsrc;
 3 
 4 		assignsrc = appendassigns(xstrdup(""), &sg->assigns);
 5-		rc = parse("<command line>", assignsrc, &pre);
 6+		rc = parse("<command line>", assignsrc, &pre, sg->mode);
 7 		free(assignsrc);
 8 		if (rc < 0)
 9 			goto out;
10@@ -658,7 +658,7 @@ buildsubgraph0(struct SubGraph *sg, struct SubGraphStack *stack)
11 				pre.v[i].data.assign.origin = ORIGIN_COMMAND;
12 		}
13 	}
14-	rc = parse(path, src, &ast);
15+	rc = parse(path, src, &ast, sg->mode);
16 	if (rc < 0)
17 		goto out;
18 	rc = eval(path, &ast, sg->assigns.n ? &pre : 0, envoverride, sg->mode, &rs);
+76, -27
  1@@ -33,9 +33,12 @@ static int parseerrs;
  2 static int deadlikemake;
  3 static struct Arena *g_ast_arena;
  4 
  5-static int preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc);
  6+static int preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc,
  7+                       enum ShinMode mode);
  8 static int preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc,
  9-                        struct SpecialTargets *targets);
 10+                        struct SpecialTargets *targets, enum ShinMode mode);
 11+static int preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc *inc,
 12+                          struct SpecialTargets *targets, enum ShinMode mode);
 13 
 14 static char *
 15 astdup(const char *s, size_t n)
 16@@ -512,11 +515,11 @@ dirpart(const char *path)
 17 
 18 static int
 19 preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc *inc,
 20-               struct SpecialTargets *targets)
 21+               struct SpecialTargets *targets, enum ShinMode mode)
 22 {
 23 	int rc;
 24 
 25-	rc = preprocfile0(incarg, 0, pre, inc, targets);
 26+	rc = preprocfile0(incarg, 0, pre, inc, targets, mode);
 27 	if (rc == 0)
 28 		return 0;
 29 	if (strcmp(dir, ".") == 0)
 30@@ -525,7 +528,7 @@ preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc
 31 		char *full;
 32 
 33 		full = joinpath(dir, incarg);
 34-		rc = preprocfile0(full, 0, pre, inc, targets);
 35+		rc = preprocfile0(full, 0, pre, inc, targets, mode);
 36 		free(full);
 37 		return rc;
 38 	}
 39@@ -533,7 +536,7 @@ preprocinclude(const char *dir, const char *incarg, struct Pre *pre, struct Inc
 40 
 41 static int
 42 preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc,
 43-             struct SpecialTargets *targets)
 44+             struct SpecialTargets *targets, enum ShinMode mode)
 45 {
 46 	char *src, *dir;
 47 	const char *p;
 48@@ -564,15 +567,37 @@ preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct
 49 
 50 			stripcomment(line.text);
 51 			trim = trimdup(line.text, strlen(line.text));
 52-				if (haskw(trim, "include") || haskw(trim, "-include")) {
 53+				if (haskw(trim, "include") || haskw(trim, "-include") || haskw(trim, "sinclude")) {
 54 					int rc;
 55 					size_t kwlen;
 56 
 57-					opt = haskw(trim, "-include");
 58-					kwlen = haskw(trim, "-include") ? 8 : 7;
 59+					opt = haskw(trim, "-include") || haskw(trim, "sinclude");
 60+					if (mode == MODE_POSIX_2008 && opt) {
 61+						dielikemake(path, line.line0,
 62+						            "optional includes are not valid in POSIX 2008", 0);
 63+						free(trim);
 64+						free(line.path);
 65+						free(line.text);
 66+						free(dir);
 67+						free(src);
 68+						popinc(inc);
 69+						return -2;
 70+					}
 71+					if (mode == MODE_POSIX_2024 && haskw(trim, "sinclude")) {
 72+						dielikemake(path, line.line0,
 73+						            "'sinclude' is not valid in POSIX 2024; use '-include'", 0);
 74+						free(trim);
 75+						free(line.path);
 76+						free(line.text);
 77+						free(dir);
 78+						free(src);
 79+						popinc(inc);
 80+						return -2;
 81+					}
 82+					kwlen = (haskw(trim, "-include") || haskw(trim, "sinclude")) ? 8 : 7;
 83 				incarg = trimdup(trim + kwlen, strlen(trim + kwlen));
 84 				if (isplainpath(incarg)) {
 85-					rc = preprocinclude(dir, incarg, pre, inc, targets);
 86+					rc = preprocinclude(dir, incarg, pre, inc, targets, mode);
 87 					if (rc == 0) {
 88 						free(incarg);
 89 						free(line.path);
 90@@ -620,22 +645,23 @@ preprocfile0(const char *path, const char *src_override, struct Pre *pre, struct
 91 }
 92 
 93 static int
 94-preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc)
 95+preprocfile(const char *path, const char *src_override, struct Pre *pre, struct Inc *inc,
 96+            enum ShinMode mode)
 97 {
 98 	struct SpecialTargets targets;
 99 
100 	initspecialtargets(&targets);
101-	return preprocfile0(path, src_override, pre, inc, &targets);
102+	return preprocfile0(path, src_override, pre, inc, &targets, mode);
103 }
104 
105 int
106-preproc(const char *path, struct Pre *pre)
107+preproc(const char *path, struct Pre *pre, enum ShinMode mode)
108 {
109 	struct Inc inc;
110 
111 	memset(pre, 0, sizeof(*pre));
112 	memset(&inc, 0, sizeof(inc));
113-	if (preprocfile(path, 0, pre, &inc) < 0) {
114+	if (preprocfile(path, 0, pre, &inc, mode) < 0) {
115 		free(inc.v);
116 		freepre(pre);
117 		return -1;
118@@ -659,10 +685,12 @@ freepre(struct Pre *pre)
119 }
120 
121 static struct Node
122-parseinclude(const struct PreLine *line, const char *s)
123+parseinclude(const struct PreLine *line, const char *s, enum ShinMode mode)
124 {
125 	struct Node state;
126 	size_t off;
127+	size_t i, nwords;
128+	const char *p;
129 
130 	memset(&state, 0, sizeof(state));
131 	state.kind = NODE_INCLUDE;
132@@ -680,6 +708,27 @@ parseinclude(const struct PreLine *line, const char *s)
133 		off = strlen("include");
134 	}
135 	state.data.include.path = atrimdup(s + off, strlen(s + off));
136+	if (mode == MODE_POSIX_2008) {
137+		p = state.data.include.path;
138+		nwords = 0;
139+		for (i = 0; p[i];) {
140+			while (p[i] && isspace((unsigned char)p[i]))
141+				i++;
142+			if (!p[i])
143+				break;
144+			nwords++;
145+			while (p[i] && !isspace((unsigned char)p[i]))
146+				i++;
147+		}
148+		if (nwords != 1) {
149+			memset(&state, 0, sizeof(state));
150+			state.kind = NODE_BLANK;
151+			state.loc.line0 = line->line0;
152+			state.loc.line1 = line->line1;
153+			parseerr(line, "include in POSIX 2008 must specify exactly one file", 0);
154+			return state;
155+		}
156+	}
157 	return state;
158 }
159 
160@@ -996,7 +1045,7 @@ parsedefine(const struct Pre *pre, size_t *i, struct Node *out)
161 }
162 
163 static struct Node
164-parseline(const struct PreLine *line, const struct SpecialTargets *targets)
165+parseline(const struct PreLine *line, const struct SpecialTargets *targets, enum ShinMode mode)
166 {
167 	struct Node state;
168 	struct AssignScan as;
169@@ -1068,7 +1117,7 @@ parseline(const struct PreLine *line, const struct SpecialTargets *targets)
170 		return state;
171 	}
172 	if (haskw(trim, "include") || haskw(trim, "-include") || haskw(trim, "sinclude")) {
173-		state = parseinclude(line, trim);
174+		state = parseinclude(line, trim, mode);
175 		free(trim);
176 		return state;
177 	}
178@@ -1158,7 +1207,7 @@ parseline(const struct PreLine *line, const struct SpecialTargets *targets)
179 
180 static int
181 parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node **last_rulep,
182-           struct SpecialTargets *targets)
183+           struct SpecialTargets *targets, enum ShinMode mode)
184 {
185 	struct Node state;
186 	struct Node *last_rule;
187@@ -1233,7 +1282,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
188 			state = parsecond(line, trim);
189 			free(trim);
190 			(*i)++;
191-			if (parseblock(pre, i, &state.data.cond.thenpart, &last_rule, targets) < 0)
192+			if (parseblock(pre, i, &state.data.cond.thenpart, &last_rule, targets, mode) < 0)
193 				return -1;
194 			if (*i < pre->n) {
195 				struct PreLine *endline;
196@@ -1253,7 +1302,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
197 						free(endline->text);
198 						endline->text = rep;
199 						free(endtrim);
200-						if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets) < 0)
201+						if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets, mode) < 0)
202 							return -1;
203 						state.loc.line1 = endline->line1;
204 						addnode(out, state);
205@@ -1263,7 +1312,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
206 					}
207 					free(endtrim);
208 					(*i)++;
209-					if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets) < 0)
210+					if (parseblock(pre, i, &state.data.cond.elsepart, &last_rule, targets, mode) < 0)
211 						return -1;
212 					if (*i >= pre->n) {
213 						parseerr(&pre->v[pre->n - 1], "missing 'endif'", 0);
214@@ -1296,7 +1345,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
215 		}
216 		free(trim);
217 
218-		state = parseline(line, targets);
219+		state = parseline(line, targets, mode);
220 		if (deadlikemake)
221 			return -1;
222 		if (state.kind == NODE_ASSIGN)
223@@ -1314,7 +1363,7 @@ parseblock(const struct Pre *pre, size_t *i, struct NodeList *out, struct Node *
224 }
225 
226 int
227-buildast(const char *path, const struct Pre *pre, struct Ast *ast)
228+buildast(const char *path, const struct Pre *pre, struct Ast *ast, enum ShinMode mode)
229 {
230 	size_t i;
231 	struct SpecialTargets targets;
232@@ -1328,7 +1377,7 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
233 	deadlikemake = 0;
234 	initspecialtargets(&targets);
235 	i = 0;
236-	rc = parseblock(pre, &i, (struct NodeList *)ast, 0, &targets);
237+	rc = parseblock(pre, &i, (struct NodeList *)ast, 0, &targets, mode);
238 	g_ast_arena = 0;
239 	if (rc < 0)
240 		return deadlikemake ? -2 : -1;
241@@ -1350,7 +1399,7 @@ buildast(const char *path, const struct Pre *pre, struct Ast *ast)
242 
243 /* preprocess and parse */
244 int
245-parse(const char *path, const char *src, struct Ast *ast)
246+parse(const char *path, const char *src, struct Ast *ast, enum ShinMode mode)
247 {
248 	struct Pre pre;
249 	struct Inc inc;
250@@ -1358,13 +1407,13 @@ parse(const char *path, const char *src, struct Ast *ast)
251 
252 	memset(&pre, 0, sizeof(pre));
253 	memset(&inc, 0, sizeof(inc));
254-	rc = preprocfile(path, src, &pre, &inc);
255+	rc = preprocfile(path, src, &pre, &inc, mode);
256 	free(inc.v);
257 	if (rc < 0) {
258 		freepre(&pre);
259 		return rc;
260 	}
261-	rc = buildast(path, &pre, ast);
262+	rc = buildast(path, &pre, ast, mode);
263 	freepre(&pre);
264 	return rc;
265 }
+3, -3
 1@@ -247,11 +247,11 @@ struct SubGraph {
 2 	enum ShinMode mode;
 3 };
 4 
 5-int preproc(const char *path, struct Pre *pre);
 6+int preproc(const char *path, struct Pre *pre, enum ShinMode mode);
 7 void freepre(struct Pre *pre);
 8-int buildast(const char *path, const struct Pre *pre, struct Ast *ast);
 9+int buildast(const char *path, const struct Pre *pre, struct Ast *ast, enum ShinMode mode);
10 int eval(const char *path, const struct Ast *ast, const struct Ast *pre, int envoverride, enum ShinMode mode, struct RuleSet *out);
11-int parse(const char *path, const char *src, struct Ast *ast);
12+int parse(const char *path, const char *src, struct Ast *ast, enum ShinMode mode);
13 void freeast(struct Ast *ast);
14 void freeruleset(struct RuleSet *ruleset);
15 int buildgraph(const struct RuleSet *ruleset, const struct StrList *goals, struct Graph *graph, enum ShinMode mode);