commit: 7409da07febb22f9a96b9fe7a76bcd197fcb8878
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Thu Jan 29 15:17:18 2026 +0000
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Thu Jan 29 15:17:18 2026 +0000
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=7409da07
libq/dep: rewrite using 2026 codestyle
- hide inner details of structs so they cannot be tampered with
- fix the dep parser (grow tree) to correctly pick up grouped entries
- support lax mode (where spacing is not mandatory around tokens)
- retain || (ANY) structure, so a resolver can try to satisfy any one
(which may be a USE or ALL (group) that need to be considered)
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>
libq/dep.c | 888 +++++++++++++++++++++++++++++++-------------------
libq/dep.h | 47 +--
main.h | 2 +-
qdepends.c | 6 +-
tests/qdepends/dotest | 16 +
5 files changed, 574 insertions(+), 385 deletions(-)
diff --git a/libq/dep.c b/libq/dep.c
index 1288a531..fd93098a 100644
--- a/libq/dep.c
+++ b/libq/dep.c
@@ -14,7 +14,6 @@
#include <stdlib.h>
#include <string.h>
#include <xalloc.h>
-#include <assert.h>
#include <ctype.h>
#include "array.h"
@@ -22,371 +21,574 @@
#include "dep.h"
#include "set.h"
#include "tree.h"
-#include "xasprintf.h"
-static const dep_node null_node = {
- .type = DEP_NULL,
+#define DEP_TYPES(X) \
+ X(NULL) \
+ X(ATOM) \
+ X(USE) \
+ X(ANY) \
+ X(ALL) \
+ X(NOT) \
+ X(HUH) \
+ X(POPEN) \
+ X(PCLOSE) \
+ X(WORD)
+
+#define DEP_TYPE_ENUM(E) DEP_##E,
+#define DEP_TYPE_NAME(E) #E,
+
+typedef enum dep_type_ {
+ DEP_TYPES(DEP_TYPE_ENUM)
+ DEP_MAX_TYPES
+} dep_type_t;
+
+static const char * const dep_type_names[] = {
+ DEP_TYPES(DEP_TYPE_NAME)
+ NULL
};
-static void _dep_attach(dep_node *root, dep_node *attach_me, int type);
-static void _dep_burn_node(dep_node *node);
+struct dep_node_ {
+ const char *word;
+ size_t wordlen;
+ depend_atom *atom;
+ dep_node_t *parent;
+ array *members;
+ dep_type_t type;
+ bool invert:1;
+ bool atom_resolved:1;
+};
-static dep_node *
-_dep_grow_node(dep_type type, const char *info, size_t info_len)
+static void dep_burn_node
+(
+ dep_node_t *node
+)
{
- dep_node *ret;
- size_t len;
-
- if (type == DEP_OR || type == DEP_GROUP)
- info = NULL;
-
- len = sizeof(*ret);
- if (info) {
- if (!info_len)
- info_len = strlen(info);
- len += info_len + 1;
- }
- ret = xzalloc(len);
-
- ret->type = type;
- if (info) {
- ret->info = ((char*)ret) + sizeof(*ret);
- memcpy(ret->info, info, info_len);
- if (type == DEP_NORM)
- ret->atom = atom_explode(ret->info);
- }
-
- return ret;
-}
+ if (node == NULL)
+ return;
-static void
-_dep_burn_node(dep_node *node)
-{
- assert(node);
- if (node->atom)
- atom_implode(node->atom);
- free(node);
+ if (node->atom)
+ atom_implode(node->atom);
+ free(node);
}
-enum {
- _DEP_NEIGH = 1,
- _DEP_CHILD = 2
-};
-
-static void
-_dep_attach(dep_node *root, dep_node *attach_me, int type)
+dep_node_t *dep_grow_tree
+(
+ const char *depend
+)
{
- if (type == _DEP_NEIGH) {
- if (!root->neighbor) {
- root->neighbor = attach_me;
- attach_me->parent = root->parent;
- } else
- _dep_attach(root->neighbor, attach_me, _DEP_NEIGH);
- } else {
- if (!root->children) {
- root->children = attach_me;
- attach_me->parent = root;
- } else
- _dep_attach(root->children, attach_me, _DEP_NEIGH);
- }
+ char buf[_Q_PATH_MAX];
+ dep_node_t *ret = NULL;
+ dep_node_t *curr_node;
+ dep_node_t *next_node;
+ dep_node_t *final_node;
+ array *tokens = array_new();
+ array *res = array_new();
+ const char *ptr = NULL;
+ const char *word;
+ size_t n;
+ int level = 0;
+ int nots = 0;
+
+ /* the language is mostly token oriented, and officially whitespace is
+ * required around tokens, but we try to parse very liberal,
+ * because there's no real ambiguity
+ * constructs: (from PMS)
+ * - [!]use? ( ... ) DEP_USE (use-conditional group)
+ * - || ( ... ) DEP_ANY (any-of group)
+ * - ( ... ) DEP_ALL (all-of group)
+ * - foo-bar/fnord-x.y DEP_ATOM (package dependency specification)
+ * we first tokenise the input, then try to parse it */
+
+#define dep_push_word(W,L) \
+ do { \
+ dep_node_t *new_node = xzalloc(sizeof(*new_node)); \
+ new_node->type = DEP_WORD; \
+ new_node->word = W; \
+ new_node->wordlen = L; \
+ array_append(tokens, new_node); \
+ } while (0)
+#define dep_push(T) \
+ do { \
+ dep_node_t *new_node = xzalloc(sizeof(*new_node)); \
+ new_node->type = DEP_##T; \
+ array_append(tokens, new_node); \
+ } while (0)
+
+ for (ptr = word = depend; *ptr != '\0'; ptr++)
+ {
+ char p = *ptr;
+ if (isspace((int)p))
+ p = ' '; /* simplify switch below */
+
+ switch (p)
+ {
+ case ' ':
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+ break;
+ case '!':
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+ dep_push(NOT);
+ break;
+ case '|':
+ if (ptr[1] != '|')
+ {
+ warn("Found a |, did you mean ||? (in %s)", depend);
+ goto dep_grow_tree_fail;
+ }
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+ ptr++;
+ dep_push(ANY);
+ break;
+ case '?':
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+ dep_push(HUH);
+ break;
+ case '(':
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+ dep_push(POPEN);
+ break;
+ case ')':
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+ dep_push(PCLOSE);
+ break;
+ case '[':
+ /* these are atom's USE-conditionals which may include ( ) ? !
+ * too, so just scan until matching ], luckily these can't be
+ * nested */
+ for (ptr++; *ptr != '\0' && *ptr != ']'; ptr++)
+ ;
+ ptr--; /* rewind for outer for-loop */
+ continue;
+ default:
+ /* count as word */
+ continue;
+ }
+
+ word = ptr + 1;
+ }
+ /* push final word, if there is one (like a single atom as depstring) */
+ if (word != ptr)
+ dep_push_word(word, ptr - word);
+#undef dep_push_word
+#undef dep_push
+
+#ifdef EBUG
+ array_for_each(tokens, n, curr_node)
+ {
+ warnf("token %s %.*s",
+ dep_type_names[curr_node->type],
+ (int)curr_node->wordlen,
+ curr_node->word != NULL ? curr_node->word : "");
+ }
+#endif
+
+ /* create top node */
+ ret = xzalloc(sizeof(*ret));
+ ret->type = DEP_ALL;
+ ret->members = array_new();
+ array_append(res, ret); /* == level */
+ ret = NULL;
+
+ array_for_each(tokens, n, curr_node)
+ {
+ DBG("n: %zu, %s, level %d", n, dep_type_names[curr_node->type], level);
+ switch (curr_node->type)
+ {
+ case DEP_POPEN:
+ ret = xzalloc(sizeof(*ret));
+ ret->type = DEP_ALL;
+ ret->members = array_new();
+ ret->parent = array_get(res, level);
+ array_append(ret->parent->members, ret);
+
+ array_append(res, ret);
+ level++;
+ break;
+ case DEP_PCLOSE:
+ if (level > 0)
+ {
+ array_remove(res, level);
+ level--;
+ }
+ else
+ {
+ warn("Found stray ) (in %s)", depend);
+ ret = NULL;
+ goto dep_grow_tree_fail;
+ }
+ break;
+ case DEP_ANY:
+ next_node = array_get(tokens, n + 1);
+ if (next_node == NULL ||
+ next_node->type != DEP_POPEN)
+ {
+ warn("Missing ( after ||, got %s (in %s)",
+ dep_type_names[next_node->type], depend);
+ ret = NULL;
+ goto dep_grow_tree_fail;
+ }
+
+ ret = xzalloc(sizeof(*ret));
+ ret->type = DEP_ANY;
+ ret->members = array_new();
+ ret->parent = array_get(res, level);
+ array_append(ret->parent->members, ret);
+
+ array_append(res, ret);
+ n++; /* skip POPEN */
+ level++;
+ break;
+ case DEP_NOT:
+ nots = 1;
+ next_node = array_get(tokens, n + 1);
+ /* !! is hard blocker for atom syntax */
+ if (next_node != NULL &&
+ next_node->type == DEP_NOT)
+ {
+ nots = 2;
+ n++;
+ next_node = array_get(tokens, n + 1);
+ }
+
+ if (next_node == NULL ||
+ next_node->type != DEP_WORD)
+ {
+ warn("Found dangling ! (in %s)", depend);
+ ret = NULL;
+ goto dep_grow_tree_fail;
+ }
+
+ break;
+ case DEP_WORD:
+ next_node = array_get(tokens, n + 1);
+ if (next_node != NULL &&
+ next_node->type == DEP_HUH)
+ {
+ /* must be USE */
+ if (nots > 1)
+ {
+ warn("Too many !s for use? (in %s)", depend);
+ ret = NULL;
+ goto dep_grow_tree_fail;
+ }
+
+ final_node = array_get(tokens, n + 2);
+ if (final_node == NULL ||
+ final_node->type != DEP_POPEN)
+ {
+ warn("Missing ( after use? (in %s)", depend);
+ ret = NULL;
+ goto dep_grow_tree_fail;
+ }
+ else
+ {
+ ret = xzalloc(sizeof(*ret) + curr_node->wordlen + 1);
+ ret->type = DEP_USE;
+ ret->members = array_new();
+ ret->invert = nots == 1;
+ ret->word = (char *)ret + sizeof(*ret);
+ ret->wordlen = curr_node->wordlen;
+ memcpy((char *)ret + sizeof(*ret),
+ curr_node->word, curr_node->wordlen);
+ ((char *)ret)[sizeof(*ret) + ret->wordlen] = '\0';
+ ret->parent = array_get(res, level);
+ array_append(ret->parent->members, ret);
+
+ array_append(res, ret);
+ n += 2;
+ level++;
+ }
+ }
+ else
+ {
+ /* atom, WORD */
+ snprintf(buf, sizeof(buf), "%s%s%.*s",
+ nots > 0 ? "!" : "",
+ nots > 1 ? "!" : "",
+ (int)curr_node->wordlen, curr_node->word);
+
+ ret = xzalloc(sizeof(*ret));
+ ret->type = DEP_ATOM;
+ ret->atom = atom_explode(buf);
+ ret->parent = array_get(res, level);
+ array_append(ret->parent->members, ret);
+ }
+ nots = 0;
+ break;
+ default:
+ /* ignore/invalid */
+ break;
+ }
+ }
+
+#ifdef EBUG
+ array_for_each(res, n, ret)
+ {
+ warnf("[%zu] token %s %.*s (%zu)",
+ n,
+ dep_type_names[ret->type],
+ (int)ret->wordlen,
+ ret->word != NULL ? ret->word : "",
+ ret->members == NULL ? 0 : array_cnt(ret->members));
+ }
+#endif
+
+ ret = array_get(res, 0); /* pseudo top-level again */
+
+dep_grow_tree_fail:
+ array_deepfree(tokens, (array_free_cb *)dep_burn_node);
+ array_deepfree(res, (array_free_cb *)array_free);
+
+ if (ret != NULL &&
+ array_cnt(ret->members) == 0)
+ ret->type = DEP_NULL;
+
+ return ret;
}
-dep_node *
-dep_grow_tree(const char *depend)
+void dep_print_tree
+(
+ FILE *fp,
+ const dep_node_t *root,
+ size_t space,
+ array *hlatoms,
+ const char *hlcolor,
+ int verbose
+)
{
- bool saw_whitespace;
- signed long paren_balanced;
- const char *ptr, *word;
- int curr_attach;
- dep_node *ret, *curr_node, *new_node;
- dep_type prev_type;
-
- ret = curr_node = new_node = NULL;
- prev_type = DEP_NULL;
- paren_balanced = 0;
- curr_attach = _DEP_NEIGH;
- word = NULL;
-
-#define _maybe_consume_word(t) \
- do { \
- if (word == NULL) \
- break; \
- new_node = _dep_grow_node(t, word, ptr-word); \
- if (!ret) \
- ret = curr_node = new_node; \
- else { \
- _dep_attach(curr_node, new_node, curr_attach); \
- curr_attach = _DEP_NEIGH; \
- curr_node = new_node; \
- } \
- prev_type = t; \
- word = NULL; \
- } while (0)
-
- saw_whitespace = true;
- for (ptr = depend; *ptr != '\0'; ptr++) {
- if (isspace((int)*ptr)) {
- saw_whitespace = true;
- _maybe_consume_word(DEP_NORM);
- continue;
- }
-
- switch (*ptr) {
- case '?': {
- if (word == NULL) {
- warnf("Found a ? but no USE flag");
- goto error_out;
- }
- _maybe_consume_word(DEP_USE);
- curr_attach = _DEP_CHILD;
- continue;
- }
- case '|': {
- if (!saw_whitespace)
- break;
- if (ptr[1] != '|') {
- warnf("Found a | but not ||");
- goto error_out;
- }
- word = ptr++;
- _maybe_consume_word(DEP_OR);
- curr_attach = _DEP_CHILD;
- continue;
- }
- case '(': {
- ++paren_balanced;
- if (!saw_whitespace)
- break;
- if (prev_type == DEP_OR || prev_type == DEP_USE) {
- _maybe_consume_word(DEP_NORM);
- prev_type = DEP_NULL;
- } else {
- if (word) {
- warnf("New group has word in queue");
- goto error_out;
- }
- word = ptr;
- _maybe_consume_word(DEP_GROUP);
- curr_attach = _DEP_CHILD;
- }
- break;
- }
- case ')': {
- --paren_balanced;
- if (!saw_whitespace)
- break;
- _maybe_consume_word(DEP_NORM);
-
- if (curr_node == NULL || curr_node->parent == NULL) {
- warnf("Group lacks a parent");
- goto error_out;
- }
- curr_node = curr_node->parent;
- curr_attach = _DEP_NEIGH;
- break;
- }
- case '[': {
- /* USE-dep, seek to matching ']', since they cannot be
- * nested, this is simple */
- while (*ptr != '\0' && *ptr != ']')
- ptr++;
- break;
- }
- default:
- if (!word)
- word = ptr;
- }
- saw_whitespace = false;
-
- /* fall through to the paren failure below */
- if (paren_balanced < 0)
- break;
- }
-
- if (paren_balanced != 0) {
- warnf("Parenthesis unbalanced");
- goto error_out;
- }
-
- /* if the depend buffer wasnt terminated with a space,
- * we may have a word sitting in the buffer to consume */
- _maybe_consume_word(DEP_NORM);
-
-#undef _maybe_consume_word
-
- return ret ? : xmemdup(&null_node, sizeof(null_node));
-
-error_out:
- warnf("DEPEND: %s", depend);
- if (ret) {
- dep_dump_tree(ret);
- dep_burn_tree(ret);
- }
- return NULL;
+ dep_node_t *memb;
+ size_t s;
+ int indent = 4; /* Gentoo 4-wide indent standard */
+ bool nonewline = false;
+ bool dohl = false;
+
+ if (root == NULL)
+ return;
+
+ if (verbose < 0)
+ {
+ nonewline = true;
+ verbose = -verbose - 1;
+ }
+
+ /* handle pseudo root node */
+ if (root->parent == NULL)
+ {
+ array_for_each(root->members, s, memb)
+ {
+ dep_print_tree(fp, memb, space, hlatoms, hlcolor, verbose);
+ }
+ if (!nonewline &&
+ array_cnt(root->members) > 0)
+ fprintf(fp, "\n");
+ return;
+ }
+
+ if (hlatoms != NULL &&
+ array_cnt(hlatoms) > 0)
+ dohl = true;
+
+ if (verbose > 0)
+ fprintf(fp, "Node [%s]: ", dep_type_names[root->type]);
+
+ if (nonewline)
+ {
+ if (space > 1)
+ fprintf(fp, " ");
+ }
+ else
+ {
+ if (space > 1)
+ fprintf(fp, "\n");
+ for (s = space; s > 0; s--)
+ fprintf(fp, "%*s", indent, "");
+ }
+
+ if (root->type == DEP_ANY)
+ {
+ fprintf(fp, "|| ");
+ }
+ else if (root->type == DEP_USE)
+ {
+ fprintf(fp, "%s? ", root->word);
+ }
+ else if (root->type == DEP_ATOM)
+ {
+ bool match;
+
+ if (dohl)
+ {
+ size_t i;
+ depend_atom *m;
+
+ array_for_each(hlatoms, i, m)
+ {
+ /* make m query, such that any specifics (SLOT,
+ * pfx/sfx) from the depstring are ignored while
+ * highlighting */
+ if (atom_compare(root->atom, m) == EQUAL) {
+ match = true;
+ break;
+ }
+ }
+ }
+
+ fprintf(fp, "%s%s%s",
+ match ? hlcolor : "",
+ atom_to_string(root->atom),
+ match ? NORM : "");
+ }
+
+ if (root->type == DEP_ANY ||
+ root->type == DEP_USE ||
+ root->type == DEP_ALL)
+ {
+ bool singlechild = false;
+
+ /* print on single line, when it's just one atom */
+ if (array_cnt(root->members) == 1 &&
+ ((dep_node_t *)array_get(root->members, 0))->type == DEP_ATOM)
+ singlechild = true;
+
+ /* write leading space in singlechild mode, because we set indent
+ * level to 0 in that case, which suppresses the leading space, for
+ * it assumes that's the start of the output */
+ fprintf(fp, "(%s", singlechild ? " " : "");
+
+ array_for_each(root->members, s, memb)
+ {
+ dep_print_tree(fp, memb,
+ singlechild ? 0 : space + 1,
+ hlatoms, hlcolor, singlechild ? -verbose - 1 : verbose);
+ }
+
+ if (singlechild ||
+ nonewline)
+ {
+ fprintf(fp, " )");
+ }
+ else
+ {
+ fprintf(fp, "\n");
+ for (s = space; s; s--)
+ fprintf(fp, "%*s", indent, "");
+ fprintf(fp, ")");
+ }
+ }
}
-void
-dep_print_tree(
- FILE *fp,
- const dep_node *root,
- size_t space,
- array *hlatoms,
- const char *hlcolor,
- int verbose)
+void dep_burn_tree
+(
+ dep_node_t *root
+)
{
- size_t s;
- int indent = 4; /* Gentoo 4-wide indent standard */
- bool singlechild = false;
- bool nonewline = false;
-
- if (verbose < 0) {
- nonewline = true;
- verbose = -verbose - 1;
- }
-
- assert(root);
- if (root->type == DEP_NULL)
- goto this_node_sucks;
-
- for (s = space; s; --s)
- fprintf(fp, "%*s", indent, "");
-
- if (verbose > 0)
- fprintf(fp, "Node [%s]: ", _dep_names[root->type]);
- /*printf("Node %p [%s] %p %p %p: ", root, _dep_names[root->type],
- * root->parent, root->neighbor, root->children);*/
- if (root->type == DEP_OR)
- fprintf(fp, "|| (");
- if (root->info) {
- if (root->type == DEP_NORM) {
- bool dohl = false;
-
- if (hlatoms != NULL && array_cnt(hlatoms) > 0)
- {
- size_t i;
- depend_atom *m;
-
- array_for_each(hlatoms, i, m) {
- /* make m query, such that any
specifics (SLOT,
- * pfx/sfx) from the depstring are
ignored while
- * highlighting */
- if (atom_compare(root->atom, m) ==
EQUAL) {
- dohl = true;
- break;
- }
- }
- }
-
- fprintf(fp, "%s%s%s",
- dohl ? hlcolor : "",
- atom_to_string(root->atom),
- dohl ? NORM : "");
- if (root->atom_resolved && verbose > 0)
- fprintf(fp, " # %s", root->info);
- } else {
- fprintf(fp, "%s", root->info);
- }
- /* If there is only one child, be nice to one-line: foo? ( pkg
) */
- if (root->type == DEP_USE)
- fprintf(fp, "? (");
- }
-
- if (root->children &&
- root->children->children == NULL &&
- root->children->neighbor == NULL)
- {
- singlechild = true;
- }
-
- if (singlechild)
- fprintf(fp, " ");
- else if (!nonewline)
- fprintf(fp, "\n");
-
- if (root->children)
- dep_print_tree(fp, root->children,
- singlechild ? 0 : space + 1,
- hlatoms, hlcolor, singlechild ?
-verbose - 1 : verbose);
-
- if (root->type == DEP_OR || root->type == DEP_USE) {
- if (singlechild)
- fprintf(fp, " ");
- else
- for (s = space; s; --s)
- fprintf(fp, "%*s", indent, "");
- fprintf(fp, ")\n");
- }
- this_node_sucks:
- if (root->neighbor)
- dep_print_tree(fp, root->neighbor, space, hlatoms, hlcolor,
verbose);
-}
+ if (root == NULL)
+ return;
-void
-dep_burn_tree(dep_node *root)
-{
- assert(root);
- if (root->children)
- dep_burn_tree(root->children);
- if (root->neighbor)
- dep_burn_tree(root->neighbor);
- _dep_burn_node(root);
+ if (root->members != NULL)
+ array_deepfree(root->members, (array_free_cb *)dep_burn_tree);
+ dep_burn_node(root);
}
-void
-dep_prune_use(dep_node *root, set *use)
+/* eliminate all DEP_USE nodes in the dep tree that do not match the set
+ * of given USE words by setting them to DEP_NULL, this takes into
+ * account negatives/inverted conditionals */
+void dep_prune_use
+(
+ dep_node_t *root,
+ set_t *use
+)
{
- if (root->neighbor)
- dep_prune_use(root->neighbor, use);
- if (root->type == DEP_USE) {
- bool invert = (root->info[0] == '!' ? 1 : 0);
- bool notfound =
- contains_set(root->info + (invert ? 1 : 0), use) ==
NULL;
-
- if (notfound ^ invert) {
- root->type = DEP_NULL;
- return;
- }
- }
- if (root->children)
- dep_prune_use(root->children, use);
+ if (root->type == DEP_USE)
+ {
+ bool found = set_contains(use, root->word);
+
+ if (!found ^ root->invert)
+ {
+ root->type = DEP_NULL;
+ return; /* skip traversal of members */
+ }
+ }
+
+ if (root->members != NULL)
+ {
+ dep_node_t *memb;
+ size_t n;
+
+ array_for_each(root->members, n, memb)
+ dep_prune_use(memb, use);
+ }
}
-void
-dep_resolve_tree(dep_node *root, tree_ctx *t)
+void dep_resolve_tree
+(
+ dep_node_t *root,
+ tree_ctx *t
+)
{
- if (root->type != DEP_NULL) {
- if (root->type == DEP_NORM && root->atom &&
!root->atom_resolved) {
- depend_atom *d = root->atom;
- array *r = tree_match_atom(t, d,
-
TREE_MATCH_DEFAULT |
-
TREE_MATCH_LATEST);
- if (array_cnt(r) > 0) {
- tree_pkg_ctx *pkg = array_get(r, 0);
- atom_implode(d);
- root->atom = atom_clone(tree_pkg_atom(pkg,
false));
- root->atom_resolved = 1;
- }
- array_free(r);
- }
-
- if (root->children)
- dep_resolve_tree(root->children, t);
- }
-
- if (root->neighbor)
- dep_resolve_tree(root->neighbor, t);
+ if (root->type == DEP_NULL)
+ return; /* avoid recursing below */
+
+ if (root->type == DEP_ATOM &&
+ root->atom &&
+ !root->atom_resolved)
+ {
+ atom_ctx *d = root->atom;
+ array *r = tree_match_atom(t, d, (TREE_MATCH_DEFAULT |
+ TREE_MATCH_LATEST));
+ if (array_cnt(r) > 0)
+ {
+ tree_pkg_ctx *pkg = array_get(r, 0);
+ atom_implode(d);
+ root->atom = atom_clone(tree_pkg_atom(pkg, false));
+ root->atom_resolved = 1;
+ }
+ array_free(r);
+ }
+
+ if (root->members)
+ {
+ dep_node_t *memb;
+ size_t n;
+
+ array_for_each(root->members, n, memb)
+ dep_resolve_tree(memb, t);
+ }
}
-void
-dep_flatten_tree(dep_node *root, array *out)
+/* drop any (USE-)indirections and add all atoms in the dep node to the
+ * array pointed to by out
+ * because there is no knowledge here of the tree, ANY nodes are treated
+ * as ALL nodes, e.g. all of their atoms are added
+ * use dep_prune_tree to eliminate USE-conditionals */
+void dep_flatten_tree
+(
+ dep_node_t *root,
+ array *out
+)
{
- if (root->type != DEP_NULL) {
- if (root->type == DEP_NORM) {
- array_append(out, atom_clone(root->atom));
- }
- if (root->children)
- dep_flatten_tree(root->children, out);
- }
- if (root->neighbor)
- dep_flatten_tree(root->neighbor, out);
+ if (root->type == DEP_NULL ||
+ out == NULL)
+ return;
+
+ if (root->members != NULL)
+ {
+ dep_node_t *memb;
+ size_t n;
+
+ array_for_each(root->members, n, memb)
+ dep_flatten_tree(memb, out);
+ }
+ else if (root->atom != NULL)
+ {
+ array_append(out, root->atom);
+ }
}
+
+/* vim: set ts=2 sw=2 expandtab cino+=\:0 foldmethod=marker: */
diff --git a/libq/dep.h b/libq/dep.h
index 18bb903e..db828966 100644
--- a/libq/dep.h
+++ b/libq/dep.h
@@ -12,45 +12,16 @@
#include "set.h"
#include "tree.h"
-typedef enum {
- DEP_NULL = 0,
- DEP_NORM = 1,
- DEP_USE = 2,
- DEP_OR = 3,
- DEP_GROUP = 4
-} dep_type;
-
-static const char * const _dep_names[] = {
- "NULL",
- "NORM",
- "USE",
- "OR",
- "GROUP"
-};
-
-struct _dep_node {
- dep_type type;
- char *info;
- char atom_resolved:1;
- depend_atom *atom;
- struct _dep_node *parent;
- struct _dep_node *neighbor;
- struct _dep_node *children;
-};
-typedef struct _dep_node dep_node;
+typedef struct dep_node_ dep_node_t;
/* prototypes */
-#ifdef NDEBUG
-# define dep_dump_tree(r)
-#else
-# define dep_dump_tree(r) dep_print_tree(stdout, r, 0, NULL, NORM, 0)
-#endif
-
-dep_node *dep_grow_tree(const char *depend);
-void dep_print_tree(FILE *fp, const dep_node *root, size_t space, array *m,
const char *c, int verbose);
-void dep_resolve_tree(dep_node *root, tree_ctx *t);
-void dep_burn_tree(dep_node *root);
-void dep_prune_use(dep_node *root, set *use);
-void dep_flatten_tree(dep_node *root, array *out);
+dep_node_t *dep_grow_tree(const char *depend);
+void dep_print_tree(FILE *fp, const dep_node_t *root, size_t space, array *m,
const char *c, int verbose);
+void dep_resolve_tree(dep_node_t *root, tree_ctx *t);
+void dep_burn_tree(dep_node_t *root);
+void dep_prune_use(dep_node_t *root, set *use);
+void dep_flatten_tree(dep_node_t *root, array *out);
#endif
+
+/* vim: set ts=2 sw=2 expandtab cino+=\:0 foldmethod=marker: */
diff --git a/main.h b/main.h
index 632d4181..67946aef 100644
--- a/main.h
+++ b/main.h
@@ -129,7 +129,7 @@ extern FILE *warnout;
#define warn(fmt, args...) \
fprintf(warnout, "%s%s%s: " fmt "\n", RED, argv0, NORM , ## args)
#endif
-#define warnf(fmt, args...) warn("%s%s()%s: " fmt, YELLOW, __func__, NORM , ##
args)
+#define warnf(fmt, args...) warn("%s%s(%u)%s: " fmt, YELLOW, __func__,
__LINE__, NORM , ## args)
#define warnl(fmt, args...) warn("%s%i()%s: " fmt, YELLOW, __LINE__, NORM , ##
args)
#define warnp(fmt, args...) warn(fmt ": %s" , ## args , strerror(errno))
#define warnfp(fmt, args...) warnf(fmt ": %s" , ## args , strerror(errno))
diff --git a/qdepends.c b/qdepends.c
index 40b1d15f..64467dfd 100644
--- a/qdepends.c
+++ b/qdepends.c
@@ -93,7 +93,7 @@ const char *depend_files[] = { /* keep *DEPEND aligned with
above defines */
static bool
qdepends_print_depend(FILE *fp, const char *depend)
{
- dep_node *dep_tree;
+ dep_node_t *dep_tree;
dep_tree = dep_grow_tree(depend);
if (dep_tree == NULL)
@@ -126,7 +126,7 @@ qdepends_results_cb(tree_pkg_ctx *pkg_ctx, void *priv)
size_t n;
size_t m;
int ret = 0;
- dep_node *dep_tree;
+ dep_node_t *dep_tree;
char **d;
char *depstr;
@@ -207,7 +207,7 @@ qdepends_results_cb(tree_pkg_ctx *pkg_ctx, void *priv)
tree_pkg_ctx *pkg = array_get(match, 0);
depstr = get_depstr(i, pkg);
if (depstr != NULL) {
- dep_node *dep_vdb =
dep_grow_tree(depstr);
+ dep_node_t *dep_vdb =
dep_grow_tree(depstr);
if (dep_vdb != NULL) {
dep_flatten_tree(dep_vdb, deps);
dep_burn_tree(dep_vdb);
diff --git a/tests/qdepends/dotest b/tests/qdepends/dotest
index 6517e01b..ca25f651 100755
--- a/tests/qdepends/dotest
+++ b/tests/qdepends/dotest
@@ -69,6 +69,22 @@ testq() { test "$1" "${3:-0}" -q "$2"; }
# forward checks #504636
testq 08 xdm
+# liberal tokenisation tests
+testf 09 'use ? ( bla )'
+testf 09 'use?(bla)'
+testf 10 '|| ( foo ( bar baz ) )'
+testf 10 '||(foo(bar baz))'
+testf 11 \
+'|| (
+ foo
+ (
+ bar
+ baz
+ )
+ !use? ( fnord )
+)'
+testf 11 '||(foo(bar baz)!use?(fnord))'
+
cleantmpdir
end