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