]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: add generic function to walk on tree
authorKarel Zak <kzak@redhat.com>
Tue, 7 May 2019 09:44:24 +0000 (11:44 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 7 May 2019 09:44:24 +0000 (11:44 +0200)
Now we implement the same thing on more places. Let's add one set of
functions to walk the tree and use it everywhere.

Signed-off-by: Karel Zak <kzak@redhat.com>
libsmartcols/src/Makemodule.am
libsmartcols/src/smartcolsP.h
libsmartcols/src/walk.c [new file with mode: 0644]

index c458715f548c8fe68d40a03a7b33d980c9fc69c2..fff314c73e6b3b7c0d31707e4d23a9a9835e25f5 100644 (file)
@@ -22,6 +22,7 @@ libsmartcols_la_SOURCES= \
        libsmartcols/src/buffer.c \
        libsmartcols/src/calculate.c \
        libsmartcols/src/grouping.c \
+       libsmartcols/src/walk.c \
        libsmartcols/src/init.c
 
 libsmartcols_la_LIBADD = $(LDADD) libcommon.la
index 543588b10ca2a584ead1d0532ebeaf80b5dbd6fe..3653eda0704e1fb73390f3f5bdfd62c5589324b4 100644 (file)
@@ -217,7 +217,9 @@ struct libscols_table {
        struct list_head        tb_groups;      /* all defined groups */
        struct libscols_group   **grpset;
        size_t                  grpset_size;
+
        size_t                  ngrpchlds_pending;      /* groups with not yet printed children */
+       struct libscols_line    *walk_last_tree_root;   /* last root, used by scols_walk_() */
 
        struct libscols_symbols *symbols;
        struct libscols_cell    title;          /* optional table title (for humans) */
@@ -239,6 +241,7 @@ struct libscols_table {
                        header_repeat   :1,     /* print header after libscols_table->termheight */
                        header_printed  :1,     /* header already printed */
                        priv_symbols    :1,     /* default private symbols */
+                       walk_last_done  :1,     /* last tree root walked */
                        no_headings     :1,     /* don't print header */
                        no_encode       :1,     /* don't care about control and non-printable chars */
                        no_linesep      :1,     /* don't print line separator */
@@ -317,6 +320,18 @@ int scols_groups_update_grpset(struct libscols_table *tb, struct libscols_line *
 void scols_groups_reset_state(struct libscols_table *tb);
 struct libscols_group *scols_grpset_get_printable_children(struct libscols_table *tb);
 
+/*
+ * walk.c
+ */
+extern int scols_walk_tree(struct libscols_table *tb,
+                    struct libscols_column *cl,
+                    int (*callback)(struct libscols_table *,
+                                    struct libscols_line *,
+                                    struct libscols_column *,
+                                    void *),
+                    void *data);
+extern int scols_walk_is_last(struct libscols_table *tb, struct libscols_line *ln);
+
 /*
  * calculate.c
  */
@@ -352,14 +367,40 @@ extern void fput_children_close(struct libscols_table *tb);
 extern void fput_line_open(struct libscols_table *tb);
 extern void fput_line_close(struct libscols_table *tb, int last, int last_in_table);
 
+static inline int is_tree_root(struct libscols_line *ln)
+{
+       return ln && !ln->parent && !ln->parent_group;
+}
+
+static inline int is_last_tree_root(struct libscols_table *tb, struct libscols_line *ln)
+{
+       if (!ln || !tb || tb->walk_last_tree_root != ln)
+               return 0;
+
+       return 1;
+}
+
+static inline int is_child(struct libscols_line *ln)
+{
+       return ln && ln->parent;
+}
+
 static inline int is_last_child(struct libscols_line *ln)
 {
        if (!ln || !ln->parent)
-               return 1;
+               return 0;
 
        return list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch);
 }
 
+static inline int is_first_child(struct libscols_line *ln)
+{
+       if (!ln || !ln->parent)
+               return 0;
+
+       return list_entry_is_first(&ln->ln_children, &ln->parent->ln_branch);
+}
+
 
 static inline int is_last_column(struct libscols_column *cl)
 {
diff --git a/libsmartcols/src/walk.c b/libsmartcols/src/walk.c
new file mode 100644 (file)
index 0000000..a75fde6
--- /dev/null
@@ -0,0 +1,152 @@
+#include "smartcolsP.h"
+
+static int walk_line(struct libscols_table *tb,
+                    struct libscols_line *ln,
+                    struct libscols_column *cl,
+                    int (*callback)(struct libscols_table *,
+                                   struct libscols_line *,
+                                   struct libscols_column *,
+                                   void *),
+                   void *data)
+{
+       int rc = 0;
+
+       DBG(LINE, ul_debugobj(ln, " wall line"));
+
+       /* we list group children in __scols_print_tree() after tree root node */
+       if (is_group_member(ln) && is_last_group_member(ln) && has_group_children(ln))
+               tb->ngrpchlds_pending++;
+
+       if (has_groups(tb))
+               rc = scols_groups_update_grpset(tb, ln);
+       if (rc == 0)
+               rc = callback(tb, ln, cl, data);
+
+       /* children */
+       if (rc == 0 && has_children(ln)) {
+               struct list_head *p;
+
+               DBG(LINE, ul_debugobj(ln, " children walk"));
+
+               list_for_each(p, &ln->ln_branch) {
+                       struct libscols_line *chld = list_entry(p,
+                                       struct libscols_line, ln_children);
+
+                       rc = walk_line(tb, chld, cl, callback, data);
+                       if (rc)
+                               break;
+               }
+       }
+
+       DBG(LINE, ul_debugobj(ln, "<- walk line done [rc=%d]", rc));
+       return rc;
+}
+
+/* last line in the tree? */
+int scols_walk_is_last(struct libscols_table *tb, struct libscols_line *ln)
+{
+       if (tb->walk_last_done == 0)
+               return 0;
+       if (tb->ngrpchlds_pending > 0)
+               return 0;
+       if (has_children(ln))
+               return 0;
+       if (is_tree_root(ln) && !is_last_tree_root(tb, ln))
+               return 0;
+       if (is_group_member(ln) && (!is_last_group_member(ln) || has_group_children(ln)))
+               return 0;
+       if (is_child(ln)) {
+               struct libscols_line *parent = ln->parent;
+
+               if (!is_last_child(ln))
+                       return 0;
+               while (parent) {
+                       if (is_child(parent) && !is_last_child(parent))
+                               return 0;
+                       if (!parent->parent)
+                               break;
+                       parent = parent->parent;
+               }
+               if (is_tree_root(parent) && !is_last_tree_root(tb, parent))
+                       return 0;
+       }
+       if (is_group_child(ln) && !is_last_group_child(ln))
+               return 0;
+
+       DBG(LINE, ul_debugobj(ln, "last in table"));
+       return 1;
+}
+
+int scols_walk_tree(struct libscols_table *tb,
+                   struct libscols_column *cl,
+                   int (*callback)(struct libscols_table *,
+                                   struct libscols_line *,
+                                   struct libscols_column *,
+                                   void *),
+                   void *data)
+{
+       int rc = 0;
+       struct libscols_line *ln;
+       struct libscols_iter itr;
+
+       assert(tb);
+       DBG(TAB, ul_debugobj(tb, ">> walk start"));
+
+       /* init */
+       tb->ngrpchlds_pending = 0;
+       tb->walk_last_tree_root = NULL;
+       tb->walk_last_done = 0;
+
+       if (has_groups(tb))
+               scols_groups_reset_state(tb);
+
+       /* set pointer to last tree root */
+       scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+       while (scols_table_next_line(tb, &itr, &ln) == 0) {
+               if (!tb->walk_last_tree_root)
+                       tb->walk_last_tree_root = ln;
+               if (is_child(ln) || is_group_child(ln))
+                       continue;
+               tb->walk_last_tree_root = ln;
+       }
+
+       /* walk */
+       scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+       while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
+               if (ln->parent || ln->parent_group)
+                       continue;
+
+               if (tb->walk_last_tree_root == ln)
+                       tb->walk_last_done = 1;
+               rc = walk_line(tb, ln, cl, callback, data);
+
+               /* walk group's children */
+               while (rc == 0 && tb->ngrpchlds_pending) {
+                       struct libscols_group *gr = scols_grpset_get_printable_children(tb);
+                       struct list_head *p;
+
+                       DBG(LINE, ul_debugobj(ln, " walk group children [pending=%zu]", tb->ngrpchlds_pending));
+                       if (!gr) {
+                               DBG(LINE, ul_debugobj(ln, " *** ngrpchlds_pending counter invalid"));
+                               tb->ngrpchlds_pending = 0;
+                               break;
+                       }
+
+                       tb->ngrpchlds_pending--;
+
+                       list_for_each(p, &gr->gr_children) {
+                               struct libscols_line *chld =
+                                       list_entry(p, struct libscols_line, ln_children);
+
+                               rc = walk_line(tb, chld, cl, callback, data);
+                               if (rc)
+                                       break;
+                       }
+               }
+       }
+
+       tb->ngrpchlds_pending = 0;
+       tb->walk_last_done = 0;
+       DBG(TAB, ul_debugobj(tb, "<< walk end [rc=%d]", rc));
+       return rc;
+}