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
 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}