main shrub/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,
144              const struct Target *target, const char *stem)
145{
146	size_t i;
147
148	for (i = 0; i < rule->prereqs.n; i++) {
149		char *s;
150		int ok;
151
152		s = patapplystem(rule->prereqs.v[i], stem);
153		ok = pattargetexists(graph, s) || hasword(&target->prereqs, s) ||
154		     hasword(&target->order_only, s);
155		free(s);
156		if (!ok)
157			return 0;
158	}
159	return 1;
160}
161
162int
163instpatrule(const struct PatRules *rules, const struct Graph *graph, struct Target *t, struct EvalCtx *ctx, int allow_builtin)
164{
165	size_t i, j;
166
167	for (i = 0; i < rules->n; i++) {
168		char *stem;
169
170		if (rules->v[i].builtin && !allow_builtin)
171			continue;
172		stem = patmatchstem(rules->v[i].target, t->name);
173		if (!stem)
174			continue;
175		if (!patruleviable(&rules->v[i], graph, t, stem)) {
176			free(stem);
177			continue;
178		}
179		/* prepend so $< is the inferred pattern source, before explicit prereqs */
180		if (rules->v[i].prereqs.n > 0) {
181			size_t np = rules->v[i].prereqs.n;
182			t->impprereqs.v = xrealloc(t->impprereqs.v,
183			                           (t->impprereqs.n + np) * sizeof(t->impprereqs.v[0]));
184			memmove(t->impprereqs.v + np, t->impprereqs.v,
185			        t->impprereqs.n * sizeof(t->impprereqs.v[0]));
186			for (j = 0; j < np; j++)
187				t->impprereqs.v[j] = patapplystem(rules->v[i].prereqs.v[j], stem);
188			t->impprereqs.n += np;
189		}
190		for (j = 0; j < rules->v[i].order_only.n; j++) {
191			char *s;
192
193			s = patapplystem(rules->v[i].order_only.v[j], stem);
194			t->order_only.v = xrealloc(t->order_only.v,
195			                           (t->order_only.n + 1) * sizeof(t->order_only.v[0]));
196			t->order_only.v[t->order_only.n++] = s;
197		}
198		for (j = 0; j < rules->v[i].recipes.n; j++) {
199			char *exp;
200
201			exp = patexpandstem(rules->v[i].recipes.v[j].body, stem);
202			if (!exp[0]) {
203				free(exp);
204				continue;
205			}
206			t->recipes.v = xrealloc(t->recipes.v, (t->recipes.n + 1) * sizeof(t->recipes.v[0]));
207			t->recipes.v[t->recipes.n].body = exp;
208			t->recipes.v[t->recipes.n].silent = rules->v[i].recipes.v[j].silent;
209			t->recipes.v[t->recipes.n].ignore = rules->v[i].recipes.v[j].ignore;
210			t->recipes.v[t->recipes.n].recursive = rules->v[i].recipes.v[j].recursive;
211			t->recipes.v[t->recipes.n].submake = rules->v[i].recipes.v[j].submake;
212			copysubmake(&t->recipes.v[t->recipes.n].sm, &rules->v[i].recipes.v[j].sm);
213			t->recipes.n++;
214		}
215		freeenv(&t->env);
216		copyenv(&t->env, ctx->env);
217		free(stem);
218		return 1;
219	}
220	return 0;
221}
222
223void
224freepatrule(struct PatRules *rules)
225{
226	size_t i;
227
228	for (i = 0; i < rules->n; i++) {
229		free(rules->v[i].target);
230		freestrs(&rules->v[i].prereqs);
231		freestrs(&rules->v[i].order_only);
232		freerecipes(&rules->v[i].recipes);
233	}
234	free(rules->v);
235	rules->v = 0;
236	rules->n = 0;
237}