main shinobi / src / gnu / pattern.c
  1#include "gnu/pattern.h"
  2
  3#include <unistd.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7/*helpers for handling gnu % pattern rules*/
  8
  9char *
 10patmatchstem(const char *pat, const char *name)
 11{
 12	const char *p;
 13	size_t pre, suf, n, plen;
 14
 15	p = strchr(pat, '%');
 16	if (!p)
 17		return 0;
 18	pre = (size_t)(p - pat);
 19	suf = strlen(p + 1);
 20	n = strlen(name);
 21	plen = strlen(pat);
 22
 23	if (n < plen - 1)
 24		return 0;
 25	if (memcmp(pat, name, pre) != 0)
 26		return 0;
 27	if (memcmp(p + 1, name + n - suf, suf) != 0)
 28		return 0;
 29	return xstrndup(name + pre, n - pre - suf);
 30}
 31
 32char *
 33patapplystem(const char *s, const char *stem)
 34{
 35	const char *p;
 36	size_t pre, suf, stemlen, slen;
 37	char *out;
 38
 39	p = strchr(s, '%');
 40	if (!p)
 41		return xstrdup(s);
 42	pre = (size_t)(p - s);
 43	slen = strlen(s);
 44	suf = slen - pre - 1;
 45	stemlen = strlen(stem);
 46	out = xmalloc(pre + stemlen + suf + 1);
 47	memcpy(out, s, pre);
 48	memcpy(out + pre, stem, stemlen);
 49	memcpy(out + pre + stemlen, p + 1, suf);
 50	out[pre + stemlen + suf] = 0;
 51	return out;
 52}
 53
 54char *
 55patexpandstem(const char *s, const char *stem)
 56{
 57	size_t i, n, cap, len;
 58	char *out;
 59
 60	n = strlen(s);
 61	cap = n + 1;
 62	len = 0;
 63	out = xmalloc(cap);
 64	for (i = 0; i < n; i++) {
 65		if (s[i] == '$' && i + 1 < n && s[i + 1] == '*') {
 66			size_t slen;
 67
 68			slen = stem ? strlen(stem) : 0;
 69			if (len + slen + 1 > cap) {
 70				cap = len + slen + (n - i) + 1;
 71				out = xrealloc(out, cap);
 72			}
 73			if (slen) {
 74				memcpy(out + len, stem, slen);
 75				len += slen;
 76			}
 77			i++;
 78			continue;
 79		}
 80		if (len + 2 > cap) {
 81			cap *= 2;
 82			out = xrealloc(out, cap);
 83		}
 84		out[len++] = s[i];
 85	}
 86	out[len] = 0;
 87	return out;
 88}
 89
 90int
 91ispat(const char *s)
 92{
 93	return strchr(s, '%') != 0;
 94}
 95
 96int
 97patmatches(const char *pat, const char *name)
 98{
 99	char *stem;
100
101	stem = patmatchstem(pat, name);
102	if (!stem)
103		return 0;
104	free(stem);
105	return 1;
106}
107
108void
109collectpat(struct PatRules *rules, const struct RuleNode *rule, int builtin)
110{
111	size_t i;
112
113	for (i = 0; i < rule->targets.n; i++) {
114		struct PatRule *p;
115
116		if (!ispat(rule->targets.v[i]))
117			continue;
118		rules->v = xrealloc(rules->v, (rules->n + 1) * sizeof(rules->v[0]));
119		p = &rules->v[rules->n++];
120		memset(p, 0, sizeof(*p));
121		p->target = xstrdup(rule->targets.v[i]);
122		addwords(&p->prereqs, &rule->prereqs);
123		addwords(&p->order_only, &rule->order_only);
124		addrecipes(&p->recipes, &rule->recipes);
125		p->builtin = builtin;
126	}
127}
128
129static int
130pattargetexists(const struct Graph *graph, const char *name)
131{
132	const struct Target *t;
133
134	if (access(name, F_OK) == 0)
135		return 1;
136	t = findctarget(graph, name);
137	if (!t)
138		return 0;
139	return t->recipes.n > 0;
140}
141
142static int
143patruleviable(const struct PatRule *rule, const struct Graph *graph, const char *stem)
144{
145	size_t i;
146
147	for (i = 0; i < rule->prereqs.n; i++) {
148		char *s;
149		int ok;
150
151		s = patapplystem(rule->prereqs.v[i], stem);
152		ok = pattargetexists(graph, s);
153		free(s);
154		if (!ok)
155			return 0;
156	}
157	return 1;
158}
159
160int
161instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx, int allow_builtin)
162{
163	size_t i, j;
164
165	for (i = 0; i < rules->n; i++) {
166		char *stem;
167
168		if (rules->v[i].builtin && !allow_builtin)
169			continue;
170		stem = patmatchstem(rules->v[i].target, t->name);
171		if (!stem)
172			continue;
173		if (!patruleviable(&rules->v[i], graph, stem)) {
174			free(stem);
175			continue;
176		}
177		/* prepend so $< is the inferred pattern source, before explicit prereqs */
178		if (rules->v[i].prereqs.n > 0) {
179			size_t np = rules->v[i].prereqs.n;
180			t->impprereqs.v = xrealloc(t->impprereqs.v,
181			                           (t->impprereqs.n + np) * sizeof(t->impprereqs.v[0]));
182			memmove(t->impprereqs.v + np, t->impprereqs.v,
183			        t->impprereqs.n * sizeof(t->impprereqs.v[0]));
184			for (j = 0; j < np; j++)
185				t->impprereqs.v[j] = patapplystem(rules->v[i].prereqs.v[j], stem);
186			t->impprereqs.n += np;
187		}
188		for (j = 0; j < rules->v[i].order_only.n; j++) {
189			char *s;
190
191			s = patapplystem(rules->v[i].order_only.v[j], stem);
192			t->order_only.v = xrealloc(t->order_only.v,
193			                           (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
194			t->order_only.v[t->order_only.n++] = s;
195		}
196		for (j = 0; j < rules->v[i].recipes.n; j++) {
197			char *exp;
198
199			exp = patexpandstem(rules->v[i].recipes.v[j].body, stem);
200			if (!exp[0]) {
201				free(exp);
202				continue;
203			}
204			t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
205			t->recipes.v[t->recipes.n].body = exp;
206			t->recipes.v[t->recipes.n].silent = rules->v[i].recipes.v[j].silent;
207			t->recipes.v[t->recipes.n].ignore = rules->v[i].recipes.v[j].ignore;
208			t->recipes.v[t->recipes.n].recursive = rules->v[i].recipes.v[j].recursive;
209			t->recipes.v[t->recipes.n].submake = rules->v[i].recipes.v[j].submake;
210			copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->v[i].recipes.v[j].sm);
211			t->recipes.n++;
212		}
213		freeenv(&t->env);
214		copyenv(&t->env, ctx->env);
215		free(stem);
216		return 1;
217	}
218	return 0;
219}
220
221void
222freepatrule(struct PatRules *rules)
223{
224	size_t i;
225
226	for (i = 0; i < rules->n; i++) {
227		free(rules->v[i].target);
228		freestrs(&rules->v[i].prereqs);
229		freestrs(&rules->v[i].order_only);
230		freerecipes(&rules->v[i].recipes);
231	}
232	free(rules->v);
233	rules->v = 0;
234	rules->n = 0;
235}