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}