]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libsmartcols/src/calculate.c
[clang-tidy] do not use else after return
[thirdparty/util-linux.git] / libsmartcols / src / calculate.c
CommitLineData
06a8decd
KZ
1#include "smartcolsP.h"
2#include "mbsalign.h"
3
4static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
5{
6 if (scols_column_is_hidden(cl)) {
7 DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data));
8 return;
9 }
10
11 DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
12 "hint=%d, avg=%zu, max=%zu, min=%zu, "
13 "extreme=%s %s",
14
15 cl->header.data, cl->seqnum, cl->width,
16 cl->width_hint > 1 ? (int) cl->width_hint :
17 (int) (cl->width_hint * tb->termwidth),
18 cl->width_avg,
19 cl->width_max,
20 cl->width_min,
21 cl->is_extreme ? "yes" : "not",
22 cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
23}
24
25static void dbg_columns(struct libscols_table *tb)
26{
27 struct libscols_iter itr;
28 struct libscols_column *cl;
29
30 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
31 while (scols_table_next_column(tb, &itr, &cl) == 0)
32 dbg_column(tb, cl);
33}
34
281a2f32
KZ
35static int count_cell_width(struct libscols_table *tb,
36 struct libscols_line *ln,
37 struct libscols_column *cl,
38 struct libscols_buffer *buf)
39{
40 size_t len;
41 char *data;
42 int rc;
43
44 rc = __cell_to_buffer(tb, ln, cl, buf);
45 if (rc)
46 return rc;
47
48 data = buffer_get_data(buf);
49 if (!data)
50 len = 0;
51 else if (scols_column_is_customwrap(cl))
52 len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
53 else
54 len = mbs_safe_width(data);
55
56 if (len == (size_t) -1) /* ignore broken multibyte strings */
57 len = 0;
58 cl->width_max = max(len, cl->width_max);
59
60 if (cl->is_extreme && cl->width_avg && len > cl->width_avg * 2)
61 return 0;
62
042f62df 63 if (scols_column_is_noextremes(cl)) {
281a2f32
KZ
64 cl->extreme_sum += len;
65 cl->extreme_count++;
66 }
67 cl->width = max(len, cl->width);
68 if (scols_column_is_tree(cl)) {
69 size_t treewidth = buffer_get_safe_art_size(buf);
70 cl->width_treeart = max(cl->width_treeart, treewidth);
71 }
72 return 0;
73}
74
47b6eccc
KZ
75
76static int walk_count_cell_width(struct libscols_table *tb,
77 struct libscols_line *ln,
78 struct libscols_column *cl,
79 void *data)
80{
81 return count_cell_width(tb, ln, cl, (struct libscols_buffer *) data);
82}
83
06a8decd
KZ
84/*
85 * This function counts column width.
86 *
87 * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
88 * two times. The first pass counts the width and average width. If the column
89 * contains fields that are too large (a width greater than 2 * average) then
90 * the column is marked as "extreme". In the second pass all extreme fields
91 * are ignored and the column width is counted from non-extreme fields only.
92 */
93static int count_column_width(struct libscols_table *tb,
94 struct libscols_column *cl,
95 struct libscols_buffer *buf)
96{
281a2f32 97 int rc = 0, no_header = 0;
06a8decd
KZ
98
99 assert(tb);
100 assert(cl);
101
102 cl->width = 0;
103 if (!cl->width_min) {
104 if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) {
105 cl->width_min = (size_t) (cl->width_hint * tb->termwidth);
106 if (cl->width_min && !is_last_column(cl))
107 cl->width_min--;
108 }
109 if (scols_cell_get_data(&cl->header)) {
110 size_t len = mbs_safe_width(scols_cell_get_data(&cl->header));
111 cl->width_min = max(cl->width_min, len);
112 } else
113 no_header = 1;
114
115 if (!cl->width_min)
116 cl->width_min = 1;
117 }
118
47b6eccc
KZ
119 if (scols_table_is_tree(tb)) {
120 /* Count width for tree */
121 rc = scols_walk_tree(tb, cl, walk_count_cell_width, (void *) buf);
06a8decd
KZ
122 if (rc)
123 goto done;
47b6eccc
KZ
124 } else {
125 /* Count width for list */
126 struct libscols_iter itr;
127 struct libscols_line *ln;
128
129 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
130 while (scols_table_next_line(tb, &itr, &ln) == 0) {
131 rc = count_cell_width(tb, ln, cl, buf);
132 if (rc)
133 goto done;
134 }
06a8decd
KZ
135 }
136
ea7fb72e
KZ
137 if (scols_column_is_tree(cl) && has_groups(tb)) {
138 /* We don't fill buffer with groups tree ascii art during width
218b1dd6 139 * calculation. The print function only enlarge grpset[] and we
ea7fb72e
KZ
140 * calculate final width from grpset_size.
141 */
142 size_t gprwidth = tb->grpset_size + 1;
143 cl->width_treeart += gprwidth;
144 cl->width_max += gprwidth;
145 cl->width += gprwidth;
281a2f32
KZ
146 if (cl->extreme_count)
147 cl->extreme_sum += gprwidth;
ea7fb72e
KZ
148 }
149
281a2f32
KZ
150 if (cl->extreme_count && cl->width_avg == 0) {
151 cl->width_avg = cl->extreme_sum / cl->extreme_count;
06a8decd
KZ
152 if (cl->width_avg && cl->width_max > cl->width_avg * 2)
153 cl->is_extreme = 1;
154 }
155
156 /* enlarge to minimal width */
157 if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
158 cl->width = cl->width_min;
159
160 /* use absolute size for large columns */
161 else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
162 && cl->width_min < (size_t) cl->width_hint)
163
164 cl->width = (size_t) cl->width_hint;
165
166
167 /* Column without header and data, set minimal size to zero (default is 1) */
168 if (cl->width_max == 0 && no_header && cl->width_min == 1 && cl->width <= 1)
169 cl->width = cl->width_min = 0;
170
171done:
172 ON_DBG(COL, dbg_column(tb, cl));
173 return rc;
174}
175
176/*
177 * This is core of the scols_* voodoo...
178 */
179int __scols_calculate(struct libscols_table *tb, struct libscols_buffer *buf)
180{
181 struct libscols_column *cl;
182 struct libscols_iter itr;
183 size_t width = 0, width_min = 0; /* output width */
184 int stage, rc = 0;
d52f5542 185 int extremes = 0, group_ncolumns = 0;
06a8decd
KZ
186 size_t colsepsz;
187
188
d52f5542 189 DBG(TAB, ul_debugobj(tb, "-----calculate-(termwidth=%zu)-----", tb->termwidth));
ea7fb72e 190 tb->is_dummy_print = 1;
06a8decd
KZ
191
192 colsepsz = mbs_safe_width(colsep(tb));
193
ea7fb72e 194 if (has_groups(tb))
d52f5542 195 group_ncolumns = 1;
d52f5542 196
06a8decd
KZ
197 /* set basic columns width
198 */
199 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
200 while (scols_table_next_column(tb, &itr, &cl) == 0) {
201 int is_last;
202
203 if (scols_column_is_hidden(cl))
204 continue;
d52f5542
KZ
205
206 /* we print groups chart only for the for the first tree column */
207 if (scols_column_is_tree(cl) && group_ncolumns == 1) {
208 cl->is_groups = 1;
209 group_ncolumns++;
210 }
211
06a8decd
KZ
212 rc = count_column_width(tb, cl, buf);
213 if (rc)
214 goto done;
215
216 is_last = is_last_column(cl);
217
218 width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */
219 width_min += cl->width_min + (is_last ? 0 : colsepsz);
d52f5542
KZ
220 if (cl->is_extreme)
221 extremes++;
06a8decd
KZ
222 }
223
224 if (!tb->is_term) {
225 DBG(TAB, ul_debugobj(tb, " non-terminal output"));
226 goto done;
227 }
228
229 /* be paranoid */
230 if (width_min > tb->termwidth && scols_table_is_maxout(tb)) {
231 DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth));
232
233 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
234 while (width_min > tb->termwidth
235 && scols_table_next_column(tb, &itr, &cl) == 0) {
236 if (scols_column_is_hidden(cl))
237 continue;
238 width_min--;
239 cl->width_min--;
240 }
241 DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
242 }
243
244 /* reduce columns with extreme fields */
245 if (width > tb->termwidth && extremes) {
246 DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
247
248 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
249 while (scols_table_next_column(tb, &itr, &cl) == 0) {
250 size_t org_width;
251
252 if (!cl->is_extreme || scols_column_is_hidden(cl))
253 continue;
254
255 org_width = cl->width;
256 rc = count_column_width(tb, cl, buf);
257 if (rc)
258 goto done;
259
260 if (org_width > cl->width)
261 width -= org_width - cl->width;
262 else
263 extremes--; /* hmm... nothing reduced */
264 }
265 }
266
267 if (width < tb->termwidth) {
268 if (extremes) {
269 DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
270
271 /* enlarge the first extreme column */
272 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
273 while (scols_table_next_column(tb, &itr, &cl) == 0) {
274 size_t add;
275
276 if (!cl->is_extreme || scols_column_is_hidden(cl))
277 continue;
278
279 /* this column is too large, ignore?
280 if (cl->width_max - cl->width >
281 (tb->termwidth - width))
282 continue;
283 */
284
285 add = tb->termwidth - width;
286 if (add && cl->width + add > cl->width_max)
287 add = cl->width_max - cl->width;
288
289 cl->width += add;
290 width += add;
291
292 if (width == tb->termwidth)
293 break;
294 }
295 }
296
297 if (width < tb->termwidth && scols_table_is_maxout(tb)) {
298 DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
299
300 /* try enlarging all columns */
301 while (width < tb->termwidth) {
302 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
303 while (scols_table_next_column(tb, &itr, &cl) == 0) {
304 if (scols_column_is_hidden(cl))
305 continue;
306 cl->width++;
307 width++;
308 if (width == tb->termwidth)
309 break;
310 }
311 }
312 } else if (width < tb->termwidth) {
313 /* enlarge the last column */
314 struct libscols_column *col = list_entry(
315 tb->tb_columns.prev, struct libscols_column, cl_columns);
316
317 DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
318
319 if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
320 col->width += tb->termwidth - width;
321 width = tb->termwidth;
322 }
323 }
324 }
325
326 /* bad, we have to reduce output width, this is done in three stages:
327 *
328 * 1) trunc relative with trunc flag if the column width is greater than
329 * expected column width (it means "width_hint * terminal_width").
330 *
331 * 2) trunc all with trunc flag
332 *
333 * 3) trunc relative without trunc flag
334 *
335 * Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is
336 * interpreted as SCOLS_FL_TRUNC.
337 */
338 for (stage = 1; width > tb->termwidth && stage <= 3; ) {
339 size_t org_width = width;
340
341 DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)",
342 stage, width, tb->termwidth));
343
344 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
345 while (scols_table_next_column(tb, &itr, &cl) == 0) {
346
347 int trunc_flag = 0;
348
349 DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
350 cl->header.data, cl->width, cl->width_treeart));
351 if (scols_column_is_hidden(cl))
352 continue;
353 if (width <= tb->termwidth)
354 break;
355
356 /* never truncate if already minimal width */
357 if (cl->width == cl->width_min)
358 continue;
359
360 /* never truncate the tree */
361 if (scols_column_is_tree(cl) && width <= cl->width_treeart)
362 continue;
363
364 /* nothing to truncate */
f6b6beaf 365 if (cl->width == 0)
06a8decd
KZ
366 continue;
367
368 trunc_flag = scols_column_is_trunc(cl)
369 || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl));
370
371 switch (stage) {
372 /* #1 stage - trunc relative with TRUNC flag */
373 case 1:
374 if (!trunc_flag) /* ignore: missing flag */
375 break;
376 if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
377 break;
378 if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */
379 break;
380
381 DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)"));
382 cl->width--;
383 width--;
384 break;
385
386 /* #2 stage - trunc all with TRUNC flag */
387 case 2:
388 if (!trunc_flag) /* ignore: missing flag */
389 break;
390
391 DBG(TAB, ul_debugobj(tb, " reducing (all with flag)"));
392 cl->width--;
393 width--;
394 break;
395
396 /* #3 stage - trunc relative without flag */
397 case 3:
398 if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
399 break;
400
401 DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)"));
402 cl->width--;
403 width--;
404 break;
405 }
406
407 /* hide zero width columns */
408 if (cl->width == 0)
409 cl->flags |= SCOLS_FL_HIDDEN;
410 }
411
412 /* the current stage is without effect, go to the next */
413 if (org_width == width)
414 stage++;
415 }
416
417 /* ignore last column(s) or force last column to be truncated if
418 * nowrap mode enabled */
419 if (tb->no_wrap && width > tb->termwidth) {
420 scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
421 while (scols_table_next_column(tb, &itr, &cl) == 0) {
422
423 if (scols_column_is_hidden(cl))
424 continue;
425 if (width <= tb->termwidth)
426 break;
427 if (width - cl->width < tb->termwidth) {
428 size_t r = width - tb->termwidth;
429
430 cl->flags |= SCOLS_FL_TRUNC;
431 cl->width -= r;
432 width -= r;
433 } else {
434 cl->flags |= SCOLS_FL_HIDDEN;
435 width -= cl->width + colsepsz;
436 }
437 }
438 }
439done:
ea7fb72e 440 tb->is_dummy_print = 0;
883246a1 441 DBG(TAB, ul_debugobj(tb, "-----final width: %zu (rc=%d)-----", width, rc));
06a8decd
KZ
442 ON_DBG(TAB, dbg_columns(tb));
443
444 return rc;
445}