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