1#include "internal.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <ctype.h>
7#include <unistd.h>
8
9/* shared utility functions */
10
11const char *
12shinmode_name(enum ShinMode mode)
13{
14 switch (mode) {
15 case MODE_GNU:
16 return "gnu";
17 case MODE_POSIX_2024:
18 return "posix2024";
19 case MODE_POSIX_2008:
20 return "posix2008";
21 }
22 return "gnu";
23}
24
25int
26shinmode_parse(const char *s, enum ShinMode *out)
27{
28 if (strcmp(s, "gnu") == 0) {
29#if SHIN_WITH_GNU
30 *out = MODE_GNU;
31 return 0;
32#else
33 /* gnu support was not compiled in */
34 return -1;
35#endif
36 }
37 if (strcmp(s, "posix2024") == 0 || strcmp(s, "posix") == 0) {
38 *out = MODE_POSIX_2024;
39 return 0;
40 }
41 if (strcmp(s, "posix2008") == 0) {
42 *out = MODE_POSIX_2008;
43 return 0;
44 }
45 return -1;
46}
47
48static const char *progname = "shin";
49
50void
51set_progname(const char *name)
52{
53 progname = name;
54}
55
56void *
57xmalloc(size_t n)
58{
59 void *p;
60
61 p = malloc(n ? n : 1);
62 if (!p) {
63 fprintf(stderr, "out of memory\n");
64 exit(1);
65 }
66 return p;
67}
68
69void *
70xrealloc(void *p, size_t n)
71{
72 void *q;
73
74 q = realloc(p, n ? n : 1);
75 if (!q) {
76 fprintf(stderr, "out of memory\n");
77 exit(1);
78 }
79 return q;
80}
81
82char *
83xstrndup(const char *s, size_t n)
84{
85 char *p;
86
87 p = xmalloc(n + 1);
88 memcpy(p, s, n);
89 p[n] = 0;
90 return p;
91}
92
93char *
94xstrdup(const char *s)
95{
96 return xstrndup(s, strlen(s));
97}
98
99/* bump-pointer arena: alloc grows a slab chain, arena_free drops all slabs */
100
101struct ArenaBlock {
102 struct ArenaBlock *next;
103 size_t cap;
104 size_t pos;
105 /* data follows in memory */
106};
107
108#define ARENA_DEFAULT_BLOCK (64 * 1024)
109#define ARENA_ALIGN sizeof(void *)
110
111void
112arena_init(struct Arena *a, size_t block_size)
113{
114 a->head = 0;
115 a->block_size = block_size ? block_size : ARENA_DEFAULT_BLOCK;
116}
117
118void *
119arena_alloc(struct Arena *a, size_t n)
120{
121 struct ArenaBlock *b;
122 size_t aligned;
123 char *p;
124
125 aligned = (n + ARENA_ALIGN - 1) & ~(ARENA_ALIGN - 1);
126 b = a->head;
127 if (!b || b->pos + aligned > b->cap) {
128 size_t bsz;
129
130 bsz = aligned > a->block_size ? aligned : a->block_size;
131 b = xmalloc(sizeof(*b) + bsz);
132 b->cap = bsz;
133 b->pos = 0;
134 b->next = a->head;
135 a->head = b;
136 }
137 p = (char *)(b + 1) + b->pos;
138 b->pos += aligned;
139 return p;
140}
141
142char *
143arena_strndup(struct Arena *a, const char *s, size_t n)
144{
145 char *p;
146
147 p = arena_alloc(a, n + 1);
148 memcpy(p, s, n);
149 p[n] = 0;
150 return p;
151}
152
153char *
154arena_strdup(struct Arena *a, const char *s)
155{
156 return arena_strndup(a, s, strlen(s));
157}
158
159void
160arena_free(struct Arena *a)
161{
162 struct ArenaBlock *b, *next;
163
164 for (b = a->head; b; b = next) {
165 next = b->next;
166 free(b);
167 }
168 a->head = 0;
169}
170
171/* intern table: == is enough to compare, strings live til exit */
172
173#define INTERN_INIT_CAP 512
174
175struct InternEntry {
176 const char *s;
177 size_t h;
178};
179
180static struct InternEntry *intern_table;
181static size_t intern_n;
182static size_t intern_cap;
183
184struct GraphIndexEnt {
185 const char *name;
186 size_t idx;
187};
188
189struct EnvIndexEnt {
190 const char *name;
191 size_t idx;
192};
193
194static size_t
195strhash(const char *s)
196{
197 /* fnv-1a */
198 size_t h = 2166136261u;
199 while (*s) {
200 h ^= (unsigned char)*s++;
201 h *= 16777619u;
202 }
203 return h;
204}
205
206static void
207interngrow(void)
208{
209 size_t i, newcap;
210 struct InternEntry *newtbl;
211
212 newcap = intern_cap ? intern_cap * 2 : INTERN_INIT_CAP;
213 newtbl = xmalloc(newcap * sizeof(newtbl[0]));
214 memset(newtbl, 0, newcap * sizeof(newtbl[0]));
215 for (i = 0; i < intern_cap; i++) {
216 size_t j;
217
218 if (!intern_table[i].s)
219 continue;
220 j = intern_table[i].h & (newcap - 1);
221 while (newtbl[j].s)
222 j = (j + 1) & (newcap - 1);
223 newtbl[j] = intern_table[i];
224 }
225 free(intern_table);
226 intern_table = newtbl;
227 intern_cap = newcap;
228}
229
230const char *
231intern(const char *s)
232{
233 size_t h, i;
234
235 if (!intern_cap || intern_n * 3 >= intern_cap * 2)
236 interngrow();
237 h = strhash(s);
238 i = h & (intern_cap - 1);
239 for (;;) {
240 if (!intern_table[i].s) {
241 intern_table[i].s = xstrdup(s);
242 intern_table[i].h = h;
243 intern_n++;
244 return intern_table[i].s;
245 }
246 if (intern_table[i].h == h && strcmp(intern_table[i].s, s) == 0)
247 return intern_table[i].s;
248 i = (i + 1) & (intern_cap - 1);
249 }
250}
251
252char *
253readfile(const char *path)
254{
255 FILE *fp;
256 long n;
257 char *buf;
258
259 fp = fopen(path, "rb");
260 if (!fp)
261 return 0;
262 if (fseek(fp, 0, SEEK_END) < 0) {
263 fclose(fp);
264 return 0;
265 }
266 n = ftell(fp);
267 if (n < 0) {
268 fclose(fp);
269 return 0;
270 }
271 if (fseek(fp, 0, SEEK_SET) < 0) {
272 fclose(fp);
273 return 0;
274 }
275 buf = xmalloc((size_t)n + 1);
276 if (fread(buf, 1, (size_t)n, fp) != (size_t)n) {
277 fclose(fp);
278 free(buf);
279 return 0;
280 }
281 buf[n] = 0;
282 fclose(fp);
283 return buf;
284}
285
286int
287loadmakefile(const char *path, char **path_out, char **src_out)
288{
289 static const char *const defaults[] = {
290 "GNUmakefile",
291 "makefile",
292 "Makefile",
293 0,
294 };
295 size_t i;
296 char *src;
297 char *fullpath;
298
299 src = 0;
300 fullpath = 0;
301 if (path) {
302 fullpath = xstrdup(path);
303 src = readfile(fullpath);
304 } else {
305 for (i = 0; defaults[i]; i++) {
306 src = readfile(defaults[i]);
307 if (!src)
308 continue;
309 fullpath = xstrdup(defaults[i]);
310 break;
311 }
312 }
313 if (!src) {
314 free(fullpath);
315 return -1;
316 }
317 *path_out = fullpath;
318 *src_out = src;
319 return 0;
320}
321
322char *
323appendassigns(char *src, const struct StrList *assigns)
324{
325 size_t i, len, extra;
326 char *out;
327
328 if (!assigns->n)
329 return src;
330 len = strlen(src);
331 extra = len > 0 && src[len - 1] != '\n' ? 1 : 0;
332 for (i = 0; i < assigns->n; i++)
333 extra += strlen(assigns->v[i]) + 1;
334 out = xmalloc(len + extra + 1);
335 memcpy(out, src, len);
336 extra = len;
337 if (extra > 0 && out[extra - 1] != '\n')
338 out[extra++] = '\n';
339 for (i = 0; i < assigns->n; i++) {
340 size_t n;
341
342 n = strlen(assigns->v[i]);
343 memcpy(out + extra, assigns->v[i], n);
344 extra += n;
345 out[extra++] = '\n';
346 }
347 out[extra] = 0;
348 free(src);
349 return out;
350}
351
352char *
353getcwddup(void)
354{
355 size_t size;
356 char *buf;
357
358 size = 128;
359 for (;;) {
360 buf = xmalloc(size);
361 if (getcwd(buf, size))
362 return buf;
363 free(buf);
364 size *= 2;
365 }
366}
367
368char *
369joinpath(const char *dir, const char *name)
370{
371 size_t ndir, nname;
372 char *out;
373
374 if (!name || !name[0])
375 return xstrdup(dir);
376 if (name[0] == '/')
377 return xstrdup(name);
378 ndir = strlen(dir);
379 nname = strlen(name);
380 out = xmalloc(ndir + 1 + nname + 1);
381 memcpy(out, dir, ndir);
382 out[ndir] = '/';
383 memcpy(out + ndir + 1, name, nname);
384 out[ndir + 1 + nname] = 0;
385 return out;
386}
387
388char *
389normpath(const char *path)
390{
391 size_t i, n, parts_n, outlen;
392 int absolute;
393 char **parts;
394 char *out;
395
396 absolute = path[0] == '/';
397 n = strlen(path);
398 parts = xmalloc((n + 1) * sizeof(parts[0]));
399 parts_n = 0;
400 for (i = 0; i < n;) {
401 size_t start, len;
402
403 while (path[i] == '/')
404 i++;
405 start = i;
406 while (path[i] && path[i] != '/')
407 i++;
408 len = i - start;
409 if (!len)
410 continue;
411 if (len == 1 && path[start] == '.')
412 continue;
413 if (len == 2 && path[start] == '.' && path[start + 1] == '.') {
414 if (parts_n > 0 && strcmp(parts[parts_n - 1], "..") != 0) {
415 free(parts[--parts_n]);
416 continue;
417 }
418 if (!absolute)
419 parts[parts_n++] = xstrndup(path + start, len);
420 continue;
421 }
422 parts[parts_n++] = xstrndup(path + start, len);
423 }
424 if (absolute && parts_n == 0) {
425 free(parts);
426 return xstrdup("/");
427 }
428 if (!absolute && parts_n == 0) {
429 free(parts);
430 return xstrdup(".");
431 }
432 outlen = absolute ? 1 : 0;
433 for (i = 0; i < parts_n; i++)
434 outlen += strlen(parts[i]) + 1;
435 out = xmalloc(outlen + 1);
436 n = 0;
437 if (absolute)
438 out[n++] = '/';
439 for (i = 0; i < parts_n; i++) {
440 size_t len;
441
442 if (n > 0 && out[n - 1] != '/')
443 out[n++] = '/';
444 len = strlen(parts[i]);
445 memcpy(out + n, parts[i], len);
446 n += len;
447 free(parts[i]);
448 }
449 out[n] = 0;
450 free(parts);
451 return out;
452}
453
454char *
455joinstrs(const struct StrList *list, const char *sep)
456{
457 size_t i, seplen, total, pos;
458 char *s;
459
460 if (!list->n)
461 return xstrdup("");
462 seplen = strlen(sep);
463 total = 0;
464 for (i = 0; i < list->n; i++)
465 total += strlen(list->v[i]);
466 total += seplen * (list->n - 1);
467 s = xmalloc(total + 1);
468 pos = 0;
469 for (i = 0; i < list->n; i++) {
470 size_t n;
471
472 if (i > 0) {
473 memcpy(s + pos, sep, seplen);
474 pos += seplen;
475 }
476 n = strlen(list->v[i]);
477 memcpy(s + pos, list->v[i], n);
478 pos += n;
479 }
480 s[pos] = 0;
481 return s;
482}
483
484void
485addstr(struct StrList *list, const char *s)
486{
487 if (list->n >= list->cap) {
488 list->cap = list->cap ? list->cap * 2 : 4;
489 list->v = xrealloc(list->v, list->cap * sizeof(list->v[0]));
490 }
491 list->v[list->n++] = xstrdup(s);
492}
493
494int
495hasword(const struct StrList *list, const char *word)
496{
497 size_t i;
498
499 for (i = 0; i < list->n; i++) {
500 if (strcmp(list->v[i], word) == 0)
501 return 1;
502 }
503 return 0;
504}
505
506int
507targetownedby(const struct Target *t, const char *owner)
508{
509 if (!t)
510 return 0;
511 if (!owner || !owner[0])
512 return !t->owner || !t->owner[0];
513 return t->owner && strcmp(t->owner, owner) == 0;
514}
515
516int
517usesub(const struct Graph *graph, size_t idx)
518{
519 size_t i;
520 int conflict, wanted;
521
522 conflict = wanted = 0;
523 for (i = 0; i < graph->nsubs; i++) {
524 if (i == idx || strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) != 0)
525 continue;
526 conflict = 1;
527 wanted |= graph->subs[i].wanted;
528 }
529 if (!conflict)
530 return 1;
531 if (graph->subs[idx].wanted) {
532 for (i = 0; i < idx; i++)
533 if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) == 0 &&
534 graph->subs[i].wanted)
535 return 0;
536 return 1;
537 }
538 if (wanted)
539 return 0;
540 for (i = 0; i < idx; i++)
541 if (strcmp(graph->subs[i].prefix, graph->subs[idx].prefix) == 0)
542 return 0;
543 return 1;
544}
545
546const struct Target *
547defaulttarget(const struct Graph *graph, const char *owner)
548{
549 const struct Target *all;
550 const char *base;
551 char *ownedall;
552 size_t i;
553
554 ownedall = 0;
555 if (owner && owner[0]) {
556 ownedall = cat3(owner, "/", "all");
557 all = findctarget(graph, ownedall);
558 free(ownedall);
559 } else {
560 all = findctarget(graph, "all");
561 }
562 if (targetownedby(all, owner))
563 return all;
564 for (i = 0; i < graph->n; i++) {
565 if (!targetownedby(&graph->v[i], owner))
566 continue;
567 base = strrchr(graph->v[i].name, '/');
568 base = base ? base + 1 : graph->v[i].name;
569 if (base[0] == '.')
570 continue;
571 if (strcmp(base, "_PHONY") == 0)
572 continue;
573 if (graph->v[i].name == intern("clean") || graph->v[i].name == intern("fmt"))
574 continue;
575 if (graph->v[i].recipes.n > 0 || graph->v[i].prereqs.n > 0 ||
576 graph->v[i].impprereqs.n > 0 || graph->v[i].order_only.n > 0)
577 return &graph->v[i];
578 }
579 return 0;
580}
581
582char *
583cat3(const char *a, const char *b, const char *c)
584{
585 size_t na, nb, nc;
586 char *s;
587
588 na = strlen(a);
589 nb = strlen(b);
590 nc = strlen(c);
591 s = xmalloc(na + nb + nc + 1);
592 memcpy(s, a, na);
593 memcpy(s + na, b, nb);
594 memcpy(s + na + nb, c, nc);
595 s[na + nb + nc] = 0;
596 return s;
597}
598
599void
600addnode(struct NodeList *list, struct Node node)
601{
602 if (list->n >= list->cap) {
603 list->cap = list->cap ? list->cap * 2 : 4;
604 list->v = xrealloc(list->v, list->cap * sizeof(list->v[0]));
605 }
606 list->v[list->n++] = node;
607}
608
609void
610addwords(struct StrList *dest, const struct StrList *src)
611{
612 size_t i;
613
614 if (dest->n + src->n > dest->cap) {
615 dest->cap = dest->n + src->n;
616 dest->v = xrealloc(dest->v, dest->cap * sizeof(dest->v[0]));
617 }
618 for (i = 0; i < src->n; i++)
619 dest->v[dest->n++] = xstrdup(src->v[i]);
620}
621
622void
623adduniqwords(struct StrList *dest, const struct StrList *src)
624{
625 size_t i;
626
627 for (i = 0; i < src->n; i++) {
628 if (hasword(dest, src->v[i]))
629 continue;
630 addstr(dest, src->v[i]);
631 }
632}
633
634void
635addrecipe(struct RecipeList *dest, const char *raw)
636{
637 struct Recipe *r;
638 const char *s;
639 size_t n;
640
641 s = raw;
642 while (*s == ' ' || *s == '\t')
643 s++;
644 if (dest->n >= dest->cap) {
645 dest->cap = dest->cap ? dest->cap * 2 : 4;
646 dest->v = xrealloc(dest->v, dest->cap * sizeof(dest->v[0]));
647 }
648 r = &dest->v[dest->n++];
649 memset(r, 0, sizeof(*r));
650 while (*s == '@' || *s == '+' || *s == '-') {
651 if (*s == '@')
652 r->silent = 1;
653 else if (*s == '+')
654 r->recursive = 1;
655 else if (*s == '-')
656 r->ignore = 1;
657 s++;
658 while (*s == ' ' || *s == '\t')
659 s++;
660 }
661 n = strlen(s);
662 while (n > 0 && isspace((unsigned char)s[n - 1]))
663 n--;
664 r->body = xstrndup(s, n);
665 r->submake = parsesubmake(&r->sm, r->body);
666}
667
668void
669addrecipes(struct RecipeList *dest, const struct RecipeList *src)
670{
671 size_t i;
672
673 if (dest->n + src->n > dest->cap) {
674 dest->cap = dest->n + src->n;
675 dest->v = xrealloc(dest->v, dest->cap * sizeof(dest->v[0]));
676 }
677 for (i = 0; i < src->n; i++) {
678 dest->v[dest->n].body = xstrdup(src->v[i].body);
679 dest->v[dest->n].silent = src->v[i].silent;
680 dest->v[dest->n].ignore = src->v[i].ignore;
681 dest->v[dest->n].recursive = src->v[i].recursive;
682 dest->v[dest->n].submake = src->v[i].submake;
683 copysubmake(&dest->v[dest->n].sm, &src->v[i].sm);
684 dest->n++;
685 }
686}
687
688static const struct Target *
689findtarget0(const struct Graph *graph, const char *name)
690{
691 const char *iname;
692 size_t i;
693
694 iname = intern(name);
695 if (!graph->cap_targetindex)
696 return 0;
697 i = strhash(iname) & (graph->cap_targetindex - 1);
698 for (;;) {
699 if (!graph->targetindex[i].name)
700 return 0;
701 if (graph->targetindex[i].name == iname)
702 return &graph->v[graph->targetindex[i].idx];
703 i = (i + 1) & (graph->cap_targetindex - 1);
704 }
705}
706
707static void
708graphindexgrow(struct Graph *graph)
709{
710 size_t i, newcap;
711 struct GraphIndexEnt *newtab;
712
713 newcap = graph->cap_targetindex ? graph->cap_targetindex * 2 : 16;
714 newtab = xmalloc(newcap * sizeof(newtab[0]));
715 memset(newtab, 0, newcap * sizeof(newtab[0]));
716 for (i = 0; i < graph->cap_targetindex; i++) {
717 size_t j;
718
719 if (!graph->targetindex[i].name)
720 continue;
721 j = strhash(graph->targetindex[i].name) & (newcap - 1);
722 while (newtab[j].name)
723 j = (j + 1) & (newcap - 1);
724 newtab[j] = graph->targetindex[i];
725 }
726 free(graph->targetindex);
727 graph->targetindex = newtab;
728 graph->cap_targetindex = newcap;
729}
730
731static void
732graphindexput(struct Graph *graph, const char *name, size_t idx)
733{
734 size_t i;
735
736 if (!graph->cap_targetindex || graph->ntargetindex * 3 >= graph->cap_targetindex * 2)
737 graphindexgrow(graph);
738 i = strhash(name) & (graph->cap_targetindex - 1);
739 while (graph->targetindex[i].name)
740 i = (i + 1) & (graph->cap_targetindex - 1);
741 graph->targetindex[i].name = name;
742 graph->targetindex[i].idx = idx;
743 graph->ntargetindex++;
744}
745
746void
747reindexgraph(struct Graph *graph)
748{
749 size_t i;
750
751 free(graph->targetindex);
752 graph->targetindex = 0;
753 graph->ntargetindex = 0;
754 graph->cap_targetindex = 0;
755 for (i = 0; i < graph->n; i++)
756 graphindexput(graph, graph->v[i].name, i);
757}
758
759static void
760envindexgrow(struct Env *env)
761{
762 size_t i, newcap;
763 struct EnvIndexEnt *newtab;
764
765 newcap = env->cap_varindex ? env->cap_varindex * 2 : 16;
766 newtab = xmalloc(newcap * sizeof(newtab[0]));
767 memset(newtab, 0, newcap * sizeof(newtab[0]));
768 for (i = 0; i < env->cap_varindex; i++) {
769 size_t j;
770
771 if (!env->varindex[i].name)
772 continue;
773 j = strhash(env->varindex[i].name) & (newcap - 1);
774 while (newtab[j].name)
775 j = (j + 1) & (newcap - 1);
776 newtab[j] = env->varindex[i];
777 }
778 free(env->varindex);
779 env->varindex = newtab;
780 env->cap_varindex = newcap;
781}
782
783static void
784envindexput(struct Env *env, const char *name, size_t idx)
785{
786 size_t i;
787
788 if (!env->cap_varindex || env->nvarindex * 3 >= env->cap_varindex * 2)
789 envindexgrow(env);
790 i = strhash(name) & (env->cap_varindex - 1);
791 while (env->varindex[i].name)
792 i = (i + 1) & (env->cap_varindex - 1);
793 env->varindex[i].name = name;
794 env->varindex[i].idx = idx;
795 env->nvarindex++;
796}
797
798static void
799envindexrebuild(struct Env *env)
800{
801 size_t i;
802
803 free(env->varindex);
804 env->varindex = 0;
805 env->nvarindex = 0;
806 env->cap_varindex = 0;
807 for (i = 0; i < env->n; i++)
808 envindexput(env, env->v[i].name, i);
809}
810
811struct Var *
812findvar(struct Env *env, const char *name)
813{
814 const char *iname;
815 size_t i;
816
817 iname = intern(name);
818 if (!env->cap_varindex)
819 return 0;
820 i = strhash(iname) & (env->cap_varindex - 1);
821 for (;;) {
822 if (!env->varindex[i].name)
823 return 0;
824 if (env->varindex[i].name == iname)
825 return &env->v[env->varindex[i].idx];
826 i = (i + 1) & (env->cap_varindex - 1);
827 }
828}
829
830void
831freeenv(struct Env *env)
832{
833 size_t i;
834
835 for (i = 0; i < env->n; i++)
836 free(env->v[i].val);
837 free(env->v);
838 free(env->varindex);
839 env->v = 0;
840 env->n = 0;
841 env->cap = 0;
842 env->varindex = 0;
843 env->nvarindex = 0;
844 env->cap_varindex = 0;
845}
846
847void
848copyenv(struct Env *dst, const struct Env *src)
849{
850 size_t i;
851
852 memset(dst, 0, sizeof(*dst));
853 for (i = 0; i < src->n; i++)
854 envsetvar(dst, src->v[i].name, xstrdup(src->v[i].val), src->v[i].simple,
855 src->v[i].origin, src->v[i].exported);
856}
857
858struct Target *
859gettarget(struct Graph *graph, const char *name, int *added)
860{
861 struct Target *t;
862 const char *iname;
863
864 t = findtarget(graph, name);
865 if (t) {
866 if (added)
867 *added = 0;
868 return t;
869 }
870 iname = intern(name);
871 graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
872 t = &graph->v[graph->n];
873 memset(t, 0, sizeof(*t));
874 t->name = iname;
875 graphindexput(graph, iname, graph->n);
876 graph->n++;
877 if (added)
878 *added = 1;
879 return t;
880}
881
882struct Target *
883findtarget(struct Graph *graph, const char *name)
884{
885 return (struct Target *)findtarget0(graph, name);
886}
887
888const struct Target *
889findctarget(const struct Graph *graph, const char *name)
890{
891 return findtarget0(graph, name);
892}
893
894const char *
895firstprereq(const struct Target *t)
896{
897 if (t->impprereqs.n > 0)
898 return t->impprereqs.v[0];
899 if (t->prereqs.n > 0)
900 return t->prereqs.v[0];
901 return 0;
902}
903
904char *
905joinallprereqs(const struct Target *t, const char *sep)
906{
907 struct StrList list;
908 char *s;
909
910 memset(&list, 0, sizeof(list));
911 addwords(&list, &t->impprereqs);
912 addwords(&list, &t->prereqs);
913 s = joinstrs(&list, sep);
914 freestrs(&list);
915 return s;
916}
917
918size_t
919totalprereqs(const struct Target *t)
920{
921 return t->prereqs.n + t->impprereqs.n;
922}
923
924void
925freestrs(struct StrList *list)
926{
927 size_t i;
928
929 if (!list)
930 return;
931 for (i = 0; i < list->n; i++)
932 free(list->v[i]);
933 free(list->v);
934 list->v = 0;
935 list->n = 0;
936 list->cap = 0;
937}
938
939void
940freerecipes(struct RecipeList *list)
941{
942 size_t i;
943
944 if (!list)
945 return;
946 for (i = 0; i < list->n; i++)
947 free(list->v[i].body);
948 for (i = 0; i < list->n; i++)
949 freesubmake(&list->v[i].sm);
950 free(list->v);
951 list->v = 0;
952 list->n = 0;
953 list->cap = 0;
954}
955
956void
957envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin, int exported)
958{
959 struct Var *v;
960 const char *iname;
961
962 v = findvar(env, name);
963 if (v) {
964 if ((int)origin < (int)v->origin) {
965 free(val);
966 return;
967 }
968 free(v->val);
969 v->val = val;
970 v->simple = simple;
971 v->origin = origin;
972 if (exported)
973 v->exported = 1;
974 return;
975 }
976 if (env->n >= env->cap) {
977 env->cap = env->cap ? env->cap * 2 : 4;
978 env->v = xrealloc(env->v, env->cap * sizeof(env->v[0]));
979 }
980 iname = intern(name);
981 env->v[env->n].name = iname;
982 env->v[env->n].val = val;
983 env->v[env->n].simple = simple;
984 env->v[env->n].origin = origin;
985 env->v[env->n].exported = exported;
986 envindexput(env, iname, env->n);
987 env->n++;
988}
989
990/* foreach puts a loop variable into Env and then removes it
991 * we need deletion so we keep the hash table index in sync with the v array */
992void
993envdelvar(struct Env *env, const char *name)
994{
995 const char *iname;
996 size_t i;
997
998 iname = intern(name);
999 for (i = 0; i < env->n; i++) {
1000 if (env->v[i].name != iname)
1001 continue;
1002 free(env->v[i].val);
1003 memmove(&env->v[i], &env->v[i + 1], (env->n - i - 1) * sizeof(env->v[0]));
1004 env->n--;
1005 envindexrebuild(env);
1006 return;
1007 }
1008}
1009
1010void
1011warnlikemake(const char *path, int line, const char *msg)
1012{
1013 fprintf(stderr, "%s:%d: warning: %s\n", path, line, msg);
1014}
1015
1016void
1017dielikemake(const char *path, int line, const char *msg, const char *detail)
1018{
1019 if (path)
1020 fprintf(stderr, "%s:%d: *** %s%s%s. Stop.\n",
1021 path, line,
1022 msg,
1023 detail ? ": " : "",
1024 detail ? detail : "");
1025 else
1026 fprintf(stderr, "%s: *** %s%s%s. Stop.\n",
1027 progname,
1028 msg,
1029 detail ? " " : "",
1030 detail ? detail : "");
1031}