Skip to content

Commit

Permalink
libsmartcols: support multi-line cells
Browse files Browse the repository at this point in the history
The initial implementation has been introduced by SCOLS_FL_WRAP columns,
but this patch clean ups all and makes things more elegant.

Note that use SCOLS_FL_TREE | SCOLS_FL_WRAP for a column is bad idea
and I don't think we need to fix it.

References: #269
Signed-off-by: Karel Zak <kzak@redhat.com>
  • Loading branch information
karelzak committed Feb 10, 2016
1 parent e865838 commit d94c519
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 43 deletions.
3 changes: 2 additions & 1 deletion lib/mbsalign.c
Expand Up @@ -246,6 +246,7 @@ wc_truncate (wchar_t *wc, size_t width)
}
if (cells + next_cells > width)
break;

cells += next_cells;
wc++;
}
Expand Down Expand Up @@ -290,7 +291,7 @@ mbs_truncate(char *str, size_t *width)
if (sz == (ssize_t) -1)
goto done;

wcs = malloc((sz + 1) * sizeof(wchar_t));
wcs = calloc(1, (sz + 1) * sizeof(wchar_t));
if (!wcs)
goto done;

Expand Down
1 change: 1 addition & 0 deletions libsmartcols/src/column.c
Expand Up @@ -70,6 +70,7 @@ void scols_unref_column(struct libscols_column *cl)
list_del(&cl->cl_columns);
scols_reset_cell(&cl->header);
free(cl->color);
free(cl->pending_data_buf);
free(cl);
}
}
Expand Down
2 changes: 1 addition & 1 deletion libsmartcols/src/libsmartcols.h.in
Expand Up @@ -84,7 +84,7 @@ enum {
SCOLS_FL_STRICTWIDTH = (1 << 3), /* don't reduce width if column is empty */
SCOLS_FL_NOEXTREMES = (1 << 4), /* ignore extreme fields when count column width*/
SCOLS_FL_HIDDEN = (1 << 5), /* maintain data, but don't print */
SCOLS_FL_WRAP = (1 << 6), /* wrap long cells across lines */
SCOLS_FL_WRAP = (1 << 6), /* wrap long lines to multi-line cells */
};

/*
Expand Down
4 changes: 4 additions & 0 deletions libsmartcols/src/smartcolsP.h
Expand Up @@ -84,6 +84,10 @@ struct libscols_column {
int is_extreme;
char *color; /* default column color */

char *pending_data;
size_t pending_data_sz;
char *pending_data_buf;

int (*cmpfunc)(struct libscols_cell *,
struct libscols_cell *,
void *); /* cells comparison function */
Expand Down
209 changes: 168 additions & 41 deletions libsmartcols/src/table_print.c
Expand Up @@ -194,6 +194,22 @@ static int is_last_column(struct libscols_table *tb, struct libscols_column *cl)
#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")


static int has_pending_data(struct libscols_table *tb)
{
struct libscols_column *cl;
struct libscols_iter itr;

scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_column(tb, &itr, &cl) == 0) {
if (scols_column_is_hidden(cl))
continue;
if (cl->pending_data)
return 1;
}
return 0;
}

