]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libsmartcols/src/table_print.c
misc: Fix various typos
[thirdparty/util-linux.git] / libsmartcols / src / table_print.c
1 /*
2 * table.c - functions handling the data at the table level
3 *
4 * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
5 * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
6 *
7 * This file may be redistributed under the terms of the
8 * GNU Lesser General Public License.
9 */
10
11 /**
12 * SECTION: table_print
13 * @title: Table print
14 * @short_description: output functions
15 *
16 * Table output API.
17 */
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <termios.h>
23 #include <ctype.h>
24
25 #include "nls.h"
26 #include "mbsalign.h"
27 #include "widechar.h"
28 #include "ttyutils.h"
29 #include "carefulputc.h"
30 #include "smartcolsP.h"
31
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() */
37
38 size_t bufsz; /* size of the buffer */
39 size_t art_idx; /* begin of the tree ascii art or zero */
40 };
41
42 static struct libscols_buffer *new_buffer(size_t sz)
43 {
44 struct libscols_buffer *buf = malloc(sz + sizeof(struct libscols_buffer));
45
46 if (!buf)
47 return NULL;
48
49 buf->cur = buf->begin = ((char *) buf) + sizeof(struct libscols_buffer);
50 buf->encdata = NULL;
51 buf->bufsz = sz;
52
53 DBG(BUFF, ul_debugobj(buf, "alloc (size=%zu)", sz));
54 return buf;
55 }
56
57 static void free_buffer(struct libscols_buffer *buf)
58 {
59 if (!buf)
60 return;
61 DBG(BUFF, ul_debugobj(buf, "dealloc"));
62 free(buf->encdata);
63 free(buf);
64 }
65
66 static int buffer_reset_data(struct libscols_buffer *buf)
67 {
68 if (!buf)
69 return -EINVAL;
70
71 /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/
72 buf->begin[0] = '\0';
73 buf->cur = buf->begin;
74 buf->art_idx = 0;
75 return 0;
76 }
77
78 static int buffer_append_data(struct libscols_buffer *buf, const char *str)
79 {
80 size_t maxsz, sz;
81
82 if (!buf)
83 return -EINVAL;
84 if (!str || !*str)
85 return 0;
86
87 sz = strlen(str);
88 maxsz = buf->bufsz - (buf->cur - buf->begin);
89
90 if (maxsz <= sz)
91 return -EINVAL;
92 memcpy(buf->cur, str, sz + 1);
93 buf->cur += sz;
94 return 0;
95 }
96
97 static int buffer_set_data(struct libscols_buffer *buf, const char *str)
98 {
99 int rc = buffer_reset_data(buf);
100 return rc ? rc : buffer_append_data(buf, str);
101 }
102
103 /* save the current buffer position to art_idx */
104 static void buffer_set_art_index(struct libscols_buffer *buf)
105 {
106 if (buf) {
107 buf->art_idx = buf->cur - buf->begin;
108 /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/
109 }
110 }
111
112 static char *buffer_get_data(struct libscols_buffer *buf)
113 {
114 return buf ? buf->begin : NULL;
115 }
116
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)
119 {
120 char *data = buffer_get_data(buf);
121 char *res = NULL;
122
123 if (!data)
124 goto nothing;
125
126 if (!buf->encdata) {
127 buf->encdata = malloc(mbs_safe_encode_size(buf->bufsz) + 1);
128 if (!buf->encdata)
129 goto nothing;
130 }
131
132 res = mbs_safe_encode_to_buffer(data, cells, buf->encdata);
133 if (!res || !*cells || *cells == (size_t) -1)
134 goto nothing;
135 return res;
136 nothing:
137 *cells = 0;
138 return NULL;
139 }
140
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)
143 {
144 char *data = buffer_get_data(buf);
145 size_t bytes = 0;
146
147 if (!data || !buf->art_idx)
148 return 0;
149
150 mbs_safe_nwidth(data, buf->art_idx, &bytes);
151 return bytes;
152 }
153
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)
158 {
159 const char *art;
160 int rc;
161
162 assert(ln);
163 assert(buf);
164
165 if (!ln->parent)
166 return 0;
167
168 rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
169 if (rc)
170 return rc;
171
172 if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
173 art = " ";
174 else
175 art = tb->symbols->vert;
176
177 return buffer_append_data(buf, art);
178 }
179
180 static int is_last_column(struct libscols_column *cl)
181 {
182 int rc = list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns);
183 struct libscols_column *next;
184
185 if (rc)
186 return 1;
187
188 next = list_entry(cl->cl_columns.next, struct libscols_column, cl_columns);
189 if (next && scols_column_is_hidden(next))
190 return 1;
191 return 0;
192 }
193
194 #define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
195 #define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
196
197
198 static int has_pending_data(struct libscols_table *tb)
199 {
200 struct libscols_column *cl;
201 struct libscols_iter itr;
202
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))
206 continue;
207 if (cl->pending_data)
208 return 1;
209 }
210 return 0;
211 }
212
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 */
217 size_t bufsz)
218 {
219 size_t len_pad = 0; /* in screen cells as opposed to bytes */
220
221 /* generate tree ASCII-art rather than padding */
222 if (ln && scols_column_is_tree(cl)) {
223 if (!ln->parent) {
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);
228 }
229 } else {
230 /* use the same draw function as though we were intending to draw an L-shape */
231 struct libscols_buffer *art = new_buffer(bufsz);
232 char *data;
233
234 if (art) {
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);
240 if (data && len_pad)
241 fputs(data, tb->out);
242 free_buffer(art);
243 }
244 }
245 }
246 /* fill rest of cell with space */
247 for(; len_pad <= cl->width; ++len_pad)
248 fputc(' ', tb->out);
249 }
250
251
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 */
256 {
257 const char *color = NULL;
258
259 if (tb && tb->colors_wanted) {
260 if (ce && !color)
261 color = ce->color;
262 if (ln && !color)
263 color = ln->color;
264 if (!color)
265 color = cl->color;
266 }
267 return color;
268 }
269
270 /* Fill the start of a line with padding (or with tree ascii-art).
271 *
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'):
274 *
275 * aaa bbb ccc ddd eee
276 * AAA BBB CCCCCCC
277 * DDD EEE
278 * ^^^^^^^^^^^^
279 * new line padding
280 */
281 static void print_newline_padding(struct libscols_table *tb,
282 struct libscols_column *cl,
283 struct libscols_line *ln, /* optional */
284 size_t bufsz)
285 {
286 size_t i;
287
288 assert(tb);
289 assert(cl);
290
291 fputs(linesep(tb), tb->out); /* line break */
292
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);
296 }
297
298 /*
299 * Pending data
300 *
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.
303 *
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
308 * columns.
309 */
310
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)
313 {
314 char *p = NULL;
315
316 if (data) {
317 DBG(COL, ul_debugobj(cl, "setting pending data"));
318 assert(sz);
319 p = strdup(data);
320 if (!p)
321 return -ENOMEM;
322 }
323
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;
328 return 0;
329 }
330
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)
333 {
334 DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
335
336 if (bytes >= cl->pending_data_sz)
337 return set_pending_data(cl, NULL, 0);
338
339 cl->pending_data += bytes;
340 cl->pending_data_sz -= bytes;
341 return 0;
342 }
343
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)
350 {
351 const char *color = get_cell_color(tb, cl, ln, ce);
352 size_t width = cl->width, bytes;
353 size_t len = width, i;
354 char *data;
355
356 if (!cl->pending_data)
357 return 0;
358
359 DBG(COL, ul_debugobj(cl, "printing pending data"));
360
361 data = strdup(cl->pending_data);
362 if (!data)
363 goto err;
364 bytes = mbs_truncate(data, &len);
365 if (bytes == (size_t) -1)
366 goto err;
367
368 step_pending_data(cl, bytes);
369
370 if (color)
371 fputs(color, tb->out);
372 fputs(data, tb->out);
373 if (color)
374 fputs(UL_COLOR_RESET, tb->out);
375 free(data);
376
377 for (i = len; i < width; i++)
378 fputc(' ', tb->out); /* padding */
379
380 if (is_last_column(cl))
381 return 0;
382
383 fputs(colsep(tb), tb->out); /* columns separator */
384 return 0;
385 err:
386 free(data);
387 return -errno;
388 }
389
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)
395 {
396 size_t len = 0, i, width, bytes;
397 const char *color = NULL;
398 char *data;
399
400 assert(tb);
401 assert(cl);
402
403 DBG(TAB, ul_debugobj(tb,
404 " -> data, column=%p, line=%p, cell=%p, buff=%p",
405 cl, ln, ce, buf));
406
407 data = buffer_get_data(buf);
408 if (!data)
409 data = "";
410
411 switch (tb->format) {
412 case SCOLS_FMT_RAW:
413 fputs_nonblank(data, tb->out);
414 if (!is_last_column(cl))
415 fputs(colsep(tb), tb->out);
416 return 0;
417
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);
423 return 0;
424
425 case SCOLS_FMT_JSON:
426 fputs_quoted_lower(scols_cell_get_data(&cl->header), tb->out);
427 fputs(": ", tb->out);
428 if (!data || !*data)
429 fputs("null", tb->out);
430 else
431 fputs_quoted(data, tb->out);
432 if (!is_last_column(cl))
433 fputs(", ", tb->out);
434 return 0;
435
436 case SCOLS_FMT_HUMAN:
437 break; /* continue below */
438 }
439
440 color = get_cell_color(tb, cl, ln, ce);
441
442 /* encode, note that 'len' and 'width' are number of cells, not bytes */
443 data = buffer_get_safe_data(buf, &len);
444 if (!data)
445 data = "";
446 width = cl->width;
447 bytes = strlen(data);
448
449 if (is_last_column(cl)
450 && len < width
451 && !scols_table_is_maxout(tb)
452 && !scols_column_is_right(cl)
453 && !scols_column_is_wrap(cl))
454 width = len;
455
456 /* truncate data */
457 if (len > width && scols_column_is_trunc(cl)) {
458 len = width;
459 bytes = mbs_truncate(data, &len); /* updates 'len' */
460 }
461
462 /* multi-line cell */
463 if (len > width && scols_column_is_wrap(cl)) {
464 set_pending_data(cl, data, bytes);
465
466 len = width;
467 bytes = mbs_truncate(data, &len);
468 if (bytes != (size_t) -1 && bytes > 0)
469 step_pending_data(cl, bytes);
470 }
471
472 if (bytes == (size_t) -1) {
473 bytes = len = 0;
474 data = NULL;
475 }
476
477 if (data) {
478 if (scols_column_is_right(cl)) {
479 if (color)
480 fputs(color, tb->out);
481 for (i = len; i < width; i++)
482 fputc(' ', tb->out);
483 fputs(data, tb->out);
484 if (color)
485 fputs(UL_COLOR_RESET, tb->out);
486 len = width;
487
488 } else if (color) {
489 char *p = data;
490 size_t art = buffer_get_safe_art_size(buf);
491
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);
495 p += art;
496 }
497
498 fputs(color, tb->out);
499 fputs(p, tb->out);
500 fputs(UL_COLOR_RESET, tb->out);
501 } else
502 fputs(data, tb->out);
503 }
504 for (i = len; i < width; i++)
505 fputc(' ', tb->out); /* padding */
506
507 if (is_last_column(cl))
508 return 0;
509
510 if (len > width && !scols_column_is_trunc(cl))
511 print_newline_padding(tb, cl, ln, buf->bufsz); /* next column starts on next line */
512 else
513 fputs(colsep(tb), tb->out); /* columns separator */
514
515 return 0;
516 }
517
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)
522 {
523 const char *data;
524 struct libscols_cell *ce;
525 int rc = 0;
526
527 assert(tb);
528 assert(ln);
529 assert(cl);
530 assert(buf);
531 assert(cl->seqnum <= tb->ncols);
532
533 buffer_reset_data(buf);
534
535 ce = scols_line_get_cell(ln, cl->seqnum);
536 data = ce ? scols_cell_get_data(ce) : NULL;
537 if (!data)
538 return 0;
539
540 if (!scols_column_is_tree(cl))
541 return buffer_set_data(buf, data);
542
543 /*
544 * Tree stuff
545 */
546 if (ln->parent && !scols_table_is_json(tb)) {
547 rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
548
549 if (!rc && list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
550 rc = buffer_append_data(buf, tb->symbols->right);
551 else if (!rc)
552 rc = buffer_append_data(buf, tb->symbols->branch);
553 if (!rc)
554 buffer_set_art_index(buf);
555 }
556
557 if (!rc)
558 rc = buffer_append_data(buf, data);
559 return rc;
560 }
561
562 static void fput_indent(struct libscols_table *tb)
563 {
564 int i;
565
566 for (i = 0; i <= tb->indent; i++)
567 fputs(" ", tb->out);
568 }
569
570 static void fput_table_open(struct libscols_table *tb)
571 {
572 tb->indent = 0;
573
574 if (scols_table_is_json(tb)) {
575 fputc('{', tb->out);
576 fputs(linesep(tb), tb->out);
577
578 fput_indent(tb);
579 fputs_quoted(tb->name, tb->out);
580 fputs(": [", tb->out);
581 fputs(linesep(tb), tb->out);
582
583 tb->indent++;
584 tb->indent_last_sep = 1;
585 }
586 }
587
588 static void fput_table_close(struct libscols_table *tb)
589 {
590 tb->indent--;
591
592 if (scols_table_is_json(tb)) {
593 fput_indent(tb);
594 fputc(']', tb->out);
595 tb->indent--;
596 fputs(linesep(tb), tb->out);
597 fputc('}', tb->out);
598 fputs(linesep(tb), tb->out);
599 tb->indent_last_sep = 1;
600 }
601 }
602
603 static void fput_children_open(struct libscols_table *tb)
604 {
605 if (scols_table_is_json(tb)) {
606 fputc(',', tb->out);
607 fputs(linesep(tb), tb->out);
608 fput_indent(tb);
609 fputs("\"children\": [", tb->out);
610 }
611 /* between parent and child is separator */
612 fputs(linesep(tb), tb->out);
613 tb->indent_last_sep = 1;
614 tb->indent++;
615 }
616
617 static void fput_children_close(struct libscols_table *tb)
618 {
619 tb->indent--;
620
621 if (scols_table_is_json(tb)) {
622 fput_indent(tb);
623 fputc(']', tb->out);
624 fputs(linesep(tb), tb->out);
625 tb->indent_last_sep = 1;
626 }
627 }
628
629 static void fput_line_open(struct libscols_table *tb)
630 {
631 if (scols_table_is_json(tb)) {
632 fput_indent(tb);
633 fputc('{', tb->out);
634 tb->indent_last_sep = 0;
635 }
636 tb->indent++;
637 }
638
639 static void fput_line_close(struct libscols_table *tb, int last)
640 {
641 tb->indent--;
642 if (scols_table_is_json(tb)) {
643 if (tb->indent_last_sep)
644 fput_indent(tb);
645 fputs(last ? "}" : "},", tb->out);
646 }
647 if (!tb->no_linesep)
648 fputs(linesep(tb), tb->out);
649 tb->indent_last_sep = 1;
650 }
651
652 /*
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.
655 */
656 static int print_line(struct libscols_table *tb,
657 struct libscols_line *ln,
658 struct libscols_buffer *buf)
659 {
660 int rc = 0, pending = 0;
661 struct libscols_column *cl;
662 struct libscols_iter itr;
663
664 assert(ln);
665
666 DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf));
667
668 /* regular line */
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))
672 continue;
673 rc = cell_to_buffer(tb, ln, cl, buf);
674 if (rc == 0)
675 rc = print_data(tb, cl, ln,
676 scols_line_get_cell(ln, cl->seqnum),
677 buf);
678 if (rc == 0 && cl->pending_data)
679 pending = 1;
680 }
681
682 /* extra lines of the multi-line cells */
683 while (rc == 0 && pending) {
684 pending = 0;
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))
689 continue;
690
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)
694 pending = 1;
695 } else
696 print_empty_cell(tb, cl, ln, buf->bufsz);
697 }
698 }
699
700 return 0;
701 }
702
703 static int print_title(struct libscols_table *tb)
704 {
705 int rc;
706 mbs_align_t align;
707 size_t len = 0, width;
708 char *title = NULL, *buf = NULL;
709
710 assert(tb);
711
712 if (!tb->title.data)
713 return 0;
714
715 DBG(TAB, ul_debugobj(tb, "printing title"));
716
717 /* encode data */
718 len = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
719 if (len == 1)
720 return 0;
721 buf = malloc(len);
722 if (!buf) {
723 rc = -ENOMEM;
724 goto done;
725 }
726
727 if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf) ||
728 !len || len == (size_t) -1) {
729 rc = -EINVAL;
730 goto done;
731 }
732
733 /* truncate and align */
734 title = malloc(tb->termwidth + len);
735 if (!title) {
736 rc = -EINVAL;
737 goto done;
738 }
739
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;
746 else
747 align = MBS_ALIGN_LEFT; /* default */
748
749 width = tb->termwidth;
750 rc = mbsalign_with_padding(buf, title, tb->termwidth + len,
751 &width, align,
752 0, (int) *tb->symbols->title_padding);
753
754 if (rc == -1) {
755 rc = -EINVAL;
756 goto done;
757 }
758
759 if (tb->title.color)
760 fputs(tb->title.color, tb->out);
761
762 fputs(title, tb->out);
763
764 if (tb->title.color)
765 fputs(UL_COLOR_RESET, tb->out);
766 fputc('\n', tb->out);
767 rc = 0;
768 done:
769 free(buf);
770 free(title);
771 return rc;
772 }
773
774 static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
775 {
776 int rc = 0;
777 struct libscols_column *cl;
778 struct libscols_iter itr;
779
780 assert(tb);
781
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))
787 return 0;
788
789 DBG(TAB, ul_debugobj(tb, "printing header"));
790
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))
795 continue;
796 rc = buffer_set_data(buf, scols_cell_get_data(&cl->header));
797 if (!rc)
798 rc = print_data(tb, cl, NULL, &cl->header, buf);
799 }
800
801 if (rc == 0)
802 fputs(linesep(tb), tb->out);
803
804 tb->header_printed = 1;
805 return rc;
806 }
807
808 static int print_range( struct libscols_table *tb,
809 struct libscols_buffer *buf,
810 struct libscols_iter *itr,
811 struct libscols_line *end)
812 {
813 int rc = 0;
814 struct libscols_line *ln;
815
816 assert(tb);
817
818 while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
819
820 fput_line_open(tb);
821 rc = print_line(tb, ln, buf);
822 fput_line_close(tb, scols_iter_is_last(itr));
823
824 if (end && ln == end)
825 break;
826 }
827
828 return rc;
829
830 }
831
832 static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
833 {
834 struct libscols_iter itr;
835
836 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
837 return print_range(tb, buf, &itr, NULL);
838 }
839
840
841 static int print_tree_line(struct libscols_table *tb,
842 struct libscols_line *ln,
843 struct libscols_buffer *buf,
844 int last)
845 {
846 int rc;
847 struct list_head *p;
848
849 fput_line_open(tb);
850
851 rc = print_line(tb, ln, buf);
852 if (rc)
853 goto done;
854
855 if (list_empty(&ln->ln_branch)) {
856 fput_line_close(tb, last);
857 return 0;
858 }
859
860 fput_children_open(tb);
861
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);
866
867 rc = print_tree_line(tb, chld, buf, p->next == &ln->ln_branch);
868 if (rc)
869 goto done;
870 }
871
872 fput_children_close(tb);
873
874 if (scols_table_is_json(tb))
875 fput_line_close(tb, last);
876 done:
877 return rc;
878 }
879
880 static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
881 {
882 int rc = 0;
883 struct libscols_line *ln, *last = NULL;
884 struct libscols_iter itr;
885
886 assert(tb);
887
888 DBG(TAB, ul_debugobj(tb, "printing tree"));
889
890 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
891
892 while (scols_table_next_line(tb, &itr, &ln) == 0)
893 if (!last || !ln->parent)
894 last = ln;
895
896 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
897 while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
898 if (ln->parent)
899 continue;
900 rc = print_tree_line(tb, ln, buf, ln == last);
901 }
902
903 return rc;
904 }
905
906 static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
907 {
908 if (scols_column_is_hidden(cl)) {
909 DBG(COL, ul_debugobj(cl, "%s ignored", cl->header.data));
910 return;
911 }
912
913 DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
914 "hint=%d, avg=%zu, max=%zu, min=%zu, "
915 "extreme=%s %s",
916
917 cl->header.data, cl->seqnum, cl->width,
918 cl->width_hint > 1 ? (int) cl->width_hint :
919 (int) (cl->width_hint * tb->termwidth),
920 cl->width_avg,
921 cl->width_max,
922 cl->width_min,
923 cl->is_extreme ? "yes" : "not",
924 cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
925 }
926
927 static void dbg_columns(struct libscols_table *tb)
928 {
929 struct libscols_iter itr;
930 struct libscols_column *cl;
931
932 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
933 while (scols_table_next_column(tb, &itr, &cl) == 0)
934 dbg_column(tb, cl);
935 }
936
937 /*
938 * This function counts column width.
939 *
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.
945 */
946 static int count_column_width(struct libscols_table *tb,
947 struct libscols_column *cl,
948 struct libscols_buffer *buf)
949 {
950 struct libscols_line *ln;
951 struct libscols_iter itr;
952 int count = 0, rc = 0;
953 size_t sum = 0;
954
955 assert(tb);
956 assert(cl);
957
958 cl->width = 0;
959
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);
966 }
967 }
968
969 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
970 while (scols_table_next_line(tb, &itr, &ln) == 0) {
971 size_t len;
972 char *data;
973
974 rc = cell_to_buffer(tb, ln, cl, buf);
975 if (rc)
976 goto done;
977
978 data = buffer_get_data(buf);
979 len = data ? mbs_safe_width(data) : 0;
980
981 if (len == (size_t) -1) /* ignore broken multibyte strings */
982 len = 0;
983 cl->width_max = max(len, cl->width_max);
984
985 if (cl->is_extreme && len > cl->width_avg * 2)
986 continue;
987 else if (scols_column_is_noextremes(cl)) {
988 sum += len;
989 count++;
990 }
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);
995 }
996 }
997
998 if (count && cl->width_avg == 0) {
999 cl->width_avg = sum / count;
1000 if (cl->width_max > cl->width_avg * 2)
1001 cl->is_extreme = 1;
1002 }
1003
1004 /* enlarge to minimal width */
1005 if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
1006 cl->width = cl->width_min;
1007
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)
1011
1012 cl->width = (size_t) cl->width_hint;
1013
1014 done:
1015 ON_DBG(COL, dbg_column(tb, cl));
1016 return rc;
1017 }
1018
1019 /*
1020 * This is core of the scols_* voodoo...
1021 */
1022 static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf)
1023 {
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;
1028 int extremes = 0;
1029
1030
1031 DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
1032
1033 /* set basic columns width
1034 */
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);
1038 if (rc)
1039 goto done;
1040
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;
1044 }
1045
1046 if (!tb->is_term) {
1047 DBG(TAB, ul_debugobj(tb, " non-terminal output"));
1048 goto done;
1049 }
1050
1051 /* be paranoid */
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));
1054
1055 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1056 while (width_min > tb->termwidth
1057 && scols_table_next_column(tb, &itr, &cl) == 0) {
1058 width_min--;
1059 cl->width_min--;
1060 }
1061 DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
1062 }
1063
1064 /* reduce columns with extreme fields */
1065 if (width > tb->termwidth && extremes) {
1066 DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
1067
1068 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1069 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1070 size_t org_width;
1071
1072 if (!cl->is_extreme)
1073 continue;
1074
1075 org_width = cl->width;
1076 rc = count_column_width(tb, cl, buf);
1077 if (rc)
1078 goto done;
1079
1080 if (org_width > cl->width)
1081 width -= org_width - cl->width;
1082 else
1083 extremes--; /* hmm... nothing reduced */
1084 }
1085 }
1086
1087 if (width < tb->termwidth) {
1088 if (extremes) {
1089 DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
1090
1091 /* enlarge the first extreme column */
1092 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1093 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1094 size_t add;
1095
1096 if (!cl->is_extreme)
1097 continue;
1098
1099 /* this column is too large, ignore?
1100 if (cl->width_max - cl->width >
1101 (tb->termwidth - width))
1102 continue;
1103 */
1104
1105 add = tb->termwidth - width;
1106 if (add && cl->width + add > cl->width_max)
1107 add = cl->width_max - cl->width;
1108
1109 cl->width += add;
1110 width += add;
1111
1112 if (width == tb->termwidth)
1113 break;
1114 }
1115 }
1116
1117 if (width < tb->termwidth && scols_table_is_maxout(tb)) {
1118 DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
1119
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) {
1124 cl->width++;
1125 width++;
1126 if (width == tb->termwidth)
1127 break;
1128 }
1129 }
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);
1134
1135 DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
1136
1137 if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
1138 col->width += tb->termwidth - width;
1139 width = tb->termwidth;
1140 }
1141 }
1142 }
1143
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
1147 */
1148 trunc_only = 1;
1149 while (width > tb->termwidth) {
1150 size_t org = width;
1151
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"));
1156
1157 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1158 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1159
1160 DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
1161 cl->header.data, cl->width, cl->width_treeart));
1162
1163 if (width <= tb->termwidth)
1164 break;
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)))
1170 continue;
1171 if (cl->width == cl->width_min)
1172 continue;
1173
1174 DBG(TAB, ul_debugobj(tb, " trying to reduce: %s (width=%zu)", cl->header.data, cl->width));
1175
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)) {
1179 cl->width--;
1180 width--;
1181 }
1182
1183 /* truncate column with absolute size */
1184 if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
1185 !trunc_only) {
1186 cl->width--;
1187 width--;
1188 }
1189 }
1190 if (org == width) {
1191 if (trunc_only)
1192 trunc_only = 0;
1193 else
1194 break;
1195 }
1196 }
1197
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) {
1203
1204 if (width <= tb->termwidth)
1205 break;
1206 if (width - cl->width < tb->termwidth) {
1207 size_t r = width - tb->termwidth;
1208
1209 cl->flags |= SCOLS_FL_TRUNC;
1210 cl->width -= r;
1211 width -= r;
1212 } else {
1213 cl->flags |= SCOLS_FL_HIDDEN;
1214 width -= cl->width + 1; /* +1 means separator between columns */
1215 }
1216 }
1217 }
1218 done:
1219 DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
1220 ON_DBG(TAB, dbg_columns(tb));
1221
1222 return rc;
1223 }
1224
1225 static size_t strlen_line(struct libscols_line *ln)
1226 {
1227 size_t i, sz = 0;
1228
1229 assert(ln);
1230
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;
1234
1235 sz += data ? strlen(data) : 0;
1236 }
1237
1238 return sz;
1239 }
1240
1241 static int initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
1242 {
1243 size_t bufsz, extra_bufsz = 0;
1244 struct libscols_line *ln;
1245 struct libscols_iter itr;
1246 int rc;
1247
1248 DBG(TAB, ul_debugobj(tb, "initialize printing"));
1249
1250 if (!tb->symbols)
1251 scols_table_set_symbols(tb, NULL); /* use default */
1252
1253 if (tb->format == SCOLS_FMT_HUMAN)
1254 tb->is_term = isatty(STDOUT_FILENO) ? 1 : 0;
1255
1256 if (tb->is_term) {
1257 tb->termwidth = get_terminal_width(80);
1258 if (tb->termreduce < tb->termwidth)
1259 tb->termwidth -= tb->termreduce;
1260 bufsz = tb->termwidth;
1261 } else
1262 bufsz = BUFSIZ;
1263
1264 /*
1265 * Estimate extra space necessary for tree, JSON or another output
1266 * decoration.
1267 */
1268 if (scols_table_is_tree(tb))
1269 extra_bufsz += tb->nlines * strlen(tb->symbols->vert);
1270
1271 switch (tb->format) {
1272 case SCOLS_FMT_RAW:
1273 extra_bufsz += tb->ncols; /* separator between columns */
1274 break;
1275 case SCOLS_FMT_JSON:
1276 if (tb->format == SCOLS_FMT_JSON)
1277 extra_bufsz += tb->nlines * 3; /* indention */
1278 /* fallthrough */
1279 case SCOLS_FMT_EXPORT:
1280 {
1281 struct libscols_column *cl;
1282
1283 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1284
1285 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1286 if (scols_column_is_hidden(cl))
1287 continue;
1288 extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */
1289 extra_bufsz += 2; /* separators */
1290 }
1291 break;
1292 }
1293 case SCOLS_FMT_HUMAN:
1294 break;
1295 }
1296
1297 /*
1298 * Enlarge buffer if necessary, the buffer should be large enough to
1299 * store line data and tree ascii art (or another decoration).
1300 */
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;
1304 if (sz > bufsz)
1305 bufsz = sz;
1306 }
1307
1308 *buf = new_buffer(bufsz + 1); /* data + space for \0 */
1309 if (!*buf)
1310 return -ENOMEM;
1311
1312 if (tb->format == SCOLS_FMT_HUMAN) {
1313 rc = recount_widths(tb, *buf);
1314 if (rc != 0)
1315 goto err;
1316 }
1317
1318 return 0;
1319 err:
1320 free_buffer(*buf);
1321 return rc;
1322 }
1323
1324 /**
1325 * scola_table_print_range:
1326 * @tb: table
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.
1329 *
1330 * If the start is the first line in the table than prints table header too.
1331 * The header is printed only once.
1332 *
1333 * Returns: 0, a negative value in case of an error.
1334 */
1335 int scols_table_print_range( struct libscols_table *tb,
1336 struct libscols_line *start,
1337 struct libscols_line *end)
1338 {
1339 struct libscols_buffer *buf;
1340 struct libscols_iter itr;
1341 int rc;
1342
1343 if (scols_table_is_tree(tb))
1344 return -EINVAL;
1345
1346 DBG(TAB, ul_debugobj(tb, "printing range"));
1347
1348 rc = initialize_printing(tb, &buf);
1349 if (rc)
1350 return rc;
1351
1352 if (start) {
1353 itr.direction = SCOLS_ITER_FORWARD;
1354 itr.head = &tb->tb_lines;
1355 itr.p = &start->ln_lines;
1356 } else
1357 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1358
1359 if (!start || itr.p == tb->tb_lines.next) {
1360 rc = print_header(tb, buf);
1361 if (rc)
1362 goto done;
1363 }
1364
1365 rc = print_range(tb, buf, &itr, end);
1366 done:
1367 free_buffer(buf);
1368 return rc;
1369 }
1370
1371 /**
1372 * scols_table_print_range_to_string:
1373 * @tb: table
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
1377 *
1378 * The same as scols_table_print_range(), but prints to @data instead of
1379 * stream.
1380 *
1381 * Returns: 0, a negative value in case of an error.
1382 */
1383 int scols_table_print_range_to_string( struct libscols_table *tb,
1384 struct libscols_line *start,
1385 struct libscols_line *end,
1386 char **data)
1387 {
1388 #ifdef HAVE_OPEN_MEMSTREAM
1389 FILE *stream, *old_stream;
1390 size_t sz;
1391 int rc;
1392
1393 if (!tb)
1394 return -EINVAL;
1395
1396 DBG(TAB, ul_debugobj(tb, "printing range to string"));
1397
1398 /* create a stream for output */
1399 stream = open_memstream(data, &sz);
1400 if (!stream)
1401 return -ENOMEM;
1402
1403 old_stream = scols_table_get_stream(tb);
1404 scols_table_set_stream(tb, stream);
1405 rc = scols_table_print_range(tb, start, end);
1406 fclose(stream);
1407 scols_table_set_stream(tb, old_stream);
1408
1409 return rc;
1410 #else
1411 return -ENOSYS;
1412 #endif
1413 }
1414
1415 /**
1416 * scols_print_table:
1417 * @tb: table
1418 *
1419 * Prints the table to the output stream.
1420 *
1421 * Returns: 0, a negative value in case of an error.
1422 */
1423 int scols_print_table(struct libscols_table *tb)
1424 {
1425 int rc = 0;
1426 struct libscols_buffer *buf;
1427
1428 if (!tb)
1429 return -EINVAL;
1430
1431 DBG(TAB, ul_debugobj(tb, "printing"));
1432
1433 if (list_empty(&tb->tb_lines)) {
1434 DBG(TAB, ul_debugobj(tb, "ignore -- empty table"));
1435 return 0;
1436 }
1437
1438 tb->header_printed = 0;
1439 rc = initialize_printing(tb, &buf);
1440 if (rc)
1441 return rc;
1442
1443 fput_table_open(tb);
1444
1445 if (tb->format == SCOLS_FMT_HUMAN)
1446 print_title(tb);
1447
1448 rc = print_header(tb, buf);
1449 if (rc)
1450 goto done;
1451
1452 if (scols_table_is_tree(tb))
1453 rc = print_tree(tb, buf);
1454 else
1455 rc = print_table(tb, buf);
1456
1457 fput_table_close(tb);
1458 done:
1459 free_buffer(buf);
1460 return rc;
1461 }
1462
1463 /**
1464 * scols_print_table_to_string:
1465 * @tb: table
1466 * @data: pointer to the beginning of a memory area to print to
1467 *
1468 * Prints the table to @data.
1469 *
1470 * Returns: 0, a negative value in case of an error.
1471 */
1472 int scols_print_table_to_string(struct libscols_table *tb, char **data)
1473 {
1474 #ifdef HAVE_OPEN_MEMSTREAM
1475 FILE *stream, *old_stream;
1476 size_t sz;
1477 int rc;
1478
1479 if (!tb)
1480 return -EINVAL;
1481
1482 DBG(TAB, ul_debugobj(tb, "printing to string"));
1483
1484 /* create a stream for output */
1485 stream = open_memstream(data, &sz);
1486 if (!stream)
1487 return -ENOMEM;
1488
1489 old_stream = scols_table_get_stream(tb);
1490 scols_table_set_stream(tb, stream);
1491 rc = scols_print_table(tb);
1492 fclose(stream);
1493 scols_table_set_stream(tb, old_stream);
1494
1495 return rc;
1496 #else
1497 return -ENOSYS;
1498 #endif
1499 }
1500