diff options
Diffstat (limited to 'libsmartcols/src/walk.c')
-rw-r--r-- | libsmartcols/src/walk.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/libsmartcols/src/walk.c b/libsmartcols/src/walk.c new file mode 100644 index 0000000..a75fde6 --- /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; +} |