/* print padding or asci-art instead of data of @cl */
static void print_empty_cell(struct libscols_table *tb,
struct libscols_column *cl,
Expand All @@ -205,7 +221,7 @@ static void print_empty_cell(struct libscols_table *tb,
/* generate tree asci-art rather than padding */
if (ln && scols_column_is_tree(cl)) {
if (!ln->parent) {
/* only print symbols->vert if followed by something */
/* only print symbols->vert if followed by child */
if (!list_empty(&ln->ln_branch)) {
fputs(tb->symbols->vert, tb->out);
len_pad = mbs_safe_width(tb->symbols->vert);
Expand All @@ -218,6 +234,8 @@ static void print_empty_cell(struct libscols_table *tb,
if (art) {
/* whatever the rc, len_pad will be sensible */
line_ascii_art_to_buffer(tb, ln, art);
if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
buffer_append_data(art, tb->symbols->vert);
data = buffer_get_safe_data(art, &len_pad);
if (data && len_pad)
fputs(data, tb->out);
Expand All @@ -230,6 +248,25 @@ static void print_empty_cell(struct libscols_table *tb,
fputc(' ', tb->out);
}


static const char *get_cell_color(struct libscols_table *tb,
struct libscols_column *cl,
struct libscols_line *ln, /* optional */
struct libscols_cell *ce) /* optional */
{
const char *color = NULL;

if (tb && tb->colors_wanted) {
if (ce && !color)
color = ce->color;
if (ln && !color)
color = ln->color;
if (!color)
color = cl->color;
}
return color;
}

/* Fill the start of a line with padding (or with tree ascii-art).
*
* This is necessary after a long non-truncated column, as this requires the
Expand Down Expand Up @@ -259,6 +296,98 @@ static void print_newline_padding(struct libscols_table *tb,
print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz);
}

/*
* Pending data
*
* The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
* printed as usually and output is truncated to match column width.
*
* The rest of the long text is printed on next extra line(s). The extra lines
* don't exist in the table (not represented by libscols_line). The data for
* the extra lines are stored in libscols_column->pending_data_buf and the
* function print_line() adds extra lines until the buffer is not empty in all
* columns.
*/

/* set data that will be printed by extra lines */
static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
{
char *p = NULL;

if (data) {
DBG(COL, ul_debugobj(cl, "setting pending data"));
assert(sz);
p = strdup(data);
if (!p)
return -ENOMEM;
}

free(cl->pending_data_buf);
cl->pending_data_buf = p;
cl->pending_data_sz = sz;
cl->pending_data = cl->pending_data_buf;
return 0;
}

/* the next extra line has been printed, move pending data cursor */
static int step_pending_data(struct libscols_column *cl, size_t bytes)
{
DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));

if (bytes >= cl->pending_data_sz)
return set_pending_data(cl, NULL, 0);

cl->pending_data += bytes;
cl->pending_data_sz -= bytes;
return 0;
}

/* print next pending data for the column @cl */
static int print_pending_data(
struct libscols_table *tb,
struct libscols_column *cl,
struct libscols_line *ln, /* optional */
struct libscols_cell *ce)
{
const char *color = get_cell_color(tb, cl, ln, ce);
size_t width = cl->width, bytes;
size_t len = width, i;
char *data;

if (!cl->pending_data)
return 0;

DBG(COL, ul_debugobj(cl, "printing pending data"));

data = strdup(cl->pending_data);
if (!data)
goto err;
bytes = mbs_truncate(data, &len);
if (bytes == (size_t) -1)
goto err;

step_pending_data(cl, bytes);

if (color)
fputs(color, tb->out);
fputs(data, tb->out);
if (color)
fputs(UL_COLOR_RESET, tb->out);
free(data);

for (i = len; i < width; i++)
fputc('x', tb->out); /* padding */

if (is_last_column(tb, cl))
return 0;

fputs(colsep(tb), tb->out); /* columns separator */
return 0;
err:
free(data);
return -errno;
}

static int print_data(struct libscols_table *tb,
struct libscols_column *cl,
struct libscols_line *ln, /* optional */
Expand Down Expand Up @@ -309,14 +438,7 @@ static int print_data(struct libscols_table *tb,
break; /* continue below */
}

if (tb->colors_wanted) {
if (ce && !color)
color = ce->color;
if (ln && !color)
color = ln->color;
if (!color)
color = cl->color;
}
color = get_cell_color(tb, cl, ln, ce);

/* encode, note that 'len' and 'width' are number of cells, not bytes */
data = buffer_get_safe_data(buf, &len);
Expand All @@ -336,11 +458,21 @@ static int print_data(struct libscols_table *tb,
if (len > width && scols_column_is_trunc(cl)) {
len = width;
bytes = mbs_truncate(data, &len); /* updates 'len' */
}

if (bytes == (size_t) -1) {
bytes = len = 0;
data = NULL;
}
/* multi-line cell */
if (len > width && scols_column_is_wrap(cl)) {
set_pending_data(cl, data, bytes);

len = width;
bytes = mbs_truncate(data, &len);
if (bytes != (size_t) -1 && bytes > 0)
step_pending_data(cl, bytes);
}

if (bytes == (size_t) -1) {
bytes = len = 0;
data = NULL;
}

if (data) {
Expand All @@ -353,33 +485,7 @@ static int print_data(struct libscols_table *tb,
if (color)
fputs(UL_COLOR_RESET, tb->out);
len = width;
} else if (len > width && scols_column_is_wrap(cl)) {
char *p = data;
i = 0;

if (color)
fputs(color, tb->out);

while (*p) {
len = width;
p = strdup(p);
bytes = mbs_truncate(p, &len);
if (bytes == (size_t) -1) {
free(p);
break;
}
fputs(p, tb->out);
free(p);
i += bytes;
p = data + i;
if (*p)
for (size_t j = 0; j < cl->seqnum; j++)
print_empty_cell (tb, scols_table_get_column(tb, j),
ln, buf->bufsz);
}

if (color)
fputs(UL_COLOR_RESET, tb->out);
} else if (color) {
char *p = data;
size_t art = buffer_get_safe_art_size(buf);
Expand Down Expand Up @@ -551,23 +657,44 @@ static int print_line(struct libscols_table *tb,
struct libscols_line *ln,
struct libscols_buffer *buf)
{
int rc = 0;
int rc = 0, pending = 0;
struct libscols_column *cl;
struct libscols_iter itr;

assert(ln);

DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf));

/* regular line */
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
if (scols_column_is_hidden(cl))
continue;
rc = cell_to_buffer(tb, ln, cl, buf);
if (!rc)
if (rc == 0)
rc = print_data(tb, cl, ln,
scols_line_get_cell(ln, cl->seqnum),
buf);
if (rc == 0 && cl->pending_data)
pending = 1;
}

/* extra lines of the multi-line cells */
while (rc == 0 && pending) {
pending = 0;
fputs(linesep(tb), tb->out);
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
if (scols_column_is_hidden(cl))
continue;

if (cl->pending_data) {
rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum));
if (rc == 0 && cl->pending_data)
pending = 1;
} else
print_empty_cell(tb, cl, ln, buf->bufsz);
}
}

return 0;
Expand Down

0 comments on commit d94c519

Please sign in to comment.