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