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