#define NSEC_PER_SEC 1000000000
+static const struct pakfire_rich_operation {
+ const char* keyword;
+ const int flags;
+} pakfire_rich_operations[] = {
+ { "and ", REL_AND },
+ { "or ", REL_OR },
+ { "if ", REL_COND },
+ { "unless ", REL_UNLESS },
+ { "else ", REL_ELSE },
+ { "with ", REL_WITH },
+ { "without ", REL_WITHOUT },
+ { NULL, 0 },
+};
+
const char* pakfire_dep2str(struct pakfire* pakfire, Id id) {
Pool* pool = pakfire_get_solv_pool(pakfire);
if (!pool)
return pool_dep2str(pool, id);
}
+/*
+ Reads the string until the next space is found.
+
+ This function considers opening and closing brackets.
+*/
+static size_t skip(const char** s, const char** n) {
+ const char* p = *s;
+
+ // Store p in n
+ *n = p;
+
+ int brackets = 0;
+
+ while (*p) {
+ switch (*p) {
+ // End on space or comma
+ case ' ':
+ case ',':
+ goto END;
+
+ // Increment counter on opening bracket
+ case '(':
+ brackets++;
+ break;
+
+ // End on the last closing bracket
+ case ')':
+ if (brackets-- <= 0)
+ goto END;
+ break;
+ }
+
+ // Move to next character
+ p++;
+ }
+
+END:
+ *s = p;
+
+ // Return the length of the skipped string
+ return p - *n;
+}
+
+/*
+ This function parses any namespaced dependencies
+*/
static Id pakfire_parse_namespace(Pool* pool, const char* s) {
const char* p = strchr(s, '(');
if (!p)
return pool_rel2id(pool, namespace, id, REL_NAMESPACE, 1);
}
-Id pakfire_str2dep(struct pakfire* pakfire, const char* s) {
- Id id;
+/*
+ This function parses a simple dependency like "foo = 1.2.3"
+*/
+static Id pakfire_parse_dep(struct pakfire* pakfire, const char** s) {
+ Id id = ID_NULL;
if (!s) {
errno = EINVAL;
return 0;
}
+ const char* p = *s;
+ const char* n = NULL;
+
// Ignore empty strings
- if (!*s)
- return 0;
+ if (!p)
+ return ID_NULL;
Pool* pool = pakfire_get_solv_pool(pakfire);
// Consume any leading space
- if (isspace(*s))
- s++;
-
- const char* p = s;
-
- // Find the first part (before =, >= or <=)
- while (*p && !isspace(*p) && *p != '<' && *p != '=' && *p != '>')
+ if (isspace(*p))
p++;
- // The length of the first part
- size_t l = p - s;
+ // Find the first part
+ size_t l = skip(&p, &n);
// Add name to pool
- if (pakfire_string_startswith(s, "pakfire("))
- id = pakfire_parse_namespace(pool, s);
+ if (pakfire_string_startswith(n, "pakfire("))
+ id = pakfire_parse_namespace(pool, n);
else
- id = pool_strn2id(pool, s, l, 1);
+ id = pool_strn2id(pool, n, l, 1);
// Consume any more space
if (isspace(*p))
if (isspace(*p))
p++;
+ // Find the length of EVR
+ l = skip(&p, &n);
+
+ // Strip zero epoch
+ if (pakfire_string_startswith(n, "0:"))
+ n += 2;
+
// Add EVR to pool
- Id evr = pool_str2id(pool, p, 1);
+ Id evr = pool_strn2id(pool, n, l, 1);
// Combine everything
id = pool_rel2id(pool, id, evr, flags, 1);
}
+ *s = p;
+
+ return id;
+}
+
+/*
+ This function parses any rich dependencies
+*/
+static Id pakfire_parse_rich_dep(struct pakfire* pakfire, const char** dep, int flags) {
+ const char* p = *dep;
+ Id id;
+
+ // Do not try parsing empty strings
+ if (!*p)
+ return ID_NULL;
+
+ // Must be starting with an opening bracket
+ if (!flags && *p++ != '(')
+ return ID_NULL;
+
+ switch (*p) {
+ // A new rich dependency
+ case '(':
+ id = pakfire_parse_rich_dep(pakfire, &p, 0);
+ if (!id)
+ return id;
+ break;
+
+ // End
+ case ')':
+ return ID_NULL;
+
+ // Parse a regular dependency
+ default:
+ id = pakfire_parse_dep(pakfire, &p);
+ if (!id)
+ return id;
+ break;
+ }
+
+ // Consume any space
+ if (isspace(*p))
+ p++;
+
+ // If we have successfully parsed something, we would expect a closing bracket
+ if (*p == ')') {
+ *dep = p + 1;
+ return id;
+ }
+
+ // Search for a keyword
+ for (const struct pakfire_rich_operation* op = pakfire_rich_operations; op->keyword; op++) {
+ if (pakfire_string_startswith(p, op->keyword)) {
+ // Skip the keyword
+ p += strlen(op->keyword);
+
+ // Store the flags
+ flags = op->flags;
+ break;
+ }
+ }
+
+ // If there was no keyword, we are done
+ if (!flags)
+ return ID_NULL;
+
+ // Parse the next bit
+ Id evr = pakfire_parse_rich_dep(pakfire, &p, flags);
+
+ // Abort if there was a problem
+ if (!evr)
+ return ID_NULL;
+
+ // Store until where we have parsed the string
+ *dep = p;
+
+ Pool* pool = pakfire_get_solv_pool(pakfire);
+
+ // Combine everything
+ return pool_rel2id(pool, id, evr, flags, 1);
+}
+
+Id pakfire_str2dep(struct pakfire* pakfire, const char* s) {
+ Id id = ID_NULL;
+
+ // Invalid input
+ if (!s) {
+ errno = EINVAL;
+ return id;
+ }
+
+ // Ignore empty strings
+ if (!*s)
+ return id;
+
+ // Consume any leading space
+ if (isspace(*s))
+ s++;
+
+ // Parse any rich dependencies
+ if (*s == '(')
+ id = pakfire_parse_rich_dep(pakfire, &s, 0);
+ else
+ id = pakfire_parse_dep(pakfire, &s);
+
+ // Return nothing if we could not parse the entire string
+ if (id && *s)
+ id = ID_NULL;
+
return id;
}
// Namespaces
"pakfire(test)",
+ // Rich dependencies
+ "(foo >= 1.0 with foo < 2.0)",
+ "(kernel with flavor = desktop)",
+ "(beep or bash or kernel)",
+
+ // Nested rich dependencies
+ "(beep or (bash and kernel))",
+ "(beep or (bash and kernel) or foo)",
+
+ NULL,
+};
+
+static const char* invalid_relations[] = {
+ "beep something-else",
+ "beep >= 1 something else",
+
+ // Broken rich deps
+ "()",
+ "(a ",
+ "(a unless)",
+ "(a unless )",
+
+ // Broken nested rich deps
+ "(a and (b or c)",
+
NULL,
};
ASSERT_STRING_EQUALS(*relation, result);
}
+ // Check invalid inputs
+ for (const char** relation = invalid_relations; *relation; relation++) {
+ printf("Parsing '%s'...\n", *relation);
+
+ // Parse relation
+ dep = pakfire_str2dep(t->pakfire, *relation);
+
+ // The return value must be ID_NULL
+ ASSERT(dep == ID_NULL);
+ }
+
return EXIT_SUCCESS;
FAIL: