]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libsmartcols/src/table_print.c
libsmartcols: fill wrapped lines with space instead of 'x'
[thirdparty/util-linux.git] / libsmartcols / src / table_print.c
CommitLineData
3e542c76
KZ
1/*
2 * table.c - functions handling the data at the table level
3 *
4 * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
b3256eff 5 * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
3e542c76
KZ
6 *
7 * This file may be redistributed under the terms of the
8 * GNU Lesser General Public License.
9 */
ce44112b 10
e2310281
KZ
11/**
12 * SECTION: table_print
13 * @title: Table print
2473b711 14 * @short_description: output functions
e2310281
KZ
15 *
16 * Table output API.
17 */
18
3e542c76
KZ
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
b233dcb6
KZ
32/* This is private struct to work with output data */
33struct libscols_buffer {
34 char *begin; /* begin of the buffer */
35 char *cur; /* current end of the buffer */
d6303e28 36 char *encdata; /* encoded buffer mbs_safe_encode() */
b233dcb6
KZ
37
38 size_t bufsz; /* size of the buffer */
ee75308c 39 size_t art_idx; /* begin of the tree ascii art or zero */
b233dcb6
KZ
40};
41
42static 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);
d6303e28 50 buf->encdata = NULL;
b233dcb6 51 buf->bufsz = sz;
710ed55d
KZ
52
53 DBG(BUFF, ul_debugobj(buf, "alloc (size=%zu)", sz));
b233dcb6
KZ
54 return buf;
55}
56
57static void free_buffer(struct libscols_buffer *buf)
58{
710ed55d
KZ
59 if (!buf)
60 return;
61 DBG(BUFF, ul_debugobj(buf, "dealloc"));
d6303e28 62 free(buf->encdata);
b233dcb6
KZ
63 free(buf);
64}
65
66static int buffer_reset_data(struct libscols_buffer *buf)
67{
68 if (!buf)
69 return -EINVAL;
70
710ed55d 71 /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/
b233dcb6
KZ
72 buf->begin[0] = '\0';
73 buf->cur = buf->begin;
ee75308c 74 buf->art_idx = 0;
b233dcb6
KZ
75 return 0;
76}
77
78static 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;
b233dcb6
KZ
92 memcpy(buf->cur, str, sz + 1);
93 buf->cur += sz;
94 return 0;
95}
96
97static 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
1bcf491a 103/* save the current buffer position to art_idx */
ee75308c
KZ
104static void buffer_set_art_index(struct libscols_buffer *buf)
105{
710ed55d 106 if (buf) {
ee75308c 107 buf->art_idx = buf->cur - buf->begin;
710ed55d
KZ
108 /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/
109 }
ee75308c
KZ
110}
111
b233dcb6
KZ
112static char *buffer_get_data(struct libscols_buffer *buf)
113{
114 return buf ? buf->begin : NULL;
115}
116
ee75308c 117/* encode data by mbs_safe_encode() to avoid control and non-printable chars */
d6303e28
KZ
118static 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;
d6303e28
KZ
135 return res;
136nothing:
137 *cells = 0;
138 return NULL;
139}
140
ee75308c
KZ
141/* returns size in bytes of the ascii art (according to art_idx) in safe encoding */
142static 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
8501d9be
KZ
154/* returns pointer to the end of used data */
155static 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
df73852b
KZ
180static int is_last_column(struct libscols_table *tb, struct libscols_column *cl)
181{
182 int rc = list_entry_is_last(&cl->cl_columns, &tb->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);
6d6b6d18 189 if (next && scols_column_is_hidden(next))
df73852b
KZ
190 return 1;
191 return 0;
192}
3e542c76 193
d1b4d14f
OO
194#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
195#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
b233dcb6 196
d94c5198
KZ
197
198static 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
8501d9be
KZ
213/* print padding or asci-art instead of data of @cl */
214static 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 asci-art rather than padding */
222 if (ln && scols_column_is_tree(cl)) {
223 if (!ln->parent) {
d94c5198 224 /* only print symbols->vert if followed by child */
8501d9be
KZ
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);
d94c5198
KZ
237 if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
238 buffer_append_data(art, tb->symbols->vert);
8501d9be
KZ
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
d94c5198
KZ
251
252static 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
b295bdb1 270/* Fill the start of a line with padding (or with tree ascii-art).
8501d9be 271 *
b295bdb1
BS
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'):
8501d9be
KZ
274 *
275 * aaa bbb ccc ddd eee
276 * AAA BBB CCCCCCC
277 * DDD EEE
278 * ^^^^^^^^^^^^
279 * new line padding
280 */
281static void print_newline_padding(struct libscols_table *tb,
282 struct libscols_column *cl,
283 struct libscols_line *ln, /* optional */
284 size_t bufsz,
285 size_t len)
286{
287 size_t i;
288
289 assert(tb);
290 assert(cl);
291
292 fputs(linesep(tb), tb->out); /* line break */
293
294 /* fill cells after line break */
295 for (i = 0; i <= (size_t) cl->seqnum; i++)
296 print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz);
297}
298
d94c5198
KZ
299/*
300 * Pending data
301 *
302 * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
303 * printed as usually and output is truncated to match column width.
304 *
305 * The rest of the long text is printed on next extra line(s). The extra lines
306 * don't exist in the table (not represented by libscols_line). The data for
307 * the extra lines are stored in libscols_column->pending_data_buf and the
308 * function print_line() adds extra lines until the buffer is not empty in all
309 * columns.
310 */
311
312/* set data that will be printed by extra lines */
313static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
314{
315 char *p = NULL;
316
317 if (data) {
318 DBG(COL, ul_debugobj(cl, "setting pending data"));
319 assert(sz);
320 p = strdup(data);
321 if (!p)
322 return -ENOMEM;
323 }
324
325 free(cl->pending_data_buf);
326 cl->pending_data_buf = p;
327 cl->pending_data_sz = sz;
328 cl->pending_data = cl->pending_data_buf;
329 return 0;
330}
331
332/* the next extra line has been printed, move pending data cursor */
333static int step_pending_data(struct libscols_column *cl, size_t bytes)
334{
335 DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
336
337 if (bytes >= cl->pending_data_sz)
338 return set_pending_data(cl, NULL, 0);
339
340 cl->pending_data += bytes;
341 cl->pending_data_sz -= bytes;
342 return 0;
343}
344
345/* print next pending data for the column @cl */
346static int print_pending_data(
347 struct libscols_table *tb,
348 struct libscols_column *cl,
349 struct libscols_line *ln, /* optional */
350 struct libscols_cell *ce)
351{
352 const char *color = get_cell_color(tb, cl, ln, ce);
353 size_t width = cl->width, bytes;
354 size_t len = width, i;
355 char *data;
356
357 if (!cl->pending_data)
358 return 0;
359
360 DBG(COL, ul_debugobj(cl, "printing pending data"));
361
362 data = strdup(cl->pending_data);
363 if (!data)
364 goto err;
365 bytes = mbs_truncate(data, &len);
366 if (bytes == (size_t) -1)
367 goto err;
368
369 step_pending_data(cl, bytes);
370
371 if (color)
372 fputs(color, tb->out);
373 fputs(data, tb->out);
374 if (color)
375 fputs(UL_COLOR_RESET, tb->out);
376 free(data);
377
378 for (i = len; i < width; i++)
d381f708 379 fputc(' ', tb->out); /* padding */
d94c5198
KZ
380
381 if (is_last_column(tb, cl))
382 return 0;
383
384 fputs(colsep(tb), tb->out); /* columns separator */
385 return 0;
386err:
387 free(data);
388 return -errno;
389}
390
b233dcb6
KZ
391static int print_data(struct libscols_table *tb,
392 struct libscols_column *cl,
393 struct libscols_line *ln, /* optional */
394 struct libscols_cell *ce, /* optional */
395 struct libscols_buffer *buf)
3e542c76 396{
47329bbc 397 size_t len = 0, i, width, bytes;
3e542c76 398 const char *color = NULL;
d6303e28 399 char *data;
3e542c76
KZ
400
401 assert(tb);
402 assert(cl);
403
710ed55d
KZ
404 DBG(TAB, ul_debugobj(tb,
405 " -> data, column=%p, line=%p, cell=%p, buff=%p",
406 cl, ln, ce, buf));
407
b233dcb6 408 data = buffer_get_data(buf);
3e542c76
KZ
409 if (!data)
410 data = "";
411
2a6cfc13
KZ
412 switch (tb->format) {
413 case SCOLS_FMT_RAW:
571441e2 414 fputs_nonblank(data, tb->out);
3e542c76 415 if (!is_last_column(tb, cl))
d1b4d14f 416 fputs(colsep(tb), tb->out);
b233dcb6 417 return 0;
3e542c76 418
2a6cfc13 419 case SCOLS_FMT_EXPORT:
571441e2
KZ
420 fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header));
421 fputs_quoted(data, tb->out);
3e542c76 422 if (!is_last_column(tb, cl))
d1b4d14f 423 fputs(colsep(tb), tb->out);
b233dcb6 424 return 0;
2a6cfc13
KZ
425
426 case SCOLS_FMT_JSON:
6a768b55 427 fputs_quoted_lower(scols_cell_get_data(&cl->header), tb->out);
2a6cfc13
KZ
428 fputs(": ", tb->out);
429 if (!data || !*data)
430 fputs("null", tb->out);
431 else
432 fputs_quoted(data, tb->out);
433 if (!is_last_column(tb, cl))
434 fputs(", ", tb->out);
435 return 0;
436
437 case SCOLS_FMT_HUMAN:
438 break; /* continue below */
3e542c76
KZ
439 }
440
d94c5198 441 color = get_cell_color(tb, cl, ln, ce);
3e542c76 442
d6303e28
KZ
443 /* encode, note that 'len' and 'width' are number of cells, not bytes */
444 data = buffer_get_safe_data(buf, &len);
3e542c76
KZ
445 if (!data)
446 data = "";
3e542c76 447 width = cl->width;
47329bbc 448 bytes = strlen(data);
3e542c76 449
96561604
KZ
450 if (is_last_column(tb, cl)
451 && len < width
452 && !scols_table_is_maxout(tb)
a6404093
IG
453 && !scols_column_is_right(cl)
454 && !scols_column_is_wrap(cl))
3e542c76
KZ
455 width = len;
456
457 /* truncate data */
8a38a8d3 458 if (len > width && scols_column_is_trunc(cl)) {
47329bbc
KZ
459 len = width;
460 bytes = mbs_truncate(data, &len); /* updates 'len' */
d94c5198 461 }
47329bbc 462
d94c5198
KZ
463 /* multi-line cell */
464 if (len > width && scols_column_is_wrap(cl)) {
465 set_pending_data(cl, data, bytes);
466
467 len = width;
468 bytes = mbs_truncate(data, &len);
469 if (bytes != (size_t) -1 && bytes > 0)
470 step_pending_data(cl, bytes);
471 }
472
473 if (bytes == (size_t) -1) {
474 bytes = len = 0;
475 data = NULL;
3e542c76 476 }
47329bbc 477
3e542c76 478 if (data) {
ee75308c 479 if (scols_column_is_right(cl)) {
3e542c76 480 if (color)
571441e2 481 fputs(color, tb->out);
05c1123b
KZ
482 for (i = len; i < width; i++)
483 fputc(' ', tb->out);
484 fputs(data, tb->out);
3e542c76 485 if (color)
571441e2 486 fputs(UL_COLOR_RESET, tb->out);
96561604 487 len = width;
57c7b2a1 488
ee75308c
KZ
489 } else if (color) {
490 char *p = data;
491 size_t art = buffer_get_safe_art_size(buf);
492
493 /* we don't want to colorize tree ascii art */
494 if (scols_column_is_tree(cl) && art && art < bytes) {
495 fwrite(p, 1, art, tb->out);
496 p += art;
497 }
498
499 fputs(color, tb->out);
500 fputs(p, tb->out);
501 fputs(UL_COLOR_RESET, tb->out);
502 } else
571441e2 503 fputs(data, tb->out);
3e542c76
KZ
504 }
505 for (i = len; i < width; i++)
8501d9be 506 fputc(' ', tb->out); /* padding */
3e542c76 507
8501d9be 508 if (is_last_column(tb, cl))
b233dcb6 509 return 0;
3e542c76 510
8501d9be
KZ
511 if (len > width && !scols_column_is_trunc(cl))
512 print_newline_padding(tb, cl, ln, buf->bufsz, len); /* next column starts on next line */
3e542c76 513 else
8501d9be 514 fputs(colsep(tb), tb->out); /* columns separator */
3e542c76 515
8501d9be 516 return 0;
3e542c76
KZ
517}
518
b233dcb6
KZ
519static int cell_to_buffer(struct libscols_table *tb,
520 struct libscols_line *ln,
521 struct libscols_column *cl,
522 struct libscols_buffer *buf)
3e542c76
KZ
523{
524 const char *data;
3e542c76 525 struct libscols_cell *ce;
b233dcb6 526 int rc = 0;
3e542c76
KZ
527
528 assert(tb);
529 assert(ln);
530 assert(cl);
b233dcb6 531 assert(buf);
3e542c76
KZ
532 assert(cl->seqnum <= tb->ncols);
533
2dc0c628
KZ
534 buffer_reset_data(buf);
535
3e542c76
KZ
536 ce = scols_line_get_cell(ln, cl->seqnum);
537 data = ce ? scols_cell_get_data(ce) : NULL;
538 if (!data)
b233dcb6 539 return 0;
3e542c76 540
b233dcb6
KZ
541 if (!scols_column_is_tree(cl))
542 return buffer_set_data(buf, data);
3e542c76
KZ
543
544 /*
545 * Tree stuff
546 */
2a6cfc13 547 if (ln->parent && !scols_table_is_json(tb)) {
b233dcb6 548 rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
3e542c76 549
b233dcb6
KZ
550 if (!rc && list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
551 rc = buffer_append_data(buf, tb->symbols->right);
552 else if (!rc)
553 rc = buffer_append_data(buf, tb->symbols->branch);
ee75308c
KZ
554 if (!rc)
555 buffer_set_art_index(buf);
b233dcb6 556 }
3e542c76 557
b233dcb6
KZ
558 if (!rc)
559 rc = buffer_append_data(buf, data);
560 return rc;
3e542c76
KZ
561}
562
2a6cfc13
KZ
563static void fput_indent(struct libscols_table *tb)
564{
565 int i;
566
567 for (i = 0; i <= tb->indent; i++)
568 fputs(" ", tb->out);
569}
570
571static void fput_table_open(struct libscols_table *tb)
572{
573 tb->indent = 0;
574
575 if (scols_table_is_json(tb)) {
576 fputc('{', tb->out);
577 fputs(linesep(tb), tb->out);
578
579 fput_indent(tb);
580 fputs_quoted(tb->name, tb->out);
581 fputs(": [", tb->out);
582 fputs(linesep(tb), tb->out);
583
584 tb->indent++;
585 tb->indent_last_sep = 1;
586 }
587}
588
589static void fput_table_close(struct libscols_table *tb)
590{
591 tb->indent--;
592
593 if (scols_table_is_json(tb)) {
594 fput_indent(tb);
595 fputc(']', tb->out);
596 tb->indent--;
597 fputs(linesep(tb), tb->out);
598 fputc('}', tb->out);
599 fputs(linesep(tb), tb->out);
600 tb->indent_last_sep = 1;
601 }
602}
603
604static void fput_children_open(struct libscols_table *tb)
605{
606 if (scols_table_is_json(tb)) {
607 fputc(',', tb->out);
608 fputs(linesep(tb), tb->out);
609 fput_indent(tb);
610 fputs("\"children\": [", tb->out);
611 }
612 /* between parent and child is separator */
613 fputs(linesep(tb), tb->out);
614 tb->indent_last_sep = 1;
615 tb->indent++;
616}
617
618static void fput_children_close(struct libscols_table *tb)
619{
620 tb->indent--;
621
622 if (scols_table_is_json(tb)) {
623 fput_indent(tb);
624 fputc(']', tb->out);
625 fputs(linesep(tb), tb->out);
626 tb->indent_last_sep = 1;
627 }
628}
629
630static void fput_line_open(struct libscols_table *tb)
631{
632 if (scols_table_is_json(tb)) {
633 fput_indent(tb);
634 fputc('{', tb->out);
635 tb->indent_last_sep = 0;
636 }
637 tb->indent++;
638}
639
640static void fput_line_close(struct libscols_table *tb, int last)
641{
642 tb->indent--;
643 if (scols_table_is_json(tb)) {
644 if (tb->indent_last_sep)
645 fput_indent(tb);
646 fputs(last ? "}" : "},", tb->out);
647 }
648 fputs(linesep(tb), tb->out);
649 tb->indent_last_sep = 1;
650}
651
3e542c76 652/*
b295bdb1
BS
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.
3e542c76 655 */
b233dcb6
KZ
656static int print_line(struct libscols_table *tb,
657 struct libscols_line *ln,
658 struct libscols_buffer *buf)
3e542c76 659{
d94c5198 660 int rc = 0, pending = 0;
3e542c76
KZ
661 struct libscols_column *cl;
662 struct libscols_iter itr;
663
664 assert(ln);
665
710ed55d
KZ
666 DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf));
667
d94c5198 668 /* regular line */
3e542c76 669 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
b233dcb6 670 while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
6d6b6d18 671 if (scols_column_is_hidden(cl))
df73852b 672 continue;
b233dcb6 673 rc = cell_to_buffer(tb, ln, cl, buf);
d94c5198 674 if (rc == 0)
b233dcb6
KZ
675 rc = print_data(tb, cl, ln,
676 scols_line_get_cell(ln, cl->seqnum),
677 buf);
d94c5198
KZ
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 }
b233dcb6
KZ
698 }
699
b233dcb6 700 return 0;
3e542c76
KZ
701}
702
57867795 703static int print_title(struct libscols_table *tb)
b3256eff 704{
e865838d 705 int rc, align;
57867795
KZ
706 size_t len = 0, width;
707 char *title = NULL, *buf = NULL;
b3256eff
IG
708
709 assert(tb);
710
e865838d 711 if (!tb->title.data)
57867795 712 return 0;
284ee2a9 713
b3256eff
IG
714 DBG(TAB, ul_debugobj(tb, "printing title"));
715
57867795 716 /* encode data */
e865838d 717 len = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
57867795
KZ
718 if (len == 1)
719 return 0;
720 buf = malloc(len);
721 if (!buf) {
722 rc = -ENOMEM;
723 goto done;
724 }
b3256eff 725
e865838d 726 if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf) ||
57867795
KZ
727 !len || len == (size_t) -1) {
728 rc = -EINVAL;
729 goto done;
730 }
b3256eff 731
57867795
KZ
732 /* truncate and align */
733 title = malloc(tb->termwidth + len);
734 if (!title) {
735 rc = -EINVAL;
736 goto done;
737 }
b3256eff 738
e865838d
KZ
739 if (tb->title.flags & SCOLS_CELL_FL_LEFT)
740 align = MBS_ALIGN_LEFT;
741 else if (tb->title.flags & SCOLS_CELL_FL_RIGHT)
742 align = MBS_ALIGN_RIGHT;
743 else if (tb->title.flags & SCOLS_CELL_FL_CENTER)
744 align = MBS_ALIGN_CENTER;
745 else
746 align = MBS_ALIGN_LEFT; /* default */
747
57867795 748 width = tb->termwidth;
e865838d
KZ
749 rc = mbsalign_with_padding(buf, title, tb->termwidth + len,
750 &width, align,
751 0, (int) *tb->symbols->title_padding);
752
57867795
KZ
753 if (rc == (size_t) -1) {
754 rc = -EINVAL;
755 goto done;
756 }
b3256eff 757
e865838d
KZ
758 if (tb->title.color)
759 fputs(tb->title.color, tb->out);
b3256eff 760
57867795 761 fputs(title, tb->out);
b3256eff 762
e865838d 763 if (tb->title.color)
b3256eff 764 fputs(UL_COLOR_RESET, tb->out);
2f62d9fe 765 fputc('\n', tb->out);
57867795
KZ
766 rc = 0;
767done:
768 free(buf);
769 free(title);
770 return rc;
b3256eff
IG
771}
772
b233dcb6 773static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
3e542c76 774{
b233dcb6 775 int rc = 0;
3e542c76
KZ
776 struct libscols_column *cl;
777 struct libscols_iter itr;
778
779 assert(tb);
780
0925a9dd 781 if (scols_table_is_noheadings(tb) ||
8a38a8d3 782 scols_table_is_export(tb) ||
4827093d 783 scols_table_is_json(tb) ||
3e542c76 784 list_empty(&tb->tb_lines))
b233dcb6 785 return 0;
3e542c76 786
710ed55d
KZ
787 DBG(TAB, ul_debugobj(tb, "printing header"));
788
b295bdb1 789 /* set the width according to the size of the data */
3e542c76 790 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
b233dcb6 791 while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
6d6b6d18 792 if (scols_column_is_hidden(cl))
df73852b 793 continue;
b233dcb6
KZ
794 rc = buffer_set_data(buf, scols_cell_get_data(&cl->header));
795 if (!rc)
796 rc = print_data(tb, cl, NULL, &cl->header, buf);
3e542c76 797 }
b233dcb6
KZ
798
799 if (rc == 0)
800 fputs(linesep(tb), tb->out);
801 return rc;
3e542c76
KZ
802}
803
b233dcb6 804static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
3e542c76 805{
2a6cfc13 806 int rc = 0;
3e542c76
KZ
807 struct libscols_line *ln;
808 struct libscols_iter itr;
809
810 assert(tb);
811
3e542c76 812 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
2a6cfc13
KZ
813 while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
814 fput_line_open(tb);
b233dcb6 815 rc = print_line(tb, ln, buf);
2a6cfc13
KZ
816 fput_line_close(tb, scols_iter_is_last(&itr));
817 }
b233dcb6
KZ
818
819 return rc;
3e542c76
KZ
820}
821
b233dcb6
KZ
822static int print_tree_line(struct libscols_table *tb,
823 struct libscols_line *ln,
2a6cfc13
KZ
824 struct libscols_buffer *buf,
825 int last)
3e542c76 826{
b233dcb6 827 int rc;
3e542c76
KZ
828 struct list_head *p;
829
2a6cfc13
KZ
830 fput_line_open(tb);
831
b233dcb6
KZ
832 rc = print_line(tb, ln, buf);
833 if (rc)
2a6cfc13
KZ
834 goto done;
835
836 if (list_empty(&ln->ln_branch)) {
837 fput_line_close(tb, last);
b233dcb6 838 return 0;
2a6cfc13
KZ
839 }
840
841 fput_children_open(tb);
3e542c76
KZ
842
843 /* print all children */
844 list_for_each(p, &ln->ln_branch) {
845 struct libscols_line *chld =
846 list_entry(p, struct libscols_line, ln_children);
2a6cfc13
KZ
847
848 rc = print_tree_line(tb, chld, buf, p->next == &ln->ln_branch);
b233dcb6 849 if (rc)
2a6cfc13 850 goto done;
3e542c76 851 }
b233dcb6 852
2a6cfc13
KZ
853 fput_children_close(tb);
854
855 if (scols_table_is_json(tb))
856 fput_line_close(tb, last);
857done:
b233dcb6 858 return rc;
3e542c76
KZ
859}
860
b233dcb6 861static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
3e542c76 862{
2a6cfc13
KZ
863 int rc = 0;
864 struct libscols_line *ln, *last = NULL;
3e542c76
KZ
865 struct libscols_iter itr;
866
867 assert(tb);
868
710ed55d
KZ
869 DBG(TAB, ul_debugobj(tb, "printing tree"));
870
2a6cfc13
KZ
871 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
872
873 while (scols_table_next_line(tb, &itr, &ln) == 0)
874 if (!last || !ln->parent)
875 last = ln;
3e542c76
KZ
876
877 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
b233dcb6 878 while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
3e542c76
KZ
879 if (ln->parent)
880 continue;
2a6cfc13 881 rc = print_tree_line(tb, ln, buf, ln == last);
3e542c76 882 }
b233dcb6
KZ
883
884 return rc;
3e542c76
KZ
885}
886
710ed55d
KZ
887static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
888{
6d6b6d18 889 if (scols_column_is_hidden(cl)) {
df73852b
KZ
890 DBG(COL, ul_debugobj(cl, "%s ignored", cl->header.data));
891 return;
892 }
893
710ed55d
KZ
894 DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
895 "hint=%d, avg=%zu, max=%zu, min=%zu, "
df73852b 896 "extreme=%s %s",
710ed55d
KZ
897
898 cl->header.data, cl->seqnum, cl->width,
899 cl->width_hint > 1 ? (int) cl->width_hint :
900 (int) (cl->width_hint * tb->termwidth),
901 cl->width_avg,
902 cl->width_max,
903 cl->width_min,
df73852b
KZ
904 cl->is_extreme ? "yes" : "not",
905 cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
710ed55d
KZ
906}
907
908static void dbg_columns(struct libscols_table *tb)
909{
910 struct libscols_iter itr;
911 struct libscols_column *cl;
912
913 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
914 while (scols_table_next_column(tb, &itr, &cl) == 0)
915 dbg_column(tb, cl);
916}
917
3e542c76
KZ
918/*
919 * This function counts column width.
920 *
b295bdb1
BS
921 * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
922 * two times. The first pass counts the width and average width. If the column
923 * contains fields that are too large (a width greater than 2 * average) then
924 * the column is marked as "extreme". In the second pass all extreme fields
925 * are ignored and the column width is counted from non-extreme fields only.
3e542c76 926 */
b233dcb6
KZ
927static int count_column_width(struct libscols_table *tb,
928 struct libscols_column *cl,
929 struct libscols_buffer *buf)
3e542c76
KZ
930{
931 struct libscols_line *ln;
932 struct libscols_iter itr;
b233dcb6 933 int count = 0, rc = 0;
3e542c76
KZ
934 size_t sum = 0;
935
936 assert(tb);
937 assert(cl);
938
939 cl->width = 0;
940
941 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
942 while (scols_table_next_line(tb, &itr, &ln) == 0) {
b233dcb6
KZ
943 size_t len;
944 char *data;
945
946 rc = cell_to_buffer(tb, ln, cl, buf);
947 if (rc)
c600d82a 948 goto done;
b233dcb6
KZ
949
950 data = buffer_get_data(buf);
951 len = data ? mbs_safe_width(data) : 0;
3e542c76
KZ
952
953 if (len == (size_t) -1) /* ignore broken multibyte strings */
954 len = 0;
440f73b7 955 cl->width_max = max(len, cl->width_max);
3e542c76
KZ
956
957 if (cl->is_extreme && len > cl->width_avg * 2)
958 continue;
0925a9dd 959 else if (scols_column_is_noextremes(cl)) {
3e542c76
KZ
960 sum += len;
961 count++;
962 }
440f73b7
KZ
963 cl->width = max(len, cl->width);
964 if (scols_column_is_tree(cl)) {
965 size_t treewidth = buffer_get_safe_art_size(buf);
966 cl->width_treeart = max(cl->width_treeart, treewidth);
967 }
3e542c76
KZ
968 }
969
970 if (count && cl->width_avg == 0) {
971 cl->width_avg = sum / count;
972
973 if (cl->width_max > cl->width_avg * 2)
974 cl->is_extreme = 1;
975 }
976
977 /* check and set minimal column width */
978 if (scols_cell_get_data(&cl->header))
979 cl->width_min = mbs_safe_width(scols_cell_get_data(&cl->header));
980
981 /* enlarge to minimal width */
8a38a8d3 982 if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
3e542c76
KZ
983 cl->width = cl->width_min;
984
985 /* use relative size for large columns */
986 else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
987 && cl->width_min < (size_t) cl->width_hint)
988
989 cl->width = (size_t) cl->width_hint;
b233dcb6 990
c600d82a 991done:
710ed55d 992 ON_DBG(COL, dbg_column(tb, cl));
c600d82a 993 DBG(COL, ul_debugobj(cl, "column width=%zu, rc=%d", cl->width, rc));
b233dcb6 994 return rc;
3e542c76
KZ
995}
996
997/*
998 * This is core of the scols_* voodo...
999 */
b233dcb6 1000static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf)
3e542c76
KZ
1001{
1002 struct libscols_column *cl;
1003 struct libscols_iter itr;
1004 size_t width = 0; /* output width */
b233dcb6 1005 int trunc_only, rc = 0;
3e542c76
KZ
1006 int extremes = 0;
1007
710ed55d
KZ
1008
1009 DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
1010
3e542c76
KZ
1011 /* set basic columns width
1012 */
1013 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1014 while (scols_table_next_column(tb, &itr, &cl) == 0) {
b233dcb6
KZ
1015 rc = count_column_width(tb, cl, buf);
1016 if (rc)
c600d82a 1017 goto done;
b233dcb6 1018
df73852b 1019 width += cl->width + (is_last_column(tb, cl) ? 0 : 1); /* separator for non-last column */
3e542c76
KZ
1020 extremes += cl->is_extreme;
1021 }
1022
c600d82a
KZ
1023 if (!tb->is_term) {
1024 DBG(TAB, ul_debugobj(tb, " non-terminal output"));
1025 goto done;
1026 }
3e542c76 1027
b295bdb1 1028 /* reduce columns with extreme fields */
3e542c76 1029 if (width > tb->termwidth && extremes) {
710ed55d
KZ
1030 DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
1031
3e542c76
KZ
1032 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1033 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1034 size_t org_width;
1035
1036 if (!cl->is_extreme)
1037 continue;
1038
1039 org_width = cl->width;
b233dcb6
KZ
1040 rc = count_column_width(tb, cl, buf);
1041 if (rc)
c600d82a 1042 goto done;
3e542c76
KZ
1043
1044 if (org_width > cl->width)
1045 width -= org_width - cl->width;
1046 else
1047 extremes--; /* hmm... nothing reduced */
1048 }
1049 }
1050
1051 if (width < tb->termwidth) {
3e542c76 1052 if (extremes) {
710ed55d
KZ
1053 DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
1054
3e542c76
KZ
1055 /* enlarge the first extreme column */
1056 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1057 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1058 size_t add;
1059
1060 if (!cl->is_extreme)
1061 continue;
1062
1063 /* this column is tooo large, ignore?
1064 if (cl->width_max - cl->width >
1065 (tb->termwidth - width))
1066 continue;
1067 */
1068
1069 add = tb->termwidth - width;
1070 if (add && cl->width + add > cl->width_max)
1071 add = cl->width_max - cl->width;
1072
1073 cl->width += add;
1074 width += add;
1075
1076 if (width == tb->termwidth)
1077 break;
1078 }
1079 }
62d2e2e5 1080
0925a9dd 1081 if (width < tb->termwidth && scols_table_is_maxout(tb)) {
710ed55d
KZ
1082 DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
1083
b295bdb1 1084 /* try enlarging all columns */
62d2e2e5
KZ
1085 while (width < tb->termwidth) {
1086 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1087 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1088 cl->width++;
1089 width++;
1090 if (width == tb->termwidth)
1091 break;
1092 }
1093 }
1094 } else if (width < tb->termwidth) {
1095 /* enlarge the last column */
7ee26cbf 1096 struct libscols_column *col = list_entry(
3e542c76
KZ
1097 tb->tb_columns.prev, struct libscols_column, cl_columns);
1098
710ed55d
KZ
1099 DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
1100
7ee26cbf
SK
1101 if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
1102 col->width += tb->termwidth - width;
3e542c76
KZ
1103 width = tb->termwidth;
1104 }
1105 }
1106 }
1107
1108 /* bad, we have to reduce output width, this is done in two steps:
b295bdb1 1109 * 1) reduce columns with a relative width and with truncate flag
3e542c76
KZ
1110 * 2) reduce columns with a relative width without truncate flag
1111 */
1112 trunc_only = 1;
1113 while (width > tb->termwidth) {
1114 size_t org = width;
1115
710ed55d
KZ
1116 DBG(TAB, ul_debugobj(tb, " reduce width (current=%zu, "
1117 "wanted=%zu, mode=%s)",
1118 width, tb->termwidth,
1119 trunc_only ? "trunc-only" : "all-relative"));
1120
3e542c76
KZ
1121 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1122 while (scols_table_next_column(tb, &itr, &cl) == 0) {
440f73b7
KZ
1123
1124 DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
1125 cl->header.data, cl->width, cl->width_treeart));
1126
3e542c76
KZ
1127 if (width <= tb->termwidth)
1128 break;
8a38a8d3 1129 if (cl->width_hint > 1 && !scols_column_is_trunc(cl))
3e542c76 1130 continue; /* never truncate columns with absolute sizes */
440f73b7 1131 if (scols_column_is_tree(cl) && width <= cl->width_treeart)
3e542c76 1132 continue; /* never truncate the tree */
5243e4eb 1133 if (trunc_only && !(scols_column_is_trunc(cl) || scols_column_is_wrap(cl)))
3e542c76
KZ
1134 continue;
1135 if (cl->width == cl->width_min)
1136 continue;
1137
5627fcec
KZ
1138 DBG(TAB, ul_debugobj(tb, " tring to reduce: %s (width=%zu)", cl->header.data, cl->width));
1139
3e542c76
KZ
1140 /* truncate column with relative sizes */
1141 if (cl->width_hint < 1 && cl->width > 0 && width > 0 &&
5627fcec 1142 cl->width >= (size_t) (cl->width_hint * tb->termwidth)) {
3e542c76
KZ
1143 cl->width--;
1144 width--;
1145 }
5627fcec 1146
3e542c76
KZ
1147 /* truncate column with absolute size */
1148 if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
1149 !trunc_only) {
1150 cl->width--;
1151 width--;
1152 }
3e542c76
KZ
1153 }
1154 if (org == width) {
1155 if (trunc_only)
1156 trunc_only = 0;
1157 else
1158 break;
1159 }
1160 }
1161
df73852b
KZ
1162 /* ignore last column(s) or force last column to be truncated if
1163 * nowrap mode enabled */
1164 if (tb->no_wrap && width > tb->termwidth) {
1165 scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
1166 while (scols_table_next_column(tb, &itr, &cl) == 0) {
1167
1168 if (width <= tb->termwidth)
1169 break;
1170 if (width - cl->width < tb->termwidth) {
1171 size_t r = width - tb->termwidth;
1172
1173 cl->flags |= SCOLS_FL_TRUNC;
1174 cl->width -= r;
1175 width -= r;
1176 } else {
6d6b6d18 1177 cl->flags |= SCOLS_FL_HIDDEN;
df73852b
KZ
1178 width -= cl->width + 1; /* +1 means separator between columns */
1179 }
1180 }
1181 }
c600d82a
KZ
1182done:
1183 DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
710ed55d 1184 ON_DBG(TAB, dbg_columns(tb));
3e542c76 1185
b233dcb6 1186 return rc;
3e542c76 1187}
b233dcb6 1188
3e542c76
KZ
1189static size_t strlen_line(struct libscols_line *ln)
1190{
1191 size_t i, sz = 0;
1192
1193 assert(ln);
1194
1195 for (i = 0; i < ln->ncells; i++) {
1196 struct libscols_cell *ce = scols_line_get_cell(ln, i);
1197 const char *data = ce ? scols_cell_get_data(ce) : NULL;
1198
1199 sz += data ? strlen(data) : 0;
1200 }
1201
1202 return sz;
1203}
1204
b233dcb6
KZ
1205
1206
e2310281 1207/**
1d90bcb1 1208 * scols_print_table:
3e542c76
KZ
1209 * @tb: table
1210 *
571441e2 1211 * Prints the table to the output stream.
1d90bcb1
OO
1212 *
1213 * Returns: 0, a negative value in case of an error.
3e542c76
KZ
1214 */
1215int scols_print_table(struct libscols_table *tb)
1216{
b233dcb6 1217 int rc = 0;
19981b07 1218 size_t bufsz, extra_bufsz = 0;
3e542c76
KZ
1219 struct libscols_line *ln;
1220 struct libscols_iter itr;
b233dcb6 1221 struct libscols_buffer *buf;
3e542c76 1222
3e542c76 1223 if (!tb)
323da9ce 1224 return -EINVAL;
3e542c76 1225
710ed55d 1226 DBG(TAB, ul_debugobj(tb, "printing"));
4827093d
KZ
1227
1228 if (list_empty(&tb->tb_lines)) {
1229 DBG(TAB, ul_debugobj(tb, "ignore -- epmty table"));
1230 return 0;
1231 }
1232
1424fe8c
KZ
1233 if (!tb->symbols)
1234 scols_table_set_symbols(tb, NULL); /* use default */
1235
19981b07
KZ
1236 if (tb->format == SCOLS_FMT_HUMAN)
1237 tb->is_term = isatty(STDOUT_FILENO) ? 1 : 0;
3e542c76 1238
19981b07
KZ
1239 if (tb->is_term) {
1240 tb->termwidth = get_terminal_width(80);
1241 if (tb->termreduce < tb->termwidth)
1242 tb->termwidth -= tb->termreduce;
1243 bufsz = tb->termwidth;
1244 } else
1245 bufsz = BUFSIZ;
3e542c76 1246
19981b07
KZ
1247 /*
1248 * Estimate extra space necessary for tree, JSON or another output
1249 * decoration.
1250 */
1251 if (scols_table_is_tree(tb))
1252 extra_bufsz += tb->nlines * strlen(tb->symbols->vert);
1253
1254 switch (tb->format) {
1255 case SCOLS_FMT_RAW:
1256 extra_bufsz += tb->ncols; /* separator between columns */
1257 break;
1258 case SCOLS_FMT_JSON:
1259 if (tb->format == SCOLS_FMT_JSON)
1260 extra_bufsz += tb->nlines * 3; /* indention */
1261 /* fallthrough */
1262 case SCOLS_FMT_EXPORT:
1263 {
1264 struct libscols_column *cl;
1265 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1266 while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
1267 if (scols_column_is_hidden(cl))
1268 continue;
1269 extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */
1270 extra_bufsz += 2; /* separators */
1271 }
1272 break;
1273 }
1274 case SCOLS_FMT_HUMAN:
1275 break;
1276 }
1277
1278
1279 /*
1280 * Enlarge buffer if necessary, the buffer should be large enough to
1281 * store line data and tree ascii art (or another decoration).
1282 */
3e542c76
KZ
1283 scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
1284 while (scols_table_next_line(tb, &itr, &ln) == 0) {
19981b07 1285 size_t sz = strlen_line(ln) + extra_bufsz;
b233dcb6
KZ
1286 if (sz > bufsz)
1287 bufsz = sz;
3e542c76
KZ
1288 }
1289
b233dcb6
KZ
1290 buf = new_buffer(bufsz + 1); /* data + space for \0 */
1291 if (!buf)
3e542c76
KZ
1292 return -ENOMEM;
1293
4827093d 1294 if (tb->format == SCOLS_FMT_HUMAN) {
b233dcb6 1295 rc = recount_widths(tb, buf);
140b0061
KZ
1296 if (rc != 0)
1297 goto done;
1298 }
3e542c76 1299
2a6cfc13
KZ
1300 fput_table_open(tb);
1301
f16f28b0
IG
1302 if (tb->format == SCOLS_FMT_HUMAN)
1303 print_title(tb);
b3256eff 1304
4827093d
KZ
1305 rc = print_header(tb, buf);
1306 if (rc)
1307 goto done;
2a6cfc13 1308
8a38a8d3 1309 if (scols_table_is_tree(tb))
b233dcb6 1310 rc = print_tree(tb, buf);
3e542c76 1311 else
b233dcb6 1312 rc = print_table(tb, buf);
3e542c76 1313
2a6cfc13 1314 fput_table_close(tb);
140b0061 1315done:
b233dcb6
KZ
1316 free_buffer(buf);
1317 return rc;
3e542c76 1318}
2a965b80 1319
e2310281 1320/**
1d90bcb1
OO
1321 * scols_print_table_to_string:
1322 * @tb: table
1323 * @data: pointer to the beginning of a memory area to print to
1324 *
1325 * Prints the table to @data.
1326 *
1327 * Returns: 0, a negative value in case of an error.
1328 */
2a965b80
KZ
1329int scols_print_table_to_string(struct libscols_table *tb, char **data)
1330{
1331#ifdef HAVE_OPEN_MEMSTREAM
5e8461a5 1332 FILE *stream, *old_stream;
2a965b80 1333 size_t sz;
b233dcb6 1334 int rc;
2a965b80
KZ
1335
1336 if (!tb)
1337 return -EINVAL;
1338
710ed55d
KZ
1339 DBG(TAB, ul_debugobj(tb, "printing to string"));
1340
1d90bcb1 1341 /* create a stream for output */
2a965b80
KZ
1342 stream = open_memstream(data, &sz);
1343 if (!stream)
1344 return -ENOMEM;
1345
5e8461a5 1346 old_stream = scols_table_get_stream(tb);
2a965b80 1347 scols_table_set_stream(tb, stream);
b233dcb6 1348 rc = scols_print_table(tb);
2a965b80 1349 fclose(stream);
5e8461a5 1350 scols_table_set_stream(tb, old_stream);
2a965b80 1351
b233dcb6 1352 return rc;
2a965b80
KZ
1353#else
1354 return -ENOSYS;
1355#endif
1356}
1357