]> git.ipfire.org Git - pakfire.git/commitdiff
libpakfire: parser: Move logic into the C file
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 May 2019 14:26:27 +0000 (15:26 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 May 2019 14:26:27 +0000 (15:26 +0100)
The bison file was getting very messy and the parser is large
enough to have its own object with ref counting.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c
src/libpakfire/include/pakfire/parser.h
src/libpakfire/include/pakfire/types.h
src/libpakfire/libpakfire.sym
src/libpakfire/parser/grammar.y
src/libpakfire/parser/parser.c
tests/libpakfire/makefile.c

index d24419351fbf744a801bfe5270cb7dd042f4da77..160bed6319e360150e0876b14b0ab8c1d55d1e58 100644 (file)
@@ -60,6 +60,7 @@ struct _PakfireArchive {
 
        // metadata
        int format;
+       PakfireParser parser;
 
        PakfireFile filelist;
        archive_checksum_t** checksums;
@@ -287,6 +288,7 @@ PAKFIRE_EXPORT PakfireArchive pakfire_archive_create(Pakfire pakfire) {
                archive->nrefs = 1;
 
                archive->format = -1;
+               archive->parser = pakfire_parser_create(pakfire);
                archive->signatures = NULL;
        }
 
@@ -319,6 +321,7 @@ static void pakfire_archive_free(PakfireArchive archive) {
                pakfire_free(archive->signatures);
        }
 
+       pakfire_parser_unref(archive->parser);
        pakfire_unref(archive->pakfire);
        pakfire_free(archive);
 }
@@ -354,21 +357,14 @@ static int pakfire_archive_parse_entry_metadata(PakfireArchive archive,
        size_t data_size;
 
        int r = archive_read(a, &data, &data_size);
-       if (r) {
-               return 1;
-       }
+       if (r)
+               return r;
 
        // Parse metadata file
-       struct pakfire_parser_declaration** declarations = \
-               pakfire_parser_parse_metadata(archive->pakfire, (const char*)data, data_size);
-
+       r = pakfire_parser_parse_data(archive->parser, (const char*)data, data_size);
        pakfire_free(data);
 
-       // Error when nothing was returned
-       if (!declarations)
-               return 1;
-
-       return 0;
+       return r;
 }
 
 static int pakfire_archive_parse_entry_filelist(PakfireArchive archive,
index 51d8da63c12ae7dd370f6205ca7190ae8f05b254..2d48124c5f1d8c9cc0985a67ad4b73044066be08 100644 (file)
 #ifndef PAKFIRE_PARSER_H
 #define PAKFIRE_PARSER_H
 
-#ifdef PAKFIRE_PRIVATE
-
-#include <stddef.h>
 #include <stdio.h>
 
 #include <pakfire/types.h>
 
+PakfireParser pakfire_parser_create(Pakfire pakfire);
+PakfireParser pakfire_parser_unref(PakfireParser parser);
+int pakfire_parser_add_declaration(PakfireParser parser,
+               const char* name, const char* value);
+int pakfire_parser_append_declaration(PakfireParser parser,
+       const char* name, const char* value);
+char* pakfire_parser_get(PakfireParser parser, const char* name);
+
+int pakfire_parser_read(PakfireParser parser, FILE* f);
+
+#ifdef PAKFIRE_PRIVATE
+
+Pakfire pakfire_parser_get_pakfire(PakfireParser parser);
+
 struct pakfire_parser_declaration {
        char* name;
        char* value;
 };
 
-struct pakfire_parser_declaration** pakfire_parser_parse_metadata(Pakfire pakfire,
-       const char* data, size_t len);
-
-struct pakfire_parser_declaration** pakfire_parser_parse_metadata_from_file(
-       Pakfire pakfire, FILE* f);
-
-char* pakfire_parser_get(Pakfire pakfire,
-       struct pakfire_parser_declaration** declarations, const char* name);
+int pakfire_parser_parse_data(PakfireParser parser, const char* data, size_t len);
 
 #endif /* PAKFIRE_PRIVATE */
 
index e7f57680fd8fc1d13308ea5b307deb8a519e66b0..2136853bb0eab820cca48ffc9c8638ab9b298380 100644 (file)
@@ -31,6 +31,7 @@ typedef struct _PakfireFilter* PakfireFilter;
 typedef struct _PakfireKey* PakfireKey;
 typedef struct _PakfirePackage* PakfirePackage;
 typedef struct _PakfirePackageList* PakfirePackageList;
+typedef struct _PakfireParser* PakfireParser;
 typedef struct _PakfireProblem* PakfireProblem;
 typedef struct _PakfireRelation* PakfireRelation;
 typedef struct _PakfireRelationList* PakfireRelationList;
index 6f86f242991aa2c9259403e3aa664d7ddb234641..3aa4b30928bc7282a02e57e7ccc1cffdfcd1ed19 100644 (file)
@@ -210,9 +210,11 @@ global:
        pakfire_packagelist_unref;
 
        # parser
+       pakfire_parser_create;
        pakfire_parser_get;
-       pakfire_parser_parse_metadata;
-       pakfire_parser_parse_metadata_from_file;
+       pakfire_parser_parse_data;
+       pakfire_parser_read;
+       pakfire_parser_unref;
 
        # problem
        pakfire_problem_append;
index 7743f4c855322f336fde674a1a476429c5646e00..be1e58011933e2984dadd4ccac33c4e6171d7b46 100644 (file)
 
 %glr-parser
 
-%parse-param {Pakfire pakfire} {struct pakfire_parser_declaration** declarations}
+%parse-param {PakfireParser parser}
 
 // Generate verbose error messages
 %error-verbose
 
 %{
-#include <regex.h>
 #include <stdio.h>
 
 #include <pakfire/constants.h>
@@ -35,8 +34,6 @@
 #include <pakfire/types.h>
 #include <pakfire/util.h>
 
-#define VARIABLE_PATTERN "%\\{([A-Za-z0-9_\\-]+)\\}"
-
 #define YYERROR_VERBOSE 1
 
 #define YYDEBUG 1
@@ -52,18 +49,13 @@ extern int yylex();
 extern int yyparse();
 
 extern int num_lines;
-static void yyerror(Pakfire pakfire, struct pakfire_parser_declaration** declarations, const char* s);
+static void yyerror(PakfireParser parser, const char* s);
 
 static void cleanup(void);
 #define ABORT do { cleanup(); YYABORT; } while (0);
 
-#define NUM_DECLARATIONS 128
-static int pakfire_parser_add_declaration(Pakfire pakfire,
-       struct pakfire_parser_declaration** delcarations, const char* name, const char* value);
-static int pakfire_parser_append_declaration(Pakfire pakfire,
-       struct pakfire_parser_declaration** declarations, const char* name, const char* value);
-
 char* current_block = NULL;
+static char* pakfire_parser_make_canonical_name(const char* name);
 %}
 
 %token                                                 T_APPEND
@@ -120,7 +112,9 @@ words                                               : word
                                                        {
                                                                int r = asprintf(&$$, "%s %s", $1, $2);
                                                                if (r < 0) {
+                                                                       Pakfire pakfire = pakfire_parser_get_pakfire(parser);
                                                                        ERROR(pakfire, "Could not allocate memory");
+                                                                       pakfire_unref(pakfire);
                                                                        ABORT;
                                                                }
                                                        };
@@ -138,7 +132,9 @@ text                                                : text line
                                                        {
                                                                int r = asprintf(&$$, "%s\n%s", $1, $2);
                                                                if (r < 0) {
+                                                                       Pakfire pakfire = pakfire_parser_get_pakfire(parser);
                                                                        ERROR(pakfire, "Could not allocate memory");
+                                                                       pakfire_unref(pakfire);
                                                                        ABORT;
                                                                }
                                                        }
@@ -174,19 +170,37 @@ block_assignment                  : assignment
 
 assignment                                     : variable T_ASSIGN value T_EOL
                                                        {
-                                                               int r = pakfire_parser_add_declaration(pakfire, declarations, $1, $3);
+                                                               char* name = pakfire_parser_make_canonical_name($1);
+                                                               if (!name)
+                                                                       ABORT;
+
+                                                               int r = pakfire_parser_add_declaration(parser, name, $3);
+                                                               pakfire_free(name);
+
                                                                if (r < 0)
                                                                        ABORT;
                                                        }
                                                        | variable T_APPEND value T_EOL
                                                        {
-                                                               int r = pakfire_parser_append_declaration(pakfire, declarations, $1, $3);
+                                                               char* name = pakfire_parser_make_canonical_name($1);
+                                                               if (!name)
+                                                                       ABORT;
+
+                                                               int r = pakfire_parser_append_declaration(parser, name, $3);
+                                                               pakfire_free(name);
+
                                                                if (r < 0)
                                                                        ABORT;
                                                        }
                                                        | define text end
                                                        {
-                                                               int r = pakfire_parser_add_declaration(pakfire, declarations, $1, $2);
+                                                               char* name = pakfire_parser_make_canonical_name($1);
+                                                               if (!name)
+                                                                       ABORT;
+
+                                                               int r = pakfire_parser_add_declaration(parser, name, $2);
+                                                               pakfire_free(name);
+
                                                                if (r < 0)
                                                                        ABORT;
                                                        };
@@ -220,277 +234,26 @@ static char* pakfire_parser_make_canonical_name(const char* name) {
        return buffer;
 }
 
-static char* pakfire_parser_split_namespace(char* s) {
-       char* pos = strrpos(s, '.');
-
-       if (pos) {
-               s[s - pos] = '\0';
-       }
-
-       return s;
-}
-
-static struct pakfire_parser_declaration* pakfire_parser_get_declaration(Pakfire pakfire,
-               struct pakfire_parser_declaration** declarations, const char* name) {
-       if (!declarations)
-               return NULL;
-
-       struct pakfire_parser_declaration* d = *declarations;
-       while (d) {
-               if (strcmp(d->name, name) == 0)
-                       return d;
-
-               d++;
-       }
-
-       return NULL;
-}
-
-static struct pakfire_parser_declaration* pakfire_parser_get_declaration_in_namespace(
-               Pakfire pakfire, struct pakfire_parser_declaration** declarations,
-               const char* namespace, const char* name) {
-       if (!namespace || !*namespace)
-               return pakfire_parser_get_declaration(pakfire, declarations, name);
-
-       char* n = pakfire_strdup(namespace);
-
-       size_t length = strlen(n) + strlen(name) + 1;
-       char* buffer = pakfire_malloc(length + 1);
-
-       struct pakfire_parser_declaration* d = NULL;
-
-       while (1) {
-               if (n)
-                       snprintf(buffer, length + 1, "%s.%s", n, name);
-               else
-                       snprintf(buffer, length + 1, "%s", name);
-
-               DEBUG(pakfire, "Looking up %s\n", buffer);
-
-               // Lookup declaration
-               d = pakfire_parser_get_declaration(pakfire, declarations, buffer);
-
-               // End if we have found a match
-               if (d)
-                       break;
-
-               // End if we have hit the root namespace
-               if (!n)
-                       break;
-
-               /*
-                       If we did not find a match, we will remove one level of the
-                       namespace and try again...
-               */
-               char* p = strrchr(n, '.');
-               if (p) {
-                       n[p - n] = '\0';
-               } else {
-                       pakfire_free(n);
-                       n = NULL;
-               }
-       }
-
-       if (n)
-               pakfire_free(n);
-       pakfire_free(buffer);
-
-       return d;
-}
-
-static char* pakfire_parser_expand_declaration(Pakfire pakfire,
-               struct pakfire_parser_declaration** declarations,
-               const struct pakfire_parser_declaration* declaration) {
-       // Return NULL when the value of the declaration is NULL
-       if (!declaration || !declaration->value)
-               return NULL;
-
-       // Compile the regular expression
-       regex_t preg;
-       int r = regcomp(&preg, VARIABLE_PATTERN, REG_EXTENDED);
-       if (r) {
-               char error[1024];
-               regerror(r, &preg, error, sizeof(error));
-
-               ERROR(pakfire, "Could not compile regular expression (%s): %s",
-                       VARIABLE_PATTERN, error);
-
-               return NULL;
-       }
-
-       char* namespace = pakfire_strdup(declaration->name);
-       char* p = strrchr(namespace, '.');
-       if (p)
-               namespace[p - namespace] = '\0';
-       else
-               namespace[0] = '\0';
-
-       // Create a working copy of the string we are expanding
-       char* buffer = pakfire_strdup(declaration->value);
-
-       const size_t max_groups = 2;
-       regmatch_t groups[max_groups];
-
-       // Search for any variables
-       while (1) {
-               // Perform matching
-               r = regexec(&preg, buffer, max_groups, groups, 0);
-
-               // End loop when we have expanded all variables
-               if (r == REG_NOMATCH) {
-                       DEBUG(pakfire, "No (more) matches found in: %s\n", buffer);
-                       break;
-               }
-
-               // Set offsets to the matched variable name
-               off_t start = groups[1].rm_so, end = groups[1].rm_eo;
-
-               // Get the name of the variable
-               char* variable = pakfire_malloc(end - start + 1);
-               snprintf(variable, end - start + 1, "%s", buffer + start);
-
-               DEBUG(pakfire, "Expanding variable: %s\n", variable);
-
-               // Search for a declaration of this variable
-               struct pakfire_parser_declaration* v =
-                       pakfire_parser_get_declaration_in_namespace(pakfire, declarations, namespace, variable);
-
-               DEBUG(pakfire, "v = %p\n", v);
-
-               const char* value = NULL;
-               if (v && v->value) {
-                       DEBUG(pakfire, "Replacing %%{%s} with %s = '%s'\n",
-                               variable, v->name, value);
-                       value = v->value;
-               } else {
-                       DEBUG(pakfire, "Replacing %%{%s} with an empty string\n", variable);
-               }
-
-               // Reset offsets to the whole matched string
-               start = groups[0].rm_so; end = groups[0].rm_eo;
-
-               // Length of the new buffer
-               size_t length = strlen(buffer) - (end - start) + ((value) ? strlen(value) : 0);
-
-               char* b = pakfire_malloc(length + 1);
-
-               // Copy buffer up to the beginning of the match
-               snprintf(b, start + 1, "%s", buffer);
-
-               // Append the new value (if any)
-               if (value)
-                       strcat(b, value);
-
-               // Append the rest of the buffer
-               if (buffer + end)
-                       strcat(b, buffer + end);
-
-               DEBUG(pakfire, "New buffer: %s\n", b);
-
-               // Drop old buffer
-               pakfire_free(buffer);
-               buffer = b;
-       }
-
-       regfree(&preg);
-
-       return buffer;
-}
-
-static int pakfire_parser_add_declaration(Pakfire pakfire,
-               struct pakfire_parser_declaration** declarations, 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 & value
-       d->name = pakfire_parser_make_canonical_name(name);
-       d->value = pakfire_strdup(value);
-
-       DEBUG(pakfire, "New declaration: %s = %s\n", d->name, d->value);
+int pakfire_parser_parse_data(PakfireParser parser, const char* data, size_t len) {
+       Pakfire pakfire = pakfire_parser_get_pakfire(parser);
 
-       return 0;
-}
-
-static int pakfire_parser_append_declaration(Pakfire pakfire,
-               struct pakfire_parser_declaration** declarations, const char* name, const char* value) {
-       struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(pakfire, declarations, name);
-
-       // Add the declaration if we could not find it
-       if (!d)
-               return pakfire_parser_add_declaration(pakfire, declarations, name, value);
-
-       char* buffer = NULL;
-
-       // Concat value
-       int r = asprintf(&buffer, "%s %s", d->value, value);
-       if (r < 0)
-               return r;
-
-       DEBUG(pakfire, "Appended declaration: %s = %s (was: %s)\n", d->name, buffer, d->value);
-
-       // Replace value in declaration
-       if (d->value)
-               pakfire_free(d->value);
-
-       d->value = buffer;
-
-       return 0;
-}
-
-struct pakfire_parser_declaration** pakfire_parser_parse_metadata(Pakfire pakfire, const char* data, size_t len) {
        DEBUG(pakfire, "Parsing the following data:\n%s\n", data);
 
        num_lines = 1;
 
-       // Reserve some space for parsed declarations
-       struct pakfire_parser_declaration** declarations = \
-               pakfire_calloc(NUM_DECLARATIONS, sizeof(*declarations));
-
        YY_BUFFER_STATE buffer = yy_scan_bytes(data, len);
-       int r = yyparse(pakfire, declarations);
+       int r = yyparse(parser);
        yy_delete_buffer(buffer);
 
-       // Cleanup declarations in case of an error
-       if (r) {
-               for (unsigned int i = 0; i < NUM_DECLARATIONS; i++) {
-                       if (declarations[i])
-                               pakfire_free(declarations[i]);
-               }
-
-               pakfire_free(declarations);
-
-               // Return nothing
-               return NULL;
-       }
+       pakfire_unref(pakfire);
 
-       return declarations;
+       return r;
 }
 
-void yyerror(Pakfire pakfire, struct pakfire_parser_declaration** declarations, const char* s) {
-       ERROR(pakfire, "Error (line %d): %s\n", num_lines, s);
-}
+void yyerror(PakfireParser parser, const char* s) {
+       Pakfire pakfire = pakfire_parser_get_pakfire(parser);
 
-char* pakfire_parser_get(Pakfire pakfire,
-               struct pakfire_parser_declaration** declarations, const char* name) {
-       struct pakfire_parser_declaration* declaration = pakfire_parser_get_declaration(pakfire, declarations, name);
-
-       // Return NULL when nothing was found
-       if (!declaration)
-               return NULL;
+       ERROR(pakfire, "Error (line %d): %s\n", num_lines, s);
 
-       // Otherwise return the expanded value
-       return pakfire_parser_expand_declaration(pakfire, declarations, declaration);
+       pakfire_unref(pakfire);
 }
index 403f4bca6d4aba8dc6eefc42d461277d2669fe13..a6511d1e7635c4c3803bf5a42b7837fd7f69eb80 100644 (file)
 #                                                                             #
 #############################################################################*/
 
+#include <regex.h>
+#include <string.h>
+
 #include <pakfire/errno.h>
+#include <pakfire/logging.h>
 #include <pakfire/parser.h>
+#include <pakfire/pakfire.h>
+#include <pakfire/private.h>
 #include <pakfire/util.h>
 
-struct pakfire_parser_declaration** pakfire_parser_parse_metadata_from_file(
-               Pakfire pakfire, FILE* f) {
-       char* data;
-       size_t len;
+#define NUM_DECLARATIONS 1024
+#define VARIABLE_PATTERN "%\\{([A-Za-z0-9_\\-]+)\\}"
 
-       int r = pakfire_read_file_into_buffer(f, &data, &len);
+struct _PakfireParser {
+       Pakfire pakfire;
+       int nrefs;
+
+       struct pakfire_parser_declaration** declarations;
+       unsigned int next_declaration;
+       unsigned int num_declarations;
+};
+
+PAKFIRE_EXPORT PakfireParser pakfire_parser_create(Pakfire pakfire) {
+       PakfireParser parser = pakfire_calloc(1, sizeof(*parser));
+       if (parser) {
+               parser->pakfire = pakfire_ref(pakfire);
+
+               parser->num_declarations = NUM_DECLARATIONS;
+
+               // Allocate a decent number of declarations
+               parser->declarations = pakfire_calloc(
+                       parser->num_declarations, sizeof(*parser->declarations));
+
+               parser->next_declaration = 0;
+       }
+
+       return parser;
+}
+
+Pakfire pakfire_parser_get_pakfire(PakfireParser parser) {
+       return pakfire_ref(parser->pakfire);
+}
+
+static void pakfire_parser_free_declarations(
+               struct pakfire_parser_declaration** declarations, unsigned int num) {
+       for (unsigned int i = 0; i < num; i++) {
+               if (declarations[i])
+                       pakfire_free(declarations[i]);
+       }
+
+       pakfire_free(declarations);
+}
+
+static void pakfire_parser_free(PakfireParser parser) {
+       DEBUG(parser->pakfire, "Releasing parser at %p\n", parser);
+
+       pakfire_parser_free_declarations(parser->declarations, parser->num_declarations);
+
+       pakfire_unref(parser->pakfire);
+       pakfire_free(parser);
+}
+
+PAKFIRE_EXPORT PakfireParser pakfire_parser_unref(PakfireParser parser) {
+       if (!parser)
+               return NULL;
+
+       if (--parser->nrefs > 0)
+               return parser;
+
+       pakfire_parser_free(parser);
+       return NULL;
+}
+
+static struct pakfire_parser_declaration* pakfire_parser_get_declaration(
+               PakfireParser parser, const char* name) {
+       struct pakfire_parser_declaration* d;
+
+       for (unsigned i = 0; i < parser->num_declarations; i++) {
+               d = parser->declarations[i];
+               if (!d)
+                       break;
+
+               // Compare the name
+               if (strcmp(d->name, name) == 0)
+                       return d;
+       }
+
+       return NULL;
+}
+
+PAKFIRE_EXPORT int pakfire_parser_add_declaration(PakfireParser parser,
+               const char* name, const char* value) {
+       // Check if we have any space left
+       if (parser->next_declaration >= parser->num_declarations) {
+               ERROR(parser->pakfire, "No free declarations left\n");
+               return -1;
+       }
+
+       // XXX handle when name already exists
+
+       // Allocate a new declaration
+       struct pakfire_parser_declaration* d = pakfire_calloc(1, sizeof(*d));
+       if (!d)
+               return -1;
+
+       // Import name & value
+       d->name = pakfire_strdup(name);
+       d->value = pakfire_strdup(value);
+
+       DEBUG(parser->pakfire, "New declaration: %s = %s\n", d->name, d->value);
+
+       // Assign new declaration to array
+       parser->declarations[parser->next_declaration++] = d;
+
+       return 0;
+}
+
+PAKFIRE_EXPORT int pakfire_parser_append_declaration(PakfireParser parser,
+               const char* name, const char* value) {
+       struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, name);
+
+       // Add the declaration if we could not find it
+       if (!d)
+               return pakfire_parser_add_declaration(parser, name, value);
+
+       char* buffer = NULL;
+
+       // Concat value
+       int r = asprintf(&buffer, "%s %s", d->value, value);
+       if (r < 0)
+               return r;
+
+       DEBUG(parser->pakfire, "Appended declaration: %s = %s (was: %s)\n",
+               d->name, buffer, d->value);
+
+       // Replace value in declaration
+       if (d->value)
+               pakfire_free(d->value);
+
+       d->value = buffer;
+
+       return 0;
+}
+
+static struct pakfire_parser_declaration* pakfire_parser_get_declaration_in_namespace(
+               PakfireParser parser, const char* namespace, const char* name) {
+       if (!namespace || !*namespace)
+               return pakfire_parser_get_declaration(parser, name);
+
+       char* n = pakfire_strdup(namespace);
+
+       size_t length = strlen(n) + strlen(name) + 1;
+       char* buffer = pakfire_malloc(length + 1);
+
+       struct pakfire_parser_declaration* d = NULL;
+
+       while (1) {
+               if (n)
+                       snprintf(buffer, length + 1, "%s.%s", n, name);
+               else
+                       snprintf(buffer, length + 1, "%s", name);
+
+               DEBUG(parser->pakfire, "Looking up %s\n", buffer);
+
+               // Lookup declaration
+               d = pakfire_parser_get_declaration(parser, buffer);
+
+               // End if we have found a match
+               if (d)
+                       break;
+
+               // End if we have hit the root namespace
+               if (!n)
+                       break;
+
+               /*
+                       If we did not find a match, we will remove one level of the
+                       namespace and try again...
+               */
+               char* p = strrchr(n, '.');
+               if (p) {
+                       n[p - n] = '\0';
+               } else {
+                       pakfire_free(n);
+                       n = NULL;
+               }
+       }
+
+       if (n)
+               pakfire_free(n);
+       pakfire_free(buffer);
+
+       return d;
+}
+
+static char* pakfire_parser_expand_declaration(PakfireParser parser,
+               const struct pakfire_parser_declaration* declaration) {
+       // Return NULL when the value of the declaration is NULL
+       if (!declaration || !declaration->value)
+               return NULL;
+
+       // Compile the regular expression
+       regex_t preg;
+       int r = regcomp(&preg, VARIABLE_PATTERN, REG_EXTENDED);
        if (r) {
-               pakfire_errno = r;
+               char error[1024];
+               regerror(r, &preg, error, sizeof(error));
+
+               ERROR(parser->pakfire, "Could not compile regular expression (%s): %s",
+                       VARIABLE_PATTERN, error);
+
                return NULL;
        }
 
-       struct pakfire_parser_declaration** declarations = \
-               pakfire_parser_parse_metadata(pakfire, data, len);
+       char* namespace = pakfire_strdup(declaration->name);
+       char* p = strrchr(namespace, '.');
+       if (p)
+               namespace[p - namespace] = '\0';
+       else
+               namespace[0] = '\0';
+
+       // Create a working copy of the string we are expanding
+       char* buffer = pakfire_strdup(declaration->value);
+
+       const size_t max_groups = 2;
+       regmatch_t groups[max_groups];
+
+       // Search for any variables
+       while (1) {
+               // Perform matching
+               r = regexec(&preg, buffer, max_groups, groups, 0);
+
+               // End loop when we have expanded all variables
+               if (r == REG_NOMATCH) {
+                       DEBUG(parser->pakfire, "No (more) matches found in: %s\n", buffer);
+                       break;
+               }
+
+               // Set offsets to the matched variable name
+               off_t start = groups[1].rm_so, end = groups[1].rm_eo;
+
+               // Get the name of the variable
+               char* variable = pakfire_malloc(end - start + 1);
+               snprintf(variable, end - start + 1, "%s", buffer + start);
+
+               DEBUG(parser->pakfire, "Expanding variable: %s\n", variable);
+
+               // Search for a declaration of this variable
+               struct pakfire_parser_declaration* v =
+                       pakfire_parser_get_declaration_in_namespace(parser, namespace, variable);
+
+               DEBUG(parser->pakfire, "v = %p\n", v);
+
+               const char* value = NULL;
+               if (v && v->value) {
+                       DEBUG(parser->pakfire, "Replacing %%{%s} with %s = '%s'\n",
+                               variable, v->name, value);
+                       value = v->value;
+               } else {
+                       DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n", variable);
+               }
+
+               // Reset offsets to the whole matched string
+               start = groups[0].rm_so; end = groups[0].rm_eo;
+
+               // Length of the new buffer
+               size_t length = strlen(buffer) - (end - start) + ((value) ? strlen(value) : 0);
+
+               char* b = pakfire_malloc(length + 1);
+
+               // Copy buffer up to the beginning of the match
+               snprintf(b, start + 1, "%s", buffer);
+
+               // Append the new value (if any)
+               if (value)
+                       strcat(b, value);
+
+               // Append the rest of the buffer
+               if (buffer + end)
+                       strcat(b, buffer + end);
+
+               DEBUG(parser->pakfire, "New buffer: %s\n", b);
+
+               // Drop old buffer
+               pakfire_free(buffer);
+               buffer = b;
+       }
+
+       regfree(&preg);
+
+       return buffer;
+}
+
+PAKFIRE_EXPORT char* pakfire_parser_get(PakfireParser parser, const char* name) {
+       struct pakfire_parser_declaration* d = pakfire_parser_get_declaration(parser, name);
+
+       // Return NULL when nothing was found
+       if (!d)
+               return NULL;
+
+       // Otherwise return the expanded value
+       return pakfire_parser_expand_declaration(parser, d);
+}
+
+PAKFIRE_EXPORT int pakfire_parser_read(PakfireParser parser, FILE* f) {
+       char* data;
+       size_t len;
+
+       int r = pakfire_read_file_into_buffer(f, &data, &len);
+       if (r)
+               return r;
+
+       r = pakfire_parser_parse_data(parser, data, len);
 
        if (data)
                pakfire_free(data);
 
-       return declarations;
+       return r;
 }
index b967ff681eb7d2e2e1e20b688be795dad4040ca2..28dceb6e62ffad5d2936c0f12c6618f9f0d4bf53 100644 (file)
@@ -32,14 +32,13 @@ int test_parse(const test_t* t) {
        FILE* f = fopen(path, "r");
        assert_return(f, EXIT_FAILURE);
 
-       struct pakfire_parser_declaration** declarations = \
-               pakfire_parser_parse_metadata_from_file(t->pakfire, f);
+       PakfireParser parser = pakfire_parser_create(t->pakfire);
 
-       // Check if we have found some declarations
-       assert_return(declarations, EXIT_FAILURE);
+       int r = pakfire_parser_read(parser, f);
+       assert_return(r == 0, EXIT_FAILURE);
 
        // Try to retrieve some value
-       char* value = pakfire_parser_get(t->pakfire, declarations, "sources");
+       char* value = pakfire_parser_get(parser, "sources");
        assert_return(value, EXIT_FAILURE);
 
        printf("VALUE: sources = %s\n", value);
@@ -48,6 +47,8 @@ int test_parse(const test_t* t) {
        // Cleanup
        pakfire_free(path);
 
+       pakfire_parser_unref(parser);
+
        return EXIT_SUCCESS;
 }