summaryrefslogtreecommitdiffstats
path: root/libsmartcols
diff options
context:
space:
mode:
Diffstat (limited to 'libsmartcols')
-rw-r--r--libsmartcols/src/Makemodule.am1
-rw-r--r--libsmartcols/src/smartcolsP.h43
-rw-r--r--libsmartcols/src/walk.c152
3 files changed, 195 insertions, 1 deletions
diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am
index c458715f5..fff314c73 100644
--- a/libsmartcols/src/Makemodule.am
+++ b/libsmartcols/src/Makemodule.am
@@ -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
diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h
index 543588b10..3653eda07 100644
--- a/libsmartcols/src/smartcolsP.h
+++ b/libsmartcols/src/smartcolsP.h
@@ -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 */
@@ -318,6 +321,18 @@ 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
*/
extern int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf);
@@ -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
index 000000000..a75fde6a3
--- /dev/null
+++ b/libsmartcols/src/walk.c
@@ -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;
+}