main shrub/shinobi / src / util.c
   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}