buf[bufsz - 1] = '\0';
return buf;
}
+
+ /*
+ * Tree stuff
+ */
if (ln->parent) {
p = line_get_ascii_art(ln->parent, buf, &bufsz);
if (!p)
return buf;
}
-static void recount_widths(struct tt *tb, char *buf, size_t bufsz)
+/*
+ * This function counts column width.
+ *
+ * For the TT_FL_NOEXTREMES columns is possible to call this function two
+ * times. The first pass counts width and average width. If the column
+ * contains too large fields (width greater than 2 * average) then the column
+ * is marked as "extreme". In the second pass all extreme fields are ignored
+ * and column width is counted from non-extreme fields only.
+ */
+static size_t count_column_width(struct tt *tb, struct tt_column *cl,
+ char *buf, size_t bufsz)
{
- struct list_head *p;
- size_t width = 0;
- int trunc_only;
+ struct list_head *lp;
+ int count = 0;
+ size_t sum = 0;
- /* set width according to the size of data
- */
- list_for_each(p, &tb->tb_columns) {
- struct tt_column *cl =
- list_entry(p, struct tt_column, cl_columns);
- struct list_head *lp;
+ cl->width = 0;
- list_for_each(lp, &tb->tb_lines) {
- struct tt_line *ln =
- list_entry(lp, struct tt_line, ln_lines);
+ list_for_each(lp, &tb->tb_lines) {
+ struct tt_line *ln = list_entry(lp, struct tt_line, ln_lines);
+ char *data = line_get_data(ln, cl, buf, bufsz);
+ size_t len = data ? mbs_width(data) : 0;
- char *data = line_get_data(ln, cl, buf, bufsz);
- size_t len = data ? mbs_width(data) : 0;
+ if (len > cl->width_max)
+ cl->width_max = len;
- if (cl->width < len)
- cl->width = len;
+ if (cl->is_extreme && len > cl->width_avg * 2)
+ continue;
+ else if (cl->flags & TT_FL_NOEXTREMES) {
+ sum += len;
+ count++;
}
+ if (len > cl->width)
+ cl->width = len;
+ }
+
+ if (count && cl->width_avg == 0) {
+ cl->width_avg = sum / count;
+
+ if (cl->width_max > cl->width_avg * 2)
+ cl->is_extreme = 1;
}
- /* set minimal width (= size of column header)
+ /* check and set minimal column width */
+ if (cl->name)
+ cl->width_min = mbs_width(cl->name);
+
+ /* enlarge to minimal width */
+ if (cl->width < cl->width_min && !(cl->flags & TT_FL_STRICTWIDTH))
+ cl->width = cl->width_min;
+
+ /* use relative 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;
+}
+
+/*
+ * This is core of the tt_* voodo...
+ */
+static void recount_widths(struct tt *tb, char *buf, size_t bufsz)
+{
+ struct list_head *p;
+ size_t width = 0; /* output width */
+ int trunc_only;
+ int extremes = 0;
+
+ /* set basic columns width
*/
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
- if (cl->name)
- cl->width_min = mbs_width(cl->name);
+ count_column_width(tb, cl, buf, bufsz);
+ width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
+ extremes += cl->is_extreme;
+ }
- if (cl->width < cl->width_min &&
- !(cl->flags & TT_FL_STRICTWIDTH))
- cl->width = cl->width_min;
+ /* reduce columns with extreme fields
+ */
+ if (width > tb->termwidth && extremes) {
+ list_for_each(p, &tb->tb_columns) {
+ struct tt_column *cl = list_entry(p, struct tt_column, cl_columns);
+ size_t org_width;
- else if (cl->width_hint >= 1 &&
- cl->width < (size_t) cl->width_hint &&
- cl->width_min < (size_t) cl->width_hint)
+ if (!cl->is_extreme)
+ continue;
- cl->width = (size_t) cl->width_hint;
+ org_width = cl->width;
+ count_column_width(tb, cl, buf, bufsz);
- width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
+ if (org_width > cl->width)
+ width -= org_width - cl->width;
+ else
+ extremes--; /* hmm... nothing reduced */
+ }
}
- if (width == tb->termwidth)
- goto leave;
if (width < tb->termwidth) {
- /* cool, use the extra space for the last column */
- struct tt_column *cl = list_entry(
- tb->tb_columns.prev, struct tt_column, cl_columns);
-
- if (!(cl->flags & TT_FL_RIGHT) && tb->termwidth - width > 0)
- cl->width += tb->termwidth - width;
- goto leave;
+ /* cool, use the extra space for the extreme columns or/and last column
+ */
+ if (extremes) {
+ /* enlarge the first extreme column */
+ list_for_each(p, &tb->tb_columns) {
+ struct tt_column *cl =
+ list_entry(p, struct tt_column, cl_columns);
+ size_t add;
+
+ if (!cl->is_extreme)
+ 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) {
+ /* enalarge the last column */
+ struct tt_column *cl = list_entry(
+ tb->tb_columns.prev, struct tt_column, cl_columns);
+
+ if (!(cl->flags & TT_FL_RIGHT) && tb->termwidth - width > 0) {
+ cl->width += tb->termwidth - width;
+ width = tb->termwidth;
+ }
+ }
}
/* bad, we have to reduce output width, this is done in two steps:
* 2) reduce columns with a relative width without truncate flag
*/
trunc_only = 1;
- while(width > tb->termwidth) {
+ while (width > tb->termwidth) {
size_t org = width;
list_for_each(p, &tb->tb_columns) {
cl->width--;
width--;
}
+
}
if (org == width) {
if (trunc_only)
break;
}
}
-leave:
+
/*
fprintf(stderr, "terminal: %d, output: %d\n", tb->termwidth, width);
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
- fprintf(stderr, "width: %s=%zd [hint=%d]\n",
+ fprintf(stderr, "width: %s=%zd [hint=%d, avg=%zd, max=%zd, extreme=%s]\n",
cl->name, cl->width,
cl->width_hint > 1 ? (int) cl->width_hint :
- (int) (cl->width_hint * tb->termwidth));
+ (int) (cl->width_hint * tb->termwidth),
+ cl->width_avg,
+ cl->width_max,
+ cl->is_extreme ? "yes" : "not");
}
*/
return;