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