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