1#include "shinobi.h"
2#include "internal.h"
3#include "posix.h"
4
5#include <glob.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10/*
11 * eval does the second pass over the built ast
12 * seed builtins like CC and so on
13 * apply assignment semantics
14 * execute conditionals and flatten the chosen branch
15 */
16
17struct Var *
18findvar(struct Env *env, const char *name)
19{
20 const char *iname;
21 size_t i;
22
23 iname = intern(name);
24 for (i = 0; i < env->n; i++) {
25 if (env->v[i].name == iname)
26 return &env->v[i];
27 }
28 return 0;
29}
30
31void
32freeenv(struct Env *env)
33{
34 size_t i;
35
36 for (i = 0; i < env->n; i++)
37 free(env->v[i].val);
38 free(env->v);
39 env->v = 0;
40 env->n = 0;
41 env->cap = 0;
42}
43
44void
45copyenv(struct Env *dst, const struct Env *src)
46{
47 size_t i;
48
49 memset(dst, 0, sizeof(*dst));
50 if (src->n)
51 dst->v = xrealloc(0, src->n * sizeof(dst->v[0]));
52 for (i = 0; i < src->n; i++) {
53 dst->v[i].name = src->v[i].name;
54 dst->v[i].val = xstrdup(src->v[i].val);
55 dst->v[i].simple = src->v[i].simple;
56 dst->v[i].origin = src->v[i].origin;
57 dst->v[i].exported = src->v[i].exported;
58 }
59 dst->n = src->n;
60 dst->cap = src->n;
61}
62
63static char *
64runshellassign(const char *cmd)
65{
66 FILE *fp;
67 char buf[4096];
68 char *out;
69 size_t len, cap, nread, i;
70
71 fp = popen(cmd, "r");
72 if (!fp)
73 return xstrdup("");
74
75 cap = 64;
76 len = 0;
77 out = xmalloc(cap);
78 out[0] = 0;
79
80 while ((nread = fread(buf, 1, sizeof(buf), fp)) > 0) {
81 if (len + nread + 1 > cap) {
82 while (cap < len + nread + 1)
83 cap *= 2;
84 out = xrealloc(out, cap);
85 }
86 memcpy(out + len, buf, nread);
87 len += nread;
88 }
89 out[len] = 0;
90 pclose(fp);
91 if (len > 0 && out[len - 1] == '\n') {
92 len--;
93 if (len > 0 && out[len - 1] == '\r')
94 len--;
95 out[len] = 0;
96 } else if (len > 0 && out[len - 1] == '\r') {
97 len--;
98 out[len] = 0;
99 }
100
101 for (i = 0; i < len; i++) {
102 if (out[i] == '\n' || out[i] == '\r')
103 out[i] = ' ';
104 }
105 return out;
106}
107
108static char *
109escapedollars(const char *s)
110{
111 size_t i, n, ndollar;
112 char *out;
113 size_t j;
114
115 n = strlen(s);
116 ndollar = 0;
117 for (i = 0; i < n; i++) {
118 if (s[i] == '$')
119 ndollar++;
120 }
121 out = xmalloc(n + ndollar + 1);
122 j = 0;
123 for (i = 0; i < n; i++) {
124 if (s[i] == '$')
125 out[j++] = '$';
126 out[j++] = s[i];
127 }
128 out[j] = 0;
129 return out;
130}
131
132static void
133removeexportname(struct StrList *list, const char *name)
134{
135 size_t i;
136
137 for (i = 0; i < list->n; i++) {
138 if (strcmp(list->v[i], name) != 0)
139 continue;
140 free(list->v[i]);
141 memmove(&list->v[i], &list->v[i + 1], (list->n - i - 1) * sizeof(list->v[0]));
142 list->n--;
143 return;
144 }
145}
146
147static int
148assignexported(const struct EvalCtx *ctx, const struct AssignNode *in)
149{
150 if (in->exported > 0)
151 return 1;
152 if (in->exported < 0)
153 return 0;
154 if (hasword(&ctx->unexports, in->lhs))
155 return 0;
156 if (hasword(&ctx->exports, in->lhs))
157 return 1;
158 return ctx->export_all;
159}
160
161void
162evalassign(struct EvalCtx *ctx, const struct AssignNode *in)
163{
164 struct Env *env;
165 struct Var *v;
166 char *lhs, *rhs, *joined;
167 enum Origin o;
168 int exported;
169
170 env = ctx->env;
171 o = in->origin ? in->origin : ORIGIN_FILE;
172 exported = assignexported(ctx, in);
173 lhs = expandstr(ctx, in->lhs);
174 switch (in->op) {
175 case ASSIGN_EQ:
176 envsetvar(env, lhs, xstrdup(in->rhs), 0, o, exported);
177 break;
178 case ASSIGN_DCOLON_EQ:
179 if (ctx->mode == MODE_POSIX_2008) {
180 dielikemake(ctx->cur_path, ctx->cur_line, "'::=' is not valid in POSIX 2008", 0);
181 ctx->errors++;
182 break;
183 }
184 envsetvar(env, lhs, expandstr(ctx, in->rhs), 1, o, exported);
185 break;
186 case ASSIGN_COLON_EQ:
187 /* := is a gnu extension. posix uses ::= for simple expansion.
188 * bsd := is different again, equivalent to posix :::= */
189 if (ctx->mode == MODE_POSIX_2024) {
190 dielikemake(ctx->cur_path, ctx->cur_line, "':=' is not valid in POSIX 2024, use '::='", 0);
191 ctx->errors++;
192 break;
193 }
194 if (ctx->mode == MODE_POSIX_2008) {
195 dielikemake(ctx->cur_path, ctx->cur_line, "':=' is not valid in POSIX 2008", 0);
196 ctx->errors++;
197 break;
198 }
199 envsetvar(env, lhs, expandstr(ctx, in->rhs), 1, o, exported);
200 break;
201 case ASSIGN_COLON3_EQ:
202 if (ctx->mode == MODE_POSIX_2008) {
203 dielikemake(ctx->cur_path, ctx->cur_line, "':::=' is not valid in POSIX 2008", 0);
204 ctx->errors++;
205 break;
206 }
207 /* expand now, escape the result so re-expansion on use gives back the same value */
208 rhs = expandstr(ctx, in->rhs);
209 joined = escapedollars(rhs);
210 free(rhs);
211 envsetvar(env, lhs, joined, 0, o, exported);
212 break;
213 case ASSIGN_QMARK_EQ:
214 if (ctx->mode == MODE_POSIX_2008) {
215 dielikemake(ctx->cur_path, ctx->cur_line, "'?=' is not valid in POSIX 2008", 0);
216 ctx->errors++;
217 break;
218 }
219 if (!findvar(env, lhs))
220 envsetvar(env, lhs, xstrdup(in->rhs), 0, o, exported);
221 break;
222 case ASSIGN_PLUS_EQ:
223 if (ctx->mode == MODE_POSIX_2008) {
224 dielikemake(ctx->cur_path, ctx->cur_line, "'+=' is not valid in POSIX 2008", 0);
225 ctx->errors++;
226 break;
227 }
228 v = findvar(env, lhs);
229 if (!v) {
230 envsetvar(env, lhs, xstrdup(in->rhs), 0, o, in->exported);
231 break;
232 }
233 if ((int)o < (int)v->origin)
234 break;
235 rhs = v->simple ? expandstr(ctx, in->rhs) : xstrdup(in->rhs);
236 joined = cat3(v->val, " ", rhs);
237 free(rhs);
238 free(v->val);
239 v->val = joined;
240 v->origin = o;
241 if (exported)
242 v->exported = 1;
243 break;
244 case ASSIGN_BANG_EQ:
245 if (ctx->mode == MODE_POSIX_2008) {
246 dielikemake(ctx->cur_path, ctx->cur_line, "'!=' is not valid in POSIX 2008", 0);
247 ctx->errors++;
248 break;
249 }
250 rhs = expandstr(ctx, in->rhs);
251 joined = runshellassign(rhs);
252 free(rhs);
253 envsetvar(env, lhs, joined, 1, o, exported);
254 break;
255 }
256 free(lhs);
257}
258
259static void
260evalexport(struct EvalCtx *ctx, const struct ExportNode *exp)
261{
262 struct StrList names;
263 struct Var *v;
264 size_t i, j;
265
266 if (exp->all) {
267 size_t j;
268
269 ctx->export_all = exp->exported;
270 for (i = 0; i < ctx->env->n; i++)
271 ctx->env->v[i].exported = exp->exported;
272 for (j = 0; j < ctx->out->nvars; j++)
273 ctx->out->vars[j].exported = exp->exported;
274 for (j = 0; j < ctx->out->ntvars; j++)
275 ctx->out->tvars[j].exported = exp->exported;
276 return;
277 }
278 memset(&names, 0, sizeof(names));
279 for (i = 0; i < exp->names.n; i++) {
280 char *s;
281
282 s = expandstr(ctx, exp->names.v[i]);
283 splitwords(&names, s, strlen(s));
284 free(s);
285 }
286 for (i = 0; i < names.n; i++) {
287 if (exp->exported) {
288 removeexportname(&ctx->unexports, names.v[i]);
289 if (!hasword(&ctx->exports, names.v[i]))
290 addstr(&ctx->exports, names.v[i]);
291 } else {
292 removeexportname(&ctx->exports, names.v[i]);
293 if (!hasword(&ctx->unexports, names.v[i]))
294 addstr(&ctx->unexports, names.v[i]);
295 }
296 v = findvar(ctx->env, names.v[i]);
297 if (v)
298 v->exported = exp->exported;
299 }
300 for (j = 0; j < ctx->out->nvars; j++) {
301 if (hasword(&ctx->exports, ctx->out->vars[j].lhs))
302 ctx->out->vars[j].exported = 1;
303 if (hasword(&ctx->unexports, ctx->out->vars[j].lhs))
304 ctx->out->vars[j].exported = 0;
305 }
306 for (j = 0; j < ctx->out->ntvars; j++) {
307 if (hasword(&ctx->exports, ctx->out->tvars[j].lhs))
308 ctx->out->tvars[j].exported = 1;
309 if (hasword(&ctx->unexports, ctx->out->tvars[j].lhs))
310 ctx->out->tvars[j].exported = 0;
311 }
312 freestrs(&names);
313}
314
315static int
316testcond(struct EvalCtx *ctx, const struct CondNode *cond)
317{
318 char *a, *b, *name;
319 struct Var *v;
320 int ok;
321
322 if (cond->kind == COND_IFDEF || cond->kind == COND_IFNDEF) {
323 name = expandstr(ctx, cond->arg1);
324 v = findvar(ctx->env, name);
325 ok = v != 0;
326 free(name);
327 if (cond->kind == COND_IFNDEF)
328 ok = !ok;
329 return ok;
330 }
331
332 a = cond->arg1 ? expandstr(ctx, cond->arg1) : xstrdup("");
333 b = cond->arg2 ? expandstr(ctx, cond->arg2) : xstrdup("");
334 ok = strcmp(a, b) == 0;
335 free(a);
336 free(b);
337 if (cond->kind == COND_IFNEQ)
338 ok = !ok;
339 return ok;
340}
341
342static void
343copywords(struct StrList *out, const struct StrList *in, struct EvalCtx *ctx)
344{
345 size_t i;
346
347 memset(out, 0, sizeof(*out));
348 for (i = 0; i < in->n; i++) {
349 char *s;
350
351 s = expandstr(ctx, in->v[i]);
352 splitwords(out, s, strlen(s));
353 free(s);
354 }
355}
356
357static void
358addrulesetassign(struct AssignNode **vec, size_t *n, const struct Node *src, struct EvalCtx *ctx)
359{
360 struct AssignNode *dst;
361
362 *vec = xrealloc(*vec, (*n + 1) * sizeof((*vec)[0]));
363 dst = &(*vec)[(*n)++];
364 memset(dst, 0, sizeof(*dst));
365 dst->lhs = xstrdup(src->data.assign.lhs);
366 dst->op = src->data.assign.op;
367 dst->origin = src->data.assign.origin;
368 dst->exported = assignexported(ctx, &src->data.assign);
369 dst->tspec = src->data.assign.tspec;
370 if (src->data.assign.op == ASSIGN_DCOLON_EQ ||
371 src->data.assign.op == ASSIGN_COLON_EQ ||
372 src->data.assign.op == ASSIGN_COLON3_EQ) {
373 char *rhs;
374
375 rhs = expandstr(ctx, src->data.assign.rhs);
376 dst->rhs = escapedollars(rhs);
377 free(rhs);
378 } else if (src->data.assign.op == ASSIGN_BANG_EQ) {
379 dst->rhs = expandstr(ctx, src->data.assign.rhs);
380 } else {
381 dst->rhs = xstrdup(src->data.assign.rhs);
382 }
383 copywords(&dst->targets, &src->data.assign.targets, ctx);
384}
385
386static int
387issufruletargets(const struct RuleNode *rule)
388{
389 size_t i;
390 char *a, *b;
391
392 if (rule->targets.n == 0)
393 return 0;
394 for (i = 0; i < rule->targets.n; i++) {
395 if (issinglesuf(rule->targets.v[i], &a)) {
396 free(a);
397 continue;
398 }
399 if (issuf(rule->targets.v[i], &a, &b)) {
400 free(a);
401 free(b);
402 continue;
403 }
404 return 0;
405 }
406 return 1;
407}
408
409static void
410addrulesetrule(struct RuleSet *out, const struct RuleNode *src, struct EvalCtx *ctx)
411{
412 struct RuleNode *dst;
413
414 out->rules = xrealloc(out->rules, (out->nrules + 1) * sizeof(out->rules[0]));
415 dst = &out->rules[out->nrules++];
416 memset(dst, 0, sizeof(*dst));
417 dst->dcolon = src->dcolon;
418 copywords(&dst->targets, &src->targets, ctx);
419 if (src->target_pattern)
420 dst->target_pattern = expandstr(ctx, src->target_pattern);
421 copywords(&dst->prereqs, &src->prereqs, ctx);
422 copywords(&dst->order_only, &src->order_only, ctx);
423 addrecipes(&dst->recipes, &src->recipes);
424}
425
426static int
427hasglobmeta(const char *s)
428{
429 for (; *s; s++) {
430 if (*s == '*' || *s == '?' || *s == '[')
431 return 1;
432 }
433 return 0;
434}
435
436static int
437evalinclude(struct EvalCtx *ctx, const struct IncludeNode *inc)
438{
439 struct StrList paths;
440 char *exp;
441 size_t i;
442
443 struct RuleNode *makerule;
444
445 makerule = 0;
446
447 exp = expandstr(ctx, inc->path);
448 memset(&paths, 0, sizeof(paths));
449 splitwords(&paths, exp, strlen(exp));
450 free(exp);
451 for (i = 0; i < paths.n; i++) {
452 size_t j, nmatch;
453 char *single;
454 const char *word;
455 glob_t g;
456 int grc;
457
458 memset(&g, 0, sizeof(g));
459 single = 0;
460 word = paths.v[i];
461 nmatch = 0;
462 grc = 0;
463 /* glob in include paths is a gnu extension */
464 if (ctx->mode == MODE_GNU && hasglobmeta(word)) {
465 grc = glob(word, 0, 0, &g);
466 nmatch = (grc == 0 && g.gl_pathc > 0) ? g.gl_pathc : 0;
467 } else {
468 nmatch = 1;
469 single = xstrdup(word);
470 }
471
472 if (nmatch == 0) {
473 if (!inc->optional)
474 fprintf(stderr, "%s:%d: %s: No such file or directory\n",
475 ctx->cur_path, ctx->cur_line, word);
476 globfree(&g);
477 continue;
478 }
479
480 for (j = 0; j < nmatch; j++) {
481 char *src;
482 const char *path;
483
484 path = single ? single : g.gl_pathv[j];
485 src = readfile(path);
486 if (!src) {
487 size_t r, k;
488
489 makerule = 0;
490 for (r = 0; r < ctx->out->nrules && !makerule; r++) {
491 struct RuleNode *rule = &ctx->out->rules[r];
492
493 for (k = 0; k < rule->targets.n; k++) {
494 if (strcmp(rule->targets.v[k], path) == 0) {
495 makerule = rule;
496 break;
497 }
498 }
499 }
500 if (makerule) {
501 for (k = 0; k < makerule->recipes.n; k++) {
502 char *cmd;
503 int rc;
504
505 cmd = expandstr(ctx, makerule->recipes.v[k].body);
506 rc = system(cmd);
507 free(cmd);
508 if (rc != 0)
509 break;
510 }
511 src = readfile(path);
512 }
513 }
514 if (!src) {
515 if (!inc->optional)
516 fprintf(stderr, "%s:%d: %s: No such file or directory\n",
517 ctx->cur_path, ctx->cur_line, path);
518 continue;
519 }
520 if (evalsnippet(ctx, path, src) < 0) {
521 free(src);
522 if (single)
523 free(single);
524 globfree(&g);
525 freestrs(&paths);
526 return -1;
527 }
528 free(src);
529 }
530 if (single)
531 free(single);
532 globfree(&g);
533 }
534 freestrs(&paths);
535 return 0;
536}
537
538static int
539isgnutarget(const char *s)
540{
541 /*gnu only special targets that we actually handle*/
542 static const char *const gnutargets[] = {
543 ".EXPORT_ALL_VARIABLES",
544 0
545 };
546 size_t i;
547
548 for (i = 0; gnutargets[i]; i++) {
549 if (strcmp(s, gnutargets[i]) == 0)
550 return 1;
551 }
552 return 0;
553}
554
555static int
556evalnodes(const struct NodeList *in, struct RuleSet *out, struct EvalCtx *ctx)
557{
558 size_t i;
559 struct SpecialTargets targets;
560
561 initspecialtargets(&targets);
562
563 for (i = 0; i < in->n; i++) {
564 const struct Node *src;
565
566 src = &in->v[i];
567 ctx->cur_line = src->loc.line0;
568 switch (src->kind) {
569 case NODE_BLANK:
570 break;
571 case NODE_RAW: {
572 char *exp;
573
574 exp = expandstr(ctx, src->data.raw.text);
575 free(exp);
576 break;
577 }
578 case NODE_COMMENT:
579 break;
580 case NODE_INCLUDE:
581 if (evalinclude(ctx, &src->data.include) < 0)
582 return -1;
583 break;
584 case NODE_COND:
585 if (ctx->mode != MODE_GNU) {
586 dielikemake(ctx->cur_path, ctx->cur_line, "conditionals are only valid in GNU", 0);
587 ctx->errors++;
588 break;
589 }
590 if (testcond(ctx, &src->data.cond)) {
591 if (evalnodes(&src->data.cond.thenpart, out, ctx) < 0)
592 return -1;
593 } else {
594 if (evalnodes(&src->data.cond.elsepart, out, ctx) < 0)
595 return -1;
596 }
597 continue;
598 case NODE_ASSIGN:
599 if (ctx->mode != MODE_GNU && src->data.assign.define_block) {
600 dielikemake(ctx->cur_path, ctx->cur_line, "'define'/'endef' are only valid in GNU", 0);
601 ctx->errors++;
602 break;
603 }
604 if (ctx->mode != MODE_GNU && src->data.assign.exported != 0) {
605 dielikemake(ctx->cur_path, ctx->cur_line, "'export'/'unexport' are only valid in GNU", 0);
606 ctx->errors++;
607 break;
608 }
609 updatespecialassign(&targets, src->data.assign.lhs, src->data.assign.rhs);
610 if (src->data.assign.tspec) {
611 addrulesetassign(&out->tvars, &out->ntvars, src, ctx);
612 } else {
613 addrulesetassign(&out->vars, &out->nvars, src, ctx);
614 evalassign(ctx, &src->data.assign);
615 }
616 break;
617 case NODE_EXPORT:
618 if (ctx->mode != MODE_GNU) {
619 dielikemake(ctx->cur_path, ctx->cur_line, "'export'/'unexport' are only valid in GNU", 0);
620 ctx->errors++;
621 break;
622 }
623 evalexport(ctx, &src->data.export);
624 break;
625 case NODE_RULE: {
626 struct StrList exptargets;
627 struct RuleNode tmprule;
628
629 /* expand targets before special target checks so that some pattern like
630 * "$X.POSIX:" with X undefined correctly resolves to just ".POSIX:" and
631 * set targets.posix before suffix rule prereq warnings fire
632 * (this is for gnu/features/suffixrules/t009). */
633 memset(&exptargets, 0, sizeof(exptargets));
634 copywords(&exptargets, &src->data.rule.targets, ctx);
635 tmprule = src->data.rule;
636 tmprule.targets = exptargets;
637 if (ctx->mode != MODE_GNU && tmprule.dcolon) {
638 dielikemake(ctx->cur_path, ctx->cur_line,
639 "double-colon rules are only valid in GNU", 0);
640 ctx->errors++;
641 freestrs(&exptargets);
642 break;
643 }
644 /* ignore gnu only targets if not in gnu mode */
645 if (ctx->mode != MODE_GNU &&
646 tmprule.targets.n == 1 &&
647 isgnutarget(tmprule.targets.v[0])) {
648 freestrs(&exptargets);
649 break;
650 }
651 if (tmprule.targets.n == 1 &&
652 strcmp(tmprule.targets.v[0], ".EXPORT_ALL_VARIABLES") == 0) {
653 ctx->export_all = 1;
654 freestrs(&exptargets);
655 break;
656 }
657 if (tmprule.targets.n == 1 &&
658 strcmp(tmprule.targets.v[0], ".DEFAULT") == 0) {
659 freerecipes(&out->defaultrule);
660 addrecipes(&out->defaultrule, &src->data.rule.recipes);
661 freestrs(&exptargets);
662 break;
663 }
664 if (handlespecialrule(&targets, &tmprule)) {
665 freestrs(&exptargets);
666 break;
667 }
668 if (!targets.posix &&
669 src->data.rule.prereqs.n > 0 &&
670 !issufrule(&tmprule) &&
671 issufruletargets(&tmprule))
672 warnlikemake(ctx->cur_path, ctx->cur_line,
673 "ignoring prerequisites on suffix rule definition");
674 freestrs(&exptargets);
675 addrulesetrule(out, &src->data.rule, ctx);
676 break;
677 }
678 }
679 }
680 if (out)
681 addwords(&out->phony, &targets.phony);
682 if (out)
683 out->export_all = ctx->export_all || targets.export_all;
684 if (out)
685 out->posix = targets.posix;
686 freestrs(&targets.phony);
687 return 0;
688}
689
690int
691evalsnippet(struct EvalCtx *ctx, const char *path, const char *src)
692{
693 struct Ast ast;
694 const char *saved_path;
695 int rc;
696
697 if (parse(path, src, &ast, ctx->mode) < 0)
698 return -1;
699 saved_path = ctx->cur_path;
700 ctx->cur_path = path;
701 rc = evalnodes((const struct NodeList *)&ast, ctx->out, ctx);
702 ctx->cur_path = saved_path;
703 freeast(&ast);
704 return rc;
705}
706
707int
708eval(const char *path, const struct Ast *ast, const struct Ast *pre, int envoverride, enum ShinMode mode, struct RuleSet *out)
709{
710 struct Env env;
711 struct EvalCtx ctx;
712 int rc;
713
714 memset(out, 0, sizeof(*out));
715 out->envoverride = envoverride;
716 memset(&env, 0, sizeof(env));
717 memset(&ctx, 0, sizeof(ctx));
718 seedenv(&env, 0, out->envoverride, mode);
719 ctx.env = &env;
720 ctx.out = out;
721 ctx.cur_path = path;
722 ctx.mode = mode;
723 if (pre) {
724 rc = evalnodes((const struct NodeList *)pre, out, &ctx);
725 if (rc < 0) {
726 freeenv(&env);
727 return rc;
728 }
729 }
730 rc = evalnodes((const struct NodeList *)ast, out, &ctx);
731 freeenv(&env);
732 freestrs(&ctx.exports);
733 freestrs(&ctx.unexports);
734 if (rc == 0 && ctx.errors)
735 return -1;
736 return rc;
737}
738
739static void
740freeassigns(struct AssignNode *v, size_t n)
741{
742 size_t i;
743
744 for (i = 0; i < n; i++) {
745 free(v[i].lhs);
746 free(v[i].rhs);
747 freestrs(&v[i].targets);
748 }
749 free(v);
750}
751
752static void
753freerules(struct RuleNode *v, size_t n)
754{
755 size_t i;
756
757 for (i = 0; i < n; i++) {
758 freestrs(&v[i].targets);
759 free(v[i].target_pattern);
760 freestrs(&v[i].prereqs);
761 freestrs(&v[i].order_only);
762 freerecipes(&v[i].recipes);
763 }
764 free(v);
765}
766
767void
768freeruleset(struct RuleSet *ruleset)
769{
770 if (!ruleset)
771 return;
772 freeassigns(ruleset->vars, ruleset->nvars);
773 freeassigns(ruleset->tvars, ruleset->ntvars);
774 freerules(ruleset->rules, ruleset->nrules);
775 freerecipes(&ruleset->defaultrule);
776 freestrs(&ruleset->phony);
777 memset(ruleset, 0, sizeof(*ruleset));
778}
779
780/*
781 * strings in ast nodes are arena-owned, only free the v arrays and
782 * the submake strings (xmalloc'd separately by parsesubmake)
783 */
784static void
785freenodes(struct NodeList *list)
786{
787 size_t i, j;
788
789 for (i = 0; i < list->n; i++) {
790 switch (list->v[i].kind) {
791 case NODE_COMMENT:
792 case NODE_RAW:
793 case NODE_INCLUDE:
794 break;
795 case NODE_EXPORT:
796 free(list->v[i].data.export.names.v);
797 break;
798 case NODE_ASSIGN:
799 free(list->v[i].data.assign.targets.v);
800 break;
801 case NODE_RULE:
802 free(list->v[i].data.rule.targets.v);
803 free(list->v[i].data.rule.prereqs.v);
804 free(list->v[i].data.rule.order_only.v);
805 for (j = 0; j < list->v[i].data.rule.recipes.n; j++)
806 freesubmake(&list->v[i].data.rule.recipes.v[j].sm);
807 free(list->v[i].data.rule.recipes.v);
808 break;
809 case NODE_COND:
810 freenodes(&list->v[i].data.cond.thenpart);
811 freenodes(&list->v[i].data.cond.elsepart);
812 break;
813 case NODE_BLANK:
814 break;
815 }
816 }
817 free(list->v);
818 list->v = 0;
819 list->n = 0;
820 list->cap = 0;
821}
822
823void
824freeast(struct Ast *ast)
825{
826 freenodes((struct NodeList *)ast);
827 arena_free(&ast->arena);
828}