]> git.ipfire.org Git - pakfire.git/commitdiff
parser: Attempt to implement expanding variables
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 May 2019 09:11:02 +0000 (10:11 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 May 2019 09:11:02 +0000 (10:11 +0100)
This seems to work (kind of), but there are so many bugs in the other
code that it is hard to tell.

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

index 9fb6509b7a47d4dec42c7a43aa689db345b7e0e3..5539f0a57c5410909afd9ed0dda27d13cab44a3a 100644 (file)
@@ -119,6 +119,7 @@ AC_CHECK_HEADERS([ \
        assert.h \
        ctypes.h \
        math.h \
+       regex.h \
        stdarg.h \
        stdlib.h \
        string.h \
index 1d18ae69c385c7ed121ed3de714435b8420006c0..51d8da63c12ae7dd370f6205ca7190ae8f05b254 100644 (file)
@@ -39,6 +39,9 @@ struct pakfire_parser_declaration** pakfire_parser_parse_metadata(Pakfire pakfir
 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);
+
 #endif /* PAKFIRE_PRIVATE */
 
 #endif /* PAKFIRE_PARSER_H */
index b38cb6153eb367a5d490b4df479c1192b9016512..6f86f242991aa2c9259403e3aa664d7ddb234641 100644 (file)
@@ -210,6 +210,7 @@ global:
        pakfire_packagelist_unref;
 
        # parser
+       pakfire_parser_get;
        pakfire_parser_parse_metadata;
        pakfire_parser_parse_metadata_from_file;
 
index 0b00e4140d9f67cdbad86b6d5e3d16691ba7d43e..7743f4c855322f336fde674a1a476429c5646e00 100644 (file)
 %error-verbose
 
 %{
+#include <regex.h>
 #include <stdio.h>
 
+#include <pakfire/constants.h>
 #include <pakfire/logging.h>
 #include <pakfire/parser.h>
 #include <pakfire/types.h>
 #include <pakfire/util.h>
 
+#define VARIABLE_PATTERN "%\\{([A-Za-z0-9_\\-]+)\\}"
+
 #define YYERROR_VERBOSE 1
 
 #define YYDEBUG 1
@@ -216,28 +220,183 @@ 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;
 
-       char* canonical_name = pakfire_parser_make_canonical_name(name);
-
        struct pakfire_parser_declaration* d = *declarations;
        while (d) {
-               if (strcmp(d->name, canonical_name) == 0) {
-                       goto END;
-               }
+               if (strcmp(d->name, name) == 0)
+                       return d;
 
                d++;
        }
 
-END:
-       pakfire_free(canonical_name);
+       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;
@@ -323,3 +482,15 @@ struct pakfire_parser_declaration** pakfire_parser_parse_metadata(Pakfire pakfir
 void yyerror(Pakfire pakfire, struct pakfire_parser_declaration** declarations, const char* s) {
        ERROR(pakfire, "Error (line %d): %s\n", num_lines, s);
 }
+
+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;
+
+       // Otherwise return the expanded value
+       return pakfire_parser_expand_declaration(pakfire, declarations, declaration);
+}
index 55e24ef5beeb21f0b0b22230999d95e7db402dba..b967ff681eb7d2e2e1e20b688be795dad4040ca2 100644 (file)
@@ -38,6 +38,13 @@ int test_parse(const test_t* t) {
        // Check if we have found some declarations
        assert_return(declarations, EXIT_FAILURE);
 
+       // Try to retrieve some value
+       char* value = pakfire_parser_get(t->pakfire, declarations, "sources");
+       assert_return(value, EXIT_FAILURE);
+
+       printf("VALUE: sources = %s\n", value);
+       pakfire_free(value);
+
        // Cleanup
        pakfire_free(path);