libsmartcols: move width calculation to separate file
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
ecd7cd253d
commit
06a8decd72
|
@ -18,6 +18,7 @@ libsmartcols_la_SOURCES= \
|
||||||
libsmartcols/src/print.c \
|
libsmartcols/src/print.c \
|
||||||
libsmartcols/src/version.c \
|
libsmartcols/src/version.c \
|
||||||
libsmartcols/src/buffer.c \
|
libsmartcols/src/buffer.c \
|
||||||
|
libsmartcols/src/calculate.c \
|
||||||
libsmartcols/src/init.c
|
libsmartcols/src/init.c
|
||||||
|
|
||||||
libsmartcols_la_LIBADD = $(LDADD) libcommon.la
|
libsmartcols_la_LIBADD = $(LDADD) libcommon.la
|
||||||
|
|
|
@ -0,0 +1,391 @@
|
||||||
|
#include "smartcolsP.h"
|
||||||
|
#include "mbsalign.h"
|
||||||
|
|
||||||
|
static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
|
||||||
|
{
|
||||||
|
if (scols_column_is_hidden(cl)) {
|
||||||
|
DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
|
||||||
|
"hint=%d, avg=%zu, max=%zu, min=%zu, "
|
||||||
|
"extreme=%s %s",
|
||||||
|
|
||||||
|
cl->header.data, cl->seqnum, cl->width,
|
||||||
|
cl->width_hint > 1 ? (int) cl->width_hint :
|
||||||
|
(int) (cl->width_hint * tb->termwidth),
|
||||||
|
cl->width_avg,
|
||||||
|
cl->width_max,
|
||||||
|
cl->width_min,
|
||||||
|
cl->is_extreme ? "yes" : "not",
|
||||||
|
cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbg_columns(struct libscols_table *tb)
|
||||||
|
{
|
||||||
|
struct libscols_iter itr;
|
||||||
|
struct libscols_column *cl;
|
||||||
|
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0)
|
||||||
|
dbg_column(tb, cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function counts column width.
|
||||||
|
*
|
||||||
|
* For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
|
||||||
|
* two times. The first pass counts the width and average width. If the column
|
||||||
|
* contains fields that are too large (a width greater than 2 * average) then
|
||||||
|
* the column is marked as "extreme". In the second pass all extreme fields
|
||||||
|
* are ignored and the column width is counted from non-extreme fields only.
|
||||||
|
*/
|
||||||
|
static int count_column_width(struct libscols_table *tb,
|
||||||
|
struct libscols_column *cl,
|
||||||
|
struct libscols_buffer *buf)
|
||||||
|
{
|
||||||
|
struct libscols_line *ln;
|
||||||
|
struct libscols_iter itr;
|
||||||
|
int extreme_count = 0, rc = 0, no_header = 0;
|
||||||
|
size_t extreme_sum = 0;
|
||||||
|
|
||||||
|
assert(tb);
|
||||||
|
assert(cl);
|
||||||
|
|
||||||
|
cl->width = 0;
|
||||||
|
if (!cl->width_min) {
|
||||||
|
if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) {
|
||||||
|
cl->width_min = (size_t) (cl->width_hint * tb->termwidth);
|
||||||
|
if (cl->width_min && !is_last_column(cl))
|
||||||
|
cl->width_min--;
|
||||||
|
}
|
||||||
|
if (scols_cell_get_data(&cl->header)) {
|
||||||
|
size_t len = mbs_safe_width(scols_cell_get_data(&cl->header));
|
||||||
|
cl->width_min = max(cl->width_min, len);
|
||||||
|
} else
|
||||||
|
no_header = 1;
|
||||||
|
|
||||||
|
if (!cl->width_min)
|
||||||
|
cl->width_min = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_line(tb, &itr, &ln) == 0) {
|
||||||
|
size_t len;
|
||||||
|
char *data;
|
||||||
|
|
||||||
|
rc = __cell_to_buffer(tb, ln, cl, buf);
|
||||||
|
if (rc)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
data = buffer_get_data(buf);
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
len = 0;
|
||||||
|
else if (scols_column_is_customwrap(cl))
|
||||||
|
len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
|
||||||
|
else
|
||||||
|
len = mbs_safe_width(data);
|
||||||
|
|
||||||
|
if (len == (size_t) -1) /* ignore broken multibyte strings */
|
||||||
|
len = 0;
|
||||||
|
cl->width_max = max(len, cl->width_max);
|
||||||
|
|
||||||
|
if (cl->is_extreme && cl->width_avg && len > cl->width_avg * 2)
|
||||||
|
continue;
|
||||||
|
else if (scols_column_is_noextremes(cl)) {
|
||||||
|
extreme_sum += len;
|
||||||
|
extreme_count++;
|
||||||
|
}
|
||||||
|
cl->width = max(len, cl->width);
|
||||||
|
if (scols_column_is_tree(cl)) {
|
||||||
|
size_t treewidth = buffer_get_safe_art_size(buf);
|
||||||
|
cl->width_treeart = max(cl->width_treeart, treewidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extreme_count && cl->width_avg == 0) {
|
||||||
|
cl->width_avg = extreme_sum / extreme_count;
|
||||||
|
if (cl->width_avg && cl->width_max > cl->width_avg * 2)
|
||||||
|
cl->is_extreme = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enlarge to minimal width */
|
||||||
|
if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
|
||||||
|
cl->width = cl->width_min;
|
||||||
|
|
||||||
|
/* use absolute size for large columns */
|
||||||
|
else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
|
||||||
|
&& cl->width_min < (size_t) cl->width_hint)
|
||||||
|
|
||||||
|
cl->width = (size_t) cl->width_hint;
|
||||||
|
|
||||||
|
|
||||||
|
/* Column without header and data, set minimal size to zero (default is 1) */
|
||||||
|
if (cl->width_max == 0 && no_header && cl->width_min == 1 && cl->width <= 1)
|
||||||
|
cl->width = cl->width_min = 0;
|
||||||
|
|
||||||
|
done:
|
||||||
|
ON_DBG(COL, dbg_column(tb, cl));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is core of the scols_* voodoo...
|
||||||
|
*/
|
||||||
|
int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf)
|
||||||
|
{
|
||||||
|
struct libscols_column *cl;
|
||||||
|
struct libscols_iter itr;
|
||||||
|
size_t width = 0, width_min = 0; /* output width */
|
||||||
|
int stage, rc = 0;
|
||||||
|
int extremes = 0;
|
||||||
|
size_t colsepsz;
|
||||||
|
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
|
||||||
|
|
||||||
|
colsepsz = mbs_safe_width(colsep(tb));
|
||||||
|
|
||||||
|
/* set basic columns width
|
||||||
|
*/
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
int is_last;
|
||||||
|
|
||||||
|
if (scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
rc = count_column_width(tb, cl, buf);
|
||||||
|
if (rc)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
is_last = is_last_column(cl);
|
||||||
|
|
||||||
|
width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */
|
||||||
|
width_min += cl->width_min + (is_last ? 0 : colsepsz);
|
||||||
|
extremes += cl->is_extreme;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tb->is_term) {
|
||||||
|
DBG(TAB, ul_debugobj(tb, " non-terminal output"));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* be paranoid */
|
||||||
|
if (width_min > tb->termwidth && scols_table_is_maxout(tb)) {
|
||||||
|
DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth));
|
||||||
|
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (width_min > tb->termwidth
|
||||||
|
&& scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
if (scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
width_min--;
|
||||||
|
cl->width_min--;
|
||||||
|
}
|
||||||
|
DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reduce columns with extreme fields */
|
||||||
|
if (width > tb->termwidth && extremes) {
|
||||||
|
DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
|
||||||
|
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
size_t org_width;
|
||||||
|
|
||||||
|
if (!cl->is_extreme || scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
org_width = cl->width;
|
||||||
|
rc = count_column_width(tb, cl, buf);
|
||||||
|
if (rc)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (org_width > cl->width)
|
||||||
|
width -= org_width - cl->width;
|
||||||
|
else
|
||||||
|
extremes--; /* hmm... nothing reduced */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width < tb->termwidth) {
|
||||||
|
if (extremes) {
|
||||||
|
DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
|
||||||
|
|
||||||
|
/* enlarge the first extreme column */
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
size_t add;
|
||||||
|
|
||||||
|
if (!cl->is_extreme || scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* this column is too large, ignore?
|
||||||
|
if (cl->width_max - cl->width >
|
||||||
|
(tb->termwidth - width))
|
||||||
|
continue;
|
||||||
|
*/
|
||||||
|
|
||||||
|
add = tb->termwidth - width;
|
||||||
|
if (add && cl->width + add > cl->width_max)
|
||||||
|
add = cl->width_max - cl->width;
|
||||||
|
|
||||||
|
cl->width += add;
|
||||||
|
width += add;
|
||||||
|
|
||||||
|
if (width == tb->termwidth)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width < tb->termwidth && scols_table_is_maxout(tb)) {
|
||||||
|
DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
|
||||||
|
|
||||||
|
/* try enlarging all columns */
|
||||||
|
while (width < tb->termwidth) {
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
if (scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
cl->width++;
|
||||||
|
width++;
|
||||||
|
if (width == tb->termwidth)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (width < tb->termwidth) {
|
||||||
|
/* enlarge the last column */
|
||||||
|
struct libscols_column *col = list_entry(
|
||||||
|
tb->tb_columns.prev, struct libscols_column, cl_columns);
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
|
||||||
|
|
||||||
|
if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
|
||||||
|
col->width += tb->termwidth - width;
|
||||||
|
width = tb->termwidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bad, we have to reduce output width, this is done in three stages:
|
||||||
|
*
|
||||||
|
* 1) trunc relative with trunc flag if the column width is greater than
|
||||||
|
* expected column width (it means "width_hint * terminal_width").
|
||||||
|
*
|
||||||
|
* 2) trunc all with trunc flag
|
||||||
|
*
|
||||||
|
* 3) trunc relative without trunc flag
|
||||||
|
*
|
||||||
|
* Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is
|
||||||
|
* interpreted as SCOLS_FL_TRUNC.
|
||||||
|
*/
|
||||||
|
for (stage = 1; width > tb->termwidth && stage <= 3; ) {
|
||||||
|
size_t org_width = width;
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)",
|
||||||
|
stage, width, tb->termwidth));
|
||||||
|
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
|
||||||
|
int trunc_flag = 0;
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
|
||||||
|
cl->header.data, cl->width, cl->width_treeart));
|
||||||
|
if (scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
if (width <= tb->termwidth)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* never truncate if already minimal width */
|
||||||
|
if (cl->width == cl->width_min)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* never truncate the tree */
|
||||||
|
if (scols_column_is_tree(cl) && width <= cl->width_treeart)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* nothing to truncate */
|
||||||
|
if (cl->width == 0 || width == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
trunc_flag = scols_column_is_trunc(cl)
|
||||||
|
|| (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl));
|
||||||
|
|
||||||
|
switch (stage) {
|
||||||
|
/* #1 stage - trunc relative with TRUNC flag */
|
||||||
|
case 1:
|
||||||
|
if (!trunc_flag) /* ignore: missing flag */
|
||||||
|
break;
|
||||||
|
if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
|
||||||
|
break;
|
||||||
|
if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */
|
||||||
|
break;
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)"));
|
||||||
|
cl->width--;
|
||||||
|
width--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* #2 stage - trunc all with TRUNC flag */
|
||||||
|
case 2:
|
||||||
|
if (!trunc_flag) /* ignore: missing flag */
|
||||||
|
break;
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(tb, " reducing (all with flag)"));
|
||||||
|
cl->width--;
|
||||||
|
width--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* #3 stage - trunc relative without flag */
|
||||||
|
case 3:
|
||||||
|
if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
|
||||||
|
break;
|
||||||
|
|
||||||
|
DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)"));
|
||||||
|
cl->width--;
|
||||||
|
width--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide zero width columns */
|
||||||
|
if (cl->width == 0)
|
||||||
|
cl->flags |= SCOLS_FL_HIDDEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the current stage is without effect, go to the next */
|
||||||
|
if (org_width == width)
|
||||||
|
stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ignore last column(s) or force last column to be truncated if
|
||||||
|
* nowrap mode enabled */
|
||||||
|
if (tb->no_wrap && width > tb->termwidth) {
|
||||||
|
scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
|
||||||
|
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
|
|
||||||
|
if (scols_column_is_hidden(cl))
|
||||||
|
continue;
|
||||||
|
if (width <= tb->termwidth)
|
||||||
|
break;
|
||||||
|
if (width - cl->width < tb->termwidth) {
|
||||||
|
size_t r = width - tb->termwidth;
|
||||||
|
|
||||||
|
cl->flags |= SCOLS_FL_TRUNC;
|
||||||
|
cl->width -= r;
|
||||||
|
width -= r;
|
||||||
|
} else {
|
||||||
|
cl->flags |= SCOLS_FL_HIDDEN;
|
||||||
|
width -= cl->width + colsepsz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
|
||||||
|
ON_DBG(TAB, dbg_columns(tb));
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
|
@ -26,9 +26,6 @@
|
||||||
#include "carefulputc.h"
|
#include "carefulputc.h"
|
||||||
#include "smartcolsP.h"
|
#include "smartcolsP.h"
|
||||||
|
|
||||||
#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
|
|
||||||
#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
|
|
||||||
|
|
||||||
/* Fallback for symbols
|
/* Fallback for symbols
|
||||||
*
|
*
|
||||||
* Note that by default library define all the symbols, but in case user does
|
* Note that by default library define all the symbols, but in case user does
|
||||||
|
@ -435,7 +432,7 @@ static int print_data(struct libscols_table *tb,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cell_to_buffer(struct libscols_table *tb,
|
int __cell_to_buffer(struct libscols_table *tb,
|
||||||
struct libscols_line *ln,
|
struct libscols_line *ln,
|
||||||
struct libscols_column *cl,
|
struct libscols_column *cl,
|
||||||
struct libscols_buffer *buf)
|
struct libscols_buffer *buf)
|
||||||
|
@ -595,7 +592,7 @@ static int print_line(struct libscols_table *tb,
|
||||||
while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
|
while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
|
||||||
if (scols_column_is_hidden(cl))
|
if (scols_column_is_hidden(cl))
|
||||||
continue;
|
continue;
|
||||||
rc = cell_to_buffer(tb, ln, cl, buf);
|
rc = __cell_to_buffer(tb, ln, cl, buf);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
rc = print_data(tb, cl, ln,
|
rc = print_data(tb, cl, ln,
|
||||||
scols_line_get_cell(ln, cl->seqnum),
|
scols_line_get_cell(ln, cl->seqnum),
|
||||||
|
@ -873,395 +870,6 @@ static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
|
|
||||||
{
|
|
||||||
if (scols_column_is_hidden(cl)) {
|
|
||||||
DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
|
|
||||||
"hint=%d, avg=%zu, max=%zu, min=%zu, "
|
|
||||||
"extreme=%s %s",
|
|
||||||
|
|
||||||
cl->header.data, cl->seqnum, cl->width,
|
|
||||||
cl->width_hint > 1 ? (int) cl->width_hint :
|
|
||||||
(int) (cl->width_hint * tb->termwidth),
|
|
||||||
cl->width_avg,
|
|
||||||
cl->width_max,
|
|
||||||
cl->width_min,
|
|
||||||
cl->is_extreme ? "yes" : "not",
|
|
||||||
cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dbg_columns(struct libscols_table *tb)
|
|
||||||
{
|
|
||||||
struct libscols_iter itr;
|
|
||||||
struct libscols_column *cl;
|
|
||||||
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0)
|
|
||||||
dbg_column(tb, cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This function counts column width.
|
|
||||||
*
|
|
||||||
* For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
|
|
||||||
* two times. The first pass counts the width and average width. If the column
|
|
||||||
* contains fields that are too large (a width greater than 2 * average) then
|
|
||||||
* the column is marked as "extreme". In the second pass all extreme fields
|
|
||||||
* are ignored and the column width is counted from non-extreme fields only.
|
|
||||||
*/
|
|
||||||
static int count_column_width(struct libscols_table *tb,
|
|
||||||
struct libscols_column *cl,
|
|
||||||
struct libscols_buffer *buf)
|
|
||||||
{
|
|
||||||
struct libscols_line *ln;
|
|
||||||
struct libscols_iter itr;
|
|
||||||
int extreme_count = 0, rc = 0, no_header = 0;
|
|
||||||
size_t extreme_sum = 0;
|
|
||||||
|
|
||||||
assert(tb);
|
|
||||||
assert(cl);
|
|
||||||
|
|
||||||
cl->width = 0;
|
|
||||||
if (!cl->width_min) {
|
|
||||||
if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) {
|
|
||||||
cl->width_min = (size_t) (cl->width_hint * tb->termwidth);
|
|
||||||
if (cl->width_min && !is_last_column(cl))
|
|
||||||
cl->width_min--;
|
|
||||||
}
|
|
||||||
if (scols_cell_get_data(&cl->header)) {
|
|
||||||
size_t len = mbs_safe_width(scols_cell_get_data(&cl->header));
|
|
||||||
cl->width_min = max(cl->width_min, len);
|
|
||||||
} else
|
|
||||||
no_header = 1;
|
|
||||||
|
|
||||||
if (!cl->width_min)
|
|
||||||
cl->width_min = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_line(tb, &itr, &ln) == 0) {
|
|
||||||
size_t len;
|
|
||||||
char *data;
|
|
||||||
|
|
||||||
rc = cell_to_buffer(tb, ln, cl, buf);
|
|
||||||
if (rc)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
data = buffer_get_data(buf);
|
|
||||||
|
|
||||||
if (!data)
|
|
||||||
len = 0;
|
|
||||||
else if (scols_column_is_customwrap(cl))
|
|
||||||
len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
|
|
||||||
else
|
|
||||||
len = mbs_safe_width(data);
|
|
||||||
|
|
||||||
if (len == (size_t) -1) /* ignore broken multibyte strings */
|
|
||||||
len = 0;
|
|
||||||
cl->width_max = max(len, cl->width_max);
|
|
||||||
|
|
||||||
if (cl->is_extreme && cl->width_avg && len > cl->width_avg * 2)
|
|
||||||
continue;
|
|
||||||
else if (scols_column_is_noextremes(cl)) {
|
|
||||||
extreme_sum += len;
|
|
||||||
extreme_count++;
|
|
||||||
}
|
|
||||||
cl->width = max(len, cl->width);
|
|
||||||
if (scols_column_is_tree(cl)) {
|
|
||||||
size_t treewidth = buffer_get_safe_art_size(buf);
|
|
||||||
cl->width_treeart = max(cl->width_treeart, treewidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extreme_count && cl->width_avg == 0) {
|
|
||||||
cl->width_avg = extreme_sum / extreme_count;
|
|
||||||
if (cl->width_avg && cl->width_max > cl->width_avg * 2)
|
|
||||||
cl->is_extreme = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enlarge to minimal width */
|
|
||||||
if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
|
|
||||||
cl->width = cl->width_min;
|
|
||||||
|
|
||||||
/* use absolute size for large columns */
|
|
||||||
else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
|
|
||||||
&& cl->width_min < (size_t) cl->width_hint)
|
|
||||||
|
|
||||||
cl->width = (size_t) cl->width_hint;
|
|
||||||
|
|
||||||
|
|
||||||
/* Column without header and data, set minimal size to zero (default is 1) */
|
|
||||||
if (cl->width_max == 0 && no_header && cl->width_min == 1 && cl->width <= 1)
|
|
||||||
cl->width = cl->width_min = 0;
|
|
||||||
|
|
||||||
done:
|
|
||||||
ON_DBG(COL, dbg_column(tb, cl));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is core of the scols_* voodoo...
|
|
||||||
*/
|
|
||||||
static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf)
|
|
||||||
{
|
|
||||||
struct libscols_column *cl;
|
|
||||||
struct libscols_iter itr;
|
|
||||||
size_t width = 0, width_min = 0; /* output width */
|
|
||||||
int stage, rc = 0;
|
|
||||||
int extremes = 0;
|
|
||||||
size_t colsepsz;
|
|
||||||
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
|
|
||||||
|
|
||||||
colsepsz = mbs_safe_width(colsep(tb));
|
|
||||||
|
|
||||||
/* set basic columns width
|
|
||||||
*/
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
int is_last;
|
|
||||||
|
|
||||||
if (scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
rc = count_column_width(tb, cl, buf);
|
|
||||||
if (rc)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
is_last = is_last_column(cl);
|
|
||||||
|
|
||||||
width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */
|
|
||||||
width_min += cl->width_min + (is_last ? 0 : colsepsz);
|
|
||||||
extremes += cl->is_extreme;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tb->is_term) {
|
|
||||||
DBG(TAB, ul_debugobj(tb, " non-terminal output"));
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* be paranoid */
|
|
||||||
if (width_min > tb->termwidth && scols_table_is_maxout(tb)) {
|
|
||||||
DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth));
|
|
||||||
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (width_min > tb->termwidth
|
|
||||||
&& scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
if (scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
width_min--;
|
|
||||||
cl->width_min--;
|
|
||||||
}
|
|
||||||
DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reduce columns with extreme fields */
|
|
||||||
if (width > tb->termwidth && extremes) {
|
|
||||||
DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
|
|
||||||
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
size_t org_width;
|
|
||||||
|
|
||||||
if (!cl->is_extreme || scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
org_width = cl->width;
|
|
||||||
rc = count_column_width(tb, cl, buf);
|
|
||||||
if (rc)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (org_width > cl->width)
|
|
||||||
width -= org_width - cl->width;
|
|
||||||
else
|
|
||||||
extremes--; /* hmm... nothing reduced */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width < tb->termwidth) {
|
|
||||||
if (extremes) {
|
|
||||||
DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
|
|
||||||
|
|
||||||
/* enlarge the first extreme column */
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
size_t add;
|
|
||||||
|
|
||||||
if (!cl->is_extreme || scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* this column is too large, ignore?
|
|
||||||
if (cl->width_max - cl->width >
|
|
||||||
(tb->termwidth - width))
|
|
||||||
continue;
|
|
||||||
*/
|
|
||||||
|
|
||||||
add = tb->termwidth - width;
|
|
||||||
if (add && cl->width + add > cl->width_max)
|
|
||||||
add = cl->width_max - cl->width;
|
|
||||||
|
|
||||||
cl->width += add;
|
|
||||||
width += add;
|
|
||||||
|
|
||||||
if (width == tb->termwidth)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width < tb->termwidth && scols_table_is_maxout(tb)) {
|
|
||||||
DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
|
|
||||||
|
|
||||||
/* try enlarging all columns */
|
|
||||||
while (width < tb->termwidth) {
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
if (scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
cl->width++;
|
|
||||||
width++;
|
|
||||||
if (width == tb->termwidth)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (width < tb->termwidth) {
|
|
||||||
/* enlarge the last column */
|
|
||||||
struct libscols_column *col = list_entry(
|
|
||||||
tb->tb_columns.prev, struct libscols_column, cl_columns);
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
|
|
||||||
|
|
||||||
if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
|
|
||||||
col->width += tb->termwidth - width;
|
|
||||||
width = tb->termwidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bad, we have to reduce output width, this is done in three stages:
|
|
||||||
*
|
|
||||||
* 1) trunc relative with trunc flag if the column width is greater than
|
|
||||||
* expected column width (it means "width_hint * terminal_width").
|
|
||||||
*
|
|
||||||
* 2) trunc all with trunc flag
|
|
||||||
*
|
|
||||||
* 3) trunc relative without trunc flag
|
|
||||||
*
|
|
||||||
* Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is
|
|
||||||
* interpreted as SCOLS_FL_TRUNC.
|
|
||||||
*/
|
|
||||||
for (stage = 1; width > tb->termwidth && stage <= 3; ) {
|
|
||||||
size_t org_width = width;
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)",
|
|
||||||
stage, width, tb->termwidth));
|
|
||||||
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
|
|
||||||
int trunc_flag = 0;
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
|
|
||||||
cl->header.data, cl->width, cl->width_treeart));
|
|
||||||
if (scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
if (width <= tb->termwidth)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* never truncate if already minimal width */
|
|
||||||
if (cl->width == cl->width_min)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* never truncate the tree */
|
|
||||||
if (scols_column_is_tree(cl) && width <= cl->width_treeart)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* nothing to truncate */
|
|
||||||
if (cl->width == 0 || width == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
trunc_flag = scols_column_is_trunc(cl)
|
|
||||||
|| (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl));
|
|
||||||
|
|
||||||
switch (stage) {
|
|
||||||
/* #1 stage - trunc relative with TRUNC flag */
|
|
||||||
case 1:
|
|
||||||
if (!trunc_flag) /* ignore: missing flag */
|
|
||||||
break;
|
|
||||||
if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
|
|
||||||
break;
|
|
||||||
if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */
|
|
||||||
break;
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)"));
|
|
||||||
cl->width--;
|
|
||||||
width--;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* #2 stage - trunc all with TRUNC flag */
|
|
||||||
case 2:
|
|
||||||
if (!trunc_flag) /* ignore: missing flag */
|
|
||||||
break;
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(tb, " reducing (all with flag)"));
|
|
||||||
cl->width--;
|
|
||||||
width--;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* #3 stage - trunc relative without flag */
|
|
||||||
case 3:
|
|
||||||
if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
|
|
||||||
break;
|
|
||||||
|
|
||||||
DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)"));
|
|
||||||
cl->width--;
|
|
||||||
width--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hide zero width columns */
|
|
||||||
if (cl->width == 0)
|
|
||||||
cl->flags |= SCOLS_FL_HIDDEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the current stage is without effect, go to the next */
|
|
||||||
if (org_width == width)
|
|
||||||
stage++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ignore last column(s) or force last column to be truncated if
|
|
||||||
* nowrap mode enabled */
|
|
||||||
if (tb->no_wrap && width > tb->termwidth) {
|
|
||||||
scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
|
|
||||||
while (scols_table_next_column(tb, &itr, &cl) == 0) {
|
|
||||||
|
|
||||||
if (scols_column_is_hidden(cl))
|
|
||||||
continue;
|
|
||||||
if (width <= tb->termwidth)
|
|
||||||
break;
|
|
||||||
if (width - cl->width < tb->termwidth) {
|
|
||||||
size_t r = width - tb->termwidth;
|
|
||||||
|
|
||||||
cl->flags |= SCOLS_FL_TRUNC;
|
|
||||||
cl->width -= r;
|
|
||||||
width -= r;
|
|
||||||
} else {
|
|
||||||
cl->flags |= SCOLS_FL_HIDDEN;
|
|
||||||
width -= cl->width + colsepsz;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done:
|
|
||||||
DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
|
|
||||||
ON_DBG(TAB, dbg_columns(tb));
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t strlen_line(struct libscols_line *ln)
|
static size_t strlen_line(struct libscols_line *ln)
|
||||||
{
|
{
|
||||||
size_t i, sz = 0;
|
size_t i, sz = 0;
|
||||||
|
@ -1381,7 +989,7 @@ static int initialize_printing(struct libscols_table *tb, struct libscols_buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb->format == SCOLS_FMT_HUMAN) {
|
if (tb->format == SCOLS_FMT_HUMAN) {
|
||||||
rc = recount_widths(tb, *buf);
|
rc = __scols_calculate(tb, *buf);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,9 @@ struct libscols_column {
|
||||||
struct libscols_table *table;
|
struct libscols_table *table;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
|
||||||
|
#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Table line
|
* Table line
|
||||||
*/
|
*/
|
||||||
|
@ -214,6 +217,8 @@ static inline int scols_iter_is_last(const struct libscols_iter *itr)
|
||||||
return itr->p == itr->head;
|
return itr->p == itr->head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* buffer.c
|
* buffer.c
|
||||||
*/
|
*/
|
||||||
|
@ -232,6 +237,18 @@ extern char *buffer_get_safe_data(struct libscols_table *tb,
|
||||||
const char *safechars);
|
const char *safechars);
|
||||||
extern size_t buffer_get_safe_art_size(struct libscols_buffer *buf);
|
extern size_t buffer_get_safe_art_size(struct libscols_buffer *buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate.c
|
||||||
|
*/
|
||||||
|
extern int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print.c
|
||||||
|
*/
|
||||||
|
extern int __cell_to_buffer(struct libscols_table *tb,
|
||||||
|
struct libscols_line *ln,
|
||||||
|
struct libscols_column *cl,
|
||||||
|
struct libscols_buffer *buf);
|
||||||
|
|
||||||
static inline int is_last_child(struct libscols_line *ln)
|
static inline int is_last_child(struct libscols_line *ln)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue