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/version.c \
|
||||
libsmartcols/src/buffer.c \
|
||||
libsmartcols/src/calculate.c \
|
||||
libsmartcols/src/init.c
|
||||
|
||||
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 "smartcolsP.h"
|
||||
|
||||
#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
|
||||
#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
|
||||
|
||||
/* Fallback for symbols
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
static int cell_to_buffer(struct libscols_table *tb,
|
||||
int __cell_to_buffer(struct libscols_table *tb,
|
||||
struct libscols_line *ln,
|
||||
struct libscols_column *cl,
|
||||
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) {
|
||||
if (scols_column_is_hidden(cl))
|
||||
continue;
|
||||
rc = cell_to_buffer(tb, ln, cl, buf);
|
||||
rc = __cell_to_buffer(tb, ln, cl, buf);
|
||||
if (rc == 0)
|
||||
rc = print_data(tb, cl, ln,
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
rc = recount_widths(tb, *buf);
|
||||
rc = __scols_calculate(tb, *buf);
|
||||
if (rc != 0)
|
||||
goto err;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,9 @@ struct libscols_column {
|
|||
struct libscols_table *table;
|
||||
};
|
||||
|
||||
#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
|
||||
#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
|
||||
|
||||
/*
|
||||
* Table line
|
||||
*/
|
||||
|
@ -214,6 +217,8 @@ static inline int scols_iter_is_last(const struct libscols_iter *itr)
|
|||
return itr->p == itr->head;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* buffer.c
|
||||
*/
|
||||
|
@ -232,6 +237,18 @@ extern char *buffer_get_safe_data(struct libscols_table *tb,
|
|||
const char *safechars);
|
||||
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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue