diff options
Diffstat (limited to 'libsmartcols')
-rw-r--r-- | libsmartcols/src/Makemodule.am | 1 | ||||
-rw-r--r-- | libsmartcols/src/smartcolsP.h | 43 | ||||
-rw-r--r-- | libsmartcols/src/walk.c | 152 |
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; +} |