1#include "internal.h"
2
3#include <ctype.h>
4#include <stdlib.h>
5#include <string.h>
6
7/* recursive make handling: sort of done naively, doesent always work.
8 *
9 * TODO:
10 * idea for a better implementation: create a new binary. shin replaces $(MAKE) with the
11 * name of this new binary that we create. this binary is a program that simply expands the
12 * graph of the submake and returns some sort of graph IR. shin pre-emptively runs all submake
13 * recipes with the new helper binary sibstituted in as $(MAKE), then it reads the output
14 * for each one and parses the graph IR into subgraphs. because we just shell out pre-emnptively,
15 * we don't have to implement a small shell parser (like the one down below) and it will work
16 * in 99% if not 100% of cases. */
17
18static void
19tokview(const char *s, const char **out, size_t *n)
20{
21 const char *p;
22 size_t len;
23
24 p = s;
25 while (*p == '(')
26 p++;
27 len = strlen(p);
28 while (len > 0 && (p[len - 1] == ')' || p[len - 1] == ';'))
29 len--;
30 *out = p;
31 *n = len;
32}
33
34static int
35tokeq(const char *s, const char *lit)
36{
37 const char *p;
38 size_t n, ln;
39
40 tokview(s, &p, &n);
41 ln = strlen(lit);
42 return n == ln && strncmp(p, lit, n) == 0;
43}
44
45static char *
46tokdup(const char *s)
47{
48 const char *p;
49 size_t n;
50
51 tokview(s, &p, &n);
52 return xstrndup(p, n);
53}
54
55static int
56ismakevarref(const char *s)
57{
58 const char *p;
59 char close;
60 size_t n;
61
62 if (!s)
63 return 0;
64 tokview(s, &p, &n);
65 if (n == 0 || p[0] != '$')
66 return 0;
67 if (n != 7)
68 return 0;
69 if (p[1] != '(' && p[1] != '{')
70 return 0;
71 close = p[1] == '(' ? ')' : '}';
72 return strncmp(p + 2, "MAKE", 4) == 0 && p[6] == close;
73}
74
75static int
76ismaketoken(const char *s)
77{
78 const char *p;
79 const char *base;
80 size_t n;
81 int ok;
82 char *tmp;
83
84 if (ismakevarref(s))
85 return 1;
86 tokview(s, &p, &n);
87 tmp = xstrndup(p, n);
88 base = strrchr(tmp, '/');
89 base = base ? base + 1 : tmp;
90 ok = strcmp(base, "make") == 0 || strcmp(base, "gmake") == 0;
91 free(tmp);
92 return ok;
93}
94
95static int
96isassignment(const char *s)
97{
98 const char *eq;
99 size_t i;
100
101 eq = strchr(s, '=');
102 if (!eq || eq == s)
103 return 0;
104 for (i = 0; s + i < eq; i++) {
105 if (!(isalnum((unsigned char)s[i]) || s[i] == '_'))
106 return 0;
107 }
108 return 1;
109}
110
111static int
112flagsarg(const char *s)
113{
114 static const char *const flags[] = {
115 "-C", "--directory", "-f", "--file", "-I", "-o", "-W", "-j", "-l", "--eval", 0};
116 size_t i;
117
118 for (i = 0; flags[i]; i++) {
119 if (strcmp(s, flags[i]) == 0)
120 return 1;
121 }
122 return 0;
123}
124
125static void
126addtok(struct StrList *out, const char *s, size_t n)
127{
128 out->v = xrealloc(out->v, (out->n + 1) * sizeof(out->v[0]));
129 out->v[out->n++] = xstrndup(s, n);
130}
131
132static void
133freetoks(struct StrList *toks)
134{
135 freestrs(toks);
136}
137
138static int
139tokenizecmd(struct StrList *out, const char *s)
140{
141 size_t i;
142
143 memset(out, 0, sizeof(*out));
144 for (i = 0; s[i];) {
145 size_t start;
146 char quote;
147
148 while (isspace((unsigned char)s[i]))
149 i++;
150 if (!s[i])
151 break;
152 /* comment after recope shouldnt get parsed as a target*/
153 if (s[i] == '#')
154 break;
155 if (s[i] == '&' && s[i + 1] == '&') {
156 addtok(out, "&&", 2);
157 i += 2;
158 continue;
159 }
160 start = i;
161 quote = 0;
162 while (s[i]) {
163 if (!quote && isspace((unsigned char)s[i]))
164 break;
165 if (!quote && s[i] == '&' && s[i + 1] == '&')
166 break;
167 if (!quote && s[i] == '#')
168 break;
169 if (s[i] == '\\' && s[i + 1]) {
170 i += 2;
171 continue;
172 }
173 if (!quote && (s[i] == '\'' || s[i] == '"')) {
174 quote = s[i++];
175 continue;
176 }
177 if (quote && s[i] == quote) {
178 quote = 0;
179 i++;
180 continue;
181 }
182 i++;
183 }
184 addtok(out, s + start, i - start);
185 }
186 return 0;
187}
188
189void
190freesubmake(struct SubMake *sm)
191{
192 if (!sm)
193 return;
194 free(sm->makeprog);
195 sm->makeprog = 0;
196 free(sm->dir);
197 sm->dir = 0;
198 free(sm->makefile);
199 sm->makefile = 0;
200 freestrs(&sm->assigns);
201 freestrs(&sm->flags);
202 freestrs(&sm->goals);
203}
204
205void
206copysubmake(struct SubMake *dst, const struct SubMake *src)
207{
208 memset(dst, 0, sizeof(*dst));
209 if (!src)
210 return;
211 if (src->makeprog)
212 dst->makeprog = xstrdup(src->makeprog);
213 if (src->dir)
214 dst->dir = xstrdup(src->dir);
215 if (src->makefile)
216 dst->makefile = xstrdup(src->makefile);
217 addwords(&dst->assigns, &src->assigns);
218 addwords(&dst->flags, &src->flags);
219 addwords(&dst->goals, &src->goals);
220}
221
222int
223parsesubmake(struct SubMake *dst, const char *cmd)
224{
225 struct StrList toks;
226 size_t i, start;
227 const char *cddir;
228 const char *makeprog;
229
230 memset(dst, 0, sizeof(*dst));
231 cddir = 0;
232 makeprog = 0;
233 tokenizecmd(&toks, cmd);
234 start = 0;
235 if (toks.n >= 3 && tokeq(toks.v[0], "cd") && tokeq(toks.v[2], "&&")) {
236 cddir = toks.v[1];
237 start = 3;
238 }
239 if (start >= toks.n || !ismaketoken(toks.v[start])) {
240 freetoks(&toks);
241 return 0;
242 }
243 makeprog = toks.v[start];
244 dst->makeprog = tokdup(makeprog);
245 if (cddir)
246 dst->dir = tokdup(cddir);
247 for (i = start + 1; i < toks.n; i++) {
248 const char *tok;
249 char *ntok;
250
251 tok = toks.v[i];
252 ntok = tokdup(tok);
253 if (strcmp(ntok, "&&") == 0) {
254 free(ntok);
255 break;
256 }
257 if ((strcmp(ntok, "-C") == 0 || strcmp(ntok, "--directory") == 0) && i + 1 < toks.n) {
258 addstr(&dst->flags, ntok);
259 free(dst->dir);
260 dst->dir = tokdup(toks.v[++i]);
261 free(ntok);
262 continue;
263 }
264 if (strncmp(ntok, "-C", 2) == 0 && ntok[2]) {
265 addstr(&dst->flags, "-C");
266 free(dst->dir);
267 dst->dir = xstrdup(ntok + 2);
268 free(ntok);
269 continue;
270 }
271 if (strncmp(ntok, "--directory=", 12) == 0) {
272 addstr(&dst->flags, "--directory");
273 free(dst->dir);
274 dst->dir = xstrdup(ntok + 12);
275 free(ntok);
276 continue;
277 }
278 if ((strcmp(ntok, "-f") == 0 || strcmp(ntok, "--file") == 0) && i + 1 < toks.n) {
279 addstr(&dst->flags, ntok);
280 free(dst->makefile);
281 dst->makefile = tokdup(toks.v[++i]);
282 free(ntok);
283 continue;
284 }
285 if (strncmp(ntok, "-f", 2) == 0 && ntok[2]) {
286 addstr(&dst->flags, "-f");
287 free(dst->makefile);
288 dst->makefile = xstrdup(ntok + 2);
289 free(ntok);
290 continue;
291 }
292 if (strncmp(ntok, "--file=", 7) == 0) {
293 addstr(&dst->flags, "--file");
294 free(dst->makefile);
295 dst->makefile = xstrdup(ntok + 7);
296 free(ntok);
297 continue;
298 }
299 if (flagsarg(ntok) && i + 1 < toks.n) {
300 addstr(&dst->flags, ntok);
301 i++;
302 free(ntok);
303 continue;
304 }
305 if (ntok[0] == '-') {
306 addstr(&dst->flags, ntok);
307 free(ntok);
308 continue;
309 }
310 if (isassignment(ntok)) {
311 addstr(&dst->assigns, ntok);
312 free(ntok);
313 continue;
314 }
315 addstr(&dst->goals, ntok);
316 free(ntok);
317 }
318 freetoks(&toks);
319 return 1;
320}