libsmartcols: add generic function to walk on tree

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>
This commit is contained in:
Karel Zak 2019-05-07 11:44:24 +02:00
parent f6f8a671a9
commit 19c2e73116
3 changed files with 195 additions and 1 deletions

View 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

View 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)
{

152
libsmartcols/src/walk.c Normal file
View File

@ -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;
}