]>
Commit | Line | Data |
---|---|---|
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 */ |
33 | struct 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 | ||
42 | static struct libscols_buffer *new_buffer(size_t sz) | |
43 | { | |
44 | struct libscols_buffer *buf = malloc(sz + sizeof(struct libscols_buffer)); | |
45 | ||
46 | if (!buf) | |
47 | return NULL; | |
48 | ||
49 | buf->cur = buf->begin = ((char *) buf) + sizeof(struct libscols_buffer); | |
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 | ||
57 | static 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 | ||
66 | static 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 | ||
78 | static int buffer_append_data(struct libscols_buffer *buf, const char *str) | |
79 | { | |
80 | size_t maxsz, sz; | |
81 | ||
82 | if (!buf) | |
83 | return -EINVAL; | |
84 | if (!str || !*str) | |
85 | return 0; | |
86 | ||
87 | sz = strlen(str); | |
88 | maxsz = buf->bufsz - (buf->cur - buf->begin); | |
89 | ||
90 | if (maxsz <= sz) | |
91 | return -EINVAL; | |
b233dcb6 KZ |
92 | memcpy(buf->cur, str, sz + 1); |
93 | buf->cur += sz; | |
94 | return 0; | |
95 | } | |
96 | ||
97 | static int buffer_set_data(struct libscols_buffer *buf, const char *str) | |
98 | { | |
99 | int rc = buffer_reset_data(buf); | |
100 | return rc ? rc : buffer_append_data(buf, str); | |
101 | } | |
102 | ||
1bcf491a | 103 | /* save the current buffer position to art_idx */ |
ee75308c KZ |
104 | static 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 |
112 | static 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 |
118 | static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells) |
119 | { | |
120 | char *data = buffer_get_data(buf); | |
121 | char *res = NULL; | |
122 | ||
123 | if (!data) | |
124 | goto nothing; | |
125 | ||
126 | if (!buf->encdata) { | |
127 | buf->encdata = malloc(mbs_safe_encode_size(buf->bufsz) + 1); | |
128 | if (!buf->encdata) | |
129 | goto nothing; | |
130 | } | |
131 | ||
132 | res = mbs_safe_encode_to_buffer(data, cells, buf->encdata); | |
133 | if (!res || !*cells || *cells == (size_t) -1) | |
134 | goto nothing; | |
d6303e28 KZ |
135 | return res; |
136 | nothing: | |
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 */ |
142 | static size_t buffer_get_safe_art_size(struct libscols_buffer *buf) | |
143 | { | |
144 | char *data = buffer_get_data(buf); | |
145 | size_t bytes = 0; | |
146 | ||
147 | if (!data || !buf->art_idx) | |
148 | return 0; | |
149 | ||
150 | mbs_safe_nwidth(data, buf->art_idx, &bytes); | |
151 | return bytes; | |
152 | } | |
153 | ||
8501d9be KZ |
154 | /* returns pointer to the end of used data */ |
155 | static int line_ascii_art_to_buffer(struct libscols_table *tb, | |
156 | struct libscols_line *ln, | |
157 | struct libscols_buffer *buf) | |
158 | { | |
159 | const char *art; | |
160 | int rc; | |
161 | ||
162 | assert(ln); | |
163 | assert(buf); | |
164 | ||
165 | if (!ln->parent) | |
166 | return 0; | |
167 | ||
168 | rc = line_ascii_art_to_buffer(tb, ln->parent, buf); | |
169 | if (rc) | |
170 | return rc; | |
171 | ||
172 | if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch)) | |
173 | art = " "; | |
174 | else | |
175 | art = tb->symbols->vert; | |
176 | ||
177 | return buffer_append_data(buf, art); | |
178 | } | |
179 | ||
df73852b KZ |
180 | static 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 | |
198 | static int has_pending_data(struct libscols_table *tb) | |
199 | { | |
200 | struct libscols_column *cl; | |
201 | struct libscols_iter itr; | |
202 | ||
203 | scols_reset_iter(&itr, SCOLS_ITER_FORWARD); | |
204 | while (scols_table_next_column(tb, &itr, &cl) == 0) { | |
205 | if (scols_column_is_hidden(cl)) | |
206 | continue; | |
207 | if (cl->pending_data) | |
208 | return 1; | |
209 | } | |
210 | return 0; | |
211 | } | |
212 | ||
8501d9be KZ |
213 | /* print padding or asci-art instead of data of @cl */ |
214 | static void print_empty_cell(struct libscols_table *tb, | |
215 | struct libscols_column *cl, | |
216 | struct libscols_line *ln, /* optional */ | |
217 | size_t bufsz) | |
218 | { | |
219 | size_t len_pad = 0; /* in screen cells as opposed to bytes */ | |
220 | ||
221 | /* generate tree 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 | |
252 | static const char *get_cell_color(struct libscols_table *tb, | |
253 | struct libscols_column *cl, | |
254 | struct libscols_line *ln, /* optional */ | |
255 | struct libscols_cell *ce) /* optional */ | |
256 | { | |
257 | const char *color = NULL; | |
258 | ||
259 | if (tb && tb->colors_wanted) { | |
260 | if (ce && !color) | |
261 | color = ce->color; | |
262 | if (ln && !color) | |
263 | color = ln->color; | |
264 | if (!color) | |
265 | color = cl->color; | |
266 | } | |
267 | return color; | |
268 | } | |
269 | ||
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 | */ | |
281 | static void print_newline_padding(struct libscols_table *tb, | |
282 | struct libscols_column *cl, | |
283 | struct libscols_line *ln, /* optional */ | |
284 | size_t bufsz, | |
285 | 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 */ | |
313 | static 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 */ | |
333 | static 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 */ | |
346 | static 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; | |
386 | err: | |
387 | free(data); | |
388 | return -errno; | |
389 | } | |
390 | ||
b233dcb6 KZ |
391 | static 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 |
519 | static 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 |
563 | static 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 | ||
571 | static 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 | ||
589 | static 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 | ||
604 | static 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 | ||
618 | static 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 | ||
630 | static 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 | ||
640 | static 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 |
656 | static 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 | 703 | static 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; |
767 | done: | |
768 | free(buf); | |
769 | free(title); | |
770 | return rc; | |
b3256eff IG |
771 | } |
772 | ||
b233dcb6 | 773 | static 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 | 804 | static 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 |
822 | static 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); | |
857 | done: | |
b233dcb6 | 858 | return rc; |
3e542c76 KZ |
859 | } |
860 | ||
b233dcb6 | 861 | static 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 |
887 | static 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 | ||
908 | static 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 |
927 | static 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 | 991 | done: |
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 | 1000 | static 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 |
1182 | done: |
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 |
1189 | static 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 | */ |
1215 | int 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 | 1315 | done: |
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 |
1329 | int 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 |