]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libsmartcols/src/table_print.c
2 * table.c - functions handling the data at the table level
4 * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
5 * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
7 * This file may be redistributed under the terms of the
8 * GNU Lesser General Public License.
12 * SECTION: table_print
14 * @short_description: output functions
29 #include "carefulputc.h"
30 #include "smartcolsP.h"
32 /* This is private struct to work with output data */
33 struct libscols_buffer
{
34 char *begin
; /* begin of the buffer */
35 char *cur
; /* current end of the buffer */
36 char *encdata
; /* encoded buffer mbs_safe_encode() */
38 size_t bufsz
; /* size of the buffer */
39 size_t art_idx
; /* begin of the tree ascii art or zero */
42 static struct libscols_buffer
*new_buffer(size_t sz
)
44 struct libscols_buffer
*buf
= malloc(sz
+ sizeof(struct libscols_buffer
));
49 buf
->cur
= buf
->begin
= ((char *) buf
) + sizeof(struct libscols_buffer
);
53 DBG(BUFF
, ul_debugobj(buf
, "alloc (size=%zu)", sz
));
57 static void free_buffer(struct libscols_buffer
*buf
)
61 DBG(BUFF
, ul_debugobj(buf
, "dealloc"));
66 static int buffer_reset_data(struct libscols_buffer
*buf
)
71 /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/
73 buf
->cur
= buf
->begin
;
78 static int buffer_append_data(struct libscols_buffer
*buf
, const char *str
)
88 maxsz
= buf
->bufsz
- (buf
->cur
- buf
->begin
);
92 memcpy(buf
->cur
, str
, sz
+ 1);
97 static int buffer_set_data(struct libscols_buffer
*buf
, const char *str
)
99 int rc
= buffer_reset_data(buf
);
100 return rc
? rc
: buffer_append_data(buf
, str
);
103 /* save the current buffer position to art_idx */
104 static void buffer_set_art_index(struct libscols_buffer
*buf
)
107 buf
->art_idx
= buf
->cur
- buf
->begin
;
108 /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/
112 static char *buffer_get_data(struct libscols_buffer
*buf
)
114 return buf
? buf
->begin
: NULL
;
117 /* encode data by mbs_safe_encode() to avoid control and non-printable chars */
118 static char *buffer_get_safe_data(struct libscols_buffer
*buf
, size_t *cells
)
120 char *data
= buffer_get_data(buf
);
127 buf
->encdata
= malloc(mbs_safe_encode_size(buf
->bufsz
) + 1);
132 res
= mbs_safe_encode_to_buffer(data
, cells
, buf
->encdata
);
133 if (!res
|| !*cells
|| *cells
== (size_t) -1)
141 /* returns size in bytes of the ascii art (according to art_idx) in safe encoding */
142 static size_t buffer_get_safe_art_size(struct libscols_buffer
*buf
)
144 char *data
= buffer_get_data(buf
);
147 if (!data
|| !buf
->art_idx
)
150 mbs_safe_nwidth(data
, buf
->art_idx
, &bytes
);
154 /* returns pointer to the end of used data */
155 static int line_ascii_art_to_buffer(struct libscols_table
*tb
,
156 struct libscols_line
*ln
,
157 struct libscols_buffer
*buf
)
168 rc
= line_ascii_art_to_buffer(tb
, ln
->parent
, buf
);
172 if (list_entry_is_last(&ln
->ln_children
, &ln
->parent
->ln_branch
))
175 art
= tb
->symbols
->vert
;
177 return buffer_append_data(buf
, art
);
180 static int is_last_column(struct libscols_column
*cl
)
182 int rc
= list_entry_is_last(&cl
->cl_columns
, &cl
->table
->tb_columns
);
183 struct libscols_column
*next
;
188 next
= list_entry(cl
->cl_columns
.next
, struct libscols_column
, cl_columns
);
189 if (next
&& scols_column_is_hidden(next
))
194 #define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
195 #define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
198 static int has_pending_data(struct libscols_table
*tb
)
200 struct libscols_column
*cl
;
201 struct libscols_iter itr
;
203 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
204 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
205 if (scols_column_is_hidden(cl
))
207 if (cl
->pending_data
)
213 /* print padding or ASCII-art instead of data of @cl */
214 static void print_empty_cell(struct libscols_table
*tb
,
215 struct libscols_column
*cl
,
216 struct libscols_line
*ln
, /* optional */
219 size_t len_pad
= 0; /* in screen cells as opposed to bytes */
221 /* generate tree ASCII-art rather than padding */
222 if (ln
&& scols_column_is_tree(cl
)) {
224 /* only print symbols->vert if followed by child */
225 if (!list_empty(&ln
->ln_branch
)) {
226 fputs(tb
->symbols
->vert
, tb
->out
);
227 len_pad
= mbs_safe_width(tb
->symbols
->vert
);
230 /* use the same draw function as though we were intending to draw an L-shape */
231 struct libscols_buffer
*art
= new_buffer(bufsz
);
235 /* whatever the rc, len_pad will be sensible */
236 line_ascii_art_to_buffer(tb
, ln
, art
);
237 if (!list_empty(&ln
->ln_branch
) && has_pending_data(tb
))
238 buffer_append_data(art
, tb
->symbols
->vert
);
239 data
= buffer_get_safe_data(art
, &len_pad
);
241 fputs(data
, tb
->out
);
246 /* fill rest of cell with space */
247 for(; len_pad
<= cl
->width
; ++len_pad
)
252 static const char *get_cell_color(struct libscols_table
*tb
,
253 struct libscols_column
*cl
,
254 struct libscols_line
*ln
, /* optional */
255 struct libscols_cell
*ce
) /* optional */
257 const char *color
= NULL
;
259 if (tb
&& tb
->colors_wanted
) {
270 /* Fill the start of a line with padding (or with tree ascii-art).
272 * This is necessary after a long non-truncated column, as this requires the
273 * next column to be printed on the next line. For example (see 'DDD'):
275 * aaa bbb ccc ddd eee
281 static void print_newline_padding(struct libscols_table
*tb
,
282 struct libscols_column
*cl
,
283 struct libscols_line
*ln
, /* optional */
291 fputs(linesep(tb
), tb
->out
); /* line break */
293 /* fill cells after line break */
294 for (i
= 0; i
<= (size_t) cl
->seqnum
; i
++)
295 print_empty_cell(tb
, scols_table_get_column(tb
, i
), ln
, bufsz
);
301 * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
302 * printed as usually and output is truncated to match column width.
304 * The rest of the long text is printed on next extra line(s). The extra lines
305 * don't exist in the table (not represented by libscols_line). The data for
306 * the extra lines are stored in libscols_column->pending_data_buf and the
307 * function print_line() adds extra lines until the buffer is not empty in all
311 /* set data that will be printed by extra lines */
312 static int set_pending_data(struct libscols_column
*cl
, const char *data
, size_t sz
)
317 DBG(COL
, ul_debugobj(cl
, "setting pending data"));
324 free(cl
->pending_data_buf
);
325 cl
->pending_data_buf
= p
;
326 cl
->pending_data_sz
= sz
;
327 cl
->pending_data
= cl
->pending_data_buf
;
331 /* the next extra line has been printed, move pending data cursor */
332 static int step_pending_data(struct libscols_column
*cl
, size_t bytes
)
334 DBG(COL
, ul_debugobj(cl
, "step pending data %zu -= %zu", cl
->pending_data_sz
, bytes
));
336 if (bytes
>= cl
->pending_data_sz
)
337 return set_pending_data(cl
, NULL
, 0);
339 cl
->pending_data
+= bytes
;
340 cl
->pending_data_sz
-= bytes
;
344 /* print next pending data for the column @cl */
345 static int print_pending_data(
346 struct libscols_table
*tb
,
347 struct libscols_column
*cl
,
348 struct libscols_line
*ln
, /* optional */
349 struct libscols_cell
*ce
)
351 const char *color
= get_cell_color(tb
, cl
, ln
, ce
);
352 size_t width
= cl
->width
, bytes
;
353 size_t len
= width
, i
;
356 if (!cl
->pending_data
)
359 DBG(COL
, ul_debugobj(cl
, "printing pending data"));
361 data
= strdup(cl
->pending_data
);
364 bytes
= mbs_truncate(data
, &len
);
365 if (bytes
== (size_t) -1)
368 step_pending_data(cl
, bytes
);
371 fputs(color
, tb
->out
);
372 fputs(data
, tb
->out
);
374 fputs(UL_COLOR_RESET
, tb
->out
);
377 for (i
= len
; i
< width
; i
++)
378 fputc(' ', tb
->out
); /* padding */
380 if (is_last_column(cl
))
383 fputs(colsep(tb
), tb
->out
); /* columns separator */
390 static int print_data(struct libscols_table
*tb
,
391 struct libscols_column
*cl
,
392 struct libscols_line
*ln
, /* optional */
393 struct libscols_cell
*ce
, /* optional */
394 struct libscols_buffer
*buf
)
396 size_t len
= 0, i
, width
, bytes
;
397 const char *color
= NULL
;
403 DBG(TAB
, ul_debugobj(tb
,
404 " -> data, column=%p, line=%p, cell=%p, buff=%p",
407 data
= buffer_get_data(buf
);
411 switch (tb
->format
) {
413 fputs_nonblank(data
, tb
->out
);
414 if (!is_last_column(cl
))
415 fputs(colsep(tb
), tb
->out
);
418 case SCOLS_FMT_EXPORT
:
419 fprintf(tb
->out
, "%s=", scols_cell_get_data(&cl
->header
));
420 fputs_quoted(data
, tb
->out
);
421 if (!is_last_column(cl
))
422 fputs(colsep(tb
), tb
->out
);
426 fputs_quoted_lower(scols_cell_get_data(&cl
->header
), tb
->out
);
427 fputs(": ", tb
->out
);
429 fputs("null", tb
->out
);
431 fputs_quoted(data
, tb
->out
);
432 if (!is_last_column(cl
))
433 fputs(", ", tb
->out
);
436 case SCOLS_FMT_HUMAN
:
437 break; /* continue below */
440 color
= get_cell_color(tb
, cl
, ln
, ce
);
442 /* encode, note that 'len' and 'width' are number of cells, not bytes */
443 data
= buffer_get_safe_data(buf
, &len
);
447 bytes
= strlen(data
);
449 if (is_last_column(cl
)
451 && !scols_table_is_maxout(tb
)
452 && !scols_column_is_right(cl
)
453 && !scols_column_is_wrap(cl
))
457 if (len
> width
&& scols_column_is_trunc(cl
)) {
459 bytes
= mbs_truncate(data
, &len
); /* updates 'len' */
462 /* multi-line cell */
463 if (len
> width
&& scols_column_is_wrap(cl
)) {
464 set_pending_data(cl
, data
, bytes
);
467 bytes
= mbs_truncate(data
, &len
);
468 if (bytes
!= (size_t) -1 && bytes
> 0)
469 step_pending_data(cl
, bytes
);
472 if (bytes
== (size_t) -1) {
478 if (scols_column_is_right(cl
)) {
480 fputs(color
, tb
->out
);
481 for (i
= len
; i
< width
; i
++)
483 fputs(data
, tb
->out
);
485 fputs(UL_COLOR_RESET
, tb
->out
);
490 size_t art
= buffer_get_safe_art_size(buf
);
492 /* we don't want to colorize tree ascii art */
493 if (scols_column_is_tree(cl
) && art
&& art
< bytes
) {
494 fwrite(p
, 1, art
, tb
->out
);
498 fputs(color
, tb
->out
);
500 fputs(UL_COLOR_RESET
, tb
->out
);
502 fputs(data
, tb
->out
);
504 for (i
= len
; i
< width
; i
++)
505 fputc(' ', tb
->out
); /* padding */
507 if (is_last_column(cl
))
510 if (len
> width
&& !scols_column_is_trunc(cl
))
511 print_newline_padding(tb
, cl
, ln
, buf
->bufsz
); /* next column starts on next line */
513 fputs(colsep(tb
), tb
->out
); /* columns separator */
518 static int cell_to_buffer(struct libscols_table
*tb
,
519 struct libscols_line
*ln
,
520 struct libscols_column
*cl
,
521 struct libscols_buffer
*buf
)
524 struct libscols_cell
*ce
;
531 assert(cl
->seqnum
<= tb
->ncols
);
533 buffer_reset_data(buf
);
535 ce
= scols_line_get_cell(ln
, cl
->seqnum
);
536 data
= ce
? scols_cell_get_data(ce
) : NULL
;
540 if (!scols_column_is_tree(cl
))
541 return buffer_set_data(buf
, data
);
546 if (ln
->parent
&& !scols_table_is_json(tb
)) {
547 rc
= line_ascii_art_to_buffer(tb
, ln
->parent
, buf
);
549 if (!rc
&& list_entry_is_last(&ln
->ln_children
, &ln
->parent
->ln_branch
))
550 rc
= buffer_append_data(buf
, tb
->symbols
->right
);
552 rc
= buffer_append_data(buf
, tb
->symbols
->branch
);
554 buffer_set_art_index(buf
);
558 rc
= buffer_append_data(buf
, data
);
562 static void fput_indent(struct libscols_table
*tb
)
566 for (i
= 0; i
<= tb
->indent
; i
++)
570 static void fput_table_open(struct libscols_table
*tb
)
574 if (scols_table_is_json(tb
)) {
576 fputs(linesep(tb
), tb
->out
);
579 fputs_quoted(tb
->name
, tb
->out
);
580 fputs(": [", tb
->out
);
581 fputs(linesep(tb
), tb
->out
);
584 tb
->indent_last_sep
= 1;
588 static void fput_table_close(struct libscols_table
*tb
)
592 if (scols_table_is_json(tb
)) {
596 fputs(linesep(tb
), tb
->out
);
598 fputs(linesep(tb
), tb
->out
);
599 tb
->indent_last_sep
= 1;
603 static void fput_children_open(struct libscols_table
*tb
)
605 if (scols_table_is_json(tb
)) {
607 fputs(linesep(tb
), tb
->out
);
609 fputs("\"children\": [", tb
->out
);
611 /* between parent and child is separator */
612 fputs(linesep(tb
), tb
->out
);
613 tb
->indent_last_sep
= 1;
617 static void fput_children_close(struct libscols_table
*tb
)
621 if (scols_table_is_json(tb
)) {
624 fputs(linesep(tb
), tb
->out
);
625 tb
->indent_last_sep
= 1;
629 static void fput_line_open(struct libscols_table
*tb
)
631 if (scols_table_is_json(tb
)) {
634 tb
->indent_last_sep
= 0;
639 static void fput_line_close(struct libscols_table
*tb
, int last
)
642 if (scols_table_is_json(tb
)) {
643 if (tb
->indent_last_sep
)
645 fputs(last
? "}" : "},", tb
->out
);
648 fputs(linesep(tb
), tb
->out
);
649 tb
->indent_last_sep
= 1;
653 * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and
654 * control and non-printable characters can be encoded in the \x?? encoding.
656 static int print_line(struct libscols_table
*tb
,
657 struct libscols_line
*ln
,
658 struct libscols_buffer
*buf
)
660 int rc
= 0, pending
= 0;
661 struct libscols_column
*cl
;
662 struct libscols_iter itr
;
666 DBG(TAB
, ul_debugobj(tb
, "printing line, line=%p, buff=%p", ln
, buf
));
669 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
670 while (rc
== 0 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
671 if (scols_column_is_hidden(cl
))
673 rc
= cell_to_buffer(tb
, ln
, cl
, buf
);
675 rc
= print_data(tb
, cl
, ln
,
676 scols_line_get_cell(ln
, cl
->seqnum
),
678 if (rc
== 0 && cl
->pending_data
)
682 /* extra lines of the multi-line cells */
683 while (rc
== 0 && pending
) {
685 fputs(linesep(tb
), tb
->out
);
686 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
687 while (rc
== 0 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
688 if (scols_column_is_hidden(cl
))
691 if (cl
->pending_data
) {
692 rc
= print_pending_data(tb
, cl
, ln
, scols_line_get_cell(ln
, cl
->seqnum
));
693 if (rc
== 0 && cl
->pending_data
)
696 print_empty_cell(tb
, cl
, ln
, buf
->bufsz
);
703 static int print_title(struct libscols_table
*tb
)
707 size_t len
= 0, width
;
708 char *title
= NULL
, *buf
= NULL
;
715 DBG(TAB
, ul_debugobj(tb
, "printing title"));
718 len
= mbs_safe_encode_size(strlen(tb
->title
.data
)) + 1;
727 if (!mbs_safe_encode_to_buffer(tb
->title
.data
, &len
, buf
) ||
728 !len
|| len
== (size_t) -1) {
733 /* truncate and align */
734 title
= malloc(tb
->termwidth
+ len
);
740 if (tb
->title
.flags
& SCOLS_CELL_FL_LEFT
)
741 align
= MBS_ALIGN_LEFT
;
742 else if (tb
->title
.flags
& SCOLS_CELL_FL_RIGHT
)
743 align
= MBS_ALIGN_RIGHT
;
744 else if (tb
->title
.flags
& SCOLS_CELL_FL_CENTER
)
745 align
= MBS_ALIGN_CENTER
;
747 align
= MBS_ALIGN_LEFT
; /* default */
749 width
= tb
->termwidth
;
750 rc
= mbsalign_with_padding(buf
, title
, tb
->termwidth
+ len
,
752 0, (int) *tb
->symbols
->title_padding
);
760 fputs(tb
->title
.color
, tb
->out
);
762 fputs(title
, tb
->out
);
765 fputs(UL_COLOR_RESET
, tb
->out
);
766 fputc('\n', tb
->out
);
774 static int print_header(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
777 struct libscols_column
*cl
;
778 struct libscols_iter itr
;
782 if (tb
->header_printed
== 1 ||
783 scols_table_is_noheadings(tb
) ||
784 scols_table_is_export(tb
) ||
785 scols_table_is_json(tb
) ||
786 list_empty(&tb
->tb_lines
))
789 DBG(TAB
, ul_debugobj(tb
, "printing header"));
791 /* set the width according to the size of the data */
792 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
793 while (rc
== 0 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
794 if (scols_column_is_hidden(cl
))
796 rc
= buffer_set_data(buf
, scols_cell_get_data(&cl
->header
));
798 rc
= print_data(tb
, cl
, NULL
, &cl
->header
, buf
);
802 fputs(linesep(tb
), tb
->out
);
804 tb
->header_printed
= 1;
808 static int print_range( struct libscols_table
*tb
,
809 struct libscols_buffer
*buf
,
810 struct libscols_iter
*itr
,
811 struct libscols_line
*end
)
814 struct libscols_line
*ln
;
818 while (rc
== 0 && scols_table_next_line(tb
, itr
, &ln
) == 0) {
821 rc
= print_line(tb
, ln
, buf
);
822 fput_line_close(tb
, scols_iter_is_last(itr
));
824 if (end
&& ln
== end
)
832 static int print_table(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
834 struct libscols_iter itr
;
836 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
837 return print_range(tb
, buf
, &itr
, NULL
);
841 static int print_tree_line(struct libscols_table
*tb
,
842 struct libscols_line
*ln
,
843 struct libscols_buffer
*buf
,
851 rc
= print_line(tb
, ln
, buf
);
855 if (list_empty(&ln
->ln_branch
)) {
856 fput_line_close(tb
, last
);
860 fput_children_open(tb
);
862 /* print all children */
863 list_for_each(p
, &ln
->ln_branch
) {
864 struct libscols_line
*chld
=
865 list_entry(p
, struct libscols_line
, ln_children
);
867 rc
= print_tree_line(tb
, chld
, buf
, p
->next
== &ln
->ln_branch
);
872 fput_children_close(tb
);
874 if (scols_table_is_json(tb
))
875 fput_line_close(tb
, last
);
880 static int print_tree(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
883 struct libscols_line
*ln
, *last
= NULL
;
884 struct libscols_iter itr
;
888 DBG(TAB
, ul_debugobj(tb
, "printing tree"));
890 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
892 while (scols_table_next_line(tb
, &itr
, &ln
) == 0)
893 if (!last
|| !ln
->parent
)
896 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
897 while (rc
== 0 && scols_table_next_line(tb
, &itr
, &ln
) == 0) {
900 rc
= print_tree_line(tb
, ln
, buf
, ln
== last
);
906 static void dbg_column(struct libscols_table
*tb
, struct libscols_column
*cl
)
908 if (scols_column_is_hidden(cl
)) {
909 DBG(COL
, ul_debugobj(cl
, "%s ignored", cl
->header
.data
));
913 DBG(COL
, ul_debugobj(cl
, "%15s seq=%zu, width=%zd, "
914 "hint=%d, avg=%zu, max=%zu, min=%zu, "
917 cl
->header
.data
, cl
->seqnum
, cl
->width
,
918 cl
->width_hint
> 1 ? (int) cl
->width_hint
:
919 (int) (cl
->width_hint
* tb
->termwidth
),
923 cl
->is_extreme
? "yes" : "not",
924 cl
->flags
& SCOLS_FL_TRUNC
? "trunc" : ""));
927 static void dbg_columns(struct libscols_table
*tb
)
929 struct libscols_iter itr
;
930 struct libscols_column
*cl
;
932 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
933 while (scols_table_next_column(tb
, &itr
, &cl
) == 0)
938 * This function counts column width.
940 * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
941 * two times. The first pass counts the width and average width. If the column
942 * contains fields that are too large (a width greater than 2 * average) then
943 * the column is marked as "extreme". In the second pass all extreme fields
944 * are ignored and the column width is counted from non-extreme fields only.
946 static int count_column_width(struct libscols_table
*tb
,
947 struct libscols_column
*cl
,
948 struct libscols_buffer
*buf
)
950 struct libscols_line
*ln
;
951 struct libscols_iter itr
;
952 int count
= 0, rc
= 0;
960 if (!cl
->width_min
) {
961 if (cl
->width_hint
< 1 && scols_table_is_maxout(tb
))
962 cl
->width_min
= (size_t) (cl
->width_hint
* tb
->termwidth
) - (is_last_column(cl
) ? 0 : 1);
963 if (scols_cell_get_data(&cl
->header
)) {
964 size_t len
= mbs_safe_width(scols_cell_get_data(&cl
->header
));
965 cl
->width_min
= max(cl
->width_min
, len
);
969 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
970 while (scols_table_next_line(tb
, &itr
, &ln
) == 0) {
974 rc
= cell_to_buffer(tb
, ln
, cl
, buf
);
978 data
= buffer_get_data(buf
);
979 len
= data
? mbs_safe_width(data
) : 0;
981 if (len
== (size_t) -1) /* ignore broken multibyte strings */
983 cl
->width_max
= max(len
, cl
->width_max
);
985 if (cl
->is_extreme
&& len
> cl
->width_avg
* 2)
987 else if (scols_column_is_noextremes(cl
)) {
991 cl
->width
= max(len
, cl
->width
);
992 if (scols_column_is_tree(cl
)) {
993 size_t treewidth
= buffer_get_safe_art_size(buf
);
994 cl
->width_treeart
= max(cl
->width_treeart
, treewidth
);
998 if (count
&& cl
->width_avg
== 0) {
999 cl
->width_avg
= sum
/ count
;
1000 if (cl
->width_max
> cl
->width_avg
* 2)
1004 /* enlarge to minimal width */
1005 if (cl
->width
< cl
->width_min
&& !scols_column_is_strict_width(cl
))
1006 cl
->width
= cl
->width_min
;
1008 /* use absolute size for large columns */
1009 else if (cl
->width_hint
>= 1 && cl
->width
< (size_t) cl
->width_hint
1010 && cl
->width_min
< (size_t) cl
->width_hint
)
1012 cl
->width
= (size_t) cl
->width_hint
;
1015 ON_DBG(COL
, dbg_column(tb
, cl
));
1020 * This is core of the scols_* voodoo...
1022 static int recount_widths(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
1024 struct libscols_column
*cl
;
1025 struct libscols_iter itr
;
1026 size_t width
= 0, width_min
= 0; /* output width */
1027 int trunc_only
, rc
= 0;
1031 DBG(TAB
, ul_debugobj(tb
, "recounting widths (termwidth=%zu)", tb
->termwidth
));
1033 /* set basic columns width
1035 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1036 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1037 rc
= count_column_width(tb
, cl
, buf
);
1041 width
+= cl
->width
+ (is_last_column(cl
) ? 0 : 1); /* separator for non-last column */
1042 width_min
+= cl
->width_min
+ (is_last_column(cl
) ? 0 : 1);
1043 extremes
+= cl
->is_extreme
;
1047 DBG(TAB
, ul_debugobj(tb
, " non-terminal output"));
1052 if (width_min
> tb
->termwidth
&& scols_table_is_maxout(tb
)) {
1053 DBG(TAB
, ul_debugobj(tb
, " min width larger than terminal! [width=%zu, term=%zu]", width_min
, tb
->termwidth
));
1055 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1056 while (width_min
> tb
->termwidth
1057 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1061 DBG(TAB
, ul_debugobj(tb
, " min width reduced to %zu", width_min
));
1064 /* reduce columns with extreme fields */
1065 if (width
> tb
->termwidth
&& extremes
) {
1066 DBG(TAB
, ul_debugobj(tb
, " reduce width (extreme columns)"));
1068 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1069 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1072 if (!cl
->is_extreme
)
1075 org_width
= cl
->width
;
1076 rc
= count_column_width(tb
, cl
, buf
);
1080 if (org_width
> cl
->width
)
1081 width
-= org_width
- cl
->width
;
1083 extremes
--; /* hmm... nothing reduced */
1087 if (width
< tb
->termwidth
) {
1089 DBG(TAB
, ul_debugobj(tb
, " enlarge width (extreme columns)"));
1091 /* enlarge the first extreme column */
1092 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1093 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1096 if (!cl
->is_extreme
)
1099 /* this column is too large, ignore?
1100 if (cl->width_max - cl->width >
1101 (tb->termwidth - width))
1105 add
= tb
->termwidth
- width
;
1106 if (add
&& cl
->width
+ add
> cl
->width_max
)
1107 add
= cl
->width_max
- cl
->width
;
1112 if (width
== tb
->termwidth
)
1117 if (width
< tb
->termwidth
&& scols_table_is_maxout(tb
)) {
1118 DBG(TAB
, ul_debugobj(tb
, " enlarge width (max-out)"));
1120 /* try enlarging all columns */
1121 while (width
< tb
->termwidth
) {
1122 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1123 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1126 if (width
== tb
->termwidth
)
1130 } else if (width
< tb
->termwidth
) {
1131 /* enlarge the last column */
1132 struct libscols_column
*col
= list_entry(
1133 tb
->tb_columns
.prev
, struct libscols_column
, cl_columns
);
1135 DBG(TAB
, ul_debugobj(tb
, " enlarge width (last column)"));
1137 if (!scols_column_is_right(col
) && tb
->termwidth
- width
> 0) {
1138 col
->width
+= tb
->termwidth
- width
;
1139 width
= tb
->termwidth
;
1144 /* bad, we have to reduce output width, this is done in two steps:
1145 * 1) reduce columns with a relative width and with truncate flag
1146 * 2) reduce columns with a relative width without truncate flag
1149 while (width
> tb
->termwidth
) {
1152 DBG(TAB
, ul_debugobj(tb
, " reduce width (current=%zu, "
1153 "wanted=%zu, mode=%s)",
1154 width
, tb
->termwidth
,
1155 trunc_only
? "trunc-only" : "all-relative"));
1157 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1158 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1160 DBG(TAB
, ul_debugobj(cl
, " checking %s (width=%zu, treeart=%zu)",
1161 cl
->header
.data
, cl
->width
, cl
->width_treeart
));
1163 if (width
<= tb
->termwidth
)
1165 if (cl
->width_hint
> 1 && !scols_column_is_trunc(cl
))
1166 continue; /* never truncate columns with absolute sizes */
1167 if (scols_column_is_tree(cl
) && width
<= cl
->width_treeart
)
1168 continue; /* never truncate the tree */
1169 if (trunc_only
&& !(scols_column_is_trunc(cl
) || scols_column_is_wrap(cl
)))
1171 if (cl
->width
== cl
->width_min
)
1174 DBG(TAB
, ul_debugobj(tb
, " trying to reduce: %s (width=%zu)", cl
->header
.data
, cl
->width
));
1176 /* truncate column with relative sizes */
1177 if (cl
->width_hint
< 1 && cl
->width
> 0 && width
> 0 &&
1178 cl
->width
>= (size_t) (cl
->width_hint
* tb
->termwidth
)) {
1183 /* truncate column with absolute size */
1184 if (cl
->width_hint
> 1 && cl
->width
> 0 && width
> 0 &&
1198 /* ignore last column(s) or force last column to be truncated if
1199 * nowrap mode enabled */
1200 if (tb
->no_wrap
&& width
> tb
->termwidth
) {
1201 scols_reset_iter(&itr
, SCOLS_ITER_BACKWARD
);
1202 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1204 if (width
<= tb
->termwidth
)
1206 if (width
- cl
->width
< tb
->termwidth
) {
1207 size_t r
= width
- tb
->termwidth
;
1209 cl
->flags
|= SCOLS_FL_TRUNC
;
1213 cl
->flags
|= SCOLS_FL_HIDDEN
;
1214 width
-= cl
->width
+ 1; /* +1 means separator between columns */
1219 DBG(TAB
, ul_debugobj(tb
, " final width: %zu (rc=%d)", width
, rc
));
1220 ON_DBG(TAB
, dbg_columns(tb
));
1225 static size_t strlen_line(struct libscols_line
*ln
)
1231 for (i
= 0; i
< ln
->ncells
; i
++) {
1232 struct libscols_cell
*ce
= scols_line_get_cell(ln
, i
);
1233 const char *data
= ce
? scols_cell_get_data(ce
) : NULL
;
1235 sz
+= data
? strlen(data
) : 0;
1241 static int initialize_printing(struct libscols_table
*tb
, struct libscols_buffer
**buf
)
1243 size_t bufsz
, extra_bufsz
= 0;
1244 struct libscols_line
*ln
;
1245 struct libscols_iter itr
;
1248 DBG(TAB
, ul_debugobj(tb
, "initialize printing"));
1251 scols_table_set_symbols(tb
, NULL
); /* use default */
1253 if (tb
->format
== SCOLS_FMT_HUMAN
)
1254 tb
->is_term
= isatty(STDOUT_FILENO
) ? 1 : 0;
1257 tb
->termwidth
= get_terminal_width(80);
1258 if (tb
->termreduce
< tb
->termwidth
)
1259 tb
->termwidth
-= tb
->termreduce
;
1260 bufsz
= tb
->termwidth
;
1265 * Estimate extra space necessary for tree, JSON or another output
1268 if (scols_table_is_tree(tb
))
1269 extra_bufsz
+= tb
->nlines
* strlen(tb
->symbols
->vert
);
1271 switch (tb
->format
) {
1273 extra_bufsz
+= tb
->ncols
; /* separator between columns */
1275 case SCOLS_FMT_JSON
:
1276 if (tb
->format
== SCOLS_FMT_JSON
)
1277 extra_bufsz
+= tb
->nlines
* 3; /* indention */
1279 case SCOLS_FMT_EXPORT
:
1281 struct libscols_column
*cl
;
1283 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1285 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1286 if (scols_column_is_hidden(cl
))
1288 extra_bufsz
+= strlen(scols_cell_get_data(&cl
->header
)); /* data */
1289 extra_bufsz
+= 2; /* separators */
1293 case SCOLS_FMT_HUMAN
:
1298 * Enlarge buffer if necessary, the buffer should be large enough to
1299 * store line data and tree ascii art (or another decoration).
1301 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1302 while (scols_table_next_line(tb
, &itr
, &ln
) == 0) {
1303 size_t sz
= strlen_line(ln
) + extra_bufsz
;
1308 *buf
= new_buffer(bufsz
+ 1); /* data + space for \0 */
1312 if (tb
->format
== SCOLS_FMT_HUMAN
) {
1313 rc
= recount_widths(tb
, *buf
);
1325 * scola_table_print_range:
1327 * @start: first printed line or NULL to print from the begin of the table
1328 * @end: last printed line or NULL to print all from start.
1330 * If the start is the first line in the table than prints table header too.
1331 * The header is printed only once.
1333 * Returns: 0, a negative value in case of an error.
1335 int scols_table_print_range( struct libscols_table
*tb
,
1336 struct libscols_line
*start
,
1337 struct libscols_line
*end
)
1339 struct libscols_buffer
*buf
;
1340 struct libscols_iter itr
;
1343 if (scols_table_is_tree(tb
))
1346 DBG(TAB
, ul_debugobj(tb
, "printing range"));
1348 rc
= initialize_printing(tb
, &buf
);
1353 itr
.direction
= SCOLS_ITER_FORWARD
;
1354 itr
.head
= &tb
->tb_lines
;
1355 itr
.p
= &start
->ln_lines
;
1357 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1359 if (!start
|| itr
.p
== tb
->tb_lines
.next
) {
1360 rc
= print_header(tb
, buf
);
1365 rc
= print_range(tb
, buf
, &itr
, end
);
1372 * scols_table_print_range_to_string:
1374 * @start: first printed line or NULL to print from the beggin of the table
1375 * @end: last printed line or NULL to print all from start.
1376 * @data: pointer to the beginning of a memory area to print to
1378 * The same as scols_table_print_range(), but prints to @data instead of
1381 * Returns: 0, a negative value in case of an error.
1383 int scols_table_print_range_to_string( struct libscols_table
*tb
,
1384 struct libscols_line
*start
,
1385 struct libscols_line
*end
,
1388 #ifdef HAVE_OPEN_MEMSTREAM
1389 FILE *stream
, *old_stream
;
1396 DBG(TAB
, ul_debugobj(tb
, "printing range to string"));
1398 /* create a stream for output */
1399 stream
= open_memstream(data
, &sz
);
1403 old_stream
= scols_table_get_stream(tb
);
1404 scols_table_set_stream(tb
, stream
);
1405 rc
= scols_table_print_range(tb
, start
, end
);
1407 scols_table_set_stream(tb
, old_stream
);
1416 * scols_print_table:
1419 * Prints the table to the output stream.
1421 * Returns: 0, a negative value in case of an error.
1423 int scols_print_table(struct libscols_table
*tb
)
1426 struct libscols_buffer
*buf
;
1431 DBG(TAB
, ul_debugobj(tb
, "printing"));
1433 if (list_empty(&tb
->tb_lines
)) {
1434 DBG(TAB
, ul_debugobj(tb
, "ignore -- empty table"));
1438 tb
->header_printed
= 0;
1439 rc
= initialize_printing(tb
, &buf
);
1443 fput_table_open(tb
);
1445 if (tb
->format
== SCOLS_FMT_HUMAN
)
1448 rc
= print_header(tb
, buf
);
1452 if (scols_table_is_tree(tb
))
1453 rc
= print_tree(tb
, buf
);
1455 rc
= print_table(tb
, buf
);
1457 fput_table_close(tb
);
1464 * scols_print_table_to_string:
1466 * @data: pointer to the beginning of a memory area to print to
1468 * Prints the table to @data.
1470 * Returns: 0, a negative value in case of an error.
1472 int scols_print_table_to_string(struct libscols_table
*tb
, char **data
)
1474 #ifdef HAVE_OPEN_MEMSTREAM
1475 FILE *stream
, *old_stream
;
1482 DBG(TAB
, ul_debugobj(tb
, "printing to string"));
1484 /* create a stream for output */
1485 stream
= open_memstream(data
, &sz
);
1489 old_stream
= scols_table_get_stream(tb
);
1490 scols_table_set_stream(tb
, stream
);
1491 rc
= scols_print_table(tb
);
1493 scols_table_set_stream(tb
, old_stream
);