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}