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