]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libsmartcols/src/table_print.c
libsmartcols: add fallback for symbols
[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 "mbsalign.h"
26 #include "ttyutils.h"
27 #include "carefulputc.h"
28 #include "smartcolsP.h"
29
30 #define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
31 #define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
32
33 /* Fallback for symbols
34 *
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.
38 */
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 : "-")
44
45
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() */
51
52 size_t bufsz; /* size of the buffer */
53 size_t art_idx; /* begin of the tree ascii art or zero */
54 };
55
56 static struct libscols_buffer *new_buffer(size_t sz)
57 {
58 struct libscols_buffer *buf = malloc(sz + sizeof(struct libscols_buffer));
59
60 if (!buf)
61 return NULL;
62
63 buf->cur = buf->begin = ((char *) buf) + sizeof(struct libscols_buffer);
64 buf->encdata = NULL;
65 buf->bufsz = sz;
66
67 DBG(BUFF, ul_debugobj(buf, "alloc (size=%zu)", sz));
68 return buf;
69 }
70
71 static void free_buffer(struct libscols_buffer *buf)
72 {
73 if (!buf)
74 return;
75 DBG(BUFF, ul_debugobj(buf, "dealloc"));
76 free(buf->encdata);
77 free(buf);
78 }
79
80 static int buffer_reset_data(struct libscols_buffer *buf)
81 {
82 if (!buf)
83 return -EINVAL;
84
85 /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/
86 buf->begin[0] = '\0';
87 buf->cur = buf->begin;
88 buf->art_idx = 0;
89 return 0;
90 }
91
92 static int buffer_append_data(struct libscols_buffer *buf, const char *str)
93 {
94 size_t maxsz, sz;
95
96 if (!buf)
97 return -EINVAL;
98 if (!str || !*str)
99 return 0;
100
101 sz = strlen(str);
102 maxsz = buf->bufsz - (buf->cur - buf->begin);
103
104 if (maxsz <= sz)
105 return -EINVAL;
106 memcpy(buf->cur, str, sz + 1);
107 buf->cur += sz;
108 return 0;
109 }
110
111 static int buffer_set_data(struct libscols_buffer *buf, const char *str)
112 {
113 int rc = buffer_reset_data(buf);
114 return rc ? rc : buffer_append_data(buf, str);
115 }
116
117 /* save the current buffer position to art_idx */
118 static void buffer_set_art_index(struct libscols_buffer *buf)
119 {
120 if (buf) {
121 buf->art_idx = buf->cur - buf->begin;
122 /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/
123 }
124 }
125
126 static char *buffer_get_data(struct libscols_buffer *buf)
127 {
128 return buf ? buf->begin : NULL;
129 }
130
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)
134 {
135 char *data = buffer_get_data(buf);
136 char *res = NULL;
137
138 if (!data)
139 goto nothing;
140
141 if (!buf->encdata) {
142 buf->encdata = malloc(mbs_safe_encode_size(buf->bufsz) + 1);
143 if (!buf->encdata)
144 goto nothing;
145 }
146
147 res = mbs_safe_encode_to_buffer(data, cells, buf->encdata, safechars);
148 if (!res || !*cells || *cells == (size_t) -1)
149 goto nothing;
150 return res;
151 nothing:
152 *cells = 0;
153 return NULL;
154 }
155
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)
158 {
159 char *data = buffer_get_data(buf);
160 size_t bytes = 0;
161
162 if (!data || !buf->art_idx)
163 return 0;
164
165 mbs_safe_nwidth(data, buf->art_idx, &bytes);
166 return bytes;
167 }
168
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)
173 {
174 const char *art;
175 int rc;
176
177 assert(ln);
178 assert(buf);
179
180 if (!ln->parent)
181 return 0;
182
183 rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
184 if (rc)
185 return rc;
186
187 if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
188 art = cellpadding_symbol(tb);
189 else
190 art = vertical_symbol(tb);
191
192 return buffer_append_data(buf, art);
193 }
194
195 static int is_last_column(struct libscols_column *cl)
196 {
197 int rc = list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns);
198 struct libscols_column *next;
199
200 if (rc)
201 return 1;
202
203 next = list_entry(cl->cl_columns.next, struct libscols_column, cl_columns);
204 if (next && scols_column_is_hidden(next))
205 return 1;
206 return 0;
207 }
208
209
210 static int has_pending_data(struct libscols_table *tb)
211 {
212 struct libscols_column *cl;
213 struct libscols_iter itr;
214
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))
218 continue;
219 if (cl->pending_data)
220 return 1;
221 }
222 return 0;
223 }
224
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 */
229 size_t bufsz)
230 {
231 size_t len_pad = 0; /* in screen cells as opposed to bytes */
232
233 /* generate tree ASCII-art rather than padding */
234 if (ln && scols_column_is_tree(cl)) {
235 if (!ln->parent) {
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));
240 }
241 } else {
242 /* use the same draw function as though we were intending to draw an L-shape */
243 struct libscols_buffer *art = new_buffer(bufsz);
244 char *data;
245
246 if (art) {
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);
252 if (data && len_pad)
253 fputs(data, tb->out);
254 free_buffer(art);
255 }
256 }
257 }
258
259 if (is_last_column(cl))
260 return;
261
262 /* fill rest of cell with space */
263 for(; len_pad < cl->width; ++len_pad)
264 fputs(cellpadding_symbol(tb), tb->out);
265
266 fputs(colsep(tb), tb->out);
267 }
268
269
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 */
274 {
275 const char *color = NULL;
276
277 if (tb && tb->colors_wanted) {
278 if (ce && !color)
279 color = ce->color;
280 if (ln && !color)
281 color = ln->color;
282 if (!color)
283 color = cl->color;
284 }
285 return color;
286 }
287
288 /* Fill the start of a line with padding (or with tree ascii-art).
289 *
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'):
292 *
293 * aaa bbb ccc ddd eee
294 * AAA BBB CCCCCCC
295 * DDD EEE
296 * ^^^^^^^^^^^^
297 * new line padding
298 */
299 static void print_newline_padding(struct libscols_table *tb,
300 struct libscols_column *cl,
301 struct libscols_line *ln, /* optional */
302 size_t bufsz)
303 {
304 size_t i;
305
306 assert(tb);
307 assert(cl);
308
309 fputs(linesep(tb), tb->out); /* line break */
310
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);
314 }
315
316 /*
317 * Pending data
318 *
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.
321 *
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
326 * columns.
327 */
328
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)
331 {
332 char *p = NULL;
333
334 if (data) {
335 DBG(COL, ul_debugobj(cl, "setting pending data"));
336 assert(sz);
337 p = strdup(data);
338 if (!p)
339 return -ENOMEM;
340 }
341
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;
346 return 0;
347 }
348
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)
351 {
352 DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
353
354 if (bytes >= cl->pending_data_sz)
355 return set_pending_data(cl, NULL, 0);
356
357 cl->pending_data += bytes;
358 cl->pending_data_sz -= bytes;
359 return 0;
360 }
361
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)
368 {
369 const char *color = get_cell_color(tb, cl, ln, ce);
370 size_t width = cl->width, bytes;
371 size_t len = width, i;
372 char *data;
373 char *wrapnl = NULL;
374
375 if (!cl->pending_data)
376 return 0;
377
378 DBG(COL, ul_debugobj(cl, "printing pending data"));
379
380 data = strdup(cl->pending_data);
381 if (!data)
382 goto err;
383
384 if (scols_column_is_wrapnl(cl) && (wrapnl = strchr(data, '\n'))) {
385 *wrapnl = '\0';
386 wrapnl++;
387 bytes = wrapnl - data;
388
389 len = mbs_safe_nwidth(data, bytes, NULL);
390 } else
391 bytes = mbs_truncate(data, &len);
392
393 if (bytes == (size_t) -1)
394 goto err;
395
396 step_pending_data(cl, bytes);
397
398 if (color)
399 fputs(color, tb->out);
400 fputs(data, tb->out);
401 if (color)
402 fputs(UL_COLOR_RESET, tb->out);
403 free(data);
404
405 if (is_last_column(cl))
406 return 0;
407
408 for (i = len; i < width; i++)
409 fputs(cellpadding_symbol(tb), tb->out); /* padding */
410
411 fputs(colsep(tb), tb->out); /* columns separator */
412 return 0;
413 err:
414 free(data);
415 return -errno;
416 }
417
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)
423 {
424 size_t len = 0, i, width, bytes;
425 const char *color = NULL;
426 char *data, *wrapnl;
427
428 assert(tb);
429 assert(cl);
430
431 DBG(TAB, ul_debugobj(tb,
432 " -> data, column=%p, line=%p, cell=%p, buff=%p",
433 cl, ln, ce, buf));
434
435 data = buffer_get_data(buf);
436 if (!data)
437 data = "";
438
439 switch (tb->format) {
440 case SCOLS_FMT_RAW:
441 fputs_nonblank(data, tb->out);
442 if (!is_last_column(cl))
443 fputs(colsep(tb), tb->out);
444 return 0;
445
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);
451 return 0;
452
453 case SCOLS_FMT_JSON:
454 fputs_quoted_json_lower(scols_cell_get_data(&cl->header), tb->out);
455 fputs(": ", tb->out);
456 if (!data || !*data)
457 fputs("null", tb->out);
458 else
459 fputs_quoted_json(data, tb->out);
460 if (!is_last_column(cl))
461 fputs(", ", tb->out);
462 return 0;
463
464 case SCOLS_FMT_HUMAN:
465 break; /* continue below */
466 }
467
468 color = get_cell_color(tb, cl, ln, ce);
469
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.
472 */
473 data = buffer_get_safe_data(buf, &len,
474 scols_column_is_wrapnl(cl) ? "\n" : NULL);
475 if (!data)
476 data = "";
477 bytes = strlen(data);
478 width = cl->width;
479
480 /* multi-line cell based on '\n' */
481 if (*data && scols_column_is_wrapnl(cl) && (wrapnl = strchr(data, '\n'))) {
482 *wrapnl = '\0';
483 wrapnl++;
484 set_pending_data(cl, wrapnl, bytes - (wrapnl - data));
485 bytes = wrapnl - data;
486 len = mbs_safe_nwidth(data, bytes, NULL);
487 }
488
489 if (is_last_column(cl)
490 && len < width
491 && !scols_table_is_maxout(tb)
492 && !scols_column_is_right(cl))
493 width = len;
494
495 /* truncate data */
496 if (len > width && scols_column_is_trunc(cl)) {
497 len = width;
498 bytes = mbs_truncate(data, &len); /* updates 'len' */
499 }
500
501 /* multi-line cell */
502 if (len > width && scols_column_is_wrap(cl)) {
503 set_pending_data(cl, data, bytes);
504
505 len = width;
506 bytes = mbs_truncate(data, &len);
507 if (bytes != (size_t) -1 && bytes > 0)
508 step_pending_data(cl, bytes);
509 }
510
511 if (bytes == (size_t) -1) {
512 bytes = len = 0;
513 data = NULL;
514 }
515
516 if (data) {
517 if (scols_column_is_right(cl)) {
518 if (color)
519 fputs(color, tb->out);
520 for (i = len; i < width; i++)
521 fputs(cellpadding_symbol(tb), tb->out);
522 fputs(data, tb->out);
523 if (color)
524 fputs(UL_COLOR_RESET, tb->out);
525 len = width;
526
527 } else if (color) {
528 char *p = data;
529 size_t art = buffer_get_safe_art_size(buf);
530
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);
534 p += art;
535 }
536
537 fputs(color, tb->out);
538 fputs(p, tb->out);
539 fputs(UL_COLOR_RESET, tb->out);
540 } else
541 fputs(data, tb->out);
542 }
543 for (i = len; i < width; i++)
544 fputs(cellpadding_symbol(tb), tb->out); /* padding */
545
546 if (is_last_column(cl))
547 return 0;
548
549 if (len > width && !scols_column_is_trunc(cl))
550 print_newline_padding(tb, cl, ln, buf->bufsz); /* next column starts on next line */
551 else
552 fputs(colsep(tb), tb->out); /* columns separator */
553
554 return 0;
555 }
556
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)
561 {
562 const char *data;
563 struct libscols_cell *ce;
564 int rc = 0;
565
566 assert(tb);
567 assert(ln);
568 assert(cl);
569 assert(buf);
570 assert(cl->seqnum <= tb->ncols);
571
572 buffer_reset_data(buf);
573
574 ce = scols_line_get_cell(ln, cl->seqnum);
575 data = ce ? scols_cell_get_data(ce) : NULL;
576 if (!data)
577 return 0;
578
579 if (!scols_column_is_tree(cl))
580 return buffer_set_data(buf, data);
581
582 /*
583 * Tree stuff
584 */
585 if (ln->parent && !scols_table_is_json(tb)) {
586 rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
587
588 if (!rc && list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
589 rc = buffer_append_data(buf, right_symbol(tb));
590 else if (!rc)
591 rc = buffer_append_data(buf, branch_symbol(tb));
592 if (!rc)
593 buffer_set_art_index(buf);
594 }
595
596 if (!rc)
597 rc = buffer_append_data(buf, data);
598 return rc;
599 }
600
601 static void fput_indent(struct libscols_table *tb)
602 {
603 int i;
604
605 for (i = 0; i <= tb->indent; i++)
606 fputs(" ", tb->out);
607 }
608
609 static void fput_table_open(struct libscols_table *tb)
610 {
611 tb->indent = 0;
612
613 if (scols_table_is_json(tb)) {
614 fputc('{', tb->out);
615 fputs(linesep(tb), tb->out);
616
617 fput_indent(tb);
618 fputs_quoted(tb->name, tb->out);
619 fputs(": [", tb->out);
620 fputs(linesep(tb), tb->out);
621
622 tb->indent++;
623 tb->indent_last_sep = 1;
624 }
625 }
626
627 static void fput_table_close(struct libscols_table *tb)
628 {
629 tb->indent--;
630
631 if (scols_table_is_json(tb)) {
632 fput_indent(tb);
633 fputc(']', tb->out);
634 tb->indent--;
635 fputs(linesep(tb), tb->out);
636 fputc('}', tb->out);
637 fputs(linesep(tb), tb->out);
638 tb->indent_last_sep = 1;
639 }
640 }
641
642 static void fput_children_open(struct libscols_table *tb)
643 {
644 if (scols_table_is_json(tb)) {
645 fputc(',', tb->out);
646 fputs(linesep(tb), tb->out);
647 fput_indent(tb);
648 fputs("\"children\": [", tb->out);
649 }
650 /* between parent and child is separator */
651 fputs(linesep(tb), tb->out);
652 tb->indent_last_sep = 1;
653 tb->indent++;
654 }
655
656 static void fput_children_close(struct libscols_table *tb)
657 {
658 tb->indent--;
659
660 if (scols_table_is_json(tb)) {
661 fput_indent(tb);
662 fputc(']', tb->out);
663 fputs(linesep(tb), tb->out);
664 tb->indent_last_sep = 1;
665 }
666 }
667
668 static void fput_line_open(struct libscols_table *tb)
669 {
670 if (scols_table_is_json(tb)) {
671 fput_indent(tb);
672 fputc('{', tb->out);
673 tb->indent_last_sep = 0;
674 }
675 tb->indent++;
676 }
677
678 static void fput_line_close(struct libscols_table *tb, int last)
679 {
680 tb->indent--;
681 if (scols_table_is_json(tb)) {
682 if (tb->indent_last_sep)
683 fput_indent(tb);
684 fputs(last ? "}" : "},", tb->out);
685 }
686 if (!tb->no_linesep)
687 fputs(linesep(tb), tb->out);
688 tb->indent_last_sep = 1;
689 }
690
691 /*
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.
694 */
695 static int print_line(struct libscols_table *tb,
696 struct libscols_line *ln,
697 struct libscols_buffer *buf)
698 {
699 int rc = 0, pending = 0;
700 struct libscols_column *cl;
701 struct libscols_iter itr;
702
703 assert(ln);
704
705 DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf));
706
707 /* regular line */
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))
711 continue;
712 rc = cell_to_buffer(tb, ln, cl, buf);
713 if (rc == 0)
714 rc = print_data(tb, cl, ln,
715 scols_line_get_cell(ln, cl->seqnum),
716 buf);
717 if (rc == 0 && cl->pending_data)
718 pending = 1;
719 }
720
721 /* extra lines of the multi-line cells */
722 while (rc == 0 && pending) {
723 pending = 0;
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))
728 continue;
729
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)
733 pending = 1;
734 } else
735 print_empty_cell(tb, cl, ln, buf->bufsz);
736 }
737 }
738
739 return 0;
740 }
741
742 static int print_title(struct libscols_table *tb)
743 {
744 int rc, color = 0;
745 mbs_align_t align;
746 size_t width, bufsz, titlesz;
747 char *title = NULL, *buf = NULL;
748
749 assert(tb);
750
751 if (!tb->title.data)
752 return 0;
753
754 DBG(TAB, ul_debugobj(tb, "printing title"));
755
756 /* encode data */
757 bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
758 if (bufsz == 1) {
759 DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore"));
760 return 0;
761 }
762 buf = malloc(bufsz);
763 if (!buf) {
764 rc = -ENOMEM;
765 goto done;
766 }
767
768 if (!mbs_safe_encode_to_buffer(tb->title.data, &bufsz, buf, NULL) ||
769 !bufsz || bufsz == (size_t) -1) {
770 rc = -EINVAL;
771 goto done;
772 }
773
774 /* truncate and align */
775 width = tb->is_term ? tb->termwidth : 80;
776 titlesz = width + bufsz;
777
778 title = malloc(titlesz);
779 if (!title) {
780 rc = -EINVAL;
781 goto done;
782 }
783
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;
790 else
791 align = MBS_ALIGN_LEFT; /* default */
792
793 /* copy from buf to title and align to width with title_padding */
794 rc = mbsalign_with_padding(buf, title, titlesz,
795 &width, align,
796 0, (int) *titlepadding_symbol(tb));
797
798 if (rc == -1) {
799 rc = -EINVAL;
800 goto done;
801 }
802
803 if (tb->colors_wanted && tb->title.color)
804 color = 1;
805 if (color)
806 fputs(tb->title.color, tb->out);
807
808 fputs(title, tb->out);
809
810 if (color)
811 fputs(UL_COLOR_RESET, tb->out);
812
813 fputc('\n', tb->out);
814 rc = 0;
815 done:
816 free(buf);
817 free(title);
818 DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
819 return rc;
820 }
821
822 static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
823 {
824 int rc = 0;
825 struct libscols_column *cl;
826 struct libscols_iter itr;
827
828 assert(tb);
829
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))
835 return 0;
836
837 DBG(TAB, ul_debugobj(tb, "printing header"));
838
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))
843 continue;
844 rc = buffer_set_data(buf, scols_cell_get_data(&cl->header));
845 if (!rc)
846 rc = print_data(tb, cl, NULL, &cl->header, buf);
847 }
848
849 if (rc == 0)
850 fputs(linesep(tb), tb->out);
851
852 tb->header_printed = 1;
853 return rc;
854 }
855
856 static int print_range( struct libscols_table *tb,
857 struct libscols_buffer *buf,
858 struct libscols_iter *itr,
859 struct libscols_line *end)
860 {
861 int rc = 0;
862 struct libscols_line *ln;
863
864 assert(tb);
865
866 while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
867
868 fput_line_open(tb);
869 rc = print_line(tb, ln, buf);
870 fput_line_close(tb, scols_iter_is_last(itr));
871
872 if (end && ln == end)
873 break;
874 }
875
876 return rc;
877
878 }
879
880 static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
881 {
882 struct libscols_iter itr;
883
884 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
885 return print_range(tb, buf, &itr, NULL);
886 }
887
888
889 static int print_tree_line(struct libscols_table *tb,
890 struct libscols_line *ln,
891 struct libscols_buffer *buf,
892 int last)
893 {
894 int rc;
895 struct list_head *p;
896
897 fput_line_open(tb);
898
899 rc = print_line(tb, ln, buf);
900 if (rc)
901 goto done;
902
903 if (list_empty(&ln->ln_branch)) {
904 fput_line_close(tb, last);
905 return 0;
906 }
907
908 fput_children_open(tb);
909
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);
914
915 rc = print_tree_line(tb, chld, buf, p->next == &ln->ln_branch);
916 if (rc)
917 goto done;
918 }
919
920 fput_children_close(tb);
921
922 if (scols_table_is_json(tb))
923 fput_line_close(tb, last);
924 done:
925 return rc;
926 }
927
928 static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
929 {
930 int rc = 0;
931 struct libscols_line *ln, *last = NULL;
932 struct libscols_iter itr;
933
934 assert(tb);
935
936 DBG(TAB, ul_debugobj(tb, "printing tree"));
937
938 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
939
940 while (scols_table_next_line(tb, &itr, &ln) == 0)
941 if (!last || !ln->parent)
942 last = ln;
943
944 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
945 while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
946 if (ln->parent)
947 continue;
948 rc = print_tree_line(tb, ln, buf, ln == last);
949 }
950
951 return rc;
952 }
953
954 static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
955 {
956 if (scols_column_is_hidden(cl)) {
957 DBG(COL, ul_debugobj(cl, "%s ignored", cl->header.data));
958 return;
959 }
960
961 DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
962 "hint=%d, avg=%zu, max=%zu, min=%zu, "
963 "extreme=%s %s",
964
965 cl->header.data, cl->seqnum, cl->width,
966 cl->width_hint > 1 ? (int) cl->width_hint :
967 (int) (cl->width_hint * tb->termwidth),
968 cl->width_avg,
969 cl->width_max,
970 cl->width_min,
971 cl->is_extreme ? "yes" : "not",
972 cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
973 }
974
975 static void dbg_columns(struct libscols_table *tb)
976 {
977 struct libscols_iter itr;
978 struct libscols_column *cl;
979
980 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
981 while (scols_table_next_column(tb, &itr, &cl) == 0)
982 dbg_column(tb, cl);
983 }
984
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').
987 */
988 static size_t count_wrapnl_size(const char *data)
989 {
990 size_t sum = 0;
991
992 while (data && *data) {
993 const char *p = data;
994
995 p = strchr(data, '\n');
996 if (p) {
997 size_t sz = mbs_safe_nwidth(data, p - data, NULL);
998
999 sum = max(sum, sz);
1000 p++;
1001 }
1002 data = p;;
1003 }
1004
1005 return sum;
1006 }
1007
1008 /*
1009 * This function counts column width.
1010 *
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.
1016 */
1017 static int count_column_width(struct libscols_table *tb,
1018 struct libscols_column *cl,
1019 struct libscols_buffer *buf)
1020 {
1021 struct libscols_line *ln;
1022 struct libscols_iter itr;
1023 int count = 0, rc = 0;
1024 size_t sum = 0;
1025
1026 assert(tb);
1027 assert(cl);
1028
1029 cl->width = 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))
1034 cl->width_min--;
1035 }
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);
1039 }
1040 }
1041
1042 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1043 while (scols_table_next_line(tb, &itr, &ln) == 0) {
1044 size_t len;
1045 char *data;
1046
1047 rc = cell_to_buffer(tb, ln, cl, buf);
1048 if (rc)
1049 goto done;
1050
1051 data = buffer_get_data(buf);
1052
1053 if (!data)
1054 len = 0;
1055 else if (scols_column_is_wrapnl(cl))
1056 len = count_wrapnl_size(data);
1057 else
1058 len = mbs_safe_width(data);
1059
1060 if (len == (size_t) -1) /* ignore broken multibyte strings */
1061 len = 0;
1062 cl->width_max = max(len, cl->width_max);
1063
1064 if (cl->is_extreme && len > cl->width_avg * 2)
1065 continue;
1066 else if (scols_column_is_noextremes(cl)) {
1067 sum += len;
1068 count++;
1069 }
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);
1074 }
1075 }
1076
1077 if (count && cl->width_avg == 0) {
1078 cl->width_avg = sum / count;
1079 if (cl->width_max > cl->width_avg * 2)
1080 cl->is_extreme = 1;
1081 }
1082
1083 /* enlarge to minimal width */
1084 if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
1085 cl->width = cl->width_min;
1086
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)
1090
1091 cl->width = (size_t) cl->width_hint;
1092
1093 done:
1094 ON_DBG(COL, dbg_column(tb, cl));
1095 return rc;
1096 }
1097
1098 /*
1099 * This is core of the scols_* voodoo...
1100 */
1101 static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf)
1102 {
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;
1107 int extremes = 0;
1108
1109
1110 DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
1111
1112 /* set basic columns width
1113 */
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);
1117 if (rc)
1118 goto done;
1119
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;
1123 }
1124
1125 if (!tb->is_term) {
1126 DBG(TAB, ul_debugobj(tb, " non-terminal output"));
1127 goto done;
1128 }
1129
1130 /* be paranoid */
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));
1133
1134 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1135 while (width_min > tb->termwidth
1136 && scols_table_next_column(tb, &itr, &cl) == 0) {
1137 width_min--;
1138 cl->width_min--;
1139 }
1140 DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
1141 }
1142
1143 /* reduce columns with extreme fields */
1144 if (width > tb->termwidth && extremes) {
1145 DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
1146
1147 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1148 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1149 size_t org_width;
1150
1151 if (!cl->is_extreme)
1152 continue;
1153
1154 org_width = cl->width;
1155 rc = count_column_width(tb, cl, buf);
1156 if (rc)
1157 goto done;
1158
1159 if (org_width > cl->width)
1160 width -= org_width - cl->width;
1161 else
1162 extremes--; /* hmm... nothing reduced */
1163 }
1164 }
1165
1166 if (width < tb->termwidth) {
1167 if (extremes) {
1168 DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
1169
1170 /* enlarge the first extreme column */
1171 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1172 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1173 size_t add;
1174
1175 if (!cl->is_extreme)
1176 continue;
1177
1178 /* this column is too large, ignore?
1179 if (cl->width_max - cl->width >
1180 (tb->termwidth - width))
1181 continue;
1182 */
1183
1184 add = tb->termwidth - width;
1185 if (add && cl->width + add > cl->width_max)
1186 add = cl->width_max - cl->width;
1187
1188 cl->width += add;
1189 width += add;
1190
1191 if (width == tb->termwidth)
1192 break;
1193 }
1194 }
1195
1196 if (width < tb->termwidth && scols_table_is_maxout(tb)) {
1197 DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
1198
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) {
1203 cl->width++;
1204 width++;
1205 if (width == tb->termwidth)
1206 break;
1207 }
1208 }
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);
1213
1214 DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
1215
1216 if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
1217 col->width += tb->termwidth - width;
1218 width = tb->termwidth;
1219 }
1220 }
1221 }
1222
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
1226 */
1227 trunc_only = 1;
1228 while (width > tb->termwidth) {
1229 size_t org = width;
1230
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"));
1235
1236 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1237 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1238
1239 DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
1240 cl->header.data, cl->width, cl->width_treeart));
1241
1242 if (width <= tb->termwidth)
1243 break;
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)))
1249 continue;
1250 if (cl->width == cl->width_min)
1251 continue;
1252
1253 DBG(TAB, ul_debugobj(tb, " trying to reduce: %s (width=%zu)", cl->header.data, cl->width));
1254
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)) {
1258 cl->width--;
1259 width--;
1260 }
1261
1262 /* truncate column with absolute size */
1263 if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
1264 !trunc_only) {
1265 cl->width--;
1266 width--;
1267 }
1268 }
1269 if (org == width) {
1270 if (trunc_only)
1271 trunc_only = 0;
1272 else
1273 break;
1274 }
1275 }
1276
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) {
1282
1283 if (width <= tb->termwidth)
1284 break;
1285 if (width - cl->width < tb->termwidth) {
1286 size_t r = width - tb->termwidth;
1287
1288 cl->flags |= SCOLS_FL_TRUNC;
1289 cl->width -= r;
1290 width -= r;
1291 } else {
1292 cl->flags |= SCOLS_FL_HIDDEN;
1293 width -= cl->width + 1; /* +1 means separator between columns */
1294 }
1295 }
1296 }
1297 done:
1298 DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
1299 ON_DBG(TAB, dbg_columns(tb));
1300
1301 return rc;
1302 }
1303
1304 static size_t strlen_line(struct libscols_line *ln)
1305 {
1306 size_t i, sz = 0;
1307
1308 assert(ln);
1309
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;
1313
1314 sz += data ? strlen(data) : 0;
1315 }
1316
1317 return sz;
1318 }
1319
1320 static int initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
1321 {
1322 size_t bufsz, extra_bufsz = 0;
1323 struct libscols_line *ln;
1324 struct libscols_iter itr;
1325 int rc;
1326
1327 DBG(TAB, ul_debugobj(tb, "initialize printing"));
1328
1329 if (!tb->symbols)
1330 scols_table_set_symbols(tb, NULL); /* use default */
1331
1332 if (tb->format == SCOLS_FMT_HUMAN)
1333 tb->is_term = isatty(STDOUT_FILENO) ? 1 : 0;
1334
1335 if (tb->is_term) {
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;
1340 } else
1341 bufsz = BUFSIZ;
1342
1343 /*
1344 * Estimate extra space necessary for tree, JSON or another output
1345 * decoration.
1346 */
1347 if (scols_table_is_tree(tb))
1348 extra_bufsz += tb->nlines * strlen(vertical_symbol(tb));
1349
1350 switch (tb->format) {
1351 case SCOLS_FMT_RAW:
1352 extra_bufsz += tb->ncols; /* separator between columns */
1353 break;
1354 case SCOLS_FMT_JSON:
1355 if (tb->format == SCOLS_FMT_JSON)
1356 extra_bufsz += tb->nlines * 3; /* indention */
1357 /* fallthrough */
1358 case SCOLS_FMT_EXPORT:
1359 {
1360 struct libscols_column *cl;
1361
1362 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1363
1364 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1365 if (scols_column_is_hidden(cl))
1366 continue;
1367 extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */
1368 extra_bufsz += 2; /* separators */
1369 }
1370 break;
1371 }
1372 case SCOLS_FMT_HUMAN:
1373 break;
1374 }
1375
1376 /*
1377 * Enlarge buffer if necessary, the buffer should be large enough to
1378 * store line data and tree ascii art (or another decoration).
1379 */
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;
1383 if (sz > bufsz)
1384 bufsz = sz;
1385 }
1386
1387 *buf = new_buffer(bufsz + 1); /* data + space for \0 */
1388 if (!*buf)
1389 return -ENOMEM;
1390
1391 if (tb->format == SCOLS_FMT_HUMAN) {
1392 rc = recount_widths(tb, *buf);
1393 if (rc != 0)
1394 goto err;
1395 }
1396
1397 return 0;
1398 err:
1399 free_buffer(*buf);
1400 return rc;
1401 }
1402
1403 /**
1404 * scola_table_print_range:
1405 * @tb: table
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.
1408 *
1409 * If the start is the first line in the table than prints table header too.
1410 * The header is printed only once.
1411 *
1412 * Returns: 0, a negative value in case of an error.
1413 */
1414 int scols_table_print_range( struct libscols_table *tb,
1415 struct libscols_line *start,
1416 struct libscols_line *end)
1417 {
1418 struct libscols_buffer *buf;
1419 struct libscols_iter itr;
1420 int rc;
1421
1422 if (scols_table_is_tree(tb))
1423 return -EINVAL;
1424
1425 DBG(TAB, ul_debugobj(tb, "printing range"));
1426
1427 rc = initialize_printing(tb, &buf);
1428 if (rc)
1429 return rc;
1430
1431 if (start) {
1432 itr.direction = SCOLS_ITER_FORWARD;
1433 itr.head = &tb->tb_lines;
1434 itr.p = &start->ln_lines;
1435 } else
1436 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1437
1438 if (!start || itr.p == tb->tb_lines.next) {
1439 rc = print_header(tb, buf);
1440 if (rc)
1441 goto done;
1442 }
1443
1444 rc = print_range(tb, buf, &itr, end);
1445 done:
1446 free_buffer(buf);
1447 return rc;
1448 }
1449
1450 /**
1451 * scols_table_print_range_to_string:
1452 * @tb: table
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
1456 *
1457 * The same as scols_table_print_range(), but prints to @data instead of
1458 * stream.
1459 *
1460 * Returns: 0, a negative value in case of an error.
1461 */
1462 int scols_table_print_range_to_string( struct libscols_table *tb,
1463 struct libscols_line *start,
1464 struct libscols_line *end,
1465 char **data)
1466 {
1467 #ifdef HAVE_OPEN_MEMSTREAM
1468 FILE *stream, *old_stream;
1469 size_t sz;
1470 int rc;
1471
1472 if (!tb)
1473 return -EINVAL;
1474
1475 DBG(TAB, ul_debugobj(tb, "printing range to string"));
1476
1477 /* create a stream for output */
1478 stream = open_memstream(data, &sz);
1479 if (!stream)
1480 return -ENOMEM;
1481
1482 old_stream = scols_table_get_stream(tb);
1483 scols_table_set_stream(tb, stream);
1484 rc = scols_table_print_range(tb, start, end);
1485 fclose(stream);
1486 scols_table_set_stream(tb, old_stream);
1487
1488 return rc;
1489 #else
1490 return -ENOSYS;
1491 #endif
1492 }
1493
1494 /**
1495 * scols_print_table:
1496 * @tb: table
1497 *
1498 * Prints the table to the output stream.
1499 *
1500 * Returns: 0, a negative value in case of an error.
1501 */
1502 int scols_print_table(struct libscols_table *tb)
1503 {
1504 int rc = 0;
1505 struct libscols_buffer *buf;
1506
1507 if (!tb)
1508 return -EINVAL;
1509
1510 DBG(TAB, ul_debugobj(tb, "printing"));
1511
1512 if (list_empty(&tb->tb_lines)) {
1513 DBG(TAB, ul_debugobj(tb, "ignore -- empty table"));
1514 return 0;
1515 }
1516
1517 tb->header_printed = 0;
1518 rc = initialize_printing(tb, &buf);
1519 if (rc)
1520 return rc;
1521
1522 fput_table_open(tb);
1523
1524 if (tb->format == SCOLS_FMT_HUMAN)
1525 print_title(tb);
1526
1527 rc = print_header(tb, buf);
1528 if (rc)
1529 goto done;
1530
1531 if (scols_table_is_tree(tb))
1532 rc = print_tree(tb, buf);
1533 else
1534 rc = print_table(tb, buf);
1535
1536 fput_table_close(tb);
1537 done:
1538 free_buffer(buf);
1539 return rc;
1540 }
1541
1542 /**
1543 * scols_print_table_to_string:
1544 * @tb: table
1545 * @data: pointer to the beginning of a memory area to print to
1546 *
1547 * Prints the table to @data.
1548 *
1549 * Returns: 0, a negative value in case of an error.
1550 */
1551 int scols_print_table_to_string(struct libscols_table *tb, char **data)
1552 {
1553 #ifdef HAVE_OPEN_MEMSTREAM
1554 FILE *stream, *old_stream;
1555 size_t sz;
1556 int rc;
1557
1558 if (!tb)
1559 return -EINVAL;
1560
1561 DBG(TAB, ul_debugobj(tb, "printing to string"));
1562
1563 /* create a stream for output */
1564 stream = open_memstream(data, &sz);
1565 if (!stream)
1566 return -ENOMEM;
1567
1568 old_stream = scols_table_get_stream(tb);
1569 scols_table_set_stream(tb, stream);
1570 rc = scols_print_table(tb);
1571 fclose(stream);
1572 scols_table_set_stream(tb, old_stream);
1573
1574 return rc;
1575 #else
1576 return -ENOSYS;
1577 #endif
1578 }
1579