From: Michael Tremer Date: Wed, 22 May 2019 09:11:02 +0000 (+0100) Subject: parser: Attempt to implement expanding variables X-Git-Tag: 0.9.28~1285^2~1032 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ba52fbe1c05e2f2ebe535131d0d8dca826b456f;p=pakfire.git parser: Attempt to implement expanding variables 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 --- diff --git a/configure.ac b/configure.ac index 9fb6509b7..5539f0a57 100644 --- a/configure.ac +++ b/configure.ac @@ -119,6 +119,7 @@ AC_CHECK_HEADERS([ \ assert.h \ ctypes.h \ math.h \ + regex.h \ stdarg.h \ stdlib.h \ string.h \ diff --git a/src/libpakfire/include/pakfire/parser.h b/src/libpakfire/include/pakfire/parser.h index 1d18ae69c..51d8da63c 100644 --- a/src/libpakfire/include/pakfire/parser.h +++ b/src/libpakfire/include/pakfire/parser.h @@ -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 */ diff --git a/src/libpakfire/libpakfire.sym b/src/libpakfire/libpakfire.sym index b38cb6153..6f86f2429 100644 --- a/src/libpakfire/libpakfire.sym +++ b/src/libpakfire/libpakfire.sym @@ -210,6 +210,7 @@ global: pakfire_packagelist_unref; # parser + pakfire_parser_get; pakfire_parser_parse_metadata; pakfire_parser_parse_metadata_from_file; diff --git a/src/libpakfire/parser/grammar.y b/src/libpakfire/parser/grammar.y index 0b00e4140..7743f4c85 100644 --- a/src/libpakfire/parser/grammar.y +++ b/src/libpakfire/parser/grammar.y @@ -26,13 +26,17 @@ %error-verbose %{ +#include #include +#include #include #include #include #include +#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); +} diff --git a/tests/libpakfire/makefile.c b/tests/libpakfire/makefile.c index 55e24ef5b..b967ff681 100644 --- a/tests/libpakfire/makefile.c +++ b/tests/libpakfire/makefile.c @@ -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);