]>
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
27 #include "carefulputc.h"
28 #include "smartcolsP.h"
30 #define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
31 #define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
33 /* Fallback for symbols
35 * Note that by default library define all the symbols, but in case user does
36 * not define all symbols or if we extended the symbols struct then we need
37 * fallback to be more robust and backwardly compatible.
39 #define titlepadding_symbol(tb) ((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ")
40 #define cellpadding_symbol(tb) ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " ")
41 #define branch_symbol(tb) ((tb)->symbols->branch ? (tb)->symbols->branch : "|-")
42 #define vertical_symbol(tb) ((tb)->symbols->vert ? (tb)->symbols->vert : "|")
43 #define right_symbol(tb) ((tb)->symbols->right ? (tb)->symbols->right : "-")
46 /* This is private struct to work with output data */
47 struct libscols_buffer
{
48 char *begin
; /* begin of the buffer */
49 char *cur
; /* current end of the buffer */
50 char *encdata
; /* encoded buffer mbs_safe_encode() */
52 size_t bufsz
; /* size of the buffer */
53 size_t art_idx
; /* begin of the tree ascii art or zero */
56 static struct libscols_buffer
*new_buffer(size_t sz
)
58 struct libscols_buffer
*buf
= malloc(sz
+ sizeof(struct libscols_buffer
));
63 buf
->cur
= buf
->begin
= ((char *) buf
) + sizeof(struct libscols_buffer
);
67 DBG(BUFF
, ul_debugobj(buf
, "alloc (size=%zu)", sz
));
71 static void free_buffer(struct libscols_buffer
*buf
)
75 DBG(BUFF
, ul_debugobj(buf
, "dealloc"));
80 static int buffer_reset_data(struct libscols_buffer
*buf
)
85 /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/
87 buf
->cur
= buf
->begin
;
92 static int buffer_append_data(struct libscols_buffer
*buf
, const char *str
)
102 maxsz
= buf
->bufsz
- (buf
->cur
- buf
->begin
);
106 memcpy(buf
->cur
, str
, sz
+ 1);
111 static int buffer_set_data(struct libscols_buffer
*buf
, const char *str
)
113 int rc
= buffer_reset_data(buf
);
114 return rc
? rc
: buffer_append_data(buf
, str
);
117 /* save the current buffer position to art_idx */
118 static void buffer_set_art_index(struct libscols_buffer
*buf
)
121 buf
->art_idx
= buf
->cur
- buf
->begin
;
122 /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/
126 static char *buffer_get_data(struct libscols_buffer
*buf
)
128 return buf
? buf
->begin
: NULL
;
131 /* encode data by mbs_safe_encode() to avoid control and non-printable chars */
132 static char *buffer_get_safe_data(struct libscols_buffer
*buf
, size_t *cells
,
133 const char *safechars
)
135 char *data
= buffer_get_data(buf
);
142 buf
->encdata
= malloc(mbs_safe_encode_size(buf
->bufsz
) + 1);
147 res
= mbs_safe_encode_to_buffer(data
, cells
, buf
->encdata
, safechars
);
148 if (!res
|| !*cells
|| *cells
== (size_t) -1)
156 /* returns size in bytes of the ascii art (according to art_idx) in safe encoding */
157 static size_t buffer_get_safe_art_size(struct libscols_buffer
*buf
)
159 char *data
= buffer_get_data(buf
);
162 if (!data
|| !buf
->art_idx
)
165 mbs_safe_nwidth(data
, buf
->art_idx
, &bytes
);
169 /* returns pointer to the end of used data */
170 static int line_ascii_art_to_buffer(struct libscols_table
*tb
,
171 struct libscols_line
*ln
,
172 struct libscols_buffer
*buf
)
183 rc
= line_ascii_art_to_buffer(tb
, ln
->parent
, buf
);
187 if (list_entry_is_last(&ln
->ln_children
, &ln
->parent
->ln_branch
))
188 art
= cellpadding_symbol(tb
);
190 art
= vertical_symbol(tb
);
192 return buffer_append_data(buf
, art
);
195 static int is_last_column(struct libscols_column
*cl
)
197 int rc
= list_entry_is_last(&cl
->cl_columns
, &cl
->table
->tb_columns
);
198 struct libscols_column
*next
;
203 next
= list_entry(cl
->cl_columns
.next
, struct libscols_column
, cl_columns
);
204 if (next
&& scols_column_is_hidden(next
))
210 static int has_pending_data(struct libscols_table
*tb
)
212 struct libscols_column
*cl
;
213 struct libscols_iter itr
;
215 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
216 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
217 if (scols_column_is_hidden(cl
))
219 if (cl
->pending_data
)
225 /* print padding or ASCII-art instead of data of @cl */
226 static void print_empty_cell(struct libscols_table
*tb
,
227 struct libscols_column
*cl
,
228 struct libscols_line
*ln
, /* optional */
231 size_t len_pad
= 0; /* in screen cells as opposed to bytes */
233 /* generate tree ASCII-art rather than padding */
234 if (ln
&& scols_column_is_tree(cl
)) {
236 /* only print symbols->vert if followed by child */
237 if (!list_empty(&ln
->ln_branch
)) {
238 fputs(vertical_symbol(tb
), tb
->out
);
239 len_pad
= mbs_safe_width(vertical_symbol(tb
));
242 /* use the same draw function as though we were intending to draw an L-shape */
243 struct libscols_buffer
*art
= new_buffer(bufsz
);
247 /* whatever the rc, len_pad will be sensible */
248 line_ascii_art_to_buffer(tb
, ln
, art
);
249 if (!list_empty(&ln
->ln_branch
) && has_pending_data(tb
))
250 buffer_append_data(art
, vertical_symbol(tb
));
251 data
= buffer_get_safe_data(art
, &len_pad
, NULL
);
253 fputs(data
, tb
->out
);
259 if (is_last_column(cl
))
262 /* fill rest of cell with space */
263 for(; len_pad
< cl
->width
; ++len_pad
)
264 fputs(cellpadding_symbol(tb
), tb
->out
);
266 fputs(colsep(tb
), tb
->out
);
270 static const char *get_cell_color(struct libscols_table
*tb
,
271 struct libscols_column
*cl
,
272 struct libscols_line
*ln
, /* optional */
273 struct libscols_cell
*ce
) /* optional */
275 const char *color
= NULL
;
277 if (tb
&& tb
->colors_wanted
) {
288 /* Fill the start of a line with padding (or with tree ascii-art).
290 * This is necessary after a long non-truncated column, as this requires the
291 * next column to be printed on the next line. For example (see 'DDD'):
293 * aaa bbb ccc ddd eee
299 static void print_newline_padding(struct libscols_table
*tb
,
300 struct libscols_column
*cl
,
301 struct libscols_line
*ln
, /* optional */
309 fputs(linesep(tb
), tb
->out
); /* line break */
311 /* fill cells after line break */
312 for (i
= 0; i
<= (size_t) cl
->seqnum
; i
++)
313 print_empty_cell(tb
, scols_table_get_column(tb
, i
), ln
, bufsz
);
319 * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
320 * printed as usually and output is truncated to match column width.
322 * The rest of the long text is printed on next extra line(s). The extra lines
323 * don't exist in the table (not represented by libscols_line). The data for
324 * the extra lines are stored in libscols_column->pending_data_buf and the
325 * function print_line() adds extra lines until the buffer is not empty in all
329 /* set data that will be printed by extra lines */
330 static int set_pending_data(struct libscols_column
*cl
, const char *data
, size_t sz
)
335 DBG(COL
, ul_debugobj(cl
, "setting pending data"));
342 free(cl
->pending_data_buf
);
343 cl
->pending_data_buf
= p
;
344 cl
->pending_data_sz
= sz
;
345 cl
->pending_data
= cl
->pending_data_buf
;
349 /* the next extra line has been printed, move pending data cursor */
350 static int step_pending_data(struct libscols_column
*cl
, size_t bytes
)
352 DBG(COL
, ul_debugobj(cl
, "step pending data %zu -= %zu", cl
->pending_data_sz
, bytes
));
354 if (bytes
>= cl
->pending_data_sz
)
355 return set_pending_data(cl
, NULL
, 0);
357 cl
->pending_data
+= bytes
;
358 cl
->pending_data_sz
-= bytes
;
362 /* print next pending data for the column @cl */
363 static int print_pending_data(
364 struct libscols_table
*tb
,
365 struct libscols_column
*cl
,
366 struct libscols_line
*ln
, /* optional */
367 struct libscols_cell
*ce
)
369 const char *color
= get_cell_color(tb
, cl
, ln
, ce
);
370 size_t width
= cl
->width
, bytes
;
371 size_t len
= width
, i
;
375 if (!cl
->pending_data
)
378 DBG(COL
, ul_debugobj(cl
, "printing pending data"));
380 data
= strdup(cl
->pending_data
);
384 if (scols_column_is_wrapnl(cl
) && (wrapnl
= strchr(data
, '\n'))) {
387 bytes
= wrapnl
- data
;
389 len
= mbs_safe_nwidth(data
, bytes
, NULL
);
391 bytes
= mbs_truncate(data
, &len
);
393 if (bytes
== (size_t) -1)
396 step_pending_data(cl
, bytes
);
399 fputs(color
, tb
->out
);
400 fputs(data
, tb
->out
);
402 fputs(UL_COLOR_RESET
, tb
->out
);
405 if (is_last_column(cl
))
408 for (i
= len
; i
< width
; i
++)
409 fputs(cellpadding_symbol(tb
), tb
->out
); /* padding */
411 fputs(colsep(tb
), tb
->out
); /* columns separator */
418 static int print_data(struct libscols_table
*tb
,
419 struct libscols_column
*cl
,
420 struct libscols_line
*ln
, /* optional */
421 struct libscols_cell
*ce
, /* optional */
422 struct libscols_buffer
*buf
)
424 size_t len
= 0, i
, width
, bytes
;
425 const char *color
= NULL
;
431 DBG(TAB
, ul_debugobj(tb
,
432 " -> data, column=%p, line=%p, cell=%p, buff=%p",
435 data
= buffer_get_data(buf
);
439 switch (tb
->format
) {
441 fputs_nonblank(data
, tb
->out
);
442 if (!is_last_column(cl
))
443 fputs(colsep(tb
), tb
->out
);
446 case SCOLS_FMT_EXPORT
:
447 fprintf(tb
->out
, "%s=", scols_cell_get_data(&cl
->header
));
448 fputs_quoted(data
, tb
->out
);
449 if (!is_last_column(cl
))
450 fputs(colsep(tb
), tb
->out
);
454 fputs_quoted_json_lower(scols_cell_get_data(&cl
->header
), tb
->out
);
455 fputs(": ", tb
->out
);
457 fputs("null", tb
->out
);
459 fputs_quoted_json(data
, tb
->out
);
460 if (!is_last_column(cl
))
461 fputs(", ", tb
->out
);
464 case SCOLS_FMT_HUMAN
:
465 break; /* continue below */
468 color
= get_cell_color(tb
, cl
, ln
, ce
);
470 /* Encode. Note that 'len' and 'width' are number of cells, not bytes.
471 * For the columns with WRAPNL we mark \n as a safe char.
473 data
= buffer_get_safe_data(buf
, &len
,
474 scols_column_is_wrapnl(cl
) ? "\n" : NULL
);
477 bytes
= strlen(data
);
480 /* multi-line cell based on '\n' */
481 if (*data
&& scols_column_is_wrapnl(cl
) && (wrapnl
= strchr(data
, '\n'))) {
484 set_pending_data(cl
, wrapnl
, bytes
- (wrapnl
- data
));
485 bytes
= wrapnl
- data
;
486 len
= mbs_safe_nwidth(data
, bytes
, NULL
);
489 if (is_last_column(cl
)
491 && !scols_table_is_maxout(tb
)
492 && !scols_column_is_right(cl
))
496 if (len
> width
&& scols_column_is_trunc(cl
)) {
498 bytes
= mbs_truncate(data
, &len
); /* updates 'len' */
501 /* multi-line cell */
502 if (len
> width
&& scols_column_is_wrap(cl
)) {
503 set_pending_data(cl
, data
, bytes
);
506 bytes
= mbs_truncate(data
, &len
);
507 if (bytes
!= (size_t) -1 && bytes
> 0)
508 step_pending_data(cl
, bytes
);
511 if (bytes
== (size_t) -1) {
517 if (scols_column_is_right(cl
)) {
519 fputs(color
, tb
->out
);
520 for (i
= len
; i
< width
; i
++)
521 fputs(cellpadding_symbol(tb
), tb
->out
);
522 fputs(data
, tb
->out
);
524 fputs(UL_COLOR_RESET
, tb
->out
);
529 size_t art
= buffer_get_safe_art_size(buf
);
531 /* we don't want to colorize tree ascii art */
532 if (scols_column_is_tree(cl
) && art
&& art
< bytes
) {
533 fwrite(p
, 1, art
, tb
->out
);
537 fputs(color
, tb
->out
);
539 fputs(UL_COLOR_RESET
, tb
->out
);
541 fputs(data
, tb
->out
);
543 for (i
= len
; i
< width
; i
++)
544 fputs(cellpadding_symbol(tb
), tb
->out
); /* padding */
546 if (is_last_column(cl
))
549 if (len
> width
&& !scols_column_is_trunc(cl
))
550 print_newline_padding(tb
, cl
, ln
, buf
->bufsz
); /* next column starts on next line */
552 fputs(colsep(tb
), tb
->out
); /* columns separator */
557 static int cell_to_buffer(struct libscols_table
*tb
,
558 struct libscols_line
*ln
,
559 struct libscols_column
*cl
,
560 struct libscols_buffer
*buf
)
563 struct libscols_cell
*ce
;
570 assert(cl
->seqnum
<= tb
->ncols
);
572 buffer_reset_data(buf
);
574 ce
= scols_line_get_cell(ln
, cl
->seqnum
);
575 data
= ce
? scols_cell_get_data(ce
) : NULL
;
579 if (!scols_column_is_tree(cl
))
580 return buffer_set_data(buf
, data
);
585 if (ln
->parent
&& !scols_table_is_json(tb
)) {
586 rc
= line_ascii_art_to_buffer(tb
, ln
->parent
, buf
);
588 if (!rc
&& list_entry_is_last(&ln
->ln_children
, &ln
->parent
->ln_branch
))
589 rc
= buffer_append_data(buf
, right_symbol(tb
));
591 rc
= buffer_append_data(buf
, branch_symbol(tb
));
593 buffer_set_art_index(buf
);
597 rc
= buffer_append_data(buf
, data
);
601 static void fput_indent(struct libscols_table
*tb
)
605 for (i
= 0; i
<= tb
->indent
; i
++)
609 static void fput_table_open(struct libscols_table
*tb
)
613 if (scols_table_is_json(tb
)) {
615 fputs(linesep(tb
), tb
->out
);
618 fputs_quoted(tb
->name
, tb
->out
);
619 fputs(": [", tb
->out
);
620 fputs(linesep(tb
), tb
->out
);
623 tb
->indent_last_sep
= 1;
627 static void fput_table_close(struct libscols_table
*tb
)
631 if (scols_table_is_json(tb
)) {
635 fputs(linesep(tb
), tb
->out
);
637 fputs(linesep(tb
), tb
->out
);
638 tb
->indent_last_sep
= 1;
642 static void fput_children_open(struct libscols_table
*tb
)
644 if (scols_table_is_json(tb
)) {
646 fputs(linesep(tb
), tb
->out
);
648 fputs("\"children\": [", tb
->out
);
650 /* between parent and child is separator */
651 fputs(linesep(tb
), tb
->out
);
652 tb
->indent_last_sep
= 1;
656 static void fput_children_close(struct libscols_table
*tb
)
660 if (scols_table_is_json(tb
)) {
663 fputs(linesep(tb
), tb
->out
);
664 tb
->indent_last_sep
= 1;
668 static void fput_line_open(struct libscols_table
*tb
)
670 if (scols_table_is_json(tb
)) {
673 tb
->indent_last_sep
= 0;
678 static void fput_line_close(struct libscols_table
*tb
, int last
)
681 if (scols_table_is_json(tb
)) {
682 if (tb
->indent_last_sep
)
684 fputs(last
? "}" : "},", tb
->out
);
687 fputs(linesep(tb
), tb
->out
);
688 tb
->indent_last_sep
= 1;
692 * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and
693 * control and non-printable characters can be encoded in the \x?? encoding.
695 static int print_line(struct libscols_table
*tb
,
696 struct libscols_line
*ln
,
697 struct libscols_buffer
*buf
)
699 int rc
= 0, pending
= 0;
700 struct libscols_column
*cl
;
701 struct libscols_iter itr
;
705 DBG(TAB
, ul_debugobj(tb
, "printing line, line=%p, buff=%p", ln
, buf
));
708 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
709 while (rc
== 0 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
710 if (scols_column_is_hidden(cl
))
712 rc
= cell_to_buffer(tb
, ln
, cl
, buf
);
714 rc
= print_data(tb
, cl
, ln
,
715 scols_line_get_cell(ln
, cl
->seqnum
),
717 if (rc
== 0 && cl
->pending_data
)
721 /* extra lines of the multi-line cells */
722 while (rc
== 0 && pending
) {
724 fputs(linesep(tb
), tb
->out
);
725 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
726 while (rc
== 0 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
727 if (scols_column_is_hidden(cl
))
730 if (cl
->pending_data
) {
731 rc
= print_pending_data(tb
, cl
, ln
, scols_line_get_cell(ln
, cl
->seqnum
));
732 if (rc
== 0 && cl
->pending_data
)
735 print_empty_cell(tb
, cl
, ln
, buf
->bufsz
);
742 static int print_title(struct libscols_table
*tb
)
746 size_t width
, bufsz
, titlesz
;
747 char *title
= NULL
, *buf
= NULL
;
754 DBG(TAB
, ul_debugobj(tb
, "printing title"));
757 bufsz
= mbs_safe_encode_size(strlen(tb
->title
.data
)) + 1;
759 DBG(TAB
, ul_debugobj(tb
, "title is empty string -- ignore"));
768 if (!mbs_safe_encode_to_buffer(tb
->title
.data
, &bufsz
, buf
, NULL
) ||
769 !bufsz
|| bufsz
== (size_t) -1) {
774 /* truncate and align */
775 width
= tb
->is_term
? tb
->termwidth
: 80;
776 titlesz
= width
+ bufsz
;
778 title
= malloc(titlesz
);
784 if (tb
->title
.flags
& SCOLS_CELL_FL_LEFT
)
785 align
= MBS_ALIGN_LEFT
;
786 else if (tb
->title
.flags
& SCOLS_CELL_FL_RIGHT
)
787 align
= MBS_ALIGN_RIGHT
;
788 else if (tb
->title
.flags
& SCOLS_CELL_FL_CENTER
)
789 align
= MBS_ALIGN_CENTER
;
791 align
= MBS_ALIGN_LEFT
; /* default */
793 /* copy from buf to title and align to width with title_padding */
794 rc
= mbsalign_with_padding(buf
, title
, titlesz
,
796 0, (int) *titlepadding_symbol(tb
));
803 if (tb
->colors_wanted
&& tb
->title
.color
)
806 fputs(tb
->title
.color
, tb
->out
);
808 fputs(title
, tb
->out
);
811 fputs(UL_COLOR_RESET
, tb
->out
);
813 fputc('\n', tb
->out
);
818 DBG(TAB
, ul_debugobj(tb
, "printing title done [rc=%d]", rc
));
822 static int print_header(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
825 struct libscols_column
*cl
;
826 struct libscols_iter itr
;
830 if (tb
->header_printed
== 1 ||
831 scols_table_is_noheadings(tb
) ||
832 scols_table_is_export(tb
) ||
833 scols_table_is_json(tb
) ||
834 list_empty(&tb
->tb_lines
))
837 DBG(TAB
, ul_debugobj(tb
, "printing header"));
839 /* set the width according to the size of the data */
840 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
841 while (rc
== 0 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
842 if (scols_column_is_hidden(cl
))
844 rc
= buffer_set_data(buf
, scols_cell_get_data(&cl
->header
));
846 rc
= print_data(tb
, cl
, NULL
, &cl
->header
, buf
);
850 fputs(linesep(tb
), tb
->out
);
852 tb
->header_printed
= 1;
856 static int print_range( struct libscols_table
*tb
,
857 struct libscols_buffer
*buf
,
858 struct libscols_iter
*itr
,
859 struct libscols_line
*end
)
862 struct libscols_line
*ln
;
866 while (rc
== 0 && scols_table_next_line(tb
, itr
, &ln
) == 0) {
869 rc
= print_line(tb
, ln
, buf
);
870 fput_line_close(tb
, scols_iter_is_last(itr
));
872 if (end
&& ln
== end
)
880 static int print_table(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
882 struct libscols_iter itr
;
884 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
885 return print_range(tb
, buf
, &itr
, NULL
);
889 static int print_tree_line(struct libscols_table
*tb
,
890 struct libscols_line
*ln
,
891 struct libscols_buffer
*buf
,
899 rc
= print_line(tb
, ln
, buf
);
903 if (list_empty(&ln
->ln_branch
)) {
904 fput_line_close(tb
, last
);
908 fput_children_open(tb
);
910 /* print all children */
911 list_for_each(p
, &ln
->ln_branch
) {
912 struct libscols_line
*chld
=
913 list_entry(p
, struct libscols_line
, ln_children
);
915 rc
= print_tree_line(tb
, chld
, buf
, p
->next
== &ln
->ln_branch
);
920 fput_children_close(tb
);
922 if (scols_table_is_json(tb
))
923 fput_line_close(tb
, last
);
928 static int print_tree(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
931 struct libscols_line
*ln
, *last
= NULL
;
932 struct libscols_iter itr
;
936 DBG(TAB
, ul_debugobj(tb
, "printing tree"));
938 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
940 while (scols_table_next_line(tb
, &itr
, &ln
) == 0)
941 if (!last
|| !ln
->parent
)
944 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
945 while (rc
== 0 && scols_table_next_line(tb
, &itr
, &ln
) == 0) {
948 rc
= print_tree_line(tb
, ln
, buf
, ln
== last
);
954 static void dbg_column(struct libscols_table
*tb
, struct libscols_column
*cl
)
956 if (scols_column_is_hidden(cl
)) {
957 DBG(COL
, ul_debugobj(cl
, "%s ignored", cl
->header
.data
));
961 DBG(COL
, ul_debugobj(cl
, "%15s seq=%zu, width=%zd, "
962 "hint=%d, avg=%zu, max=%zu, min=%zu, "
965 cl
->header
.data
, cl
->seqnum
, cl
->width
,
966 cl
->width_hint
> 1 ? (int) cl
->width_hint
:
967 (int) (cl
->width_hint
* tb
->termwidth
),
971 cl
->is_extreme
? "yes" : "not",
972 cl
->flags
& SCOLS_FL_TRUNC
? "trunc" : ""));
975 static void dbg_columns(struct libscols_table
*tb
)
977 struct libscols_iter itr
;
978 struct libscols_column
*cl
;
980 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
981 while (scols_table_next_column(tb
, &itr
, &cl
) == 0)
985 /* count the maximal size of \n terminated chunk in the @data
986 * for example for "AAA\nBBBB\nXX" the wrap size is 4 ('BBBB').
988 static size_t count_wrapnl_size(const char *data
)
992 while (data
&& *data
) {
993 const char *p
= data
;
995 p
= strchr(data
, '\n');
997 size_t sz
= mbs_safe_nwidth(data
, p
- data
, NULL
);
1009 * This function counts column width.
1011 * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
1012 * two times. The first pass counts the width and average width. If the column
1013 * contains fields that are too large (a width greater than 2 * average) then
1014 * the column is marked as "extreme". In the second pass all extreme fields
1015 * are ignored and the column width is counted from non-extreme fields only.
1017 static int count_column_width(struct libscols_table
*tb
,
1018 struct libscols_column
*cl
,
1019 struct libscols_buffer
*buf
)
1021 struct libscols_line
*ln
;
1022 struct libscols_iter itr
;
1023 int count
= 0, rc
= 0;
1030 if (!cl
->width_min
) {
1031 if (cl
->width_hint
< 1 && scols_table_is_maxout(tb
) && tb
->is_term
) {
1032 cl
->width_min
= (size_t) (cl
->width_hint
* tb
->termwidth
);
1033 if (cl
->width_min
&& !is_last_column(cl
))
1036 if (scols_cell_get_data(&cl
->header
)) {
1037 size_t len
= mbs_safe_width(scols_cell_get_data(&cl
->header
));
1038 cl
->width_min
= max(cl
->width_min
, len
);
1042 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1043 while (scols_table_next_line(tb
, &itr
, &ln
) == 0) {
1047 rc
= cell_to_buffer(tb
, ln
, cl
, buf
);
1051 data
= buffer_get_data(buf
);
1055 else if (scols_column_is_wrapnl(cl
))
1056 len
= count_wrapnl_size(data
);
1058 len
= mbs_safe_width(data
);
1060 if (len
== (size_t) -1) /* ignore broken multibyte strings */
1062 cl
->width_max
= max(len
, cl
->width_max
);
1064 if (cl
->is_extreme
&& len
> cl
->width_avg
* 2)
1066 else if (scols_column_is_noextremes(cl
)) {
1070 cl
->width
= max(len
, cl
->width
);
1071 if (scols_column_is_tree(cl
)) {
1072 size_t treewidth
= buffer_get_safe_art_size(buf
);
1073 cl
->width_treeart
= max(cl
->width_treeart
, treewidth
);
1077 if (count
&& cl
->width_avg
== 0) {
1078 cl
->width_avg
= sum
/ count
;
1079 if (cl
->width_max
> cl
->width_avg
* 2)
1083 /* enlarge to minimal width */
1084 if (cl
->width
< cl
->width_min
&& !scols_column_is_strict_width(cl
))
1085 cl
->width
= cl
->width_min
;
1087 /* use absolute size for large columns */
1088 else if (cl
->width_hint
>= 1 && cl
->width
< (size_t) cl
->width_hint
1089 && cl
->width_min
< (size_t) cl
->width_hint
)
1091 cl
->width
= (size_t) cl
->width_hint
;
1094 ON_DBG(COL
, dbg_column(tb
, cl
));
1099 * This is core of the scols_* voodoo...
1101 static int recount_widths(struct libscols_table
*tb
, struct libscols_buffer
*buf
)
1103 struct libscols_column
*cl
;
1104 struct libscols_iter itr
;
1105 size_t width
= 0, width_min
= 0; /* output width */
1106 int trunc_only
, rc
= 0;
1110 DBG(TAB
, ul_debugobj(tb
, "recounting widths (termwidth=%zu)", tb
->termwidth
));
1112 /* set basic columns width
1114 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1115 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1116 rc
= count_column_width(tb
, cl
, buf
);
1120 width
+= cl
->width
+ (is_last_column(cl
) ? 0 : 1); /* separator for non-last column */
1121 width_min
+= cl
->width_min
+ (is_last_column(cl
) ? 0 : 1);
1122 extremes
+= cl
->is_extreme
;
1126 DBG(TAB
, ul_debugobj(tb
, " non-terminal output"));
1131 if (width_min
> tb
->termwidth
&& scols_table_is_maxout(tb
)) {
1132 DBG(TAB
, ul_debugobj(tb
, " min width larger than terminal! [width=%zu, term=%zu]", width_min
, tb
->termwidth
));
1134 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1135 while (width_min
> tb
->termwidth
1136 && scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1140 DBG(TAB
, ul_debugobj(tb
, " min width reduced to %zu", width_min
));
1143 /* reduce columns with extreme fields */
1144 if (width
> tb
->termwidth
&& extremes
) {
1145 DBG(TAB
, ul_debugobj(tb
, " reduce width (extreme columns)"));
1147 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1148 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1151 if (!cl
->is_extreme
)
1154 org_width
= cl
->width
;
1155 rc
= count_column_width(tb
, cl
, buf
);
1159 if (org_width
> cl
->width
)
1160 width
-= org_width
- cl
->width
;
1162 extremes
--; /* hmm... nothing reduced */
1166 if (width
< tb
->termwidth
) {
1168 DBG(TAB
, ul_debugobj(tb
, " enlarge width (extreme columns)"));
1170 /* enlarge the first extreme column */
1171 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1172 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1175 if (!cl
->is_extreme
)
1178 /* this column is too large, ignore?
1179 if (cl->width_max - cl->width >
1180 (tb->termwidth - width))
1184 add
= tb
->termwidth
- width
;
1185 if (add
&& cl
->width
+ add
> cl
->width_max
)
1186 add
= cl
->width_max
- cl
->width
;
1191 if (width
== tb
->termwidth
)
1196 if (width
< tb
->termwidth
&& scols_table_is_maxout(tb
)) {
1197 DBG(TAB
, ul_debugobj(tb
, " enlarge width (max-out)"));
1199 /* try enlarging all columns */
1200 while (width
< tb
->termwidth
) {
1201 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1202 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1205 if (width
== tb
->termwidth
)
1209 } else if (width
< tb
->termwidth
) {
1210 /* enlarge the last column */
1211 struct libscols_column
*col
= list_entry(
1212 tb
->tb_columns
.prev
, struct libscols_column
, cl_columns
);
1214 DBG(TAB
, ul_debugobj(tb
, " enlarge width (last column)"));
1216 if (!scols_column_is_right(col
) && tb
->termwidth
- width
> 0) {
1217 col
->width
+= tb
->termwidth
- width
;
1218 width
= tb
->termwidth
;
1223 /* bad, we have to reduce output width, this is done in two steps:
1224 * 1) reduce columns with a relative width and with truncate flag
1225 * 2) reduce columns with a relative width without truncate flag
1228 while (width
> tb
->termwidth
) {
1231 DBG(TAB
, ul_debugobj(tb
, " reduce width (current=%zu, "
1232 "wanted=%zu, mode=%s)",
1233 width
, tb
->termwidth
,
1234 trunc_only
? "trunc-only" : "all-relative"));
1236 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1237 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1239 DBG(TAB
, ul_debugobj(cl
, " checking %s (width=%zu, treeart=%zu)",
1240 cl
->header
.data
, cl
->width
, cl
->width_treeart
));
1242 if (width
<= tb
->termwidth
)
1244 if (cl
->width_hint
> 1 && !scols_column_is_trunc(cl
))
1245 continue; /* never truncate columns with absolute sizes */
1246 if (scols_column_is_tree(cl
) && width
<= cl
->width_treeart
)
1247 continue; /* never truncate the tree */
1248 if (trunc_only
&& !(scols_column_is_trunc(cl
) || scols_column_is_wrap(cl
)))
1250 if (cl
->width
== cl
->width_min
)
1253 DBG(TAB
, ul_debugobj(tb
, " trying to reduce: %s (width=%zu)", cl
->header
.data
, cl
->width
));
1255 /* truncate column with relative sizes */
1256 if (cl
->width_hint
< 1 && cl
->width
> 0 && width
> 0 &&
1257 cl
->width
>= (size_t) (cl
->width_hint
* tb
->termwidth
)) {
1262 /* truncate column with absolute size */
1263 if (cl
->width_hint
> 1 && cl
->width
> 0 && width
> 0 &&
1277 /* ignore last column(s) or force last column to be truncated if
1278 * nowrap mode enabled */
1279 if (tb
->no_wrap
&& width
> tb
->termwidth
) {
1280 scols_reset_iter(&itr
, SCOLS_ITER_BACKWARD
);
1281 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1283 if (width
<= tb
->termwidth
)
1285 if (width
- cl
->width
< tb
->termwidth
) {
1286 size_t r
= width
- tb
->termwidth
;
1288 cl
->flags
|= SCOLS_FL_TRUNC
;
1292 cl
->flags
|= SCOLS_FL_HIDDEN
;
1293 width
-= cl
->width
+ 1; /* +1 means separator between columns */
1298 DBG(TAB
, ul_debugobj(tb
, " final width: %zu (rc=%d)", width
, rc
));
1299 ON_DBG(TAB
, dbg_columns(tb
));
1304 static size_t strlen_line(struct libscols_line
*ln
)
1310 for (i
= 0; i
< ln
->ncells
; i
++) {
1311 struct libscols_cell
*ce
= scols_line_get_cell(ln
, i
);
1312 const char *data
= ce
? scols_cell_get_data(ce
) : NULL
;
1314 sz
+= data
? strlen(data
) : 0;
1320 static int initialize_printing(struct libscols_table
*tb
, struct libscols_buffer
**buf
)
1322 size_t bufsz
, extra_bufsz
= 0;
1323 struct libscols_line
*ln
;
1324 struct libscols_iter itr
;
1327 DBG(TAB
, ul_debugobj(tb
, "initialize printing"));
1330 scols_table_set_symbols(tb
, NULL
); /* use default */
1332 if (tb
->format
== SCOLS_FMT_HUMAN
)
1333 tb
->is_term
= isatty(STDOUT_FILENO
) ? 1 : 0;
1336 tb
->termwidth
= get_terminal_width(80);
1337 if (tb
->termreduce
> 0 && tb
->termreduce
< tb
->termwidth
)
1338 tb
->termwidth
-= tb
->termreduce
;
1339 bufsz
= tb
->termwidth
;
1344 * Estimate extra space necessary for tree, JSON or another output
1347 if (scols_table_is_tree(tb
))
1348 extra_bufsz
+= tb
->nlines
* strlen(vertical_symbol(tb
));
1350 switch (tb
->format
) {
1352 extra_bufsz
+= tb
->ncols
; /* separator between columns */
1354 case SCOLS_FMT_JSON
:
1355 if (tb
->format
== SCOLS_FMT_JSON
)
1356 extra_bufsz
+= tb
->nlines
* 3; /* indention */
1358 case SCOLS_FMT_EXPORT
:
1360 struct libscols_column
*cl
;
1362 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1364 while (scols_table_next_column(tb
, &itr
, &cl
) == 0) {
1365 if (scols_column_is_hidden(cl
))
1367 extra_bufsz
+= strlen(scols_cell_get_data(&cl
->header
)); /* data */
1368 extra_bufsz
+= 2; /* separators */
1372 case SCOLS_FMT_HUMAN
:
1377 * Enlarge buffer if necessary, the buffer should be large enough to
1378 * store line data and tree ascii art (or another decoration).
1380 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1381 while (scols_table_next_line(tb
, &itr
, &ln
) == 0) {
1382 size_t sz
= strlen_line(ln
) + extra_bufsz
;
1387 *buf
= new_buffer(bufsz
+ 1); /* data + space for \0 */
1391 if (tb
->format
== SCOLS_FMT_HUMAN
) {
1392 rc
= recount_widths(tb
, *buf
);
1404 * scola_table_print_range:
1406 * @start: first printed line or NULL to print from the begin of the table
1407 * @end: last printed line or NULL to print all from start.
1409 * If the start is the first line in the table than prints table header too.
1410 * The header is printed only once.
1412 * Returns: 0, a negative value in case of an error.
1414 int scols_table_print_range( struct libscols_table
*tb
,
1415 struct libscols_line
*start
,
1416 struct libscols_line
*end
)
1418 struct libscols_buffer
*buf
;
1419 struct libscols_iter itr
;
1422 if (scols_table_is_tree(tb
))
1425 DBG(TAB
, ul_debugobj(tb
, "printing range"));
1427 rc
= initialize_printing(tb
, &buf
);
1432 itr
.direction
= SCOLS_ITER_FORWARD
;
1433 itr
.head
= &tb
->tb_lines
;
1434 itr
.p
= &start
->ln_lines
;
1436 scols_reset_iter(&itr
, SCOLS_ITER_FORWARD
);
1438 if (!start
|| itr
.p
== tb
->tb_lines
.next
) {
1439 rc
= print_header(tb
, buf
);
1444 rc
= print_range(tb
, buf
, &itr
, end
);
1451 * scols_table_print_range_to_string:
1453 * @start: first printed line or NULL to print from the beggin of the table
1454 * @end: last printed line or NULL to print all from start.
1455 * @data: pointer to the beginning of a memory area to print to
1457 * The same as scols_table_print_range(), but prints to @data instead of
1460 * Returns: 0, a negative value in case of an error.
1462 int scols_table_print_range_to_string( struct libscols_table
*tb
,
1463 struct libscols_line
*start
,
1464 struct libscols_line
*end
,
1467 #ifdef HAVE_OPEN_MEMSTREAM
1468 FILE *stream
, *old_stream
;
1475 DBG(TAB
, ul_debugobj(tb
, "printing range to string"));
1477 /* create a stream for output */
1478 stream
= open_memstream(data
, &sz
);
1482 old_stream
= scols_table_get_stream(tb
);
1483 scols_table_set_stream(tb
, stream
);
1484 rc
= scols_table_print_range(tb
, start
, end
);
1486 scols_table_set_stream(tb
, old_stream
);
1495 * scols_print_table:
1498 * Prints the table to the output stream.
1500 * Returns: 0, a negative value in case of an error.
1502 int scols_print_table(struct libscols_table
*tb
)
1505 struct libscols_buffer
*buf
;
1510 DBG(TAB
, ul_debugobj(tb
, "printing"));
1512 if (list_empty(&tb
->tb_lines
)) {
1513 DBG(TAB
, ul_debugobj(tb
, "ignore -- empty table"));
1517 tb
->header_printed
= 0;
1518 rc
= initialize_printing(tb
, &buf
);
1522 fput_table_open(tb
);
1524 if (tb
->format
== SCOLS_FMT_HUMAN
)
1527 rc
= print_header(tb
, buf
);
1531 if (scols_table_is_tree(tb
))
1532 rc
= print_tree(tb
, buf
);
1534 rc
= print_table(tb
, buf
);
1536 fput_table_close(tb
);
1543 * scols_print_table_to_string:
1545 * @data: pointer to the beginning of a memory area to print to
1547 * Prints the table to @data.
1549 * Returns: 0, a negative value in case of an error.
1551 int scols_print_table_to_string(struct libscols_table
*tb
, char **data
)
1553 #ifdef HAVE_OPEN_MEMSTREAM
1554 FILE *stream
, *old_stream
;
1561 DBG(TAB
, ul_debugobj(tb
, "printing to string"));
1563 /* create a stream for output */
1564 stream
= open_memstream(data
, &sz
);
1568 old_stream
= scols_table_get_stream(tb
);
1569 scols_table_set_stream(tb
, stream
);
1570 rc
= scols_print_table(tb
);
1572 scols_table_set_stream(tb
, old_stream
);