]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/parser.c
build: Add logging_callback
[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 87 // Commands
e614c5a6 88 if (!parser->regex_command && (parser->flags & PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS)) {
bf7acff3 89 r = pakfire_parser_compile_regex(parser,
e614c5a6 90 &parser->regex_command, "%(\\(((?>[^()]|(?1))*)\\))");
bf7acff3
MT
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
e614c5a6
MT
397 if (r == PCRE2_ERROR_NOMATCH) {
398 DEBUG(parser->pakfire, "No (more) matches found\n");
d25dd74f 399 break;
e614c5a6 400 }
d25dd74f
MT
401
402 // Extract the command
e614c5a6 403 r = pcre2_substring_get_bynumber(match, 2, &command, &command_length);
d25dd74f
MT
404 if (r)
405 goto ERROR;
406
407 DEBUG(parser->pakfire, "Expanding command: %s\n", command);
408
409 // Update argv
410 argv[2] = (const char*)command;
411
412 // The output of the command
0a1b68b3 413 char* output = NULL;
d25dd74f
MT
414
415 // Execute the command inside the Pakfire environment
0a1b68b3
MT
416 r = pakfire_execute(parser->pakfire, argv, NULL, 0,
417 pakfire_parser_command_logger, &output);
d25dd74f
MT
418 if (r) {
419 // Just log this and continue
420 DEBUG(parser->pakfire, "Command '%s' failed with return code %d\n", command, r);
421 }
422
0a1b68b3
MT
423 // Strip newline from output
424 if (output)
425 pakfire_remove_trailing_newline(output);
426
d25dd74f
MT
427 // Find the entire matched pattern
428 r = pcre2_substring_get_bynumber(match, 0, &pattern, &pattern_length);
0a1b68b3
MT
429 if (r) {
430 if (output)
431 free(output);
432
d25dd74f 433 goto ERROR;
0a1b68b3 434 }
d25dd74f
MT
435
436 // Replace all occurrences
437 char* tmp = pakfire_string_replace(*buffer, (const char*)pattern, output);
0a1b68b3
MT
438 if (!tmp) {
439 if (output)
440 free(output);
441
d25dd74f 442 goto ERROR;
0a1b68b3 443 }
d25dd74f
MT
444
445 // Replace buffer
446 free(*buffer);
447 *buffer = tmp;
448
449 // Free resources
450 pcre2_substring_free(command);
451 command = NULL;
452
453 pcre2_substring_free(pattern);
454 pattern = NULL;
0a1b68b3
MT
455
456 if (output)
457 free(output);
d25dd74f
MT
458 }
459
460ERROR:
461 pcre2_match_data_free(match);
462
463 if (command)
464 pcre2_substring_free(command);
465 if (pattern)
466 pcre2_substring_free(pattern);
467
468 return r;
469}
470
6accaf43
MT
471static int pakfire_parser_expand_variables(PakfireParser parser,
472 const char* namespace, char** buffer) {
d25dd74f 473 int r = 0;
c178c275
MT
474 PCRE2_UCHAR* variable = NULL;
475 PCRE2_SIZE variable_length;
476 PCRE2_UCHAR* pattern = NULL;
477 PCRE2_SIZE pattern_length;
478
c178c275 479 // Allocate memory for results
d25dd74f
MT
480 pcre2_match_data* match = pcre2_match_data_create_from_pattern(
481 parser->regex_variable, NULL);
fb077c4c
MT
482
483 // Search for any variables
484 while (1) {
485 // Perform matching
a41868f9
MT
486 int r = pcre2_jit_match(parser->regex_variable,
487 (PCRE2_UCHAR*)*buffer, strlen(*buffer), 0, 0, match, NULL);
fb077c4c
MT
488
489 // End loop when we have expanded all variables
c178c275 490 if (r == PCRE2_ERROR_NOMATCH) {
6accaf43 491 DEBUG(parser->pakfire, "No (more) matches found in: %s\n", *buffer);
fb077c4c
MT
492 break;
493 }
494
38fa3305 495 // Find the variable name
c178c275
MT
496 r = pcre2_substring_get_bynumber(match, 1, &variable, &variable_length);
497 if (r)
38fa3305 498 goto ERROR;
fb077c4c
MT
499
500 DEBUG(parser->pakfire, "Expanding variable: %s\n", variable);
501
502 // Search for a declaration of this variable
38fa3305 503 struct pakfire_parser_declaration* d =
c178c275 504 pakfire_parser_find_declaration(parser, namespace, (const char*)variable);
fb077c4c 505
38fa3305
MT
506 // What is its value?
507 const char* repl = NULL;
508 if (d && d->value) {
509 DEBUG(parser->pakfire, "Replacing %%{%s} with %s.%s = '%s'\n", variable,
510 d->namespace, d->name, d->value);
d310f14c 511
38fa3305 512 repl = d->value;
d310f14c 513 } else {
38fa3305 514 DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n", variable);
fb077c4c 515
38fa3305
MT
516 repl = "";
517 }
fb077c4c 518
38fa3305 519 // Find the entire matched pattern
c178c275
MT
520 r = pcre2_substring_get_bynumber(match, 0, &pattern, &pattern_length);
521 if (r)
38fa3305 522 goto ERROR;
fb077c4c 523
38fa3305 524 // Replace all occurrences
6accaf43 525 char* tmp = pakfire_string_replace(*buffer, (const char*)pattern, repl);
38fa3305
MT
526 if (!tmp)
527 goto ERROR;
fb077c4c 528
38fa3305 529 // Replace buffer
6accaf43
MT
530 free(*buffer);
531 *buffer = tmp;
fb077c4c 532
38fa3305 533 // Free resources
c178c275 534 pcre2_substring_free(variable);
6accaf43 535 variable = NULL;
fb077c4c 536
6accaf43
MT
537 pcre2_substring_free(pattern);
538 pattern = NULL;
38fa3305 539 }
fb077c4c 540
38fa3305 541ERROR:
c178c275 542 pcre2_match_data_free(match);
fb077c4c 543
38fa3305 544 if (variable)
c178c275 545 pcre2_substring_free(variable);
38fa3305 546 if (pattern)
c178c275 547 pcre2_substring_free(pattern);
38fa3305 548
6accaf43
MT
549 return r;
550}
551
552PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser,
553 const char* namespace, const char* value) {
554 // Return NULL when the value is NULL
555 if (!value)
556 return NULL;
557
558 // Create a working copy of the string we are expanding
559 char* buffer = strdup(value);
560
561 // Fast path to check if there are any variables in here whatsoever
562 char* pos = strchr(value, '%');
563 if (!pos)
564 return buffer;
565
d25dd74f 566 // Compile all regular expressions
bf7acff3 567 int r = pakfire_parser_compile_regexes(parser);
aed0b342
MT
568 if (r)
569 goto ERROR;
d25dd74f 570
6accaf43 571 // Expand all variables
d25dd74f 572 r = pakfire_parser_expand_variables(parser, namespace, &buffer);
aed0b342
MT
573 if (r)
574 goto ERROR;
6accaf43 575
d25dd74f
MT
576 // Expand all commands
577 if (parser->flags & PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS) {
578 r = pakfire_parser_expand_commands(parser, &buffer);
aed0b342
MT
579 if (r)
580 goto ERROR;
d25dd74f
MT
581 }
582
fb077c4c 583 return buffer;
aed0b342
MT
584
585ERROR:
586 if (buffer)
587 free(buffer);
588
589 return NULL;
fb077c4c
MT
590}
591
aa75cc88
MT
592PAKFIRE_EXPORT char* pakfire_parser_get(PakfireParser parser, const char* namespace, const char* name) {
593 const char* value = pakfire_parser_get_raw(parser, namespace, name);
1a7200b5 594
fb077c4c 595 // Return NULL when nothing was found
1a7200b5 596 if (!value)
fb077c4c
MT
597 return NULL;
598
599 // Otherwise return the expanded value
aa75cc88 600 return pakfire_parser_expand(parser, namespace, value);
fb077c4c
MT
601}
602
e10e1ad1
MT
603PAKFIRE_EXPORT char** pakfire_parser_get_split(PakfireParser parser,
604 const char* namespace, const char* name, char delim) {
605 char* value = pakfire_parser_get(parser, namespace, name);
606 if (!value)
607 return NULL;
608
609 // Split the string
610 char** list = pakfire_split_string(value, delim);
611 free(value);
612
613 return list;
614}
615
122e45f5 616PAKFIRE_EXPORT int pakfire_parser_merge(PakfireParser parser1, PakfireParser parser2) {
d310f14c 617 DEBUG(parser1->pakfire, "Merging parsers %p and %p\n", parser1, parser2);
20440ba7 618 char namespace[NAME_MAX*2+1];
d310f14c 619
c7577606
MT
620 if (!parser2) {
621 errno = EINVAL;
622 return 1;
623 }
624
1a7200b5
MT
625 // Do not try to merge a parser with itself
626 if (parser1 == parser2)
122e45f5 627 return EINVAL;
1a7200b5 628
fcbbe16e
MT
629 for (unsigned int i = 0; i < parser2->num_declarations; i++) {
630 struct pakfire_parser_declaration* d = parser2->declarations[i];
631 if (!d)
632 break;
d310f14c 633
20440ba7
MT
634 if (parser2->namespace && *d->namespace)
635 pakfire_string_format(namespace, "%s.%s", parser2->namespace, d->namespace);
aa75cc88 636 else if (parser2->namespace)
20440ba7
MT
637 pakfire_string_set(namespace, parser2->namespace);
638 else if (*d->namespace)
639 pakfire_string_set(namespace, d->namespace);
aa75cc88 640 else
20440ba7 641 pakfire_string_set(namespace, "");
713da225 642
aa75cc88 643 int r = pakfire_parser_set(parser1, namespace, d->name, d->value);
122e45f5
MT
644 if (r)
645 return r;
d310f14c
MT
646 }
647
122e45f5 648 return 0;
d310f14c
MT
649}
650
4b7699d9
MT
651PAKFIRE_EXPORT int pakfire_parser_read(PakfireParser parser, FILE* f,
652 struct pakfire_parser_error** error) {
fb077c4c
MT
653 char* data;
654 size_t len;
655
656 int r = pakfire_read_file_into_buffer(f, &data, &len);
657 if (r)
658 return r;
659
4b7699d9 660 r = pakfire_parser_parse_data(parser, data, len, error);
89045ec6
MT
661
662 if (data)
f0d6233d 663 free(data);
89045ec6 664
fb077c4c 665 return r;
89045ec6 666}
dac3330d 667
4b7699d9
MT
668PAKFIRE_EXPORT int pakfire_parser_read_file(PakfireParser parser, const char* path,
669 struct pakfire_parser_error** error) {
774aeac4
MT
670 FILE* f = fopen(path, "r");
671 if (!f)
672 return 1;
673
4b7699d9 674 int r = pakfire_parser_read(parser, f, error);
774aeac4
MT
675 fclose(f);
676
677 return r;
678}
679
4b7699d9
MT
680PAKFIRE_EXPORT int pakfire_parser_parse(PakfireParser parser,
681 const char* data, size_t size, struct pakfire_parser_error** error) {
682 return pakfire_parser_parse_data(parser, data, size, error);
40f17018
MT
683}
684
dac3330d 685PAKFIRE_EXPORT char* pakfire_parser_dump(PakfireParser parser) {
20440ba7 686 char buffer[NAME_MAX*2 + 1];
dac3330d
MT
687 char* s = NULL;
688
689 for (unsigned int i = 0; i < parser->num_declarations; i++) {
690 struct pakfire_parser_declaration* d = parser->declarations[i];
691
692 if (d) {
20440ba7
MT
693 if (*d->namespace)
694 pakfire_string_format(buffer, "%s.%s", d->namespace, d->name);
dac3330d 695 else
20440ba7 696 pakfire_string_set(buffer, d->name);
aa75cc88
MT
697
698 asprintf(&s, "%s%-24s = %s\n", (s) ? s : "", buffer, d->value);
dac3330d
MT
699 }
700 }
701
702 return s;
703}
40f17018
MT
704
705PAKFIRE_EXPORT const char* pakfire_parser_get_namespace(PakfireParser parser) {
706 return parser->namespace;
707}
713da225
MT
708
709PAKFIRE_EXPORT int pakfire_parser_set_namespace(PakfireParser parser, const char* namespace) {
710 if (parser->namespace)
711 free(parser->namespace);
712
713 if (namespace)
714 parser->namespace = strdup(namespace);
715 else
716 parser->namespace = NULL;
717
718 DEBUG(parser->pakfire, "%p: Set namespace to: %s\n", parser, parser->namespace);
719
720 return 0;
721}
4b7699d9 722
f59d680a 723PAKFIRE_EXPORT int pakfire_parser_create_package(PakfireParser parser,
08bcb46a 724 PakfirePackage* pkg, PakfireRepo repo, const char* namespace, const char* default_arch) {
f59d680a
MT
725 int r = 1;
726
727 char* name = NULL;
728 char* epoch = NULL;
729 char* version = NULL;
730 char* release = NULL;
731 char* arch = NULL;
732 char* evr = NULL;
733
734 // Fetch name
735 name = pakfire_parser_get(parser, namespace, "name");
f3b3ed8e 736 if (!name || !*name) {
f59d680a
MT
737 ERROR(parser->pakfire, "Name is empty\n");
738 goto CLEANUP;
739 }
740
741 // Fetch epoch
742 epoch = pakfire_parser_get(parser, namespace, "epoch");
f3b3ed8e 743 if (!epoch || !*epoch) {
f59d680a
MT
744 ERROR(parser->pakfire, "Epoch is empty\n");
745 goto CLEANUP;
746 }
747
748 // Fetch version
749 version = pakfire_parser_get(parser, namespace, "version");
f3b3ed8e 750 if (!version || !*version) {
f59d680a
MT
751 ERROR(parser->pakfire, "Version is empty\n");
752 goto CLEANUP;
753 }
754
755 // Fetch release
756 release = pakfire_parser_get(parser, namespace, "release");
f3b3ed8e 757 if (!release || !*release) {
f59d680a
MT
758 ERROR(parser->pakfire, "Release is empty\n");
759 goto CLEANUP;
760 }
761
762 // Fetch arch
763 arch = pakfire_parser_get(parser, namespace, "arch");
f3b3ed8e 764 if (!arch || !*arch) {
08bcb46a
MT
765 if (default_arch) {
766 arch = strdup(default_arch);
767 if (!arch)
768 goto CLEANUP;
769 } else {
770 ERROR(parser->pakfire, "Arch is empty\n");
771 goto CLEANUP;
772 }
f59d680a
MT
773 }
774
775 // Compile EVR
776 evr = pakfire_package_join_evr(epoch, version, release);
777 if (!evr)
778 goto CLEANUP;
779
780 // Create a new package object
781 *pkg = pakfire_package_create(parser->pakfire, repo, name, evr, arch);
782 if (!*pkg) {
783 ERROR(parser->pakfire, "Could not create package\n");
784 goto CLEANUP;
785 }
786
5848e7cb
MT
787 // Is this a source package?
788 int is_source = pakfire_package_is_source(*pkg);
789
df552c32
MT
790 // Assign a new UUID to this package
791 char* uuid = pakfire_generate_uuid();
792 if (!uuid) {
793 ERROR(parser->pakfire, "Generating a UUID failed: %s\n", strerror(errno));
794 goto CLEANUP;
795 }
796
797 pakfire_package_set_uuid(*pkg, uuid);
798 free(uuid);
799
d73ca64f
MT
800 // Set build host
801 char hostname[HOST_NAME_MAX + 1];
802 r = gethostname(hostname, HOST_NAME_MAX + 1);
803 if (r) {
804 ERROR(parser->pakfire, "gethostname() failed: %s\n", strerror(errno));
805 goto CLEANUP;
806 }
807
808 pakfire_package_set_build_host(*pkg, hostname);
809
810 // Set build time
811 time_t now = time(NULL);
812
813 pakfire_package_set_build_time(*pkg, now);
814
f00837c0
MT
815 // Assign more attributes
816 const struct attribute {
817 const char* name;
818 void(*func)(PakfirePackage pkg, const char* value);
819 } attributes[] = {
820 { "summary", pakfire_package_set_summary, },
821 { "description", pakfire_package_set_description, },
822 { "license", pakfire_package_set_license, },
823 { "url", pakfire_package_set_url, },
824 { "groups", pakfire_package_set_groups, },
825 { "vendor", pakfire_package_set_vendor, },
826 { "maintainer", pakfire_package_set_maintainer, },
827 { NULL },
828 };
829
830 for (const struct attribute* a = attributes; a->name; a++) {
831 char* value = pakfire_parser_get(parser, namespace, a->name);
832 if (!value)
833 continue;
834
835 a->func(*pkg, value);
836 free(value);
837 }
838
b81a64ec 839 // Fetch build dependencies
5848e7cb 840 if (is_source) {
5848e7cb 841 char* requires = pakfire_parser_get(parser, "build", "requires");
b81a64ec
MT
842 if (requires && *requires)
843 pakfire_parse_deps(parser->pakfire, *pkg,
844 pakfire_package_add_requires, requires);
5848e7cb
MT
845 }
846
f59d680a
MT
847 // All okay
848 r = 0;
849
850CLEANUP:
851 if (name)
852 free(name);
853 if (epoch)
854 free(epoch);
855 if (version)
856 free(version);
857 if (release)
858 free(release);
859 if (arch)
860 free(arch);
861
862 return r;
863}
864
4b7699d9
MT
865// Error
866
867struct pakfire_parser_error {
868 PakfireParser parser;
869 int nrefs;
870
871 char* filename;
872 int line;
873 char* message;
874};
875
876PAKFIRE_EXPORT int pakfire_parser_error_create(struct pakfire_parser_error** error,
877 PakfireParser parser, const char* filename, int line, const char* message) {
878 struct pakfire_parser_error* e = calloc(1, sizeof(*e));
879 if (!e)
880 return ENOMEM;
881
882 // Initialize reference counter
883 e->nrefs = 1;
884
885 e->parser = pakfire_parser_ref(parser);
886
887 // Copy all input values
888 if (filename)
889 e->filename = strdup(filename);
890
891 e->line = line;
892
893 if (message)
894 e->message = strdup(message);
895
896 *error = e;
897
898 return 0;
899}
900
901PAKFIRE_EXPORT struct pakfire_parser_error* pakfire_parser_error_ref(
902 struct pakfire_parser_error* error) {
903 ++error->nrefs;
904
905 return error;
906}
907
908static void pakfire_parser_error_free(struct pakfire_parser_error* error) {
909 pakfire_parser_unref(error->parser);
910
911 if (error->filename)
912 free(error->filename);
913
914 if (error->message)
915 free(error->message);
916
917 free(error);
918}
919
920PAKFIRE_EXPORT struct pakfire_parser_error* pakfire_parser_error_unref(
921 struct pakfire_parser_error* error) {
922 if (--error->nrefs > 0)
923 return error;
924
925 pakfire_parser_error_free(error);
926
927 return NULL;
928}
929
930PAKFIRE_EXPORT const char* pakfire_parser_error_get_filename(
931 struct pakfire_parser_error* error) {
932 return error->filename;
933}
934
935PAKFIRE_EXPORT int pakfire_parser_error_get_line(struct pakfire_parser_error* error) {
936 return error->line;
937}
938
939PAKFIRE_EXPORT const char* pakfire_parser_error_get_message(
940 struct pakfire_parser_error* error) {
941 return error->message;
942}