]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/parser.c
parser: Use default stdout log capturer
[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>
fa3cff8d 22#include <fnmatch.h>
20440ba7 23#include <linux/limits.h>
fb077c4c 24#include <regex.h>
f0d6233d 25#include <stdlib.h>
fb077c4c 26#include <string.h>
d73ca64f
MT
27#include <time.h>
28#include <unistd.h>
fb077c4c 29
c178c275
MT
30#define PCRE2_CODE_UNIT_WIDTH 8
31#include <pcre2.h>
32
d25dd74f 33#include <pakfire/execute.h>
fb077c4c 34#include <pakfire/logging.h>
f59d680a 35#include <pakfire/package.h>
89045ec6 36#include <pakfire/parser.h>
fb077c4c
MT
37#include <pakfire/pakfire.h>
38#include <pakfire/private.h>
89045ec6
MT
39#include <pakfire/util.h>
40
fb077c4c
MT
41struct _PakfireParser {
42 Pakfire pakfire;
d310f14c 43 struct _PakfireParser* parent;
fb077c4c
MT
44 int nrefs;
45
2dd267d4 46 int flags;
1141075e
MT
47 char* namespace;
48
8868318d 49 struct pakfire_parser_declaration** declarations;
6d206cc9 50 size_t num_declarations;
c178c275
MT
51
52 // Regular expressions
d25dd74f 53 pcre2_code* regex_command;
c178c275 54 pcre2_code* regex_variable;
fb077c4c
MT
55};
56
bf7acff3
MT
57static int pakfire_parser_compile_regex(PakfireParser parser, pcre2_code** regex,
58 const char* pattern) {
c178c275
MT
59 int pcre2_errno;
60 size_t pcre2_offset;
61 PCRE2_UCHAR errmsg[256];
62
bf7acff3
MT
63 // Compile the regular expression
64 *regex = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0,
65 &pcre2_errno, &pcre2_offset, NULL);
d25dd74f 66
bf7acff3
MT
67 if (!*regex) {
68 pcre2_get_error_message(pcre2_errno, errmsg, sizeof(errmsg));
69 ERROR(parser->pakfire, "PCRE2 compilation failed for '%s' at offset %zu: %s\n",
70 pattern, pcre2_offset, errmsg);
71 return 1;
72 }
d25dd74f 73
bf7acff3
MT
74 // Enable JIT
75 pcre2_errno = pcre2_jit_compile(*regex, PCRE2_JIT_COMPLETE);
76 if (pcre2_errno) {
77 pcre2_get_error_message(pcre2_errno, errmsg, sizeof(errmsg));
78 ERROR(parser->pakfire, "Enabling JIT on '%s' failed: %s\n", pattern, errmsg);
79 return 1;
d25dd74f
MT
80 }
81
bf7acff3
MT
82 return 0;
83}
c178c275 84
bf7acff3
MT
85static int pakfire_parser_compile_regexes(PakfireParser parser) {
86 int r;
c178c275 87
bf7acff3 88 // Commands
e614c5a6 89 if (!parser->regex_command && (parser->flags & PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS)) {
bf7acff3 90 r = pakfire_parser_compile_regex(parser,
e614c5a6 91 &parser->regex_command, "%(\\(((?>[^()]|(?1))*)\\))");
bf7acff3
MT
92 if (r)
93 return r;
94 }
a41868f9 95
bf7acff3
MT
96 // Variables
97 if (!parser->regex_variable) {
98 r = pakfire_parser_compile_regex(parser,
99 &parser->regex_variable, "%\\{([A-Za-z0-9_\\-]+)\\}");
100 if (r)
101 return r;
c178c275
MT
102 }
103
104 return 0;
105}
106
2dd267d4
MT
107PAKFIRE_EXPORT PakfireParser pakfire_parser_create(Pakfire pakfire,
108 PakfireParser parent, const char* namespace, int flags) {
90312f5c 109 PakfireParser parser = calloc(1, sizeof(*parser));
fb077c4c
MT
110 if (parser) {
111 parser->pakfire = pakfire_ref(pakfire);
c0f1c1f2 112 parser->nrefs = 1;
fb077c4c 113
d310f14c
MT
114 // Store a reference to the parent parser if we have one
115 if (parent)
116 parser->parent = pakfire_parser_ref(parent);
117
2dd267d4
MT
118 // Store flags
119 parser->flags = flags;
120
1141075e 121 // Make namespace
713da225 122 pakfire_parser_set_namespace(parser, namespace);
fb077c4c
MT
123 }
124
125 return parser;
126}
127
fb08a3af 128PAKFIRE_EXPORT PakfireParser pakfire_parser_create_child(PakfireParser parser, const char* namespace) {
2dd267d4 129 return pakfire_parser_create(parser->pakfire, parser, namespace, parser->flags);
fb08a3af
MT
130}
131
d310f14c
MT
132PAKFIRE_EXPORT PakfireParser pakfire_parser_ref(PakfireParser parser) {
133 ++parser->nrefs;
134
135 return parser;
136}
137
fb077c4c
MT
138Pakfire pakfire_parser_get_pakfire(PakfireParser parser) {
139 return pakfire_ref(parser->pakfire);
140}
141
6d206cc9 142static void pakfire_parser_free_declarations(PakfireParser parser) {
8868318d
MT
143 if (!parser->declarations)
144 return;
145
6d206cc9
MT
146 for (unsigned int i = 0; i < parser->num_declarations; i++) {
147 struct pakfire_parser_declaration* d = parser->declarations[i];
f2aa4592 148
f2aa4592 149 // Free everything
f2aa4592 150 if (d->value)
f0d6233d
MT
151 free(d->value);
152 free(d);
fb077c4c 153 }
8868318d
MT
154
155 free(parser->declarations);
fb077c4c
MT
156}
157
158static void pakfire_parser_free(PakfireParser parser) {
c178c275
MT
159 // Release regular expressions
160 if (parser->regex_variable)
161 pcre2_code_free(parser->regex_variable);
162
6d206cc9 163 pakfire_parser_free_declarations(parser);
7fb3defa 164
6d206cc9 165 if (parser->namespace)
f0d6233d 166 free(parser->namespace);
fb077c4c 167
7fb3defa
MT
168 if (parser->parent)
169 pakfire_parser_unref(parser->parent);
170
fb077c4c 171 pakfire_unref(parser->pakfire);
f0d6233d 172 free(parser);
fb077c4c
MT
173}
174
175PAKFIRE_EXPORT PakfireParser pakfire_parser_unref(PakfireParser parser) {
fb077c4c
MT
176 if (--parser->nrefs > 0)
177 return parser;
178
179 pakfire_parser_free(parser);
180 return NULL;
181}
182
b1b15235
MT
183PAKFIRE_EXPORT PakfireParser pakfire_parser_get_parent(PakfireParser parser) {
184 if (parser->parent)
185 return pakfire_parser_ref(parser->parent);
186
187 return NULL;
188}
189
fb077c4c 190static struct pakfire_parser_declaration* pakfire_parser_get_declaration(
aa75cc88 191 PakfireParser parser, const char* namespace, const char* name) {
20440ba7
MT
192 if (!name) {
193 errno = EINVAL;
194 return NULL;
195 }
196
197 if (!namespace)
198 namespace = "";
fb077c4c 199
f4d93071
MT
200 if (namespace)
201 DEBUG(parser->pakfire, "Looking up %s.%s\n", namespace, name);
202 else
203 DEBUG(parser->pakfire, "Looking up %s\n", name);
204
20440ba7 205 struct pakfire_parser_declaration* d;
fb077c4c
MT
206 for (unsigned i = 0; i < parser->num_declarations; i++) {
207 d = parser->declarations[i];
208 if (!d)
209 break;
210
20440ba7
MT
211 // Return if namespace and name match
212 if ((strcmp(d->namespace, namespace) == 0) && (strcmp(d->name, name) == 0))
fb077c4c
MT
213 return d;
214 }
215
216 return NULL;
217}
218
aa75cc88
MT
219PAKFIRE_EXPORT int pakfire_parser_set(PakfireParser parser,
220 const char* namespace, const char* name, const char* value) {
5d901566
MT
221 if (!name)
222 return -EINVAL;
223
20440ba7
MT
224 if (!namespace)
225 namespace = "";
226
5e5f2d2b 227 // Handle when name already exists
aa75cc88 228 struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, namespace, name);
5e5f2d2b
MT
229 if (d) {
230 // Replace value
231 if (d->value)
f0d6233d 232 free(d->value);
5d901566
MT
233
234 if (value)
235 d->value = strdup(value);
713da225
MT
236 else
237 d->value = NULL;
5e5f2d2b 238
aa75cc88
MT
239 DEBUG(parser->pakfire, "%p: Updated declaration: %s.%s = %s\n",
240 parser, d->namespace, d->name, d->value);
5e5f2d2b
MT
241
242 // All done
243 return 0;
244 }
245
fb077c4c 246 // Allocate a new declaration
90312f5c 247 d = calloc(1, sizeof(*d));
fb077c4c
MT
248 if (!d)
249 return -1;
250
20440ba7
MT
251 // Store namespace
252 pakfire_string_set(d->namespace, namespace);
aa75cc88 253
fb077c4c 254 // Import name & value
20440ba7 255 pakfire_string_set(d->name, name);
5d901566
MT
256 if (value)
257 d->value = strdup(value);
fb077c4c 258
aa75cc88
MT
259 DEBUG(parser->pakfire, "%p: New declaration: %s.%s = %s\n",
260 parser, d->namespace, d->name, d->value);
fb077c4c
MT
261
262 // Assign new declaration to array
8868318d
MT
263 parser->declarations = reallocarray(parser->declarations,
264 sizeof(*parser->declarations), parser->num_declarations + 1);
265 parser->declarations[parser->num_declarations++] = d;
fb077c4c
MT
266
267 return 0;
268}
269
79fd37b5
MT
270int pakfire_parser_apply_declaration(PakfireParser parser,
271 struct pakfire_parser_declaration* declaration) {
ec0f0419 272 if (declaration->flags & PAKFIRE_PARSER_DECLARATION_APPEND)
aa75cc88 273 return pakfire_parser_append(parser, declaration->namespace, declaration->name, declaration->value);
ec0f0419 274
aa75cc88 275 return pakfire_parser_set(parser, declaration->namespace, declaration->name, declaration->value);
79fd37b5
MT
276}
277
17689fc7
MT
278static const char* pakfire_parser_find_template(PakfireParser parser,
279 char* template, size_t size, const char* namespace) {
280 DEBUG(parser->pakfire, "Looking up template in namespace '%s'\n", namespace);
281
282 struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(
283 parser, namespace, "template");
284
285 const char* value = (d && *d->value) ? d->value : "MAIN";
286
287 snprintf(template, size, "packages.template:%s", value);
288
289 return template;
290}
291
292static void pakfire_parser_strip_namespace(char** s) {
293 char* pos = strrchr(*s, '.');
294
295 if (pos)
296 (*s)[pos - *s] = '\0';
297 else
298 (*s)[0] = '\0';
299}
300
aa75cc88 301static const char* pakfire_parser_get_raw(PakfireParser parser, const char* namespace, const char* name) {
17689fc7
MT
302 struct pakfire_parser_declaration* d = NULL;
303
304 // First, perform a simple lookup
305 d = pakfire_parser_get_declaration(parser, namespace, name);
fb077c4c 306
67738482 307 // Return a match when it actually contains a string
f4d93071 308 if (d && d->value)
17689fc7 309 return d->value;
67738482 310
17689fc7
MT
311 // We are done, if the namespace is empty
312 if (!namespace || !*namespace)
67738482 313 return NULL;
17689fc7
MT
314
315 char template[NAME_MAX] = "";
316
317 // If we couldn't find anything, we check if there is a template, and if that
318 // has our value...
319 if (pakfire_string_startswith(namespace, "packages.package:")) {
320 pakfire_parser_find_template(parser, template, sizeof(template) - 1, namespace);
321
322 if (*template) {
323 d = pakfire_parser_get_declaration(parser, template, name);
f4d93071 324 if (d && d->value)
17689fc7
MT
325 return d->value;
326 }
327 }
328
329 // Create a working copy of namespace
330 char* n = strdupa(namespace);
331
332 while (n) {
333 // Strip namespace
334 pakfire_parser_strip_namespace(&n);
335
336 d = pakfire_parser_get_declaration(parser, n, name);
f4d93071 337 if (d && d->value)
17689fc7
MT
338 return d->value;
339
340 // End if we have exhausted the namespace
341 if (!*n)
342 break;
67738482 343 }
fb077c4c 344
f16c81ca
MT
345 // Search in parent parser if available
346 if (parser->parent)
aa75cc88 347 return pakfire_parser_get_raw(parser->parent, namespace, name);
f16c81ca
MT
348
349 return NULL;
350}
351
352PAKFIRE_EXPORT int pakfire_parser_append(PakfireParser parser,
aa75cc88 353 const char* namespace, const char* name, const char* value) {
fb077c4c
MT
354 char* buffer = NULL;
355
f16c81ca 356 // Fetch the value of the current declaration
aa75cc88 357 const char* old_value = pakfire_parser_get_raw(parser, namespace, name);
f16c81ca
MT
358
359 // Set the new value when there is no old one
360 if (!old_value)
aa75cc88 361 return pakfire_parser_set(parser, namespace, name, value);
f16c81ca 362
fb077c4c 363 // Concat value
f16c81ca 364 int r = asprintf(&buffer, "%s %s", old_value, value);
fb077c4c
MT
365 if (r < 0)
366 return r;
367
f16c81ca 368 // Set the new value
aa75cc88 369 r = pakfire_parser_set(parser, namespace, name, buffer);
f0d6233d 370 free(buffer);
fb077c4c 371
f16c81ca 372 return r;
fb077c4c
MT
373}
374
d25dd74f
MT
375static int pakfire_parser_expand_commands(PakfireParser parser, char** buffer) {
376 int r = 0;
377 PCRE2_UCHAR* command = NULL;
378 PCRE2_SIZE command_length;
379 PCRE2_UCHAR* pattern = NULL;
380 PCRE2_SIZE pattern_length;
381
382 DEBUG(parser->pakfire, "Searching for commands in:\n%s\n", *buffer);
383
384 // Allocate memory for results
385 pcre2_match_data* match = pcre2_match_data_create_from_pattern(
386 parser->regex_command, NULL);
387
388 // Arguments passed to pakfire_execute
389 const char* argv[4] = {
390 "/bin/sh", "-c", NULL /* will be replaced by command later */, NULL,
391 };
392
393 while (1) {
394 // Perform matching
395 int r = pcre2_jit_match(parser->regex_command,
396 (PCRE2_UCHAR*)*buffer, strlen(*buffer), 0, 0, match, NULL);
397
398 // End loop when we have expanded all variables
e614c5a6
MT
399 if (r == PCRE2_ERROR_NOMATCH) {
400 DEBUG(parser->pakfire, "No (more) matches found\n");
d25dd74f 401 break;
e614c5a6 402 }
d25dd74f
MT
403
404 // Extract the command
e614c5a6 405 r = pcre2_substring_get_bynumber(match, 2, &command, &command_length);
d25dd74f
MT
406 if (r)
407 goto ERROR;
408
409 DEBUG(parser->pakfire, "Expanding command: %s\n", command);
410
411 // Update argv
412 argv[2] = (const char*)command;
413
414 // The output of the command
0a1b68b3 415 char* output = NULL;
d25dd74f
MT
416
417 // Execute the command inside the Pakfire environment
0a1b68b3 418 r = pakfire_execute(parser->pakfire, argv, NULL, 0,
ead9531f 419 pakfire_execute_capture_stdout, &output);
d25dd74f
MT
420 if (r) {
421 // Just log this and continue
422 DEBUG(parser->pakfire, "Command '%s' failed with return code %d\n", command, r);
423 }
424
0a1b68b3
MT
425 // Strip newline from output
426 if (output)
427 pakfire_remove_trailing_newline(output);
428
d25dd74f
MT
429 // Find the entire matched pattern
430 r = pcre2_substring_get_bynumber(match, 0, &pattern, &pattern_length);
0a1b68b3
MT
431 if (r) {
432 if (output)
433 free(output);
434
d25dd74f 435 goto ERROR;
0a1b68b3 436 }
d25dd74f
MT
437
438 // Replace all occurrences
439 char* tmp = pakfire_string_replace(*buffer, (const char*)pattern, output);
0a1b68b3
MT
440 if (!tmp) {
441 if (output)
442 free(output);
443
d25dd74f 444 goto ERROR;
0a1b68b3 445 }
d25dd74f
MT
446
447 // Replace buffer
448 free(*buffer);
449 *buffer = tmp;
450
451 // Free resources
452 pcre2_substring_free(command);
453 command = NULL;
454
455 pcre2_substring_free(pattern);
456 pattern = NULL;
0a1b68b3
MT
457
458 if (output)
459 free(output);
d25dd74f
MT
460 }
461
462ERROR:
463 pcre2_match_data_free(match);
464
465 if (command)
466 pcre2_substring_free(command);
467 if (pattern)
468 pcre2_substring_free(pattern);
469
470 return r;
471}
472
6accaf43
MT
473static int pakfire_parser_expand_variables(PakfireParser parser,
474 const char* namespace, char** buffer) {
d25dd74f 475 int r = 0;
c178c275
MT
476 PCRE2_UCHAR* variable = NULL;
477 PCRE2_SIZE variable_length;
478 PCRE2_UCHAR* pattern = NULL;
479 PCRE2_SIZE pattern_length;
480
c178c275 481 // Allocate memory for results
d25dd74f
MT
482 pcre2_match_data* match = pcre2_match_data_create_from_pattern(
483 parser->regex_variable, NULL);
fb077c4c
MT
484
485 // Search for any variables
486 while (1) {
487 // Perform matching
a41868f9
MT
488 int r = pcre2_jit_match(parser->regex_variable,
489 (PCRE2_UCHAR*)*buffer, strlen(*buffer), 0, 0, match, NULL);
fb077c4c
MT
490
491 // End loop when we have expanded all variables
c178c275 492 if (r == PCRE2_ERROR_NOMATCH) {
6accaf43 493 DEBUG(parser->pakfire, "No (more) matches found in: %s\n", *buffer);
fb077c4c
MT
494 break;
495 }
496
f4d93071
MT
497 // Find the entire matched pattern
498 r = pcre2_substring_get_bynumber(match, 0, &pattern, &pattern_length);
499 if (r)
500 goto ERROR;
501
38fa3305 502 // Find the variable name
c178c275
MT
503 r = pcre2_substring_get_bynumber(match, 1, &variable, &variable_length);
504 if (r)
38fa3305 505 goto ERROR;
fb077c4c
MT
506
507 DEBUG(parser->pakfire, "Expanding variable: %s\n", variable);
508
509 // Search for a declaration of this variable
17689fc7 510 const char* repl = pakfire_parser_get_raw(parser, namespace, (const char*)variable);
fb077c4c 511
f4d93071
MT
512 // Is this a recursive pattern?
513 if (repl && pakfire_string_matches(repl, (const char*)pattern)) {
514 DEBUG(parser->pakfire, "Recursion detected in %s\n", pattern);
515
516 // Move up one step and lookup there
517 if (namespace && *namespace) {
518 char* parent_namespace = strdupa(namespace);
519 pakfire_parser_strip_namespace(&parent_namespace);
520
521 repl = pakfire_parser_get_raw(parser, parent_namespace, (const char*)variable);
522
523 // If we have already reached the top namespace, we replace with an empty string
524 } else {
525 repl = NULL;
526 }
527 }
528
38fa3305 529 // What is its value?
17689fc7
MT
530 if (repl) {
531 DEBUG(parser->pakfire, "Replacing %%{%s} with '%s'\n", variable, repl);
d310f14c 532 } else {
38fa3305 533 DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n", variable);
38fa3305 534 }
fb077c4c 535
38fa3305 536 // Replace all occurrences
17689fc7 537 char* tmp = pakfire_string_replace(*buffer, (const char*)pattern, (repl) ? repl : "");
38fa3305
MT
538 if (!tmp)
539 goto ERROR;
fb077c4c 540
38fa3305 541 // Replace buffer
6accaf43
MT
542 free(*buffer);
543 *buffer = tmp;
fb077c4c 544
38fa3305 545 // Free resources
c178c275 546 pcre2_substring_free(variable);
6accaf43 547 variable = NULL;
fb077c4c 548
6accaf43
MT
549 pcre2_substring_free(pattern);
550 pattern = NULL;
38fa3305 551 }
fb077c4c 552
38fa3305 553ERROR:
c178c275 554 pcre2_match_data_free(match);
fb077c4c 555
38fa3305 556 if (variable)
c178c275 557 pcre2_substring_free(variable);
38fa3305 558 if (pattern)
c178c275 559 pcre2_substring_free(pattern);
38fa3305 560
6accaf43
MT
561 return r;
562}
563
564PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser,
565 const char* namespace, const char* value) {
566 // Return NULL when the value is NULL
567 if (!value)
568 return NULL;
569
570 // Create a working copy of the string we are expanding
571 char* buffer = strdup(value);
572
573 // Fast path to check if there are any variables in here whatsoever
574 char* pos = strchr(value, '%');
575 if (!pos)
576 return buffer;
577
d25dd74f 578 // Compile all regular expressions
bf7acff3 579 int r = pakfire_parser_compile_regexes(parser);
aed0b342
MT
580 if (r)
581 goto ERROR;
d25dd74f 582
6accaf43 583 // Expand all variables
d25dd74f 584 r = pakfire_parser_expand_variables(parser, namespace, &buffer);
aed0b342
MT
585 if (r)
586 goto ERROR;
6accaf43 587
d25dd74f
MT
588 // Expand all commands
589 if (parser->flags & PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS) {
590 r = pakfire_parser_expand_commands(parser, &buffer);
aed0b342
MT
591 if (r)
592 goto ERROR;
d25dd74f
MT
593 }
594
fb077c4c 595 return buffer;
aed0b342
MT
596
597ERROR:
598 if (buffer)
599 free(buffer);
600
601 return NULL;
fb077c4c
MT
602}
603
aa75cc88
MT
604PAKFIRE_EXPORT char* pakfire_parser_get(PakfireParser parser, const char* namespace, const char* name) {
605 const char* value = pakfire_parser_get_raw(parser, namespace, name);
1a7200b5 606
fb077c4c 607 // Return NULL when nothing was found
1a7200b5 608 if (!value)
fb077c4c
MT
609 return NULL;
610
611 // Otherwise return the expanded value
aa75cc88 612 return pakfire_parser_expand(parser, namespace, value);
fb077c4c
MT
613}
614
e10e1ad1
MT
615PAKFIRE_EXPORT char** pakfire_parser_get_split(PakfireParser parser,
616 const char* namespace, const char* name, char delim) {
617 char* value = pakfire_parser_get(parser, namespace, name);
618 if (!value)
619 return NULL;
620
621 // Split the string
622 char** list = pakfire_split_string(value, delim);
623 free(value);
624
625 return list;
626}
627
fa3cff8d
MT
628PAKFIRE_EXPORT char** pakfire_parser_list_namespaces(PakfireParser parser,
629 const char* filter) {
630 char** namespaces = NULL;
631 unsigned int counter = 0;
632
633 for (unsigned int i = 0; i < parser->num_declarations; i++) {
634 struct pakfire_parser_declaration* d = parser->declarations[i];
c8474e4f 635
fa3cff8d
MT
636 if (filter) {
637 int r = fnmatch(filter, d->namespace, 0);
638
639 // Skip declaration if it does not match
640 if (r == FNM_NOMATCH)
641 continue;
642
643 // Check for any errors
644 else if (r > 0) {
645 ERROR(parser->pakfire, "fnmatch failed: %s\n", strerror(errno));
646 if (namespaces)
647 free(namespaces);
648
649 return NULL;
650 }
651 }
652
653 // Does this already exist on the list?
654 if (namespaces) {
655 int found = 0;
656
17689fc7 657 for (char** namespace = namespaces; *namespace; namespace++) {
fa3cff8d
MT
658 if (strcmp(d->namespace, *namespace) == 0) {
659 found = 1;
660 break;
661 }
662 }
663
664 // Skip adding if it already exists
665 if (found)
666 continue;
667 }
668
669 namespaces = reallocarray(namespaces, counter + 2, sizeof(*namespaces));
670 if (!namespaces)
671 return NULL;
672
c8474e4f 673 // Add namespace and terminate the array
fa3cff8d 674 namespaces[counter++] = d->namespace;
fa3cff8d 675 namespaces[counter] = NULL;
c8474e4f 676 }
fa3cff8d
MT
677
678 return namespaces;
679}
680
122e45f5 681PAKFIRE_EXPORT int pakfire_parser_merge(PakfireParser parser1, PakfireParser parser2) {
d310f14c 682 DEBUG(parser1->pakfire, "Merging parsers %p and %p\n", parser1, parser2);
20440ba7 683 char namespace[NAME_MAX*2+1];
d310f14c 684
c7577606
MT
685 if (!parser2) {
686 errno = EINVAL;
687 return 1;
688 }
689
1a7200b5
MT
690 // Do not try to merge a parser with itself
691 if (parser1 == parser2)
122e45f5 692 return EINVAL;
1a7200b5 693
fcbbe16e
MT
694 for (unsigned int i = 0; i < parser2->num_declarations; i++) {
695 struct pakfire_parser_declaration* d = parser2->declarations[i];
696 if (!d)
697 break;
d310f14c 698
20440ba7
MT
699 if (parser2->namespace && *d->namespace)
700 pakfire_string_format(namespace, "%s.%s", parser2->namespace, d->namespace);
aa75cc88 701 else if (parser2->namespace)
20440ba7
MT
702 pakfire_string_set(namespace, parser2->namespace);
703 else if (*d->namespace)
704 pakfire_string_set(namespace, d->namespace);
aa75cc88 705 else
20440ba7 706 pakfire_string_set(namespace, "");
713da225 707
aa75cc88 708 int r = pakfire_parser_set(parser1, namespace, d->name, d->value);
122e45f5
MT
709 if (r)
710 return r;
d310f14c
MT
711 }
712
122e45f5 713 return 0;
d310f14c
MT
714}
715
4b7699d9
MT
716PAKFIRE_EXPORT int pakfire_parser_read(PakfireParser parser, FILE* f,
717 struct pakfire_parser_error** error) {
fb077c4c
MT
718 char* data;
719 size_t len;
720
721 int r = pakfire_read_file_into_buffer(f, &data, &len);
722 if (r)
723 return r;
724
4b7699d9 725 r = pakfire_parser_parse_data(parser, data, len, error);
89045ec6
MT
726
727 if (data)
f0d6233d 728 free(data);
89045ec6 729
fb077c4c 730 return r;
89045ec6 731}
dac3330d 732
4b7699d9
MT
733PAKFIRE_EXPORT int pakfire_parser_read_file(PakfireParser parser, const char* path,
734 struct pakfire_parser_error** error) {
774aeac4
MT
735 FILE* f = fopen(path, "r");
736 if (!f)
737 return 1;
738
4b7699d9 739 int r = pakfire_parser_read(parser, f, error);
774aeac4
MT
740 fclose(f);
741
742 return r;
743}
744
4b7699d9
MT
745PAKFIRE_EXPORT int pakfire_parser_parse(PakfireParser parser,
746 const char* data, size_t size, struct pakfire_parser_error** error) {
747 return pakfire_parser_parse_data(parser, data, size, error);
40f17018
MT
748}
749
dac3330d 750PAKFIRE_EXPORT char* pakfire_parser_dump(PakfireParser parser) {
20440ba7 751 char buffer[NAME_MAX*2 + 1];
dac3330d
MT
752 char* s = NULL;
753
754 for (unsigned int i = 0; i < parser->num_declarations; i++) {
755 struct pakfire_parser_declaration* d = parser->declarations[i];
756
757 if (d) {
20440ba7
MT
758 if (*d->namespace)
759 pakfire_string_format(buffer, "%s.%s", d->namespace, d->name);
dac3330d 760 else
20440ba7 761 pakfire_string_set(buffer, d->name);
aa75cc88
MT
762
763 asprintf(&s, "%s%-24s = %s\n", (s) ? s : "", buffer, d->value);
dac3330d
MT
764 }
765 }
766
767 return s;
768}
40f17018
MT
769
770PAKFIRE_EXPORT const char* pakfire_parser_get_namespace(PakfireParser parser) {
771 return parser->namespace;
772}
713da225
MT
773
774PAKFIRE_EXPORT int pakfire_parser_set_namespace(PakfireParser parser, const char* namespace) {
775 if (parser->namespace)
776 free(parser->namespace);
777
778 if (namespace)
779 parser->namespace = strdup(namespace);
780 else
781 parser->namespace = NULL;
782
783 DEBUG(parser->pakfire, "%p: Set namespace to: %s\n", parser, parser->namespace);
784
785 return 0;
786}
4b7699d9 787
f59d680a 788PAKFIRE_EXPORT int pakfire_parser_create_package(PakfireParser parser,
08bcb46a 789 PakfirePackage* pkg, PakfireRepo repo, const char* namespace, const char* default_arch) {
f59d680a
MT
790 int r = 1;
791
792 char* name = NULL;
793 char* epoch = NULL;
794 char* version = NULL;
795 char* release = NULL;
796 char* arch = NULL;
797 char* evr = NULL;
798
17689fc7
MT
799 DEBUG(parser->pakfire, "Building package from namespace '%s'\n", namespace);
800
f59d680a
MT
801 // Fetch name
802 name = pakfire_parser_get(parser, namespace, "name");
f3b3ed8e 803 if (!name || !*name) {
f59d680a
MT
804 ERROR(parser->pakfire, "Name is empty\n");
805 goto CLEANUP;
806 }
807
808 // Fetch epoch
809 epoch = pakfire_parser_get(parser, namespace, "epoch");
f3b3ed8e 810 if (!epoch || !*epoch) {
f59d680a
MT
811 ERROR(parser->pakfire, "Epoch is empty\n");
812 goto CLEANUP;
813 }
814
815 // Fetch version
816 version = pakfire_parser_get(parser, namespace, "version");
f3b3ed8e 817 if (!version || !*version) {
f59d680a
MT
818 ERROR(parser->pakfire, "Version is empty\n");
819 goto CLEANUP;
820 }
821
822 // Fetch release
823 release = pakfire_parser_get(parser, namespace, "release");
f3b3ed8e 824 if (!release || !*release) {
f59d680a
MT
825 ERROR(parser->pakfire, "Release is empty\n");
826 goto CLEANUP;
827 }
828
829 // Fetch arch
830 arch = pakfire_parser_get(parser, namespace, "arch");
f3b3ed8e 831 if (!arch || !*arch) {
08bcb46a
MT
832 if (default_arch) {
833 arch = strdup(default_arch);
834 if (!arch)
835 goto CLEANUP;
836 } else {
837 ERROR(parser->pakfire, "Arch is empty\n");
838 goto CLEANUP;
839 }
f59d680a
MT
840 }
841
842 // Compile EVR
843 evr = pakfire_package_join_evr(epoch, version, release);
844 if (!evr)
845 goto CLEANUP;
846
847 // Create a new package object
848 *pkg = pakfire_package_create(parser->pakfire, repo, name, evr, arch);
849 if (!*pkg) {
850 ERROR(parser->pakfire, "Could not create package\n");
851 goto CLEANUP;
852 }
853
5848e7cb
MT
854 // Is this a source package?
855 int is_source = pakfire_package_is_source(*pkg);
856
df552c32
MT
857 // Assign a new UUID to this package
858 char* uuid = pakfire_generate_uuid();
859 if (!uuid) {
860 ERROR(parser->pakfire, "Generating a UUID failed: %s\n", strerror(errno));
861 goto CLEANUP;
862 }
863
864 pakfire_package_set_uuid(*pkg, uuid);
865 free(uuid);
866
d73ca64f
MT
867 // Set build host
868 char hostname[HOST_NAME_MAX + 1];
869 r = gethostname(hostname, HOST_NAME_MAX + 1);
870 if (r) {
871 ERROR(parser->pakfire, "gethostname() failed: %s\n", strerror(errno));
872 goto CLEANUP;
873 }
874
875 pakfire_package_set_build_host(*pkg, hostname);
876
877 // Set build time
878 time_t now = time(NULL);
879
880 pakfire_package_set_build_time(*pkg, now);
881
f00837c0
MT
882 // Assign more attributes
883 const struct attribute {
884 const char* name;
885 void(*func)(PakfirePackage pkg, const char* value);
886 } attributes[] = {
887 { "summary", pakfire_package_set_summary, },
888 { "description", pakfire_package_set_description, },
889 { "license", pakfire_package_set_license, },
890 { "url", pakfire_package_set_url, },
891 { "groups", pakfire_package_set_groups, },
892 { "vendor", pakfire_package_set_vendor, },
893 { "maintainer", pakfire_package_set_maintainer, },
894 { NULL },
895 };
896
897 for (const struct attribute* a = attributes; a->name; a++) {
898 char* value = pakfire_parser_get(parser, namespace, a->name);
899 if (!value)
900 continue;
901
902 a->func(*pkg, value);
903 free(value);
904 }
905
b81a64ec 906 // Fetch build dependencies
5848e7cb 907 if (is_source) {
5848e7cb 908 char* requires = pakfire_parser_get(parser, "build", "requires");
8e3a7b94 909 if (requires) {
b81a64ec
MT
910 pakfire_parse_deps(parser->pakfire, *pkg,
911 pakfire_package_add_requires, requires);
8e3a7b94
MT
912 free(requires);
913 }
914 } else {
915 const struct relation {
916 const char* type;
917 void (*func)(PakfirePackage, const char* dep);
918 } relations[] = {
919 { "provides", pakfire_package_add_provides },
920 { "prerequires", pakfire_package_add_prerequires },
921 { "requires", pakfire_package_add_requires },
922 { "conflicts", pakfire_package_add_conflicts },
923 { "obsoletes", pakfire_package_add_obsoletes },
924 { "recommends", pakfire_package_add_recommends },
925 { "suggests", pakfire_package_add_suggests },
926 { "supplements", pakfire_package_add_supplements },
927 { "enhances", pakfire_package_add_enhances },
928 { NULL, NULL },
929 };
930
931 for (const struct relation* relation = relations; relation->type; relation++) {
932 char* relations = pakfire_parser_get(parser, namespace, relation->type);
933 if (relations) {
934 pakfire_parse_deps(parser->pakfire, *pkg, relation->func, relations);
935 free(relations);
936 }
937 }
5848e7cb
MT
938 }
939
f59d680a
MT
940 // All okay
941 r = 0;
942
943CLEANUP:
944 if (name)
945 free(name);
946 if (epoch)
947 free(epoch);
948 if (version)
949 free(version);
950 if (release)
951 free(release);
952 if (arch)
953 free(arch);
954
955 return r;
956}
957
4b7699d9
MT
958// Error
959
960struct pakfire_parser_error {
961 PakfireParser parser;
962 int nrefs;
963
964 char* filename;
965 int line;
966 char* message;
967};
968
969PAKFIRE_EXPORT int pakfire_parser_error_create(struct pakfire_parser_error** error,
970 PakfireParser parser, const char* filename, int line, const char* message) {
971 struct pakfire_parser_error* e = calloc(1, sizeof(*e));
972 if (!e)
973 return ENOMEM;
974
975 // Initialize reference counter
976 e->nrefs = 1;
977
978 e->parser = pakfire_parser_ref(parser);
979
980 // Copy all input values
981 if (filename)
982 e->filename = strdup(filename);
983
984 e->line = line;
985
986 if (message)
987 e->message = strdup(message);
988
989 *error = e;
990
991 return 0;
992}
993
994PAKFIRE_EXPORT struct pakfire_parser_error* pakfire_parser_error_ref(
995 struct pakfire_parser_error* error) {
996 ++error->nrefs;
997
998 return error;
999}
1000
1001static void pakfire_parser_error_free(struct pakfire_parser_error* error) {
1002 pakfire_parser_unref(error->parser);
1003
1004 if (error->filename)
1005 free(error->filename);
1006
1007 if (error->message)
1008 free(error->message);
1009
1010 free(error);
1011}
1012
1013PAKFIRE_EXPORT struct pakfire_parser_error* pakfire_parser_error_unref(
1014 struct pakfire_parser_error* error) {
1015 if (--error->nrefs > 0)
1016 return error;
1017
1018 pakfire_parser_error_free(error);
1019
1020 return NULL;
1021}
1022
1023PAKFIRE_EXPORT const char* pakfire_parser_error_get_filename(
1024 struct pakfire_parser_error* error) {
1025 return error->filename;
1026}
1027
1028PAKFIRE_EXPORT int pakfire_parser_error_get_line(struct pakfire_parser_error* error) {
1029 return error->line;
1030}
1031
1032PAKFIRE_EXPORT const char* pakfire_parser_error_get_message(
1033 struct pakfire_parser_error* error) {
1034 return error->message;
1035}