main shinobi / src / submake.c
  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}