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
516const struct Target *
517defaulttarget(const struct Graph *graph, const char *owner)
518{
519 const struct Target *all;
520 const char *base;
521 char *ownedall;
522 size_t i;
523
524 ownedall = 0;
525 if (owner && owner[0]) {
526 ownedall = cat3(owner, "/", "all");
527 all = findctarget(graph, ownedall);
528 free(ownedall);
529 } else {
530 all = findctarget(graph, "all");
531 }
532 if (targetownedby(all, owner))
533 return all;
534 for (i = 0; i < graph->n; i++) {
535 if (!targetownedby(&graph->v[i], owner))
536 continue;
537 base = strrchr(graph->v[i].name, '/');
538 base = base ? base + 1 : graph->v[i].name;
539 if (base[0] == '.')
540 continue;
541 if (strcmp(base, "_PHONY") == 0)
542 continue;
543 if (graph->v[i].name == intern("clean") || graph->v[i].name == intern("fmt"))
544 continue;
545 if (graph->v[i].recipes.n > 0 || graph->v[i].prereqs.n > 0 ||
546 graph->v[i].impprereqs.n > 0 || graph->v[i].order_only.n > 0)
547 return &graph->v[i];
548 }
549 return 0;
550}
551
552char *
553cat3(const char *a, const char *b, const char *c)
554{
555 size_t na, nb, nc;
556 char *s;
557
558 na = strlen(a);
559 nb = strlen(b);
560 nc = strlen(c);
561 s = xmalloc(na + nb + nc + 1);
562 memcpy(s, a, na);
563 memcpy(s + na, b, nb);
564 memcpy(s + na + nb, c, nc);
565 s[na + nb + nc] = 0;
566 return s;
567}
568
569void
570addnode(struct NodeList *list, struct Node node)
571{
572 if (list->n >= list->cap) {
573 list->cap = list->cap ? list->cap * 2 : 4;
574 list->v = xrealloc(list->v, list->cap * sizeof(list->v[0]));
575 }
576 list->v[list->n++] = node;
577}
578
579void
580addwords(struct StrList *dest, const struct StrList *src)
581{
582 size_t i;
583
584 if (dest->n + src->n > dest->cap) {
585 dest->cap = dest->n + src->n;
586 dest->v = xrealloc(dest->v, dest->cap * sizeof(dest->v[0]));
587 }
588 for (i = 0; i < src->n; i++)
589 dest->v[dest->n++] = xstrdup(src->v[i]);
590}
591
592void
593adduniqwords(struct StrList *dest, const struct StrList *src)
594{
595 size_t i;
596
597 for (i = 0; i < src->n; i++) {
598 if (hasword(dest, src->v[i]))
599 continue;
600 addstr(dest, src->v[i]);
601 }
602}
603
604void
605addrecipe(struct RecipeList *dest, const char *raw)
606{
607 struct Recipe *r;
608 const char *s;
609 size_t n;
610
611 s = raw;
612 while (*s == ' ' || *s == '\t')
613 s++;
614 if (dest->n >= dest->cap) {
615 dest->cap = dest->cap ? dest->cap * 2 : 4;
616 dest->v = xrealloc(dest->v, dest->cap * sizeof(dest->v[0]));
617 }
618 r = &dest->v[dest->n++];
619 memset(r, 0, sizeof(*r));
620 while (*s == '@' || *s == '+' || *s == '-') {
621 if (*s == '@')
622 r->silent = 1;
623 else if (*s == '+')
624 r->recursive = 1;
625 else if (*s == '-')
626 r->ignore = 1;
627 s++;
628 while (*s == ' ' || *s == '\t')
629 s++;
630 }
631 n = strlen(s);
632 while (n > 0 && isspace((unsigned char)s[n - 1]))
633 n--;
634 r->body = xstrndup(s, n);
635 r->submake = parsesubmake(&r->sm, r->body);
636}
637
638void
639addrecipes(struct RecipeList *dest, const struct RecipeList *src)
640{
641 size_t i;
642
643 if (dest->n + src->n > dest->cap) {
644 dest->cap = dest->n + src->n;
645 dest->v = xrealloc(dest->v, dest->cap * sizeof(dest->v[0]));
646 }
647 for (i = 0; i < src->n; i++) {
648 dest->v[dest->n].body = xstrdup(src->v[i].body);
649 dest->v[dest->n].silent = src->v[i].silent;
650 dest->v[dest->n].ignore = src->v[i].ignore;
651 dest->v[dest->n].recursive = src->v[i].recursive;
652 dest->v[dest->n].submake = src->v[i].submake;
653 copysubmake(&dest->v[dest->n].sm, &src->v[i].sm);
654 dest->n++;
655 }
656}
657
658static const struct Target *
659findtarget0(const struct Graph *graph, const char *name)
660{
661 const char *iname;
662 size_t i;
663
664 iname = intern(name);
665 if (!graph->cap_targetindex)
666 return 0;
667 i = strhash(iname) & (graph->cap_targetindex - 1);
668 for (;;) {
669 if (!graph->targetindex[i].name)
670 return 0;
671 if (graph->targetindex[i].name == iname)
672 return &graph->v[graph->targetindex[i].idx];
673 i = (i + 1) & (graph->cap_targetindex - 1);
674 }
675}
676
677static void
678graphindexgrow(struct Graph *graph)
679{
680 size_t i, newcap;
681 struct GraphIndexEnt *newtab;
682
683 newcap = graph->cap_targetindex ? graph->cap_targetindex * 2 : 16;
684 newtab = xmalloc(newcap * sizeof(newtab[0]));
685 memset(newtab, 0, newcap * sizeof(newtab[0]));
686 for (i = 0; i < graph->cap_targetindex; i++) {
687 size_t j;
688
689 if (!graph->targetindex[i].name)
690 continue;
691 j = strhash(graph->targetindex[i].name) & (newcap - 1);
692 while (newtab[j].name)
693 j = (j + 1) & (newcap - 1);
694 newtab[j] = graph->targetindex[i];
695 }
696 free(graph->targetindex);
697 graph->targetindex = newtab;
698 graph->cap_targetindex = newcap;
699}
700
701static void
702graphindexput(struct Graph *graph, const char *name, size_t idx)
703{
704 size_t i;
705
706 if (!graph->cap_targetindex || graph->ntargetindex * 3 >= graph->cap_targetindex * 2)
707 graphindexgrow(graph);
708 i = strhash(name) & (graph->cap_targetindex - 1);
709 while (graph->targetindex[i].name)
710 i = (i + 1) & (graph->cap_targetindex - 1);
711 graph->targetindex[i].name = name;
712 graph->targetindex[i].idx = idx;
713 graph->ntargetindex++;
714}
715
716void
717reindexgraph(struct Graph *graph)
718{
719 size_t i;
720
721 free(graph->targetindex);
722 graph->targetindex = 0;
723 graph->ntargetindex = 0;
724 graph->cap_targetindex = 0;
725 for (i = 0; i < graph->n; i++)
726 graphindexput(graph, graph->v[i].name, i);
727}
728
729static void
730envindexgrow(struct Env *env)
731{
732 size_t i, newcap;
733 struct EnvIndexEnt *newtab;
734
735 newcap = env->cap_varindex ? env->cap_varindex * 2 : 16;
736 newtab = xmalloc(newcap * sizeof(newtab[0]));
737 memset(newtab, 0, newcap * sizeof(newtab[0]));
738 for (i = 0; i < env->cap_varindex; i++) {
739 size_t j;
740
741 if (!env->varindex[i].name)
742 continue;
743 j = strhash(env->varindex[i].name) & (newcap - 1);
744 while (newtab[j].name)
745 j = (j + 1) & (newcap - 1);
746 newtab[j] = env->varindex[i];
747 }
748 free(env->varindex);
749 env->varindex = newtab;
750 env->cap_varindex = newcap;
751}
752
753static void
754envindexput(struct Env *env, const char *name, size_t idx)
755{
756 size_t i;
757
758 if (!env->cap_varindex || env->nvarindex * 3 >= env->cap_varindex * 2)
759 envindexgrow(env);
760 i = strhash(name) & (env->cap_varindex - 1);
761 while (env->varindex[i].name)
762 i = (i + 1) & (env->cap_varindex - 1);
763 env->varindex[i].name = name;
764 env->varindex[i].idx = idx;
765 env->nvarindex++;
766}
767
768static void
769envindexrebuild(struct Env *env)
770{
771 size_t i;
772
773 free(env->varindex);
774 env->varindex = 0;
775 env->nvarindex = 0;
776 env->cap_varindex = 0;
777 for (i = 0; i < env->n; i++)
778 envindexput(env, env->v[i].name, i);
779}
780
781struct Var *
782findvar(struct Env *env, const char *name)
783{
784 const char *iname;
785 size_t i;
786
787 iname = intern(name);
788 if (!env->cap_varindex)
789 return 0;
790 i = strhash(iname) & (env->cap_varindex - 1);
791 for (;;) {
792 if (!env->varindex[i].name)
793 return 0;
794 if (env->varindex[i].name == iname)
795 return &env->v[env->varindex[i].idx];
796 i = (i + 1) & (env->cap_varindex - 1);
797 }
798}
799
800void
801freeenv(struct Env *env)
802{
803 size_t i;
804
805 for (i = 0; i < env->n; i++)
806 free(env->v[i].val);
807 free(env->v);
808 free(env->varindex);
809 env->v = 0;
810 env->n = 0;
811 env->cap = 0;
812 env->varindex = 0;
813 env->nvarindex = 0;
814 env->cap_varindex = 0;
815}
816
817void
818copyenv(struct Env *dst, const struct Env *src)
819{
820 size_t i;
821
822 memset(dst, 0, sizeof(*dst));
823 for (i = 0; i < src->n; i++)
824 envsetvar(dst, src->v[i].name, xstrdup(src->v[i].val), src->v[i].simple,
825 src->v[i].origin, src->v[i].exported);
826}
827
828struct Target *
829gettarget(struct Graph *graph, const char *name, int *added)
830{
831 struct Target *t;
832 const char *iname;
833
834 t = findtarget(graph, name);
835 if (t) {
836 if (added)
837 *added = 0;
838 return t;
839 }
840 iname = intern(name);
841 graph->v = xrealloc(graph->v, (graph->n + 1) * sizeof(graph->v[0]));
842 t = &graph->v[graph->n];
843 memset(t, 0, sizeof(*t));
844 t->name = iname;
845 graphindexput(graph, iname, graph->n);
846 graph->n++;
847 if (added)
848 *added = 1;
849 return t;
850}
851
852struct Target *
853findtarget(struct Graph *graph, const char *name)
854{
855 return (struct Target *)findtarget0(graph, name);
856}
857
858const struct Target *
859findctarget(const struct Graph *graph, const char *name)
860{
861 return findtarget0(graph, name);
862}
863
864const char *
865firstprereq(const struct Target *t)
866{
867 if (t->impprereqs.n > 0)
868 return t->impprereqs.v[0];
869 if (t->prereqs.n > 0)
870 return t->prereqs.v[0];
871 return 0;
872}
873
874char *
875joinallprereqs(const struct Target *t, const char *sep)
876{
877 struct StrList list;
878 char *s;
879
880 memset(&list, 0, sizeof(list));
881 addwords(&list, &t->impprereqs);
882 addwords(&list, &t->prereqs);
883 s = joinstrs(&list, sep);
884 freestrs(&list);
885 return s;
886}
887
888size_t
889totalprereqs(const struct Target *t)
890{
891 return t->prereqs.n + t->impprereqs.n;
892}
893
894void
895freestrs(struct StrList *list)
896{
897 size_t i;
898
899 if (!list)
900 return;
901 for (i = 0; i < list->n; i++)
902 free(list->v[i]);
903 free(list->v);
904 list->v = 0;
905 list->n = 0;
906 list->cap = 0;
907}
908
909void
910freerecipes(struct RecipeList *list)
911{
912 size_t i;
913
914 if (!list)
915 return;
916 for (i = 0; i < list->n; i++)
917 free(list->v[i].body);
918 for (i = 0; i < list->n; i++)
919 freesubmake(&list->v[i].sm);
920 free(list->v);
921 list->v = 0;
922 list->n = 0;
923 list->cap = 0;
924}
925
926void
927envsetvar(struct Env *env, const char *name, char *val, int simple, enum Origin origin, int exported)
928{
929 struct Var *v;
930 const char *iname;
931
932 v = findvar(env, name);
933 if (v) {
934 if ((int)origin < (int)v->origin) {
935 free(val);
936 return;
937 }
938 free(v->val);
939 v->val = val;
940 v->simple = simple;
941 v->origin = origin;
942 if (exported)
943 v->exported = 1;
944 return;
945 }
946 if (env->n >= env->cap) {
947 env->cap = env->cap ? env->cap * 2 : 4;
948 env->v = xrealloc(env->v, env->cap * sizeof(env->v[0]));
949 }
950 iname = intern(name);
951 env->v[env->n].name = iname;
952 env->v[env->n].val = val;
953 env->v[env->n].simple = simple;
954 env->v[env->n].origin = origin;
955 env->v[env->n].exported = exported;
956 envindexput(env, iname, env->n);
957 env->n++;
958}
959
960/* foreach puts a loop variable into Env and then removes it
961 * we need deletion so we keep the hash table index in sync with the v array */
962void
963envdelvar(struct Env *env, const char *name)
964{
965 const char *iname;
966 size_t i;
967
968 iname = intern(name);
969 for (i = 0; i < env->n; i++) {
970 if (env->v[i].name != iname)
971 continue;
972 free(env->v[i].val);
973 memmove(&env->v[i], &env->v[i + 1], (env->n - i - 1) * sizeof(env->v[0]));
974 env->n--;
975 envindexrebuild(env);
976 return;
977 }
978}
979
980void
981warnlikemake(const char *path, int line, const char *msg)
982{
983 fprintf(stderr, "%s:%d: warning: %s\n", path, line, msg);
984}
985
986void
987dielikemake(const char *path, int line, const char *msg, const char *detail)
988{
989 if (path)
990 fprintf(stderr, "%s:%d: *** %s%s%s. Stop.\n",
991 path, line,
992 msg,
993 detail ? ": " : "",
994 detail ? detail : "");
995 else
996 fprintf(stderr, "%s: *** %s%s%s. Stop.\n",
997 progname,
998 msg,
999 detail ? " " : "",
1000 detail ? detail : "");
1001}