]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libsmartcols/src/print.c
docs: fix typos [codespell]
[thirdparty/util-linux.git] / libsmartcols / src / 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 "mbsalign.h"
26 #include "carefulputc.h"
27 #include "smartcolsP.h"
28
29 /* Fallback for symbols
30 *
31 * Note that by default library define all the symbols, but in case user does
32 * not define all symbols or if we extended the symbols struct then we need
33 * fallback to be more robust and backwardly compatible.
34 */
35 #define titlepadding_symbol(tb) ((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ")
36 #define branch_symbol(tb) ((tb)->symbols->tree_branch ? (tb)->symbols->tree_branch : "|-")
37 #define vertical_symbol(tb) ((tb)->symbols->tree_vert ? (tb)->symbols->tree_vert : "| ")
38 #define right_symbol(tb) ((tb)->symbols->tree_right ? (tb)->symbols->tree_right : "`-")
39
40 #define grp_vertical_symbol(tb) ((tb)->symbols->group_vert ? (tb)->symbols->group_vert : "|")
41 #define grp_horizontal_symbol(tb) ((tb)->symbols->group_horz ? (tb)->symbols->group_horz : "-")
42 #define grp_m_first_symbol(tb) ((tb)->symbols->group_first_member ? (tb)->symbols->group_first_member : ",->")
43 #define grp_m_last_symbol(tb) ((tb)->symbols->group_last_member ? (tb)->symbols->group_last_member : "\\->")
44 #define grp_m_middle_symbol(tb) ((tb)->symbols->group_middle_member ? (tb)->symbols->group_middle_member : "|->")
45 #define grp_c_middle_symbol(tb) ((tb)->symbols->group_middle_child ? (tb)->symbols->group_middle_child : "|-")
46 #define grp_c_last_symbol(tb) ((tb)->symbols->group_last_child ? (tb)->symbols->group_last_child : "`-")
47
48 #define cellpadding_symbol(tb) ((tb)->padding_debug ? "." : \
49 ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " "))
50
51 #define want_repeat_header(tb) (!(tb)->header_repeat || (tb)->header_next <= (tb)->termlines_used)
52
53
54 /* returns pointer to the end of used data */
55 static int tree_ascii_art_to_buffer(struct libscols_table *tb,
56 struct libscols_line *ln,
57 struct libscols_buffer *buf)
58 {
59 const char *art;
60 int rc;
61
62 assert(ln);
63 assert(buf);
64
65 if (!ln->parent)
66 return 0;
67
68 rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
69 if (rc)
70 return rc;
71
72 if (is_last_child(ln))
73 art = " ";
74 else
75 art = vertical_symbol(tb);
76
77 return buffer_append_data(buf, art);
78 }
79
80 static int grpset_is_empty( struct libscols_table *tb,
81 size_t idx,
82 size_t *rest)
83 {
84 size_t i;
85
86 for (i = idx; i < tb->grpset_size; i++) {
87 if (tb->grpset[i] == NULL) {
88 if (rest)
89 (*rest)++;
90 } else
91 return 0;
92 }
93 return 1;
94 }
95
96 static int groups_ascii_art_to_buffer( struct libscols_table *tb,
97 struct libscols_line *ln,
98 struct libscols_buffer *buf)
99 {
100 int rc, filled = 0;
101 size_t i, rest = 0;
102 const char *filler = cellpadding_symbol(tb);
103
104 if (!has_groups(tb) || !tb->grpset)
105 return 0;
106
107 DBG(LINE, ul_debugobj(ln, "printing groups chart"));
108
109 rc = scols_groups_update_grpset(tb, ln);
110 if (rc)
111 return rc;
112
113 for (i = 0; i < tb->grpset_size; i+=3) {
114 struct libscols_group *gr = tb->grpset[i];
115
116 if (!gr) {
117 buffer_append_ntimes(buf, 3, cellpadding_symbol(tb));
118 continue;
119 }
120
121 switch (gr->state) {
122 case SCOLS_GSTATE_FIRST_MEMBER:
123 buffer_append_data(buf, grp_m_first_symbol(tb));
124 break;
125 case SCOLS_GSTATE_MIDDLE_MEMBER:
126 buffer_append_data(buf, grp_m_middle_symbol(tb));
127 break;
128 case SCOLS_GSTATE_LAST_MEMBER:
129 buffer_append_data(buf, grp_m_last_symbol(tb));
130 break;
131 case SCOLS_GSTATE_CONT_MEMBERS:
132 buffer_append_data(buf, grp_vertical_symbol(tb));
133 buffer_append_ntimes(buf, 2, filler);
134 break;
135 case SCOLS_GSTATE_MIDDLE_CHILD:
136 buffer_append_data(buf, filler);
137 buffer_append_data(buf, grp_c_middle_symbol(tb));
138 if (grpset_is_empty(tb, i + 3, &rest)) {
139 buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
140 filled = 1;
141 }
142 filler = grp_horizontal_symbol(tb);
143 break;
144 case SCOLS_GSTATE_LAST_CHILD:
145 buffer_append_data(buf, cellpadding_symbol(tb));
146 buffer_append_data(buf, grp_c_last_symbol(tb));
147 if (grpset_is_empty(tb, i + 3, &rest)) {
148 buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
149 filled = 1;
150 }
151 filler = grp_horizontal_symbol(tb);
152 break;
153 case SCOLS_GSTATE_CONT_CHILDREN:
154 buffer_append_data(buf, filler);
155 buffer_append_data(buf, grp_vertical_symbol(tb));
156 buffer_append_data(buf, filler);
157 break;
158 }
159
160 if (filled)
161 break;
162 }
163
164 if (!filled)
165 buffer_append_data(buf, filler);
166 return 0;
167 }
168
169 static int has_pending_data(struct libscols_table *tb)
170 {
171 struct libscols_column *cl;
172 struct libscols_iter itr;
173
174 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
175 while (scols_table_next_column(tb, &itr, &cl) == 0) {
176 if (scols_column_is_hidden(cl))
177 continue;
178 if (cl->pending_data)
179 return 1;
180 }
181 return 0;
182 }
183
184 /* print padding or ASCII-art instead of data of @cl */
185 static void print_empty_cell(struct libscols_table *tb,
186 struct libscols_column *cl,
187 struct libscols_line *ln, /* optional */
188 size_t bufsz)
189 {
190 size_t len_pad = 0; /* in screen cells as opposed to bytes */
191
192 /* generate tree ASCII-art rather than padding */
193 if (ln && scols_column_is_tree(cl)) {
194 if (!ln->parent) {
195 /* only print symbols->vert if followed by child */
196 if (!list_empty(&ln->ln_branch)) {
197 fputs(vertical_symbol(tb), tb->out);
198 len_pad = mbs_safe_width(vertical_symbol(tb));
199 }
200 } else {
201 /* use the same draw function as though we were intending to draw an L-shape */
202 struct libscols_buffer *art = new_buffer(bufsz);
203 char *data;
204
205 if (art) {
206 /* whatever the rc, len_pad will be sensible */
207 tree_ascii_art_to_buffer(tb, ln, art);
208 if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
209 buffer_append_data(art, vertical_symbol(tb));
210 data = buffer_get_safe_data(tb, art, &len_pad, NULL);
211 if (data && len_pad)
212 fputs(data, tb->out);
213 free_buffer(art);
214 }
215 }
216 }
217
218 if (is_last_column(cl))
219 return;
220
221 /* fill rest of cell with space */
222 for(; len_pad < cl->width; ++len_pad)
223 fputs(cellpadding_symbol(tb), tb->out);
224
225 fputs(colsep(tb), tb->out);
226 }
227
228
229 static const char *get_cell_color(struct libscols_table *tb,
230 struct libscols_column *cl,
231 struct libscols_line *ln, /* optional */
232 struct libscols_cell *ce) /* optional */
233 {
234 const char *color = NULL;
235
236 if (tb && tb->colors_wanted) {
237 if (ce)
238 color = ce->color;
239 if (ln && !color)
240 color = ln->color;
241 if (!color)
242 color = cl->color;
243 }
244 return color;
245 }
246
247 /* Fill the start of a line with padding (or with tree ascii-art).
248 *
249 * This is necessary after a long non-truncated column, as this requires the
250 * next column to be printed on the next line. For example (see 'DDD'):
251 *
252 * aaa bbb ccc ddd eee
253 * AAA BBB CCCCCCC
254 * DDD EEE
255 * ^^^^^^^^^^^^
256 * new line padding
257 */
258 static void print_newline_padding(struct libscols_table *tb,
259 struct libscols_column *cl,
260 struct libscols_line *ln, /* optional */
261 size_t bufsz)
262 {
263 size_t i;
264
265 assert(tb);
266 assert(cl);
267
268 fputs(linesep(tb), tb->out); /* line break */
269 tb->termlines_used++;
270
271 /* fill cells after line break */
272 for (i = 0; i <= (size_t) cl->seqnum; i++)
273 print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz);
274 }
275
276 /*
277 * Pending data
278 *
279 * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
280 * printed as usually and output is truncated to match column width.
281 *
282 * The rest of the long text is printed on next extra line(s). The extra lines
283 * don't exist in the table (not represented by libscols_line). The data for
284 * the extra lines are stored in libscols_column->pending_data_buf and the
285 * function print_line() adds extra lines until the buffer is not empty in all
286 * columns.
287 */
288
289 /* set data that will be printed by extra lines */
290 static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
291 {
292 char *p = NULL;
293
294 if (data && *data) {
295 DBG(COL, ul_debugobj(cl, "setting pending data"));
296 assert(sz);
297 p = strdup(data);
298 if (!p)
299 return -ENOMEM;
300 }
301
302 free(cl->pending_data_buf);
303 cl->pending_data_buf = p;
304 cl->pending_data_sz = sz;
305 cl->pending_data = cl->pending_data_buf;
306 return 0;
307 }
308
309 /* the next extra line has been printed, move pending data cursor */
310 static int step_pending_data(struct libscols_column *cl, size_t bytes)
311 {
312 DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
313
314 if (bytes >= cl->pending_data_sz)
315 return set_pending_data(cl, NULL, 0);
316
317 cl->pending_data += bytes;
318 cl->pending_data_sz -= bytes;
319 return 0;
320 }
321
322 /* print next pending data for the column @cl */
323 static int print_pending_data(
324 struct libscols_table *tb,
325 struct libscols_column *cl,
326 struct libscols_line *ln, /* optional */
327 struct libscols_cell *ce)
328 {
329 const char *color = get_cell_color(tb, cl, ln, ce);
330 size_t width = cl->width, bytes;
331 size_t len = width, i;
332 char *data;
333 char *nextchunk = NULL;
334
335 if (!cl->pending_data)
336 return 0;
337 if (!width)
338 return -EINVAL;
339
340 DBG(COL, ul_debugobj(cl, "printing pending data"));
341
342 data = strdup(cl->pending_data);
343 if (!data)
344 goto err;
345
346 if (scols_column_is_customwrap(cl)
347 && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
348 bytes = nextchunk - data;
349
350 len = mbs_safe_nwidth(data, bytes, NULL);
351 } else
352 bytes = mbs_truncate(data, &len);
353
354 if (bytes == (size_t) -1)
355 goto err;
356
357 if (bytes)
358 step_pending_data(cl, bytes);
359
360 if (color)
361 fputs(color, tb->out);
362 fputs(data, tb->out);
363 if (color)
364 fputs(UL_COLOR_RESET, tb->out);
365 free(data);
366
367 if (is_last_column(cl))
368 return 0;
369
370 for (i = len; i < width; i++)
371 fputs(cellpadding_symbol(tb), tb->out); /* padding */
372
373 fputs(colsep(tb), tb->out); /* columns separator */
374 return 0;
375 err:
376 free(data);
377 return -errno;
378 }
379
380 static int print_data(struct libscols_table *tb,
381 struct libscols_column *cl,
382 struct libscols_line *ln, /* optional */
383 struct libscols_cell *ce, /* optional */
384 struct libscols_buffer *buf)
385 {
386 size_t len = 0, i, width, bytes;
387 const char *color = NULL;
388 char *data, *nextchunk;
389 int is_last;
390
391 assert(tb);
392 assert(cl);
393
394 data = buffer_get_data(buf);
395 if (!data)
396 data = "";
397
398 is_last = is_last_column(cl);
399
400 switch (tb->format) {
401 case SCOLS_FMT_RAW:
402 fputs_nonblank(data, tb->out);
403 if (!is_last)
404 fputs(colsep(tb), tb->out);
405 return 0;
406
407 case SCOLS_FMT_EXPORT:
408 fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header));
409 fputs_quoted(data, tb->out);
410 if (!is_last)
411 fputs(colsep(tb), tb->out);
412 return 0;
413
414 case SCOLS_FMT_JSON:
415 fputs_quoted_json_lower(scols_cell_get_data(&cl->header), tb->out);
416 fputs(":", tb->out);
417 switch (cl->json_type) {
418 case SCOLS_JSON_STRING:
419 if (!*data)
420 fputs("null", tb->out);
421 else
422 fputs_quoted_json(data, tb->out);
423 break;
424 case SCOLS_JSON_NUMBER:
425 if (!*data)
426 fputs("null", tb->out);
427 else
428 fputs(data, tb->out);
429 break;
430 case SCOLS_JSON_BOOLEAN:
431 fputs(!*data ? "false" :
432 *data == '0' ? "false" :
433 *data == 'N' || *data == 'n' ? "false" : "true",
434 tb->out);
435 break;
436 }
437 if (!is_last)
438 fputs(", ", tb->out);
439 return 0;
440
441 case SCOLS_FMT_HUMAN:
442 break; /* continue below */
443 }
444
445 color = get_cell_color(tb, cl, ln, ce);
446
447 /* Encode. Note that 'len' and 'width' are number of cells, not bytes.
448 */
449 data = buffer_get_safe_data(tb, buf, &len, scols_column_get_safechars(cl));
450 if (!data)
451 data = "";
452 bytes = strlen(data);
453 width = cl->width;
454
455 /* custom multi-line cell based */
456 if (*data && scols_column_is_customwrap(cl)
457 && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
458 set_pending_data(cl, nextchunk, bytes - (nextchunk - data));
459 bytes = nextchunk - data;
460 len = mbs_safe_nwidth(data, bytes, NULL);
461 }
462
463 if (is_last
464 && len < width
465 && !scols_table_is_maxout(tb)
466 && !scols_column_is_right(cl))
467 width = len;
468
469 /* truncate data */
470 if (len > width && scols_column_is_trunc(cl)) {
471 len = width;
472 bytes = mbs_truncate(data, &len); /* updates 'len' */
473 }
474
475 /* standard multi-line cell */
476 if (len > width && scols_column_is_wrap(cl)
477 && !scols_column_is_customwrap(cl)) {
478 set_pending_data(cl, data, bytes);
479
480 len = width;
481 bytes = mbs_truncate(data, &len);
482 if (bytes != (size_t) -1 && bytes > 0)
483 step_pending_data(cl, bytes);
484 }
485
486 if (bytes == (size_t) -1) {
487 bytes = len = 0;
488 data = NULL;
489 }
490
491 if (data) {
492 if (scols_column_is_right(cl)) {
493 if (color)
494 fputs(color, tb->out);
495 for (i = len; i < width; i++)
496 fputs(cellpadding_symbol(tb), tb->out);
497 fputs(data, tb->out);
498 if (color)
499 fputs(UL_COLOR_RESET, tb->out);
500 len = width;
501
502 } else if (color) {
503 char *p = data;
504 size_t art = buffer_get_safe_art_size(buf);
505
506 /* we don't want to colorize tree ascii art */
507 if (scols_column_is_tree(cl) && art && art < bytes) {
508 fwrite(p, 1, art, tb->out);
509 p += art;
510 }
511
512 fputs(color, tb->out);
513 fputs(p, tb->out);
514 fputs(UL_COLOR_RESET, tb->out);
515 } else
516 fputs(data, tb->out);
517 }
518 for (i = len; i < width; i++)
519 fputs(cellpadding_symbol(tb), tb->out); /* padding */
520
521 if (is_last)
522 return 0;
523
524 if (len > width && !scols_column_is_trunc(cl))
525 print_newline_padding(tb, cl, ln, buffer_get_size(buf)); /* next column starts on next line */
526 else
527 fputs(colsep(tb), tb->out); /* columns separator */
528
529 return 0;
530 }
531
532 int __cell_to_buffer(struct libscols_table *tb,
533 struct libscols_line *ln,
534 struct libscols_column *cl,
535 struct libscols_buffer *buf)
536 {
537 const char *data;
538 struct libscols_cell *ce;
539 int rc = 0;
540
541 assert(tb);
542 assert(ln);
543 assert(cl);
544 assert(buf);
545 assert(cl->seqnum <= tb->ncols);
546
547 buffer_reset_data(buf);
548
549 ce = scols_line_get_cell(ln, cl->seqnum);
550 data = ce ? scols_cell_get_data(ce) : NULL;
551 if (!data)
552 return 0;
553
554 if (!scols_column_is_tree(cl))
555 return buffer_set_data(buf, data);
556
557 /*
558 * Group stuff
559 */
560 if (!scols_table_is_json(tb) && cl->is_groups)
561 rc = groups_ascii_art_to_buffer(tb, ln, buf);
562
563 /*
564 * Tree stuff
565 */
566 if (!rc && ln->parent && !scols_table_is_json(tb)) {
567 rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
568
569 if (!rc && is_last_child(ln))
570 rc = buffer_append_data(buf, right_symbol(tb));
571 else if (!rc)
572 rc = buffer_append_data(buf, branch_symbol(tb));
573 }
574
575 if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb))
576 buffer_set_art_index(buf);
577
578 if (!rc)
579 rc = buffer_append_data(buf, data);
580 return rc;
581 }
582
583 /*
584 * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and
585 * control and non-printable characters can be encoded in the \x?? encoding.
586 */
587 static int print_line(struct libscols_table *tb,
588 struct libscols_line *ln,
589 struct libscols_buffer *buf)
590 {
591 int rc = 0, pending = 0;
592 struct libscols_column *cl;
593 struct libscols_iter itr;
594
595 assert(ln);
596
597 /* regular line */
598 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
599 while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
600 if (scols_column_is_hidden(cl))
601 continue;
602 rc = __cell_to_buffer(tb, ln, cl, buf);
603 if (rc == 0)
604 rc = print_data(tb, cl, ln,
605 scols_line_get_cell(ln, cl->seqnum),
606 buf);
607 if (rc == 0 && cl->pending_data)
608 pending = 1;
609 }
610
611 /* extra lines of the multi-line cells */
612 while (rc == 0 && pending) {
613 pending = 0;
614 fputs(linesep(tb), tb->out);
615 tb->termlines_used++;
616 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
617 while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
618 if (scols_column_is_hidden(cl))
619 continue;
620 if (cl->pending_data) {
621 rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum));
622 if (rc == 0 && cl->pending_data)
623 pending = 1;
624 } else
625 print_empty_cell(tb, cl, ln, buffer_get_size(buf));
626 }
627 }
628
629 return 0;
630 }
631
632 int __scols_print_title(struct libscols_table *tb)
633 {
634 int rc, color = 0;
635 mbs_align_t align;
636 size_t width, len = 0, bufsz, titlesz;
637 char *title = NULL, *buf = NULL;
638
639 assert(tb);
640
641 if (!tb->title.data)
642 return 0;
643
644 DBG(TAB, ul_debugobj(tb, "printing title"));
645
646 /* encode data */
647 if (tb->no_encode) {
648 len = bufsz = strlen(tb->title.data) + 1;
649 buf = strdup(tb->title.data);
650 if (!buf) {
651 rc = -ENOMEM;
652 goto done;
653 }
654 } else {
655 bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
656 if (bufsz == 1) {
657 DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore"));
658 return 0;
659 }
660 buf = malloc(bufsz);
661 if (!buf) {
662 rc = -ENOMEM;
663 goto done;
664 }
665
666 if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf, NULL) ||
667 !len || len == (size_t) -1) {
668 rc = -EINVAL;
669 goto done;
670 }
671 }
672
673 /* truncate and align */
674 width = tb->is_term ? tb->termwidth : 80;
675 titlesz = width + bufsz;
676
677 title = malloc(titlesz);
678 if (!title) {
679 rc = -EINVAL;
680 goto done;
681 }
682
683 switch (scols_cell_get_alignment(&tb->title)) {
684 case SCOLS_CELL_FL_RIGHT:
685 align = MBS_ALIGN_RIGHT;
686 break;
687 case SCOLS_CELL_FL_CENTER:
688 align = MBS_ALIGN_CENTER;
689 break;
690 case SCOLS_CELL_FL_LEFT:
691 default:
692 align = MBS_ALIGN_LEFT;
693 /*
694 * Don't print extra blank chars after the title if on left
695 * (that's same as we use for the last column in the table).
696 */
697 if (len < width
698 && !scols_table_is_maxout(tb)
699 && isblank(*titlepadding_symbol(tb)))
700 width = len;
701 break;
702
703 }
704
705 /* copy from buf to title and align to width with title_padding */
706 rc = mbsalign_with_padding(buf, title, titlesz,
707 &width, align,
708 0, (int) *titlepadding_symbol(tb));
709
710 if (rc == -1) {
711 rc = -EINVAL;
712 goto done;
713 }
714
715 if (tb->colors_wanted && tb->title.color)
716 color = 1;
717 if (color)
718 fputs(tb->title.color, tb->out);
719
720 fputs(title, tb->out);
721
722 if (color)
723 fputs(UL_COLOR_RESET, tb->out);
724
725 fputc('\n', tb->out);
726 rc = 0;
727 done:
728 free(buf);
729 free(title);
730 DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
731 return rc;
732 }
733
734 int __scols_print_header(struct libscols_table *tb, struct libscols_buffer *buf)
735 {
736 int rc = 0;
737 struct libscols_column *cl;
738 struct libscols_iter itr;
739
740 assert(tb);
741
742 if ((tb->header_printed == 1 && tb->header_repeat == 0) ||
743 scols_table_is_noheadings(tb) ||
744 scols_table_is_export(tb) ||
745 scols_table_is_json(tb) ||
746 list_empty(&tb->tb_lines))
747 return 0;
748
749 DBG(TAB, ul_debugobj(tb, "printing header"));
750
751 /* set the width according to the size of the data */
752 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
753 while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
754 if (scols_column_is_hidden(cl))
755 continue;
756
757 buffer_reset_data(buf);
758
759 if (cl->is_groups
760 && scols_table_is_tree(tb) && scols_column_is_tree(cl)) {
761 size_t i;
762 for (i = 0; i < tb->grpset_size + 1; i++) {
763 rc = buffer_append_data(buf, " ");
764 if (rc)
765 break;
766 }
767 }
768 if (!rc)
769 rc = buffer_append_data(buf, scols_cell_get_data(&cl->header));
770 if (!rc)
771 rc = print_data(tb, cl, NULL, &cl->header, buf);
772 }
773
774 if (rc == 0) {
775 fputs(linesep(tb), tb->out);
776 tb->termlines_used++;
777 }
778
779 tb->header_printed = 1;
780 tb->header_next = tb->termlines_used + tb->termheight;
781 if (tb->header_repeat)
782 DBG(TAB, ul_debugobj(tb, "\tnext header: %zu [current=%zu, rc=%d]",
783 tb->header_next, tb->termlines_used, rc));
784 return rc;
785 }
786
787
788 int __scols_print_range(struct libscols_table *tb,
789 struct libscols_buffer *buf,
790 struct libscols_iter *itr,
791 struct libscols_line *end)
792 {
793 int rc = 0;
794 struct libscols_line *ln;
795
796 assert(tb);
797 DBG(TAB, ul_debugobj(tb, "printing range"));
798
799 while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
800
801 int last = scols_iter_is_last(itr);
802
803 fput_line_open(tb);
804 rc = print_line(tb, ln, buf);
805 fput_line_close(tb, last, last);
806
807 if (end && ln == end)
808 break;
809
810 if (!last && want_repeat_header(tb))
811 __scols_print_header(tb, buf);
812 }
813
814 return rc;
815
816 }
817
818 int __scols_print_table(struct libscols_table *tb, struct libscols_buffer *buf)
819 {
820 struct libscols_iter itr;
821
822 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
823 return __scols_print_range(tb, buf, &itr, NULL);
824 }
825
826
827 static int print_tree_line(struct libscols_table *tb,
828 struct libscols_line *ln,
829 struct libscols_buffer *buf,
830 int last,
831 int last_in_table)
832 {
833 int rc, children = 0, gr_children = 0;
834
835 DBG(LINE, ul_debugobj(ln, "printing line"));
836
837 /* print the line */
838 fput_line_open(tb);
839 rc = print_line(tb, ln, buf);
840 if (rc)
841 goto done;
842
843 children = has_children(ln);
844 gr_children = is_last_group_member(ln) && has_group_children(ln);
845
846 if (children || gr_children)
847 fput_children_open(tb);
848
849 /* print children */
850 if (children) {
851 struct list_head *p;
852
853 list_for_each(p, &ln->ln_branch) {
854 struct libscols_line *chld =
855 list_entry(p, struct libscols_line, ln_children);
856 int last_child = !gr_children && p->next == &ln->ln_branch;
857
858 rc = print_tree_line(tb, chld, buf, last_child, last_in_table && last_child);
859 if (rc)
860 goto done;
861 }
862 }
863
864 /* print group's children */
865 if (gr_children) {
866 struct list_head *p;
867
868 list_for_each(p, &ln->group->gr_children) {
869 struct libscols_line *chld =
870 list_entry(p, struct libscols_line, ln_children);
871 int last_child = p->next == &ln->group->gr_children;
872
873 rc = print_tree_line(tb, chld, buf, last_child, last_in_table && last_child);
874 if (rc)
875 goto done;
876 }
877 }
878
879 if (children || gr_children)
880 fput_children_close(tb);
881
882 if ((!children && !gr_children) || scols_table_is_json(tb))
883 fput_line_close(tb, last, last_in_table);
884 done:
885 return rc;
886 }
887
888 int __scols_print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
889 {
890 int rc = 0;
891 struct libscols_line *ln, *last = NULL;
892 struct libscols_iter itr;
893
894 assert(tb);
895
896 DBG(TAB, ul_debugobj(tb, "----printing-tree-----"));
897
898 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
899
900 while (scols_table_next_line(tb, &itr, &ln) == 0) {
901 if (!last || !ln->parent)
902 last = ln;
903 }
904
905 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
906 while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
907 if (ln->parent || ln->parent_group)
908 continue;
909 rc = print_tree_line(tb, ln, buf, ln == last, ln == last);
910 }
911
912 return rc;
913 }
914
915 static size_t strlen_line(struct libscols_line *ln)
916 {
917 size_t i, sz = 0;
918
919 assert(ln);
920
921 for (i = 0; i < ln->ncells; i++) {
922 struct libscols_cell *ce = scols_line_get_cell(ln, i);
923 const char *data = ce ? scols_cell_get_data(ce) : NULL;
924
925 sz += data ? strlen(data) : 0;
926 }
927
928 return sz;
929 }
930
931 void __scols_cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf)
932 {
933 if (!tb)
934 return;
935
936 free_buffer(buf);
937
938 if (tb->priv_symbols) {
939 scols_table_set_symbols(tb, NULL);
940 tb->priv_symbols = 0;
941 }
942 }
943
944 int __scols_initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
945 {
946 size_t bufsz, extra_bufsz = 0;
947 struct libscols_line *ln;
948 struct libscols_iter itr;
949 int rc;
950
951 DBG(TAB, ul_debugobj(tb, "initialize printing"));
952 *buf = NULL;
953
954 if (!tb->symbols) {
955 rc = scols_table_set_default_symbols(tb);
956 if (rc)
957 goto err;
958 tb->priv_symbols = 1;
959 } else
960 tb->priv_symbols = 0;
961
962 if (tb->format == SCOLS_FMT_HUMAN)
963 tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER ? 0 :
964 tb->termforce == SCOLS_TERMFORCE_ALWAYS ? 1 :
965 isatty(STDOUT_FILENO);
966
967 if (tb->is_term) {
968 size_t width = (size_t) scols_table_get_termwidth(tb);
969
970 if (tb->termreduce > 0 && tb->termreduce < width) {
971 width -= tb->termreduce;
972 scols_table_set_termwidth(tb, width);
973 }
974 bufsz = width;
975 } else
976 bufsz = BUFSIZ;
977
978 if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb))
979 tb->header_repeat = 0;
980
981 /*
982 * Estimate extra space necessary for tree, JSON or another output
983 * decoration.
984 */
985 if (scols_table_is_tree(tb))
986 extra_bufsz += tb->nlines * strlen(vertical_symbol(tb));
987
988 switch (tb->format) {
989 case SCOLS_FMT_RAW:
990 extra_bufsz += tb->ncols; /* separator between columns */
991 break;
992 case SCOLS_FMT_JSON:
993 if (tb->format == SCOLS_FMT_JSON)
994 extra_bufsz += tb->nlines * 3; /* indentation */
995 /* fallthrough */
996 case SCOLS_FMT_EXPORT:
997 {
998 struct libscols_column *cl;
999
1000 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1001
1002 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1003 if (scols_column_is_hidden(cl))
1004 continue;
1005 extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */
1006 extra_bufsz += 2; /* separators */
1007 }
1008 break;
1009 }
1010 case SCOLS_FMT_HUMAN:
1011 break;
1012 }
1013
1014 /*
1015 * Enlarge buffer if necessary, the buffer should be large enough to
1016 * store line data and tree ascii art (or another decoration).
1017 */
1018 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1019 while (scols_table_next_line(tb, &itr, &ln) == 0) {
1020 size_t sz;
1021
1022 sz = strlen_line(ln) + extra_bufsz;
1023 if (sz > bufsz)
1024 bufsz = sz;
1025 }
1026
1027 *buf = new_buffer(bufsz + 1); /* data + space for \0 */
1028 if (!*buf) {
1029 rc = -ENOMEM;
1030 goto err;
1031 }
1032
1033 /*
1034 * Make sure groups members are in the same orders as the tree
1035 */
1036 if (has_groups(tb) && scols_table_is_tree(tb))
1037 scols_groups_fix_members_order(tb);
1038
1039 if (tb->format == SCOLS_FMT_HUMAN) {
1040 rc = __scols_calculate(tb, *buf);
1041 if (rc != 0)
1042 goto err;
1043 }
1044
1045 return 0;
1046 err:
1047 __scols_cleanup_printing(tb, *buf);
1048 return rc;
1049 }
1050