]>
Commit | Line | Data |
---|---|---|
89045ec6 MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2019 Pakfire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
fb077c4c MT |
21 | #include <regex.h> |
22 | #include <string.h> | |
23 | ||
89045ec6 | 24 | #include <pakfire/errno.h> |
fb077c4c | 25 | #include <pakfire/logging.h> |
89045ec6 | 26 | #include <pakfire/parser.h> |
fb077c4c MT |
27 | #include <pakfire/pakfire.h> |
28 | #include <pakfire/private.h> | |
89045ec6 MT |
29 | #include <pakfire/util.h> |
30 | ||
fb077c4c MT |
31 | #define NUM_DECLARATIONS 1024 |
32 | #define VARIABLE_PATTERN "%\\{([A-Za-z0-9_\\-]+)\\}" | |
89045ec6 | 33 | |
fb077c4c MT |
34 | struct _PakfireParser { |
35 | Pakfire pakfire; | |
d310f14c | 36 | struct _PakfireParser* parent; |
fb077c4c MT |
37 | int nrefs; |
38 | ||
39 | struct pakfire_parser_declaration** declarations; | |
40 | unsigned int next_declaration; | |
41 | unsigned int num_declarations; | |
42 | }; | |
43 | ||
d310f14c | 44 | PAKFIRE_EXPORT PakfireParser pakfire_parser_create(Pakfire pakfire, PakfireParser parent) { |
fb077c4c MT |
45 | PakfireParser parser = pakfire_calloc(1, sizeof(*parser)); |
46 | if (parser) { | |
47 | parser->pakfire = pakfire_ref(pakfire); | |
48 | ||
d310f14c MT |
49 | // Store a reference to the parent parser if we have one |
50 | if (parent) | |
51 | parser->parent = pakfire_parser_ref(parent); | |
52 | ||
fb077c4c MT |
53 | parser->num_declarations = NUM_DECLARATIONS; |
54 | ||
55 | // Allocate a decent number of declarations | |
56 | parser->declarations = pakfire_calloc( | |
57 | parser->num_declarations, sizeof(*parser->declarations)); | |
58 | ||
59 | parser->next_declaration = 0; | |
60 | } | |
61 | ||
62 | return parser; | |
63 | } | |
64 | ||
d310f14c MT |
65 | PAKFIRE_EXPORT PakfireParser pakfire_parser_ref(PakfireParser parser) { |
66 | ++parser->nrefs; | |
67 | ||
68 | return parser; | |
69 | } | |
70 | ||
fb077c4c MT |
71 | Pakfire pakfire_parser_get_pakfire(PakfireParser parser) { |
72 | return pakfire_ref(parser->pakfire); | |
73 | } | |
74 | ||
75 | static void pakfire_parser_free_declarations( | |
76 | struct pakfire_parser_declaration** declarations, unsigned int num) { | |
77 | for (unsigned int i = 0; i < num; i++) { | |
78 | if (declarations[i]) | |
79 | pakfire_free(declarations[i]); | |
80 | } | |
81 | ||
82 | pakfire_free(declarations); | |
83 | } | |
84 | ||
85 | static void pakfire_parser_free(PakfireParser parser) { | |
86 | DEBUG(parser->pakfire, "Releasing parser at %p\n", parser); | |
87 | ||
88 | pakfire_parser_free_declarations(parser->declarations, parser->num_declarations); | |
89 | ||
d310f14c | 90 | pakfire_parser_unref(parser->parent); |
fb077c4c MT |
91 | pakfire_unref(parser->pakfire); |
92 | pakfire_free(parser); | |
93 | } | |
94 | ||
95 | PAKFIRE_EXPORT PakfireParser pakfire_parser_unref(PakfireParser parser) { | |
96 | if (!parser) | |
97 | return NULL; | |
98 | ||
99 | if (--parser->nrefs > 0) | |
100 | return parser; | |
101 | ||
102 | pakfire_parser_free(parser); | |
103 | return NULL; | |
104 | } | |
105 | ||
106 | static struct pakfire_parser_declaration* pakfire_parser_get_declaration( | |
107 | PakfireParser parser, const char* name) { | |
108 | struct pakfire_parser_declaration* d; | |
109 | ||
110 | for (unsigned i = 0; i < parser->num_declarations; i++) { | |
111 | d = parser->declarations[i]; | |
112 | if (!d) | |
113 | break; | |
114 | ||
115 | // Compare the name | |
116 | if (strcmp(d->name, name) == 0) | |
117 | return d; | |
118 | } | |
119 | ||
d310f14c MT |
120 | // If nothing was found, we will try finding a match in the parent parser |
121 | if (parser->parent) | |
122 | return pakfire_parser_get_declaration(parser->parent, name); | |
123 | ||
fb077c4c MT |
124 | return NULL; |
125 | } | |
126 | ||
5e5f2d2b | 127 | PAKFIRE_EXPORT int pakfire_parser_set_declaration(PakfireParser parser, |
fb077c4c | 128 | const char* name, const char* value) { |
5e5f2d2b MT |
129 | // Handle when name already exists |
130 | struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, name); | |
131 | if (d) { | |
132 | // Replace value | |
133 | if (d->value) | |
134 | pakfire_free(d->value); | |
135 | d->value = pakfire_strdup(value); | |
136 | ||
137 | DEBUG(parser->pakfire, "Updated declaration: %s = %s\n", | |
138 | d->name, d->value); | |
139 | ||
140 | // All done | |
141 | return 0; | |
142 | } | |
143 | ||
fb077c4c MT |
144 | // Check if we have any space left |
145 | if (parser->next_declaration >= parser->num_declarations) { | |
146 | ERROR(parser->pakfire, "No free declarations left\n"); | |
147 | return -1; | |
148 | } | |
149 | ||
fb077c4c | 150 | // Allocate a new declaration |
5e5f2d2b | 151 | d = pakfire_calloc(1, sizeof(*d)); |
fb077c4c MT |
152 | if (!d) |
153 | return -1; | |
154 | ||
155 | // Import name & value | |
156 | d->name = pakfire_strdup(name); | |
157 | d->value = pakfire_strdup(value); | |
158 | ||
159 | DEBUG(parser->pakfire, "New declaration: %s = %s\n", d->name, d->value); | |
160 | ||
161 | // Assign new declaration to array | |
162 | parser->declarations[parser->next_declaration++] = d; | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
167 | PAKFIRE_EXPORT int pakfire_parser_append_declaration(PakfireParser parser, | |
168 | const char* name, const char* value) { | |
169 | struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, name); | |
170 | ||
171 | // Add the declaration if we could not find it | |
172 | if (!d) | |
5e5f2d2b | 173 | return pakfire_parser_set_declaration(parser, name, value); |
fb077c4c MT |
174 | |
175 | char* buffer = NULL; | |
176 | ||
177 | // Concat value | |
178 | int r = asprintf(&buffer, "%s %s", d->value, value); | |
179 | if (r < 0) | |
180 | return r; | |
181 | ||
182 | DEBUG(parser->pakfire, "Appended declaration: %s = %s (was: %s)\n", | |
183 | d->name, buffer, d->value); | |
184 | ||
185 | // Replace value in declaration | |
186 | if (d->value) | |
187 | pakfire_free(d->value); | |
188 | ||
189 | d->value = buffer; | |
190 | ||
191 | return 0; | |
192 | } | |
193 | ||
995654c4 MT |
194 | static void pakfire_parser_strip_namespace(char* s) { |
195 | char* pos = strrchr(s, '.'); | |
fb077c4c | 196 | |
995654c4 MT |
197 | if (pos) |
198 | s[pos - s] = '\0'; | |
199 | else | |
200 | s[0] = '\0'; | |
201 | } | |
202 | ||
203 | static struct pakfire_parser_declaration* pakfire_parser_find_declaration( | |
204 | PakfireParser parser, const char* namespace, const char* name) { | |
205 | // Create a working copy of the namespace | |
fb077c4c MT |
206 | char* n = pakfire_strdup(namespace); |
207 | ||
208 | size_t length = strlen(n) + strlen(name) + 1; | |
209 | char* buffer = pakfire_malloc(length + 1); | |
210 | ||
211 | struct pakfire_parser_declaration* d = NULL; | |
212 | ||
213 | while (1) { | |
995654c4 | 214 | if (*n) |
fb077c4c MT |
215 | snprintf(buffer, length + 1, "%s.%s", n, name); |
216 | else | |
217 | snprintf(buffer, length + 1, "%s", name); | |
218 | ||
219 | DEBUG(parser->pakfire, "Looking up %s\n", buffer); | |
220 | ||
221 | // Lookup declaration | |
222 | d = pakfire_parser_get_declaration(parser, buffer); | |
223 | ||
224 | // End if we have found a match | |
225 | if (d) | |
226 | break; | |
227 | ||
6280055f MT |
228 | // End if namespace is empty |
229 | if (!*n) | |
230 | break; | |
231 | ||
fb077c4c MT |
232 | /* |
233 | If we did not find a match, we will remove one level of the | |
234 | namespace and try again... | |
235 | */ | |
995654c4 | 236 | pakfire_parser_strip_namespace(n); |
fb077c4c MT |
237 | } |
238 | ||
fb077c4c | 239 | pakfire_free(buffer); |
995654c4 | 240 | pakfire_free(n); |
fb077c4c MT |
241 | |
242 | return d; | |
243 | } | |
244 | ||
245 | static char* pakfire_parser_expand_declaration(PakfireParser parser, | |
246 | const struct pakfire_parser_declaration* declaration) { | |
247 | // Return NULL when the value of the declaration is NULL | |
248 | if (!declaration || !declaration->value) | |
249 | return NULL; | |
250 | ||
d310f14c MT |
251 | // Get namespace of variable we are expanding |
252 | char* namespace = pakfire_strdup(declaration->name); | |
253 | pakfire_parser_strip_namespace(namespace); | |
254 | ||
255 | // Expand the value | |
256 | char* buffer = pakfire_parser_expand(parser, namespace, declaration->value); | |
257 | ||
258 | // Cleanup | |
259 | pakfire_free(namespace); | |
260 | ||
261 | return buffer; | |
262 | } | |
263 | ||
264 | PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser, | |
265 | const char* namespace, const char* value) { | |
266 | // Return NULL when the value is NULL | |
267 | if (!value) | |
268 | return NULL; | |
269 | ||
fb077c4c MT |
270 | // Compile the regular expression |
271 | regex_t preg; | |
272 | int r = regcomp(&preg, VARIABLE_PATTERN, REG_EXTENDED); | |
89045ec6 | 273 | if (r) { |
fb077c4c MT |
274 | char error[1024]; |
275 | regerror(r, &preg, error, sizeof(error)); | |
276 | ||
277 | ERROR(parser->pakfire, "Could not compile regular expression (%s): %s", | |
278 | VARIABLE_PATTERN, error); | |
279 | ||
89045ec6 MT |
280 | return NULL; |
281 | } | |
282 | ||
fb077c4c | 283 | // Create a working copy of the string we are expanding |
d310f14c | 284 | char* buffer = pakfire_strdup(value); |
fb077c4c MT |
285 | |
286 | const size_t max_groups = 2; | |
287 | regmatch_t groups[max_groups]; | |
288 | ||
289 | // Search for any variables | |
290 | while (1) { | |
291 | // Perform matching | |
292 | r = regexec(&preg, buffer, max_groups, groups, 0); | |
293 | ||
294 | // End loop when we have expanded all variables | |
295 | if (r == REG_NOMATCH) { | |
296 | DEBUG(parser->pakfire, "No (more) matches found in: %s\n", buffer); | |
297 | break; | |
298 | } | |
299 | ||
300 | // Set offsets to the matched variable name | |
301 | off_t start = groups[1].rm_so, end = groups[1].rm_eo; | |
302 | ||
303 | // Get the name of the variable | |
304 | char* variable = pakfire_malloc(end - start + 1); | |
305 | snprintf(variable, end - start + 1, "%s", buffer + start); | |
306 | ||
307 | DEBUG(parser->pakfire, "Expanding variable: %s\n", variable); | |
308 | ||
309 | // Search for a declaration of this variable | |
310 | struct pakfire_parser_declaration* v = | |
995654c4 | 311 | pakfire_parser_find_declaration(parser, namespace, variable); |
fb077c4c | 312 | |
fb077c4c MT |
313 | const char* value = NULL; |
314 | if (v && v->value) { | |
d310f14c MT |
315 | DEBUG(parser->pakfire, "Replacing %%{%s} with %s = '%s'\n", |
316 | variable, v->name, v->value); | |
317 | ||
fb077c4c | 318 | value = v->value; |
d310f14c MT |
319 | } else { |
320 | DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n", | |
321 | variable); | |
fb077c4c MT |
322 | } |
323 | ||
324 | // Reset offsets to the whole matched string | |
325 | start = groups[0].rm_so; end = groups[0].rm_eo; | |
326 | ||
327 | // Length of the new buffer | |
328 | size_t length = strlen(buffer) - (end - start) + ((value) ? strlen(value) : 0); | |
329 | ||
330 | char* b = pakfire_malloc(length + 1); | |
331 | ||
332 | // Copy buffer up to the beginning of the match | |
333 | snprintf(b, start + 1, "%s", buffer); | |
334 | ||
335 | // Append the new value (if any) | |
336 | if (value) | |
337 | strcat(b, value); | |
338 | ||
339 | // Append the rest of the buffer | |
340 | if (buffer + end) | |
341 | strcat(b, buffer + end); | |
342 | ||
343 | DEBUG(parser->pakfire, "New buffer: %s\n", b); | |
344 | ||
345 | // Drop old buffer | |
346 | pakfire_free(buffer); | |
347 | buffer = b; | |
348 | } | |
349 | ||
350 | regfree(&preg); | |
351 | ||
352 | return buffer; | |
353 | } | |
354 | ||
355 | PAKFIRE_EXPORT char* pakfire_parser_get(PakfireParser parser, const char* name) { | |
356 | struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, name); | |
357 | ||
358 | // Return NULL when nothing was found | |
359 | if (!d) | |
360 | return NULL; | |
361 | ||
362 | // Otherwise return the expanded value | |
363 | return pakfire_parser_expand_declaration(parser, d); | |
364 | } | |
365 | ||
d310f14c MT |
366 | PAKFIRE_EXPORT PakfireParser pakfire_parser_merge(PakfireParser parser1, PakfireParser parser2) { |
367 | DEBUG(parser1->pakfire, "Merging parsers %p and %p\n", parser1, parser2); | |
368 | ||
369 | if (parser2) { | |
370 | for (unsigned int i = 0; i < parser2->num_declarations; i++) { | |
371 | struct pakfire_parser_declaration* d = parser2->declarations[i]; | |
372 | ||
373 | if (d) | |
374 | pakfire_parser_set_declaration(parser1, d->name, d->value); | |
375 | } | |
376 | } | |
377 | ||
378 | return parser1; | |
379 | } | |
380 | ||
fb077c4c MT |
381 | PAKFIRE_EXPORT int pakfire_parser_read(PakfireParser parser, FILE* f) { |
382 | char* data; | |
383 | size_t len; | |
384 | ||
385 | int r = pakfire_read_file_into_buffer(f, &data, &len); | |
386 | if (r) | |
387 | return r; | |
388 | ||
389 | r = pakfire_parser_parse_data(parser, data, len); | |
89045ec6 MT |
390 | |
391 | if (data) | |
392 | pakfire_free(data); | |
393 | ||
fb077c4c | 394 | return r; |
89045ec6 | 395 | } |
dac3330d MT |
396 | |
397 | PAKFIRE_EXPORT char* pakfire_parser_dump(PakfireParser parser) { | |
398 | char* s = NULL; | |
399 | ||
400 | for (unsigned int i = 0; i < parser->num_declarations; i++) { | |
401 | struct pakfire_parser_declaration* d = parser->declarations[i]; | |
402 | ||
403 | if (d) { | |
404 | if (s) | |
405 | asprintf(&s, "%s%-24s = %s\n", s, d->name, d->value); | |
406 | else | |
407 | asprintf(&s, "%-24s = %s\n", d->name, d->value); | |
408 | } | |
409 | } | |
410 | ||
411 | return s; | |
412 | } |