]>
Commit | Line | Data |
---|---|---|
f9a691ef 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 | ||
ffbcfe72 MT |
21 | %lex-param {yyscan_t* scanner} |
22 | ||
23 | %parse-param | |
24 | {yyscan_t* scanner} | |
ac4c607b | 25 | {struct pakfire* pakfire} |
657a5c72 MT |
26 | {struct pakfire_parser** parser} |
27 | {struct pakfire_parser* parent} | |
ffbcfe72 MT |
28 | {struct pakfire_parser_error** error} |
29 | ||
30 | // Make the parser reentrant | |
31 | %define api.pure full | |
6d83a6d4 | 32 | |
529de39f | 33 | // Generate verbose error messages |
acdc19a5 | 34 | %define parse.error verbose |
529de39f | 35 | |
f9a691ef | 36 | %{ |
79fd37b5 MT |
37 | |
38 | #include <errno.h> | |
4f2a7bce | 39 | #include <stdio.h> |
f0d6233d | 40 | #include <stdlib.h> |
df8aac92 | 41 | #include <time.h> |
4f2a7bce | 42 | |
517708c8 MT |
43 | // Enable legacy logging |
44 | #define PAKFIRE_LEGACY_LOGGING | |
45 | ||
0ba52fbe | 46 | #include <pakfire/constants.h> |
a0258b56 | 47 | #include <pakfire/logging.h> |
ac4c607b | 48 | #include <pakfire/pakfire.h> |
23188111 | 49 | #include <pakfire/parser.h> |
d973a13d | 50 | #include <pakfire/string.h> |
23188111 | 51 | #include <pakfire/util.h> |
35ebb186 | 52 | |
f9a691ef MT |
53 | #define YYERROR_VERBOSE 1 |
54 | ||
a2e98734 MT |
55 | // Enable to debug the grammar |
56 | #define YYDEBUG 0 | |
d5c4772d MT |
57 | #ifdef ENABLE_DEBUG |
58 | int yydebug = YYDEBUG; | |
529de39f MT |
59 | #endif |
60 | ||
ffbcfe72 | 61 | typedef void* yyscan_t; |
d91b495f | 62 | extern int yylex_init_extra(struct pakfire_parser_state* state, yyscan_t* scanner); |
ffbcfe72 MT |
63 | int yylex_destroy(yyscan_t scanner); |
64 | ||
f9a691ef | 65 | typedef struct yy_buffer_state* YY_BUFFER_STATE; |
4b39395b | 66 | extern YY_BUFFER_STATE yy_scan_bytes(const char* buffer, int len, yyscan_t scanner); |
ffbcfe72 | 67 | extern void yy_delete_buffer(YY_BUFFER_STATE buffer, yyscan_t scanner); |
f9a691ef | 68 | |
d91b495f MT |
69 | #define YY_EXTRA_TYPE struct pakfire_parser_state* |
70 | YY_EXTRA_TYPE yyget_extra(yyscan_t scanner); | |
71 | ||
ffbcfe72 MT |
72 | #include "grammar.h" |
73 | ||
74 | extern int yylex (YYSTYPE* yylval_param, yyscan_t yyscanner); | |
6ab14b96 | 75 | |
01c2c202 | 76 | #define ABORT do { YYABORT; } while (0); |
4f2a7bce | 77 | |
d310f14c MT |
78 | enum operator { |
79 | OP_EQUALS = 0, | |
80 | }; | |
81 | ||
ac4c607b | 82 | static void yyerror(yyscan_t* scanner, struct pakfire* pakfire, struct pakfire_parser** parser, |
657a5c72 | 83 | struct pakfire_parser* parent, struct pakfire_parser_error** error, const char* s) { |
d91b495f MT |
84 | const struct pakfire_parser_state* state = yyget_extra(scanner); |
85 | ||
a8a41064 | 86 | ERROR(pakfire, "Error (line %u): %s\n", state->lineno, s); |
4b7699d9 MT |
87 | |
88 | // Create a new error object | |
89 | if (error) { | |
d91b495f | 90 | int r = pakfire_parser_error_create(error, *parser, NULL, state->lineno, s); |
4b7699d9 MT |
91 | if (r) { |
92 | ERROR(pakfire, "Could not create error object: %s\n", strerror(errno)); | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
ac4c607b | 97 | static struct pakfire_parser* make_if_stmt(struct pakfire* pakfire, const enum operator op, |
657a5c72 | 98 | const char* val1, const char* val2, struct pakfire_parser* if_block, struct pakfire_parser* else_block); |
aed19891 | 99 | |
20440ba7 | 100 | static int pakfire_parser_new_declaration( |
5ba098cf | 101 | struct pakfire_parser_declaration** declaration, const char* name, const char* value, int flags) { |
79fd37b5 MT |
102 | if (!name) |
103 | return EINVAL; | |
104 | ||
20440ba7 | 105 | struct pakfire_parser_declaration* d = calloc(1, sizeof(*d)); |
79fd37b5 MT |
106 | if (!d) |
107 | return ENOMEM; | |
108 | ||
20440ba7 MT |
109 | // Set name |
110 | pakfire_string_set(d->name, name); | |
79fd37b5 MT |
111 | |
112 | // Copy value | |
113 | if (value) | |
114 | d->value = strdup(value); | |
115 | else | |
116 | d->value = NULL; | |
117 | ||
5ba098cf MT |
118 | // Copy flags |
119 | d->flags = flags; | |
120 | ||
79fd37b5 MT |
121 | *declaration = d; |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | static void pakfire_parser_free_declaration(struct pakfire_parser_declaration* declaration) { | |
79fd37b5 MT |
127 | if (declaration->value) |
128 | free(declaration->value); | |
129 | ||
130 | free(declaration); | |
131 | } | |
132 | ||
f37774cd | 133 | %} |
d310f14c | 134 | |
15746df5 MT |
135 | %token T_INDENT |
136 | %token T_OUTDENT | |
137 | ||
f37774cd MT |
138 | %token <string> T_KEY |
139 | %token <string> T_STRING | |
ac98e979 | 140 | |
f37774cd | 141 | %token T_EOL |
f9a691ef | 142 | |
15746df5 | 143 | %token T_END |
aff85bdb MT |
144 | %token T_IF |
145 | %token T_ELSE | |
5ba098cf | 146 | %token T_EXPORT |
15746df5 | 147 | |
aff85bdb | 148 | %token T_EQUALS |
f37774cd | 149 | %token T_ASSIGN |
4784cd87 | 150 | %token T_APPEND |
f37774cd | 151 | |
aed19891 MT |
152 | %token <string> T_SUBPARSER |
153 | ||
154 | %type <parser> grammar | |
0c184457 | 155 | %type <parser> block |
aed19891 | 156 | %type <parser> subparser |
69d27848 | 157 | %type <string> subparser_name |
aff85bdb MT |
158 | %type <parser> if_stmt |
159 | %type <parser> else_stmt | |
aed19891 | 160 | |
f37774cd MT |
161 | %type <string> key |
162 | %type <string> value | |
79fd37b5 | 163 | %type <declaration> declaration |
d310f14c | 164 | |
15746df5 MT |
165 | %type <string> lines |
166 | %type <string> line | |
167 | ||
28afa9c2 | 168 | %union { |
657a5c72 | 169 | struct pakfire_parser* parser; |
28afa9c2 | 170 | char* string; |
79fd37b5 | 171 | struct pakfire_parser_declaration* declaration; |
28afa9c2 | 172 | } |
f9a691ef | 173 | |
f25d2019 MT |
174 | %initial-action { |
175 | *parser = pakfire_parser_create(pakfire, parent, NULL, 0); | |
f25d2019 MT |
176 | }; |
177 | ||
178 | %destructor { if ($$) pakfire_parser_unref($$); } <parser> | |
79fd37b5 MT |
179 | %destructor { pakfire_parser_free_declaration($$); } <declaration> |
180 | ||
f37774cd | 181 | %start grammar |
28afa9c2 | 182 | |
f37774cd | 183 | %% |
05ce4b89 | 184 | |
d54c1557 | 185 | grammar : %empty |
aed19891 | 186 | { |
0c184457 MT |
187 | $$ = pakfire_parser_create(pakfire, *parser, NULL, 0); |
188 | if (!$$) | |
189 | ABORT; | |
190 | ||
191 | // This becomes the new top parser | |
192 | if (*parser) | |
193 | pakfire_parser_unref(*parser); | |
194 | *parser = pakfire_parser_ref($$); | |
aed19891 | 195 | } |
d54c1557 | 196 | | grammar declaration |
79fd37b5 | 197 | { |
09b214b7 MT |
198 | $$ = $1; |
199 | ||
79fd37b5 MT |
200 | int r = pakfire_parser_apply_declaration($1, $2); |
201 | if (r) | |
202 | ABORT; | |
203 | ||
09b214b7 | 204 | pakfire_parser_free_declaration($2); |
79fd37b5 | 205 | } |
aed19891 MT |
206 | | grammar subparser |
207 | { | |
09b214b7 MT |
208 | $$ = $1; |
209 | ||
122e45f5 | 210 | int r = pakfire_parser_merge($1, $2); |
c7577606 | 211 | pakfire_parser_unref($2); |
122e45f5 MT |
212 | if (r) |
213 | ABORT; | |
aff85bdb MT |
214 | } |
215 | | grammar if_stmt | |
216 | { | |
09b214b7 MT |
217 | $$ = $1; |
218 | ||
122e45f5 MT |
219 | if ($2) { |
220 | int r = pakfire_parser_merge($1, $2); | |
09b214b7 | 221 | pakfire_parser_unref($2); |
122e45f5 MT |
222 | if (r) |
223 | ABORT; | |
224 | } | |
aed19891 | 225 | } |
d54c1557 | 226 | | grammar empty |
aff85bdb | 227 | { |
09b214b7 | 228 | $$ = $1; |
aff85bdb MT |
229 | } |
230 | ; | |
231 | ||
0c184457 | 232 | block : T_INDENT grammar T_OUTDENT |
f25d2019 | 233 | { |
0c184457 | 234 | // Reset the top parser |
657a5c72 | 235 | struct pakfire_parser* p = pakfire_parser_get_parent(*parser); |
0c184457 MT |
236 | pakfire_parser_unref(*parser); |
237 | *parser = p; | |
f25d2019 | 238 | |
f25d2019 | 239 | $$ = $2; |
aff85bdb | 240 | } |
28afa9c2 MT |
241 | ; |
242 | ||
f37774cd | 243 | declaration : key T_ASSIGN value T_EOL |
28afa9c2 | 244 | { |
5ba098cf | 245 | int r = pakfire_parser_new_declaration(&$$, $1, $3, 0); |
f37774cd | 246 | if (r) |
4f2a7bce | 247 | ABORT; |
4f2a7bce | 248 | } |
4784cd87 MT |
249 | | key T_APPEND value T_EOL |
250 | { | |
ec0f0419 MT |
251 | int r = pakfire_parser_new_declaration(&$$, $1, $3, |
252 | PAKFIRE_PARSER_DECLARATION_APPEND); | |
4784cd87 MT |
253 | if (r) |
254 | ABORT; | |
255 | } | |
36446af0 | 256 | | key T_EOL empty_lines T_INDENT lines T_OUTDENT T_END T_EOL |
15746df5 | 257 | { |
5ba098cf | 258 | int r = pakfire_parser_new_declaration(&$$, $1, $5, 0); |
15746df5 MT |
259 | if (r) |
260 | ABORT; | |
5f17ac71 | 261 | } |
af7cb654 MT |
262 | | key T_EOL empty_lines T_END T_EOL |
263 | { | |
5ba098cf MT |
264 | int r = pakfire_parser_new_declaration(&$$, $1, "", 0); |
265 | if (r) | |
266 | ABORT; | |
267 | } | |
268 | | T_EXPORT key T_ASSIGN value T_EOL | |
269 | { | |
270 | int r = pakfire_parser_new_declaration(&$$, $key, $value, | |
271 | PAKFIRE_PARSER_DECLARATION_EXPORT); | |
af7cb654 MT |
272 | if (r) |
273 | ABORT; | |
274 | } | |
94e7baf6 MT |
275 | | T_EXPORT key T_APPEND value T_EOL |
276 | { | |
277 | int r = pakfire_parser_new_declaration(&$$, $key, $value, | |
278 | PAKFIRE_PARSER_DECLARATION_EXPORT|PAKFIRE_PARSER_DECLARATION_APPEND); | |
279 | if (r) | |
280 | ABORT; | |
281 | } | |
6e5c9f26 | 282 | ; |
6b03e625 | 283 | |
d54c1557 MT |
284 | empty : T_EOL |
285 | ; | |
286 | ||
af7cb654 MT |
287 | empty_lines : %empty |
288 | | empty_lines empty; | |
289 | ||
f37774cd | 290 | key : T_KEY |
79469dd9 MT |
291 | ; |
292 | ||
f37774cd | 293 | value : T_STRING |
c2fcc7b5 | 294 | { |
f37774cd | 295 | $$ = $1; |
79469dd9 MT |
296 | } |
297 | ; | |
c2fcc7b5 | 298 | |
15746df5 MT |
299 | lines : lines line |
300 | { | |
301 | int r = asprintf(&$$, "%s\n%s", $1, $2); | |
302 | if (r < 0) | |
303 | ABORT; | |
147e59f0 MT |
304 | |
305 | free($1); | |
306 | free($2); | |
15746df5 MT |
307 | } |
308 | | line; | |
309 | ||
310 | line : T_STRING T_EOL | |
311 | { | |
312 | $$ = $1; | |
313 | } | |
29cedba8 MT |
314 | | T_EOL |
315 | { | |
316 | // Empty line | |
147e59f0 | 317 | $$ = strdup(""); |
29cedba8 | 318 | } |
15746df5 MT |
319 | ; |
320 | ||
0c184457 | 321 | subparser : subparser_name T_EOL block T_END T_EOL |
aed19891 | 322 | { |
09b214b7 | 323 | $$ = $3; |
18281213 MT |
324 | |
325 | pakfire_parser_set_namespace($$, $1); | |
326 | ||
6200facc MT |
327 | char* key = NULL; |
328 | char* value = NULL; | |
18281213 MT |
329 | |
330 | int r = pakfire_string_partition($1, ":", &key, &value); | |
331 | if (r == 0) { | |
6200facc | 332 | if (key && strcmp("package", key) == 0) { |
fea2e884 | 333 | pakfire_parser_set($$, NULL, "name", value, 0); |
18281213 MT |
334 | } |
335 | ||
336 | if (key) | |
337 | free(key); | |
338 | if (value) | |
339 | free(value); | |
340 | } | |
aed19891 | 341 | } |
5f17ac71 MT |
342 | | subparser_name T_EOL |
343 | { | |
6200facc MT |
344 | char* key = NULL; |
345 | char* value = NULL; | |
5f17ac71 MT |
346 | |
347 | // Create a new parser | |
0c184457 | 348 | $$ = pakfire_parser_create(pakfire, *parser, NULL, 0); |
5f17ac71 MT |
349 | if (!$$) |
350 | ABORT; | |
351 | ||
5837952f MT |
352 | int r = pakfire_string_partition($1, ":", &key, &value); |
353 | if (r) | |
354 | ABORT; | |
5f17ac71 | 355 | |
570bec8a | 356 | // Handle packages |
6200facc | 357 | if (key && strcmp("package", key) == 0) { |
570bec8a MT |
358 | pakfire_parser_set_namespace($$, $1); |
359 | ||
360 | // Set the name (because we cannot have empty parsers) | |
fea2e884 | 361 | pakfire_parser_set($$, NULL, "name", value, 0); |
570bec8a MT |
362 | |
363 | // Handle all other cases | |
364 | } else { | |
fea2e884 | 365 | pakfire_parser_set($$, NULL, key, value, 0); |
570bec8a | 366 | } |
5f17ac71 MT |
367 | |
368 | if (key) | |
369 | free(key); | |
370 | if (value) | |
371 | free(value); | |
372 | } | |
aed19891 MT |
373 | ; |
374 | ||
69d27848 MT |
375 | subparser_name : T_SUBPARSER |
376 | | T_SUBPARSER T_STRING | |
377 | { | |
378 | int r = asprintf(&$$, "%s:%s", $1, $2); | |
379 | if (r < 0) | |
380 | ABORT; | |
147e59f0 MT |
381 | |
382 | free($1); | |
383 | free($2); | |
69d27848 | 384 | } |
5f17ac71 | 385 | ; |
69d27848 | 386 | |
0c184457 | 387 | if_stmt : T_IF T_STRING T_EQUALS T_STRING T_EOL block else_stmt T_END T_EOL |
aed19891 | 388 | { |
0ac3e9f3 | 389 | $$ = make_if_stmt(pakfire, OP_EQUALS, $2, $4, $6, $7); |
1a3bcdca MT |
390 | |
391 | if ($6) | |
392 | pakfire_parser_unref($6); | |
393 | if ($7) | |
394 | pakfire_parser_unref($7); | |
aed19891 MT |
395 | } |
396 | ; | |
397 | ||
0c184457 | 398 | else_stmt : T_ELSE T_EOL block |
aff85bdb | 399 | { |
09b214b7 | 400 | $$ = $3; |
aff85bdb MT |
401 | } |
402 | | %empty | |
403 | { | |
404 | $$ = NULL; | |
405 | } | |
406 | ; | |
aed19891 | 407 | |
f9a691ef MT |
408 | %% |
409 | ||
657a5c72 | 410 | int pakfire_parser_parse_data(struct pakfire_parser* parent, const char* data, size_t len, |
4b7699d9 | 411 | struct pakfire_parser_error** error) { |
ac4c607b | 412 | struct pakfire* pakfire = pakfire_parser_get_pakfire(parent); |
44ab277d | 413 | char* dump = NULL; |
ffbcfe72 | 414 | yyscan_t scanner; |
23188111 | 415 | |
d91b495f MT |
416 | // Initialize the parser's state |
417 | struct pakfire_parser_state state = { | |
418 | .lineno = 1, | |
419 | .indent_level = 0, | |
420 | .current_indent = 0, | |
421 | .readline_indent = 0, | |
422 | }; | |
423 | ||
129176e5 | 424 | #ifdef ENABLE_DEBUG |
0264c700 MT |
425 | DEBUG(pakfire, "Parsing the following data (%zu):\n%.*s\n", |
426 | len, (int)len, data); | |
a0258b56 | 427 | |
df8aac92 MT |
428 | // Save start time |
429 | clock_t t_start = clock(); | |
129176e5 | 430 | #endif |
df8aac92 | 431 | |
ffbcfe72 | 432 | // Initialise scanner |
d91b495f | 433 | yylex_init_extra(&state, &scanner); |
ffbcfe72 | 434 | |
abb9117f | 435 | // Create a new sub-parser |
657a5c72 | 436 | struct pakfire_parser* parser = NULL; |
abb9117f | 437 | |
ffbcfe72 | 438 | YY_BUFFER_STATE buffer = yy_scan_bytes(data, len, scanner); |
c7577606 | 439 | int r = yyparse(scanner, pakfire, &parser, parent, error); |
ffbcfe72 | 440 | yy_delete_buffer(buffer, scanner); |
f9a691ef | 441 | |
abb9117f MT |
442 | // If everything was parsed successfully, we merge the sub-parser into |
443 | // the parent parser. That way, it will be untouched if something could | |
444 | // not be successfully parsed. | |
445 | if (r == 0) { | |
c7577606 MT |
446 | if (parser) |
447 | pakfire_parser_merge(parent, parser); | |
abb9117f MT |
448 | } |
449 | ||
9405ba79 | 450 | // Destroy the parser |
c7577606 | 451 | if (parser) |
f25d2019 | 452 | pakfire_parser_unref(parser); |
9405ba79 | 453 | |
129176e5 | 454 | #ifdef ENABLE_DEBUG |
df8aac92 MT |
455 | // Save end time |
456 | clock_t t_end = clock(); | |
457 | ||
dac3330d | 458 | // Log what we have in the parent parser now |
44ab277d MT |
459 | dump = pakfire_parser_dump(parent); |
460 | if (dump) | |
461 | DEBUG(pakfire, "Status of the parser %p:\n%s\n", parent, dump); | |
dac3330d | 462 | |
df8aac92 MT |
463 | // Log time we needed to parse data |
464 | DEBUG(pakfire, "Parser finished in %.4fms\n", | |
465 | (double)(t_end - t_start) * 1000 / CLOCKS_PER_SEC); | |
129176e5 | 466 | #endif |
df8aac92 | 467 | |
dac3330d | 468 | // Cleanup |
44ab277d MT |
469 | if (dump) |
470 | free(dump); | |
dac3330d | 471 | pakfire_unref(pakfire); |
ffbcfe72 | 472 | yylex_destroy(scanner); |
dac3330d | 473 | |
fb077c4c | 474 | return r; |
f9a691ef MT |
475 | } |
476 | ||
ac4c607b | 477 | static struct pakfire_parser* make_if_stmt(struct pakfire* pakfire, const enum operator op, |
657a5c72 | 478 | const char* val1, const char* val2, struct pakfire_parser* if_block, struct pakfire_parser* else_block) { |
aff85bdb MT |
479 | switch (op) { |
480 | case OP_EQUALS: | |
481 | DEBUG(pakfire, "Evaluating if statement: %s == %s?\n", val1, val2); | |
482 | break; | |
483 | } | |
484 | ||
657a5c72 | 485 | struct pakfire_parser* parent = pakfire_parser_get_parent(if_block); |
0ac3e9f3 MT |
486 | |
487 | DEBUG(pakfire, " parent = %p, if = %p, else = %p\n", parent, if_block, else_block); | |
aff85bdb MT |
488 | |
489 | // Expand values | |
0ac3e9f3 MT |
490 | char* v1 = pakfire_parser_expand(parent, NULL, val1); |
491 | char* v2 = pakfire_parser_expand(parent, NULL, val2); | |
aff85bdb | 492 | |
657a5c72 | 493 | struct pakfire_parser* result = NULL; |
aff85bdb MT |
494 | |
495 | switch (op) { | |
496 | case OP_EQUALS: | |
497 | DEBUG(pakfire, " '%s' == '%s'?\n", v1, v2); | |
498 | ||
499 | if (strcmp(v1, v2) == 0) | |
500 | result = if_block; | |
501 | else | |
502 | result = else_block; | |
503 | ||
504 | break; | |
505 | } | |
506 | ||
aff85bdb MT |
507 | free(v1); |
508 | free(v2); | |
0ac3e9f3 | 509 | pakfire_parser_unref(parent); |
aff85bdb | 510 | |
7c22cb15 MT |
511 | if (result) |
512 | pakfire_parser_ref(result); | |
513 | ||
514 | return result; | |
aff85bdb | 515 | } |