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

Reply via email to