%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
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;
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);
+}