1#include "ninqu.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7struct Gate *
8gate_new(enum GKind kind)
9{
10 struct Gate *g = ecalloc(1, sizeof *g);
11 g->kind = kind;
12 return g;
13}
14
15void
16gate_free(struct Gate *g)
17{
18 int i;
19 if (!g)
20 return;
21 for (i = 0; i < g->nkids; i++)
22 gate_free(g->kids[i]);
23 free(g->kids);
24 free(g->var);
25 free(g);
26}
27
28struct Gate *
29gate_parse(struct SNode *expr)
30{
31 const char *h;
32 struct Gate **kids;
33 int i, n;
34
35 if (!expr)
36 return gate_new(G_TRUE);
37
38 if (expr->kind == S_ATOM) {
39 struct Gate *g = gate_new(G_VAR);
40 g->var = estrdup(expr->atom);
41 return g;
42 }
43
44 h = s_head(expr);
45 if (!h)
46 eprintf("manifest: empty () in gate\n");
47
48 if (strcmp(h, "not") == 0) {
49 struct Gate *g;
50 if (expr->nkids != 2)
51 eprintf("manifest: (not X) takes one operand\n");
52 g = gate_new(G_NOT);
53 g->kids = emalloc(sizeof *g->kids);
54 g->kids[0] = gate_parse(expr->kids[1]);
55 g->nkids = 1;
56 return g;
57 }
58 if (strcmp(h, "and") == 0 || strcmp(h, "or") == 0) {
59 struct Gate *g = gate_new(h[0] == 'a' ? G_AND : G_OR);
60 if (expr->nkids < 2)
61 eprintf("manifest: (%s ...) needs one operand\n", h);
62 n = expr->nkids - 1;
63 kids = emalloc((size_t)n * sizeof *kids);
64 for (i = 0; i < n; i++)
65 kids[i] = gate_parse(expr->kids[i + 1]);
66 g->kids = kids;
67 g->nkids = n;
68 return g;
69 }
70 eprintf("manifest: unknown gate operator '%s'\n", h);
71 return NULL;
72}
73
74/* true if the gate contains any $(...) that needs per-instance
75 * expansion before it can be evaluated */
76int
77gate_has_dyn(struct Gate *g)
78{
79 int i;
80 if (!g)
81 return 0;
82 if (g->kind == G_VAR)
83 return strstr(g->var, "$(") != NULL;
84 for (i = 0; i < g->nkids; i++)
85 if (gate_has_dyn(g->kids[i]))
86 return 1;
87 return 0;
88}
89
90int
91gate_eval(struct Gate *g)
92{
93 int i, v;
94 if (!g)
95 return 1;
96 switch (g->kind) {
97 case G_TRUE:
98 return 1;
99 case G_VAR: {
100 char *vraw = kv_get(g->var);
101 return vraw && *vraw && strcmp(vraw, "0") != 0;
102 }
103 case G_NOT:
104 return !gate_eval(g->kids[0]);
105 case G_AND:
106 v = 1;
107 for (i = 0; i < g->nkids; i++)
108 v = v && gate_eval(g->kids[i]);
109 return v;
110 case G_OR:
111 v = 0;
112 for (i = 0; i < g->nkids; i++)
113 v = v || gate_eval(g->kids[i]);
114 return v;
115 }
116 return 0;
117}
118
119/* expand every $(...) so a gate like build_$(BASESTEM) resolves
120 * against the active overlay before evaluation */
121struct Gate *
122gate_materialize(struct Gate *g)
123{
124 int i;
125 struct Gate *out;
126
127 if (!g)
128 return NULL;
129 out = gate_new(g->kind);
130 if (g->var)
131 out->var = kv_expand(g->var);
132 if (g->nkids) {
133 out->kids = emalloc((size_t)g->nkids * sizeof *out->kids);
134 for (i = 0; i < g->nkids; i++)
135 out->kids[i] = gate_materialize(g->kids[i]);
136 out->nkids = g->nkids;
137 }
138 return out;
139}