]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/parser.c
libpakfire: parser: Add missing const
[people/stevee/pakfire.git] / src / libpakfire / parser.c
CommitLineData
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
34struct _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 44PAKFIRE_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
65PAKFIRE_EXPORT PakfireParser pakfire_parser_ref(PakfireParser parser) {
66 ++parser->nrefs;
67
68 return parser;
69}
70
fb077c4c
MT
71Pakfire pakfire_parser_get_pakfire(PakfireParser parser) {
72 return pakfire_ref(parser->pakfire);
73}
74
75static 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
85static 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
95PAKFIRE_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
106static 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 127PAKFIRE_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
167PAKFIRE_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
194static 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
203static 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
245static 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
264PAKFIRE_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
355PAKFIRE_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
366PAKFIRE_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
381PAKFIRE_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
397PAKFIRE_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}