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}