]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/parser.c
parser: Add support for flags
[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
5d901566 21#include <errno.h>
fb077c4c 22#include <regex.h>
f0d6233d 23#include <stdlib.h>
fb077c4c
MT
24#include <string.h>
25
89045ec6 26#include <pakfire/errno.h>
fb077c4c 27#include <pakfire/logging.h>
89045ec6 28#include <pakfire/parser.h>
fb077c4c
MT
29#include <pakfire/pakfire.h>
30#include <pakfire/private.h>
89045ec6
MT
31#include <pakfire/util.h>
32
fb077c4c 33#define VARIABLE_PATTERN "%\\{([A-Za-z0-9_\\-]+)\\}"
89045ec6 34
fb077c4c
MT
35struct _PakfireParser {
36 Pakfire pakfire;
d310f14c 37 struct _PakfireParser* parent;
fb077c4c
MT
38 int nrefs;
39
2dd267d4 40 int flags;
1141075e
MT
41 char* namespace;
42
8868318d 43 struct pakfire_parser_declaration** declarations;
6d206cc9 44 size_t num_declarations;
fb077c4c
MT
45};
46
aa75cc88 47static char* pakfire_parser_make_canonical_name(const char* namespace, const char* name) {
1141075e
MT
48 char* buffer = NULL;
49
aa75cc88
MT
50 if (namespace) {
51 int r = asprintf(&buffer, "%s.%s", namespace, name);
b1b15235
MT
52 if (r < 0)
53 return NULL;
54 } else {
5d901566 55 buffer = strdup(name);
b1b15235 56 }
1141075e
MT
57
58 return buffer;
59}
60
2dd267d4
MT
61PAKFIRE_EXPORT PakfireParser pakfire_parser_create(Pakfire pakfire,
62 PakfireParser parent, const char* namespace, int flags) {
90312f5c 63 PakfireParser parser = calloc(1, sizeof(*parser));
fb077c4c
MT
64 if (parser) {
65 parser->pakfire = pakfire_ref(pakfire);
c0f1c1f2 66 parser->nrefs = 1;
fb077c4c 67
d310f14c
MT
68 // Store a reference to the parent parser if we have one
69 if (parent)
70 parser->parent = pakfire_parser_ref(parent);
71
2dd267d4
MT
72 // Store flags
73 parser->flags = flags;
74
1141075e 75 // Make namespace
713da225 76 pakfire_parser_set_namespace(parser, namespace);
1141075e 77
09c232ab
MT
78 DEBUG(pakfire, "Allocated new parser at %p (%s, %p)\n",
79 parser, parser->namespace, parser->parent);
fb077c4c
MT
80 }
81
82 return parser;
83}
84
fb08a3af 85PAKFIRE_EXPORT PakfireParser pakfire_parser_create_child(PakfireParser parser, const char* namespace) {
2dd267d4 86 return pakfire_parser_create(parser->pakfire, parser, namespace, parser->flags);
fb08a3af
MT
87}
88
d310f14c
MT
89PAKFIRE_EXPORT PakfireParser pakfire_parser_ref(PakfireParser parser) {
90 ++parser->nrefs;
91
92 return parser;
93}
94
fb077c4c
MT
95Pakfire pakfire_parser_get_pakfire(PakfireParser parser) {
96 return pakfire_ref(parser->pakfire);
97}
98
6d206cc9 99static void pakfire_parser_free_declarations(PakfireParser parser) {
8868318d
MT
100 if (!parser->declarations)
101 return;
102
6d206cc9
MT
103 for (unsigned int i = 0; i < parser->num_declarations; i++) {
104 struct pakfire_parser_declaration* d = parser->declarations[i];
f2aa4592 105
f2aa4592 106 // Free everything
aa75cc88
MT
107 if (d->namespace)
108 free(d->namespace);
f2aa4592 109 if (d->name)
f0d6233d 110 free(d->name);
f2aa4592 111 if (d->value)
f0d6233d
MT
112 free(d->value);
113 free(d);
fb077c4c 114 }
8868318d
MT
115
116 free(parser->declarations);
fb077c4c
MT
117}
118
119static void pakfire_parser_free(PakfireParser parser) {
120 DEBUG(parser->pakfire, "Releasing parser at %p\n", parser);
121
6d206cc9 122 pakfire_parser_free_declarations(parser);
7fb3defa 123
6d206cc9 124 if (parser->namespace)
f0d6233d 125 free(parser->namespace);
fb077c4c 126
7fb3defa
MT
127 if (parser->parent)
128 pakfire_parser_unref(parser->parent);
129
fb077c4c 130 pakfire_unref(parser->pakfire);
f0d6233d 131 free(parser);
fb077c4c
MT
132}
133
134PAKFIRE_EXPORT PakfireParser pakfire_parser_unref(PakfireParser parser) {
135 if (!parser)
136 return NULL;
137
138 if (--parser->nrefs > 0)
139 return parser;
140
141 pakfire_parser_free(parser);
142 return NULL;
143}
144
b1b15235
MT
145PAKFIRE_EXPORT PakfireParser pakfire_parser_get_parent(PakfireParser parser) {
146 if (parser->parent)
147 return pakfire_parser_ref(parser->parent);
148
149 return NULL;
150}
151
fb077c4c 152static struct pakfire_parser_declaration* pakfire_parser_get_declaration(
aa75cc88 153 PakfireParser parser, const char* namespace, const char* name) {
fb077c4c
MT
154 struct pakfire_parser_declaration* d;
155
156 for (unsigned i = 0; i < parser->num_declarations; i++) {
157 d = parser->declarations[i];
158 if (!d)
159 break;
160
aa75cc88
MT
161 // Skip if d does not have a namespace set
162 if (namespace && !d->namespace)
163 continue;
164
165 // Skip if namespace does not match
166 if (namespace && strcmp(d->namespace, namespace) != 0)
167 continue;
168
fb077c4c
MT
169 // Compare the name
170 if (strcmp(d->name, name) == 0)
171 return d;
172 }
173
174 return NULL;
175}
176
aa75cc88
MT
177PAKFIRE_EXPORT int pakfire_parser_set(PakfireParser parser,
178 const char* namespace, const char* name, const char* value) {
5d901566
MT
179 if (!name)
180 return -EINVAL;
181
5e5f2d2b 182 // Handle when name already exists
aa75cc88 183 struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, namespace, name);
5e5f2d2b
MT
184 if (d) {
185 // Replace value
186 if (d->value)
f0d6233d 187 free(d->value);
5d901566
MT
188
189 if (value)
190 d->value = strdup(value);
713da225
MT
191 else
192 d->value = NULL;
5e5f2d2b 193
aa75cc88
MT
194 DEBUG(parser->pakfire, "%p: Updated declaration: %s.%s = %s\n",
195 parser, d->namespace, d->name, d->value);
5e5f2d2b
MT
196
197 // All done
198 return 0;
199 }
200
fb077c4c 201 // Allocate a new declaration
90312f5c 202 d = calloc(1, sizeof(*d));
fb077c4c
MT
203 if (!d)
204 return -1;
205
aa75cc88
MT
206 // Copy namespace
207 if (namespace)
208 d->namespace = strdup(namespace);
209 else
210 d->namespace = NULL;
211
fb077c4c 212 // Import name & value
5d901566
MT
213 d->name = strdup(name);
214 if (value)
215 d->value = strdup(value);
fb077c4c 216
aa75cc88
MT
217 DEBUG(parser->pakfire, "%p: New declaration: %s.%s = %s\n",
218 parser, d->namespace, d->name, d->value);
fb077c4c
MT
219
220 // Assign new declaration to array
8868318d
MT
221 parser->declarations = reallocarray(parser->declarations,
222 sizeof(*parser->declarations), parser->num_declarations + 1);
223 parser->declarations[parser->num_declarations++] = d;
fb077c4c
MT
224
225 return 0;
226}
227
79fd37b5
MT
228int pakfire_parser_apply_declaration(PakfireParser parser,
229 struct pakfire_parser_declaration* declaration) {
ec0f0419 230 if (declaration->flags & PAKFIRE_PARSER_DECLARATION_APPEND)
aa75cc88 231 return pakfire_parser_append(parser, declaration->namespace, declaration->name, declaration->value);
ec0f0419 232
aa75cc88 233 return pakfire_parser_set(parser, declaration->namespace, declaration->name, declaration->value);
79fd37b5
MT
234}
235
aa75cc88
MT
236static const char* pakfire_parser_get_raw(PakfireParser parser, const char* namespace, const char* name) {
237 struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, namespace, name);
fb077c4c 238
67738482
MT
239 // Return a match when it actually contains a string
240 if (d) {
241 if (d->value && *d->value)
242 return d->value;
243
244 return NULL;
245 }
fb077c4c 246
f16c81ca
MT
247 // Search in parent parser if available
248 if (parser->parent)
aa75cc88 249 return pakfire_parser_get_raw(parser->parent, namespace, name);
f16c81ca
MT
250
251 return NULL;
252}
253
254PAKFIRE_EXPORT int pakfire_parser_append(PakfireParser parser,
aa75cc88 255 const char* namespace, const char* name, const char* value) {
fb077c4c
MT
256 char* buffer = NULL;
257
f16c81ca 258 // Fetch the value of the current declaration
aa75cc88 259 const char* old_value = pakfire_parser_get_raw(parser, namespace, name);
f16c81ca
MT
260
261 // Set the new value when there is no old one
262 if (!old_value)
aa75cc88 263 return pakfire_parser_set(parser, namespace, name, value);
f16c81ca 264
fb077c4c 265 // Concat value
f16c81ca 266 int r = asprintf(&buffer, "%s %s", old_value, value);
fb077c4c
MT
267 if (r < 0)
268 return r;
269
f16c81ca 270 // Set the new value
aa75cc88 271 r = pakfire_parser_set(parser, namespace, name, buffer);
f0d6233d 272 free(buffer);
fb077c4c 273
f16c81ca 274 return r;
fb077c4c
MT
275}
276
aa75cc88
MT
277static void pakfire_parser_strip_namespace(char** s) {
278 char* pos = strrchr(*s, '.');
fb077c4c 279
995654c4 280 if (pos)
aa75cc88 281 *s[pos - *s] = '\0';
995654c4 282 else
aa75cc88 283 *s = NULL;
995654c4
MT
284}
285
286static struct pakfire_parser_declaration* pakfire_parser_find_declaration(
aa75cc88 287 PakfireParser parser, const char* namespace, const char* name) {
5d901566
MT
288 char* n = NULL;
289
aa75cc88
MT
290 // Create a working copy of the namespace variable
291 if (namespace)
292 n = strdupa(namespace);
fb077c4c
MT
293
294 struct pakfire_parser_declaration* d = NULL;
fb077c4c 295 while (1) {
aa75cc88 296 DEBUG(parser->pakfire, "Looking up %s.%s in parser %p\n", n, name, parser);
fb077c4c
MT
297
298 // Lookup declaration
aa75cc88 299 d = pakfire_parser_get_declaration(parser, n, name);
fb077c4c
MT
300
301 // End if we have found a match
302 if (d)
303 break;
304
6280055f 305 // End if namespace is empty
aa75cc88 306 if (!n)
6280055f
MT
307 break;
308
fb077c4c
MT
309 /*
310 If we did not find a match, we will remove one level of the
311 namespace and try again...
312 */
aa75cc88 313 pakfire_parser_strip_namespace(&n);
fb077c4c
MT
314 }
315
1a7200b5 316 if (!d && parser->parent)
aa75cc88 317 d = pakfire_parser_find_declaration(parser->parent, namespace, name);
fb077c4c 318
1a7200b5 319 return d;
d310f14c
MT
320}
321
aa75cc88
MT
322PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser,
323 const char* namespace, const char* value) {
d310f14c
MT
324 // Return NULL when the value is NULL
325 if (!value)
326 return NULL;
327
b7a4c0f5
MT
328 char* pos = strchr(value, '%');
329 if (!pos)
5d901566 330 return strdup(value);
b7a4c0f5 331
fb077c4c
MT
332 // Compile the regular expression
333 regex_t preg;
334 int r = regcomp(&preg, VARIABLE_PATTERN, REG_EXTENDED);
89045ec6 335 if (r) {
fb077c4c
MT
336 char error[1024];
337 regerror(r, &preg, error, sizeof(error));
338
339 ERROR(parser->pakfire, "Could not compile regular expression (%s): %s",
340 VARIABLE_PATTERN, error);
341
89045ec6
MT
342 return NULL;
343 }
344
fb077c4c 345 // Create a working copy of the string we are expanding
5d901566 346 char* buffer = strdup(value);
fb077c4c
MT
347
348 const size_t max_groups = 2;
349 regmatch_t groups[max_groups];
350
351 // Search for any variables
352 while (1) {
353 // Perform matching
354 r = regexec(&preg, buffer, max_groups, groups, 0);
355
356 // End loop when we have expanded all variables
357 if (r == REG_NOMATCH) {
358 DEBUG(parser->pakfire, "No (more) matches found in: %s\n", buffer);
359 break;
360 }
361
362 // Set offsets to the matched variable name
363 off_t start = groups[1].rm_so, end = groups[1].rm_eo;
364
365 // Get the name of the variable
547759ae 366 char* variable = malloc(end - start + 1);
fb077c4c
MT
367 snprintf(variable, end - start + 1, "%s", buffer + start);
368
369 DEBUG(parser->pakfire, "Expanding variable: %s\n", variable);
370
371 // Search for a declaration of this variable
372 struct pakfire_parser_declaration* v =
aa75cc88 373 pakfire_parser_find_declaration(parser, namespace, variable);
fb077c4c 374
fb077c4c
MT
375 const char* value = NULL;
376 if (v && v->value) {
d310f14c
MT
377 DEBUG(parser->pakfire, "Replacing %%{%s} with %s = '%s'\n",
378 variable, v->name, v->value);
379
fb077c4c 380 value = v->value;
d310f14c
MT
381 } else {
382 DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n",
383 variable);
fb077c4c
MT
384 }
385
386 // Reset offsets to the whole matched string
387 start = groups[0].rm_so; end = groups[0].rm_eo;
388
389 // Length of the new buffer
390 size_t length = strlen(buffer) - (end - start) + ((value) ? strlen(value) : 0);
391
547759ae 392 char* b = malloc(length + 1);
fb077c4c
MT
393
394 // Copy buffer up to the beginning of the match
395 snprintf(b, start + 1, "%s", buffer);
396
397 // Append the new value (if any)
398 if (value)
399 strcat(b, value);
400
401 // Append the rest of the buffer
402 if (buffer + end)
403 strcat(b, buffer + end);
404
405 DEBUG(parser->pakfire, "New buffer: %s\n", b);
406
407 // Drop old buffer
f0d6233d 408 free(buffer);
fb077c4c
MT
409 buffer = b;
410 }
411
412 regfree(&preg);
413
414 return buffer;
415}
416
aa75cc88
MT
417PAKFIRE_EXPORT char* pakfire_parser_get(PakfireParser parser, const char* namespace, const char* name) {
418 const char* value = pakfire_parser_get_raw(parser, namespace, name);
1a7200b5 419
fb077c4c 420 // Return NULL when nothing was found
1a7200b5 421 if (!value)
fb077c4c
MT
422 return NULL;
423
424 // Otherwise return the expanded value
aa75cc88 425 return pakfire_parser_expand(parser, namespace, value);
fb077c4c
MT
426}
427
122e45f5 428PAKFIRE_EXPORT int pakfire_parser_merge(PakfireParser parser1, PakfireParser parser2) {
d310f14c
MT
429 DEBUG(parser1->pakfire, "Merging parsers %p and %p\n", parser1, parser2);
430
1a7200b5
MT
431 // Do not try to merge a parser with itself
432 if (parser1 == parser2)
122e45f5 433 return EINVAL;
1a7200b5 434
aa75cc88
MT
435 char* namespace = NULL;
436
fcbbe16e
MT
437 for (unsigned int i = 0; i < parser2->num_declarations; i++) {
438 struct pakfire_parser_declaration* d = parser2->declarations[i];
439 if (!d)
440 break;
d310f14c 441
aa75cc88
MT
442 if (parser2->namespace && d->namespace)
443 asprintf(&namespace, "%s.%s", parser2->namespace, d->namespace);
444 else if (parser2->namespace)
445 asprintf(&namespace, "%s", parser2->namespace);
446 else if (d->namespace)
447 asprintf(&namespace, "%s", d->namespace);
448 else
449 namespace = NULL;
713da225 450
aa75cc88 451 int r = pakfire_parser_set(parser1, namespace, d->name, d->value);
713da225 452
aa75cc88
MT
453 if (namespace)
454 free(namespace);
122e45f5
MT
455
456 if (r)
457 return r;
d310f14c
MT
458 }
459
122e45f5 460 return 0;
d310f14c
MT
461}
462
4b7699d9
MT
463PAKFIRE_EXPORT int pakfire_parser_read(PakfireParser parser, FILE* f,
464 struct pakfire_parser_error** error) {
fb077c4c
MT
465 char* data;
466 size_t len;
467
468 int r = pakfire_read_file_into_buffer(f, &data, &len);
469 if (r)
470 return r;
471
4b7699d9 472 r = pakfire_parser_parse_data(parser, data, len, error);
89045ec6
MT
473
474 if (data)
f0d6233d 475 free(data);
89045ec6 476
fb077c4c 477 return r;
89045ec6 478}
dac3330d 479
4b7699d9
MT
480PAKFIRE_EXPORT int pakfire_parser_read_file(PakfireParser parser, const char* path,
481 struct pakfire_parser_error** error) {
774aeac4
MT
482 FILE* f = fopen(path, "r");
483 if (!f)
484 return 1;
485
4b7699d9 486 int r = pakfire_parser_read(parser, f, error);
774aeac4
MT
487 fclose(f);
488
489 return r;
490}
491
4b7699d9
MT
492PAKFIRE_EXPORT int pakfire_parser_parse(PakfireParser parser,
493 const char* data, size_t size, struct pakfire_parser_error** error) {
494 return pakfire_parser_parse_data(parser, data, size, error);
40f17018
MT
495}
496
dac3330d
MT
497PAKFIRE_EXPORT char* pakfire_parser_dump(PakfireParser parser) {
498 char* s = NULL;
499
aa75cc88
MT
500 char buffer[1024];
501
dac3330d
MT
502 for (unsigned int i = 0; i < parser->num_declarations; i++) {
503 struct pakfire_parser_declaration* d = parser->declarations[i];
504
505 if (d) {
aa75cc88
MT
506 if (d->namespace)
507 snprintf(buffer, sizeof(buffer) - 1, "%s.%s", d->namespace, d->name);
dac3330d 508 else
aa75cc88
MT
509 snprintf(buffer, sizeof(buffer) - 1, "%s", d->name);
510
511 asprintf(&s, "%s%-24s = %s\n", (s) ? s : "", buffer, d->value);
dac3330d
MT
512 }
513 }
514
515 return s;
516}
40f17018
MT
517
518PAKFIRE_EXPORT const char* pakfire_parser_get_namespace(PakfireParser parser) {
519 return parser->namespace;
520}
713da225
MT
521
522PAKFIRE_EXPORT int pakfire_parser_set_namespace(PakfireParser parser, const char* namespace) {
523 if (parser->namespace)
524 free(parser->namespace);
525
526 if (namespace)
527 parser->namespace = strdup(namespace);
528 else
529 parser->namespace = NULL;
530
531 DEBUG(parser->pakfire, "%p: Set namespace to: %s\n", parser, parser->namespace);
532
533 return 0;
534}
4b7699d9
MT
535
536// Error
537
538struct pakfire_parser_error {
539 PakfireParser parser;
540 int nrefs;
541
542 char* filename;
543 int line;
544 char* message;
545};
546
547PAKFIRE_EXPORT int pakfire_parser_error_create(struct pakfire_parser_error** error,
548 PakfireParser parser, const char* filename, int line, const char* message) {
549 struct pakfire_parser_error* e = calloc(1, sizeof(*e));
550 if (!e)
551 return ENOMEM;
552
553 // Initialize reference counter
554 e->nrefs = 1;
555
556 e->parser = pakfire_parser_ref(parser);
557
558 // Copy all input values
559 if (filename)
560 e->filename = strdup(filename);
561
562 e->line = line;
563
564 if (message)
565 e->message = strdup(message);
566
567 *error = e;
568
569 return 0;
570}
571
572PAKFIRE_EXPORT struct pakfire_parser_error* pakfire_parser_error_ref(
573 struct pakfire_parser_error* error) {
574 ++error->nrefs;
575
576 return error;
577}
578
579static void pakfire_parser_error_free(struct pakfire_parser_error* error) {
580 pakfire_parser_unref(error->parser);
581
582 if (error->filename)
583 free(error->filename);
584
585 if (error->message)
586 free(error->message);
587
588 free(error);
589}
590
591PAKFIRE_EXPORT struct pakfire_parser_error* pakfire_parser_error_unref(
592 struct pakfire_parser_error* error) {
593 if (--error->nrefs > 0)
594 return error;
595
596 pakfire_parser_error_free(error);
597
598 return NULL;
599}
600
601PAKFIRE_EXPORT const char* pakfire_parser_error_get_filename(
602 struct pakfire_parser_error* error) {
603 return error->filename;
604}
605
606PAKFIRE_EXPORT int pakfire_parser_error_get_line(struct pakfire_parser_error* error) {
607 return error->line;
608}
609
610PAKFIRE_EXPORT const char* pakfire_parser_error_get_message(
611 struct pakfire_parser_error* error) {
612 return error->message;
613}