main shrubtools / nviz / env.c
  1#include <stdbool.h>
  2#include <stdlib.h>
  3#include <string.h>
  4#include "env.h"
  5#include "graph.h"
  6#include "tree.h"
  7#include "util.h"
  8
  9struct environment {
 10	struct environment *parent;
 11	struct treenode *bindings;
 12	struct treenode *rules;
 13	struct environment *allnext;
 14};
 15
 16struct environment *rootenv;
 17struct rule phonyrule = {.name = "phony"};
 18struct pool consolepool = {.name = "console", .maxjobs = 1};
 19static struct treenode *pools;
 20static struct environment *allenvs;
 21
 22static void addpool(struct pool *);
 23static void delpool(void *);
 24static void delrule(void *);
 25
 26void
 27envinit(void)
 28{
 29	struct environment *env;
 30
 31	/* free old environments and pools in case we rebuilt the manifest */
 32	while (allenvs) {
 33		env = allenvs;
 34		allenvs = env->allnext;
 35		deltree(env->bindings, free, free);
 36		deltree(env->rules, NULL, delrule);
 37		free(env);
 38	}
 39	deltree(pools, NULL, delpool);
 40
 41	rootenv = mkenv(NULL);
 42	envaddrule(rootenv, &phonyrule);
 43	pools = NULL;
 44	addpool(&consolepool);
 45}
 46
 47static void
 48addvar(struct treenode **tree, char *var, void *val)
 49{
 50	char *old;
 51
 52	old = treeinsert(tree, var, val);
 53	if (old)
 54		free(old);
 55}
 56
 57struct environment *
 58mkenv(struct environment *parent)
 59{
 60	struct environment *env;
 61
 62	env = xmalloc(sizeof(*env));
 63	env->parent = parent;
 64	env->bindings = NULL;
 65	env->rules = NULL;
 66	env->allnext = allenvs;
 67	allenvs = env;
 68
 69	return env;
 70}
 71
 72struct string *
 73envvar(struct environment *env, char *var)
 74{
 75	struct treenode *n;
 76
 77	do {
 78		n = treefind(env->bindings, var);
 79		if (n)
 80			return n->value;
 81		env = env->parent;
 82	} while (env);
 83
 84	return NULL;
 85}
 86
 87void
 88envaddvar(struct environment *env, char *var, struct string *val)
 89{
 90	addvar(&env->bindings, var, val);
 91}
 92
 93static struct string *
 94merge(struct evalstring *str, size_t n)
 95{
 96	struct string *result;
 97	struct evalstring *p;
 98	char *s;
 99
100	result = mkstr(n);
101	s = result->s;
102	for (p = str; p; p = p->next) {
103		if (!p->str)
104			continue;
105		memcpy(s, p->str->s, p->str->n);
106		s += p->str->n;
107	}
108	*s = '\0';
109
110	return result;
111}
112
113struct string *
114enveval(struct environment *env, struct evalstring *str)
115{
116	size_t n;
117	struct evalstring *p;
118	struct string *res;
119
120	n = 0;
121	for (p = str; p; p = p->next) {
122		if (p->var)
123			p->str = envvar(env, p->var);
124		if (p->str)
125			n += p->str->n;
126	}
127	res = merge(str, n);
128	delevalstr(str);
129
130	return res;
131}
132
133void
134envaddrule(struct environment *env, struct rule *r)
135{
136	if (treeinsert(&env->rules, r->name, r))
137		fatal("rule '%s' redefined", r->name);
138}
139
140struct rule *
141envrule(struct environment *env, char *name)
142{
143	struct treenode *n;
144
145	do {
146		n = treefind(env->rules, name);
147		if (n)
148			return n->value;
149		env = env->parent;
150	} while (env);
151
152	return NULL;
153}
154
155static struct string *
156pathlist(struct node **nodes, size_t n, char sep, bool escape)
157{
158	size_t i, len;
159	struct string *path, *result;
160	char *s;
161
162	if (n == 0)
163		return NULL;
164	if (n == 1)
165		return nodepath(nodes[0], escape);
166	for (i = 0, len = 0; i < n; ++i)
167		len += nodepath(nodes[i], escape)->n;
168	result = mkstr(len + n - 1);
169	s = result->s;
170	for (i = 0; i < n; ++i) {
171		path = nodepath(nodes[i], escape);
172		memcpy(s, path->s, path->n);
173		s += path->n;
174		*s++ = sep;
175	}
176	*--s = '\0';
177
178	return result;
179}
180
181struct rule *
182mkrule(char *name)
183{
184	struct rule *r;
185
186	r = xmalloc(sizeof(*r));
187	r->name = name;
188	r->bindings = NULL;
189
190	return r;
191}
192
193static void
194delrule(void *ptr)
195{
196	struct rule *r = ptr;
197
198	if (r == &phonyrule)
199		return;
200	deltree(r->bindings, free, delevalstr);
201	free(r->name);
202	free(r);
203}
204
205void
206ruleaddvar(struct rule *r, char *var, struct evalstring *val)
207{
208	addvar(&r->bindings, var, val);
209}
210
211struct string *
212edgevar(struct edge *e, char *var, bool escape)
213{
214	static void *const cycle = (void *)&cycle;
215	struct evalstring *str, *p;
216	struct treenode *n;
217	size_t len;
218
219	if (strcmp(var, "in") == 0)
220		return pathlist(e->in, e->inimpidx, ' ', escape);
221	if (strcmp(var, "in_newline") == 0)
222		return pathlist(e->in, e->inimpidx, '\n', escape);
223	if (strcmp(var, "out") == 0)
224		return pathlist(e->out, e->outimpidx, ' ', escape);
225	n = treefind(e->env->bindings, var);
226	if (n)
227		return n->value;
228	n = treefind(e->rule->bindings, var);
229	if (!n)
230		return envvar(e->env->parent, var);
231	if (n->value == cycle)
232		fatal("cycle in rule variable involving '%s'", var);
233	str = n->value;
234	n->value = cycle;
235	len = 0;
236	for (p = str; p; p = p->next) {
237		if (p->var)
238			p->str = edgevar(e, p->var, escape);
239		if (p->str)
240			len += p->str->n;
241	}
242	n->value = str;
243	return merge(str, len);
244}
245
246static void
247addpool(struct pool *p)
248{
249	if (treeinsert(&pools, p->name, p))
250		fatal("pool '%s' redefined", p->name);
251}
252
253struct pool *
254mkpool(char *name)
255{
256	struct pool *p;
257
258	p = xmalloc(sizeof(*p));
259	p->name = name;
260	p->numjobs = 0;
261	p->maxjobs = 0;
262	p->work = NULL;
263	addpool(p);
264
265	return p;
266}
267
268static void
269delpool(void *ptr)
270{
271	struct pool *p = ptr;
272
273	if (p == &consolepool)
274		return;
275	free(p->name);
276	free(p);
277}
278
279struct pool *
280poolget(char *name)
281{
282	struct treenode *n;
283
284	n = treefind(pools, name);
285	if (!n)
286		fatal("unknown pool '%s'", name);
287
288	return n->value;
289}