From: Alessandro Ratti Date: Sun, 28 Dec 2025 18:07:55 +0000 (+0100) Subject: libsmartcols: avoid cumulative width reduction during printing X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=504456effff6740d89eb6735b4eba33e54aca3ad;p=thirdparty%2Futil-linux.git libsmartcols: avoid cumulative width reduction during printing __scols_initialize_printing() applied scols_table_reduce_termwidth() by mutating table->termwidth via scols_table_set_termwidth(). When printing is performed repeatedly on the same table (e.g. in interactive applications), this causes the effective output width to shrink on each invocation, eventually truncating output. Introduce an internally computed effective output width and stop modifying termwidth during printing. All layout calculations are updated to use the derived width instead, making the printing path idempotent. No functional change is expected for single-shot printing. Addresses: https://github.com/util-linux/util-linux/issues/3895 Suggested-by: Karel Zak Signed-off-by: Alessandro Ratti --- diff --git a/libsmartcols/src/calculate.c b/libsmartcols/src/calculate.c index 30d67c149..dbf04af15 100644 --- a/libsmartcols/src/calculate.c +++ b/libsmartcols/src/calculate.c @@ -18,7 +18,7 @@ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl) cl->seqnum, cl->header.data, cl->width, cl->width_hint >= 1.0 ? (int) cl->width_hint : - (int) (cl->width_hint * tb->termwidth), + (int) (cl->width_hint * tb->outwidth), st->width_max, st->width_min, cl->flags, @@ -166,7 +166,7 @@ static int count_column_width(struct libscols_table *tb, /* set minimal width according to width_hint */ if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) { - st->width_min = (size_t) (cl->width_hint * tb->termwidth); + st->width_min = (size_t) (cl->width_hint * tb->outwidth); if (st->width_min && !is_last_column(cl)) st->width_min--; } @@ -308,7 +308,7 @@ static int reduce_column(struct libscols_table *tb, if (stage > 6) return -1; - if (tb->termwidth >= *width) + if (tb->outwidth >= *width) return 1; /* ignore hidden columns */ if (scols_column_is_hidden(cl)) @@ -324,7 +324,7 @@ static int reduce_column(struct libscols_table *tb, return 0; org_width = cl->width; - wanted = *width - tb->termwidth; + wanted = *width - tb->outwidth; is_trunc = scols_column_is_trunc(cl) || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)); @@ -363,7 +363,7 @@ static int reduce_column(struct libscols_table *tb, break; if (cl->width_hint <= 0 || cl->width_hint >= 1) break; - if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) + if (cl->width < (size_t) (cl->width_hint * tb->outwidth)) break; reduce_to_68(cl, wanted); break; @@ -420,7 +420,7 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) size_t colsepsz; int sorted = 0; - DBG(TAB, ul_debugobj(tb, "-----calculate-(termwidth=%zu)-----", tb->termwidth)); + DBG(TAB, ul_debugobj(tb, "-----calculate-(termwidth=%zu, outwidth=%zu)-----", tb->termwidth, tb->outwidth)); tb->is_dummy_print = 1; colsepsz = scols_table_is_noencoding(tb) ? mbs_width(colsep(tb)) : @@ -465,10 +465,11 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) } /* 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)); + if (width_min > tb->outwidth && scols_table_is_maxout(tb)) { + DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu, avail=%zu]", + width_min, tb->termwidth, tb->outwidth)); scols_reset_iter(&itr, SCOLS_ITER_FORWARD); - while (width_min > tb->termwidth + while (width_min > tb->outwidth && scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) @@ -493,7 +494,7 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) last_cl = list_entry(tb->tb_columns.prev, struct libscols_column, cl_columns); /* reduce columns width */ - while (width > tb->termwidth) { + while (width > tb->outwidth) { size_t org_width = width; int xrc = 0, n = 0; @@ -504,12 +505,12 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) sorted = 1; } - DBG(TAB, ul_debugobj(tb, "#%d reduce stage (width=%zu, term=%zu)", - stage, width, tb->termwidth)); + DBG(TAB, ul_debugobj(tb, "#%d reduce stage (width=%zu, avail=%zu)", + stage, width, tb->outwidth)); scols_reset_iter(&itr, SCOLS_ITER_BACKWARD); - while (width > tb->termwidth + while (width > tb->outwidth && xrc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { xrc = reduce_column(tb, cl, &width, stage, n++); @@ -522,9 +523,9 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) } /* enlarge */ - if (width < tb->termwidth) { + if (width < tb->outwidth) { DBG(TAB, ul_debugobj(tb, " enlarge (extreme, available %zu)", - tb->termwidth - width)); + tb->outwidth - width)); if (ignore_extremes) { if (!sorted) { sort_columns(tb, cmp_deviation); @@ -542,7 +543,7 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) if (cl->width >= cl->wstat.width_max) continue; - add = tb->termwidth - width; + add = tb->outwidth - width; if (add && cl->wstat.width_max && cl->width + add > cl->wstat.width_max) add = cl->wstat.width_max - cl->width; @@ -554,17 +555,17 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) cl->width += add; width += add; - if (width == tb->termwidth) + if (width == tb->outwidth) break; } } - if (width < tb->termwidth && scols_table_is_maxout(tb)) { + if (width < tb->outwidth && scols_table_is_maxout(tb)) { DBG(TAB, ul_debugobj(tb, " enlarge (max-out, available %zu)", - tb->termwidth - width)); + tb->outwidth - width)); /* try enlarging all columns */ - while (width < tb->termwidth) { + while (width < tb->outwidth) { scols_reset_iter(&itr, SCOLS_ITER_BACKWARD); while (scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) @@ -573,21 +574,21 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) cl->header.data)); cl->width++; width++; - if (width == tb->termwidth) + if (width == tb->outwidth) break; } } - } else if (width < tb->termwidth) { + } else if (width < tb->outwidth) { /* enlarge the last column */ DBG(TAB, ul_debugobj(tb, " enlarge (last column, available %zu)", - tb->termwidth - width)); + tb->outwidth - width)); if (!scols_column_is_right(last_cl)) { DBG(COL, ul_debugobj(last_cl, " add +%zu (%s)", - tb->termwidth - width, + tb->outwidth - width, last_cl->header.data)); - last_cl->width += tb->termwidth - width; - width = tb->termwidth; + last_cl->width += tb->outwidth - width; + width = tb->outwidth; } } } @@ -600,16 +601,16 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) /* ignore last column(s) or force last column to be truncated if * nowrap mode enabled */ - if (tb->no_wrap && width > tb->termwidth) { + if (tb->no_wrap && width > tb->outwidth) { 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) + if (width <= tb->outwidth) break; - if (width - cl->width < tb->termwidth) { - size_t r = width - tb->termwidth; + if (width - cl->width < tb->outwidth) { + size_t r = width - tb->outwidth; cl->flags |= SCOLS_FL_TRUNC; cl->width -= r; diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c index e51046dc9..c1b81c50f 100644 --- a/libsmartcols/src/print.c +++ b/libsmartcols/src/print.c @@ -1265,14 +1265,12 @@ int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf if (tb->is_term) { size_t width = (size_t) scols_table_get_termwidth(tb); - - if (tb->termreduce > 0 && tb->termreduce < width) { - width -= tb->termreduce; - scols_table_set_termwidth(tb, width); - } - bufsz = width; - } else + tb->outwidth = width > tb->termreduce ? width - tb->termreduce : width; + bufsz = tb->outwidth; + } else { bufsz = BUFSIZ; + tb->outwidth = bufsz; + } if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb)) tb->header_repeat = 0; diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 9ab7e9da5..1e9dcec01 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -235,6 +235,7 @@ struct libscols_table { size_t termwidth; /* terminal width (number of columns) */ size_t termheight; /* terminal height (number of lines) */ size_t termreduce; /* extra blank space */ + size_t outwidth; /* effective output width (calculated internally) */ int termforce; /* SCOLS_TERMFORCE_* */ FILE *out; /* output stream */