/*############################################################################# # # # Pakfire - The IPFire package management system # # Copyright (C) 2019 Pakfire development team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # #############################################################################*/ %{ #include #include #include #include #include #define YYERROR_VERBOSE 1 typedef struct yy_buffer_state* YY_BUFFER_STATE; extern YY_BUFFER_STATE yy_scan_bytes(const char* buffer, size_t len); extern void yy_delete_buffer(YY_BUFFER_STATE buffer); extern int yylex(); extern int yyparse(); extern int num_lines; static Pakfire pakfire; static void yyerror(const char* s); static void cleanup(void); #define ABORT do { cleanup(); YYABORT; } while (0); #define NUM_DECLARATIONS 128 static int pakfire_parser_add_declaration(const char* name, const char* value); static struct pakfire_parser_declaration* declarations[NUM_DECLARATIONS]; char* current_block = NULL; %} %token APPEND %token ASSIGN %token DEFINE %token END %token NEWLINE %token TAB %token WHITESPACE %token WORD %type line; %type text; %type variable; %type value; %type words; %union { char* string; } %% top : top thing | thing ; thing : block | empty ; empty : WHITESPACE NEWLINE | NEWLINE ; // Optional whitespace whitespace : WHITESPACE | /* empty */ ; variable : WORD { $$ = $1; }; value : words { $$ = $1; } | /* empty */ { $$ = NULL; }; words : WORD { $$ = $1; } | words WHITESPACE WORD { int r = asprintf(&$$, "%s %s", $1, $3); if (r < 0) { ERROR(pakfire, "Could not allocate memory"); ABORT; } }; line : whitespace words NEWLINE { // Only forward words $$ = $2; } | whitespace NEWLINE { $$ = NULL; }; text : text line { int r = asprintf(&$$, "%s\n%s", $1, $2); if (r < 0) { ERROR(pakfire, "Could not allocate memory"); ABORT; } } | line | /* empty */ { $$ = NULL; }; block_opening : variable NEWLINE { current_block = pakfire_strdup($1); }; block_closing : END NEWLINE { pakfire_free(current_block); current_block = NULL; } block : block_opening assignments block_closing; assignments : assignments assignment | assignments empty | /* empty */ ; assignment : whitespace variable whitespace ASSIGN whitespace value whitespace NEWLINE { int r = pakfire_parser_add_declaration($2, $6); if (r < 0) ABORT; } | whitespace DEFINE WHITESPACE variable NEWLINE text whitespace END NEWLINE { int r = pakfire_parser_add_declaration($4, $6); if (r < 0) ABORT; } %% static void cleanup(void) { // Reset Pakfire pointer pakfire = NULL; // Free all declarations for (unsigned int i = 0; i < NUM_DECLARATIONS; i++) { pakfire_free(declarations[i]); } // Reset current_block if (current_block) { pakfire_free(current_block); current_block = NULL; } } static int pakfire_parser_add_declaration(const char* name, const char* value) { struct pakfire_parser_declaration* d; unsigned int i = 0; while (i++ < NUM_DECLARATIONS && declarations[i]) i++; if (i == NUM_DECLARATIONS) { ERROR(pakfire, "No free declarations left\n"); return -1; } // Allocate a new declaration declarations[i] = d = pakfire_calloc(1, sizeof(*d)); if (!d) return -1; // Import name if (current_block) { int r = asprintf(&d->name, "%s.%s", current_block, name); if (r < 0) return r; } else { d->name = pakfire_strdup(name); } // Import value d->value = pakfire_strdup(value); DEBUG(pakfire, "New declaration: %s = %s\n", d->name, d->value); return 0; } int pakfire_parser_parse_metadata(Pakfire _pakfire, const char* data, size_t len) { pakfire = _pakfire; DEBUG(pakfire, "Parsing the following data:\n%s\n", data); num_lines = 1; YY_BUFFER_STATE buffer = yy_scan_bytes(data, len); int r = yyparse(); yy_delete_buffer(buffer); return r; } void yyerror(const char* s) { ERROR(pakfire, "Error (line %d): %s\n", num_lines, s); }