]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/format-table.c
Merge pull request #14527 from poettering/socket-state-fix
[thirdparty/systemd.git] / src / shared / format-table.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <net/if.h>
5 #include <unistd.h>
6
7 #include "alloc-util.h"
8 #include "fd-util.h"
9 #include "fileio.h"
10 #include "format-table.h"
11 #include "format-util.h"
12 #include "gunicode.h"
13 #include "in-addr-util.h"
14 #include "memory-util.h"
15 #include "pager.h"
16 #include "parse-util.h"
17 #include "path-util.h"
18 #include "pretty-print.h"
19 #include "sort-util.h"
20 #include "string-util.h"
21 #include "strxcpyx.h"
22 #include "terminal-util.h"
23 #include "time-util.h"
24 #include "utf8.h"
25 #include "util.h"
26
27 #define DEFAULT_WEIGHT 100
28
29 /*
30 A few notes on implementation details:
31
32 - TableCell is a 'fake' structure, it's just used as data type to pass references to specific cell positions in the
33 table. It can be easily converted to an index number and back.
34
35 - TableData is where the actual data is stored: it encapsulates the data and formatting for a specific cell. It's
36 'pseudo-immutable' and ref-counted. When a cell's data's formatting is to be changed, we duplicate the object if the
37 ref-counting is larger than 1. Note that TableData and its ref-counting is mostly not visible to the outside. The
38 outside only sees Table and TableCell.
39
40 - The Table object stores a simple one-dimensional array of references to TableData objects, one row after the
41 previous one.
42
43 - There's no special concept of a "row" or "column" in the table, and no special concept of the "header" row. It's all
44 derived from the cell index: we know how many cells are to be stored in a row, and can determine the rest from
45 that. The first row is always the header row. If header display is turned off we simply skip outputting the first
46 row. Also, when sorting rows we always leave the first row where it is, as the header shouldn't move.
47
48 - Note because there's no row and no column object some properties that might be appropriate as row/column properties
49 are exposed as cell properties instead. For example, the "weight" of a column (which is used to determine where to
50 add/remove space preferable when expanding/compressing tables horizontally) is actually made the "weight" of a
51 cell. Given that we usually need it per-column though we will calculate the average across every cell of the column
52 instead.
53
54 - To make things easy, when cells are added without any explicit configured formatting, then we'll copy the formatting
55 from the same cell in the previous cell. This is particularly useful for the "weight" of the cell (see above), as
56 this means setting the weight of the cells of the header row will nicely propagate to all cells in the other rows.
57 */
58
59 typedef struct TableData {
60 unsigned n_ref;
61 TableDataType type;
62
63 size_t minimum_width; /* minimum width for the column */
64 size_t maximum_width; /* maximum width for the column */
65 unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
66 unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
67 unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
68
69 bool uppercase; /* Uppercase string on display */
70
71 const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */
72 char *url; /* A URL to use for a clickable hyperlink */
73 char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */
74
75 union {
76 uint8_t data[0]; /* data is generic array */
77 bool boolean;
78 usec_t timestamp;
79 usec_t timespan;
80 uint64_t size;
81 char string[0];
82 int int_val;
83 int8_t int8;
84 int16_t int16;
85 int32_t int32;
86 int64_t int64;
87 unsigned uint_val;
88 uint8_t uint8;
89 uint16_t uint16;
90 uint32_t uint32;
91 uint64_t uint64;
92 int percent; /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
93 int ifindex;
94 union in_addr_union address;
95 /* … add more here as we start supporting more cell data types … */
96 };
97 } TableData;
98
99 static size_t TABLE_CELL_TO_INDEX(TableCell *cell) {
100 size_t i;
101
102 assert(cell);
103
104 i = PTR_TO_SIZE(cell);
105 assert(i > 0);
106
107 return i-1;
108 }
109
110 static TableCell* TABLE_INDEX_TO_CELL(size_t index) {
111 assert(index != (size_t) -1);
112 return SIZE_TO_PTR(index + 1);
113 }
114
115 struct Table {
116 size_t n_columns;
117 size_t n_cells;
118
119 bool header; /* Whether to show the header row? */
120 size_t width; /* If != (size_t) -1 the width to format this table in */
121
122 TableData **data;
123 size_t n_allocated;
124
125 size_t *display_map; /* List of columns to show (by their index). It's fine if columns are listed multiple times or not at all */
126 size_t n_display_map;
127
128 size_t *sort_map; /* The columns to order rows by, in order of preference. */
129 size_t n_sort_map;
130
131 bool *reverse_map;
132
133 char *empty_string;
134 };
135
136 Table *table_new_raw(size_t n_columns) {
137 _cleanup_(table_unrefp) Table *t = NULL;
138
139 assert(n_columns > 0);
140
141 t = new(Table, 1);
142 if (!t)
143 return NULL;
144
145 *t = (struct Table) {
146 .n_columns = n_columns,
147 .header = true,
148 .width = (size_t) -1,
149 };
150
151 return TAKE_PTR(t);
152 }
153
154 Table *table_new_internal(const char *first_header, ...) {
155 _cleanup_(table_unrefp) Table *t = NULL;
156 size_t n_columns = 1;
157 const char *h;
158 va_list ap;
159 int r;
160
161 assert(first_header);
162
163 va_start(ap, first_header);
164 for (;;) {
165 h = va_arg(ap, const char*);
166 if (!h)
167 break;
168
169 n_columns++;
170 }
171 va_end(ap);
172
173 t = table_new_raw(n_columns);
174 if (!t)
175 return NULL;
176
177 va_start(ap, first_header);
178 for (h = first_header; h; h = va_arg(ap, const char*)) {
179 TableCell *cell;
180
181 r = table_add_cell(t, &cell, TABLE_STRING, h);
182 if (r < 0) {
183 va_end(ap);
184 return NULL;
185 }
186
187 /* Make the table header uppercase */
188 r = table_set_uppercase(t, cell, true);
189 if (r < 0) {
190 va_end(ap);
191 return NULL;
192 }
193 }
194 va_end(ap);
195
196 assert(t->n_columns == t->n_cells);
197 return TAKE_PTR(t);
198 }
199
200 static TableData *table_data_free(TableData *d) {
201 assert(d);
202
203 free(d->formatted);
204 free(d->url);
205
206 return mfree(d);
207 }
208
209 DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(TableData, table_data, table_data_free);
210 DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
211
212 Table *table_unref(Table *t) {
213 size_t i;
214
215 if (!t)
216 return NULL;
217
218 for (i = 0; i < t->n_cells; i++)
219 table_data_unref(t->data[i]);
220
221 free(t->data);
222 free(t->display_map);
223 free(t->sort_map);
224 free(t->reverse_map);
225 free(t->empty_string);
226
227 return mfree(t);
228 }
229
230 static size_t table_data_size(TableDataType type, const void *data) {
231
232 switch (type) {
233
234 case TABLE_EMPTY:
235 return 0;
236
237 case TABLE_STRING:
238 case TABLE_PATH:
239 return strlen(data) + 1;
240
241 case TABLE_BOOLEAN:
242 return sizeof(bool);
243
244 case TABLE_TIMESTAMP:
245 case TABLE_TIMESTAMP_UTC:
246 case TABLE_TIMESTAMP_RELATIVE:
247 case TABLE_TIMESPAN:
248 case TABLE_TIMESPAN_MSEC:
249 return sizeof(usec_t);
250
251 case TABLE_SIZE:
252 case TABLE_INT64:
253 case TABLE_UINT64:
254 case TABLE_BPS:
255 return sizeof(uint64_t);
256
257 case TABLE_INT32:
258 case TABLE_UINT32:
259 return sizeof(uint32_t);
260
261 case TABLE_INT16:
262 case TABLE_UINT16:
263 return sizeof(uint16_t);
264
265 case TABLE_INT8:
266 case TABLE_UINT8:
267 return sizeof(uint8_t);
268
269 case TABLE_INT:
270 case TABLE_UINT:
271 case TABLE_PERCENT:
272 case TABLE_IFINDEX:
273 return sizeof(int);
274
275 case TABLE_IN_ADDR:
276 return sizeof(struct in_addr);
277
278 case TABLE_IN6_ADDR:
279 return sizeof(struct in6_addr);
280
281 default:
282 assert_not_reached("Uh? Unexpected cell type");
283 }
284 }
285
286 static bool table_data_matches(
287 TableData *d,
288 TableDataType type,
289 const void *data,
290 size_t minimum_width,
291 size_t maximum_width,
292 unsigned weight,
293 unsigned align_percent,
294 unsigned ellipsize_percent) {
295
296 size_t k, l;
297 assert(d);
298
299 if (d->type != type)
300 return false;
301
302 if (d->minimum_width != minimum_width)
303 return false;
304
305 if (d->maximum_width != maximum_width)
306 return false;
307
308 if (d->weight != weight)
309 return false;
310
311 if (d->align_percent != align_percent)
312 return false;
313
314 if (d->ellipsize_percent != ellipsize_percent)
315 return false;
316
317 /* If a color/url/uppercase flag is set, refuse to merge */
318 if (d->color)
319 return false;
320 if (d->url)
321 return false;
322 if (d->uppercase)
323 return false;
324
325 k = table_data_size(type, data);
326 l = table_data_size(d->type, d->data);
327
328 if (k != l)
329 return false;
330
331 return memcmp_safe(data, d->data, l) == 0;
332 }
333
334 static TableData *table_data_new(
335 TableDataType type,
336 const void *data,
337 size_t minimum_width,
338 size_t maximum_width,
339 unsigned weight,
340 unsigned align_percent,
341 unsigned ellipsize_percent) {
342
343 size_t data_size;
344 TableData *d;
345
346 data_size = table_data_size(type, data);
347
348 d = malloc0(offsetof(TableData, data) + data_size);
349 if (!d)
350 return NULL;
351
352 d->n_ref = 1;
353 d->type = type;
354 d->minimum_width = minimum_width;
355 d->maximum_width = maximum_width;
356 d->weight = weight;
357 d->align_percent = align_percent;
358 d->ellipsize_percent = ellipsize_percent;
359 memcpy_safe(d->data, data, data_size);
360
361 return d;
362 }
363
364 int table_add_cell_full(
365 Table *t,
366 TableCell **ret_cell,
367 TableDataType type,
368 const void *data,
369 size_t minimum_width,
370 size_t maximum_width,
371 unsigned weight,
372 unsigned align_percent,
373 unsigned ellipsize_percent) {
374
375 _cleanup_(table_data_unrefp) TableData *d = NULL;
376 TableData *p;
377
378 assert(t);
379 assert(type >= 0);
380 assert(type < _TABLE_DATA_TYPE_MAX);
381
382 /* Special rule: patch NULL data fields to the empty field */
383 if (!data)
384 type = TABLE_EMPTY;
385
386 /* Determine the cell adjacent to the current one, but one row up */
387 if (t->n_cells >= t->n_columns)
388 assert_se(p = t->data[t->n_cells - t->n_columns]);
389 else
390 p = NULL;
391
392 /* If formatting parameters are left unspecified, copy from the previous row */
393 if (minimum_width == (size_t) -1)
394 minimum_width = p ? p->minimum_width : 1;
395
396 if (weight == (unsigned) -1)
397 weight = p ? p->weight : DEFAULT_WEIGHT;
398
399 if (align_percent == (unsigned) -1)
400 align_percent = p ? p->align_percent : 0;
401
402 if (ellipsize_percent == (unsigned) -1)
403 ellipsize_percent = p ? p->ellipsize_percent : 100;
404
405 assert(align_percent <= 100);
406 assert(ellipsize_percent <= 100);
407
408 /* Small optimization: Pretty often adjacent cells in two subsequent lines have the same data and
409 * formatting. Let's see if we can reuse the cell data and ref it once more. */
410
411 if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent))
412 d = table_data_ref(p);
413 else {
414 d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent);
415 if (!d)
416 return -ENOMEM;
417 }
418
419 if (!GREEDY_REALLOC(t->data, t->n_allocated, MAX(t->n_cells + 1, t->n_columns)))
420 return -ENOMEM;
421
422 if (ret_cell)
423 *ret_cell = TABLE_INDEX_TO_CELL(t->n_cells);
424
425 t->data[t->n_cells++] = TAKE_PTR(d);
426
427 return 0;
428 }
429
430 int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, ...) {
431 _cleanup_free_ char *buffer = NULL;
432 va_list ap;
433 int r;
434
435 va_start(ap, format);
436 r = vasprintf(&buffer, format, ap);
437 va_end(ap);
438 if (r < 0)
439 return -ENOMEM;
440
441 return table_add_cell(t, ret_cell, TABLE_STRING, buffer);
442 }
443
444 int table_fill_empty(Table *t, size_t until_column) {
445 int r;
446
447 assert(t);
448
449 /* Fill the rest of the current line with empty cells until we reach the specified column. Will add
450 * at least one cell. Pass 0 in order to fill a line to the end or insert an empty line. */
451
452 if (until_column >= t->n_columns)
453 return -EINVAL;
454
455 do {
456 r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
457 if (r < 0)
458 return r;
459
460 } while ((t->n_cells % t->n_columns) != until_column);
461
462 return 0;
463 }
464
465 int table_dup_cell(Table *t, TableCell *cell) {
466 size_t i;
467
468 assert(t);
469
470 /* Add the data of the specified cell a second time as a new cell to the end. */
471
472 i = TABLE_CELL_TO_INDEX(cell);
473 if (i >= t->n_cells)
474 return -ENXIO;
475
476 if (!GREEDY_REALLOC(t->data, t->n_allocated, MAX(t->n_cells + 1, t->n_columns)))
477 return -ENOMEM;
478
479 t->data[t->n_cells++] = table_data_ref(t->data[i]);
480 return 0;
481 }
482
483 static int table_dedup_cell(Table *t, TableCell *cell) {
484 _cleanup_free_ char *curl = NULL;
485 TableData *nd, *od;
486 size_t i;
487
488 assert(t);
489
490 /* Helper call that ensures the specified cell's data object has a ref count of 1, which we can use before
491 * changing a cell's formatting without effecting every other cell's formatting that shares the same data */
492
493 i = TABLE_CELL_TO_INDEX(cell);
494 if (i >= t->n_cells)
495 return -ENXIO;
496
497 assert_se(od = t->data[i]);
498 if (od->n_ref == 1)
499 return 0;
500
501 assert(od->n_ref > 1);
502
503 if (od->url) {
504 curl = strdup(od->url);
505 if (!curl)
506 return -ENOMEM;
507 }
508
509 nd = table_data_new(
510 od->type,
511 od->data,
512 od->minimum_width,
513 od->maximum_width,
514 od->weight,
515 od->align_percent,
516 od->ellipsize_percent);
517 if (!nd)
518 return -ENOMEM;
519
520 nd->color = od->color;
521 nd->url = TAKE_PTR(curl);
522 nd->uppercase = od->uppercase;
523
524 table_data_unref(od);
525 t->data[i] = nd;
526
527 assert(nd->n_ref == 1);
528
529 return 1;
530 }
531
532 static TableData *table_get_data(Table *t, TableCell *cell) {
533 size_t i;
534
535 assert(t);
536 assert(cell);
537
538 /* Get the data object of the specified cell, or NULL if it doesn't exist */
539
540 i = TABLE_CELL_TO_INDEX(cell);
541 if (i >= t->n_cells)
542 return NULL;
543
544 assert(t->data[i]);
545 assert(t->data[i]->n_ref > 0);
546
547 return t->data[i];
548 }
549
550 int table_set_minimum_width(Table *t, TableCell *cell, size_t minimum_width) {
551 int r;
552
553 assert(t);
554 assert(cell);
555
556 if (minimum_width == (size_t) -1)
557 minimum_width = 1;
558
559 r = table_dedup_cell(t, cell);
560 if (r < 0)
561 return r;
562
563 table_get_data(t, cell)->minimum_width = minimum_width;
564 return 0;
565 }
566
567 int table_set_maximum_width(Table *t, TableCell *cell, size_t maximum_width) {
568 int r;
569
570 assert(t);
571 assert(cell);
572
573 r = table_dedup_cell(t, cell);
574 if (r < 0)
575 return r;
576
577 table_get_data(t, cell)->maximum_width = maximum_width;
578 return 0;
579 }
580
581 int table_set_weight(Table *t, TableCell *cell, unsigned weight) {
582 int r;
583
584 assert(t);
585 assert(cell);
586
587 if (weight == (unsigned) -1)
588 weight = DEFAULT_WEIGHT;
589
590 r = table_dedup_cell(t, cell);
591 if (r < 0)
592 return r;
593
594 table_get_data(t, cell)->weight = weight;
595 return 0;
596 }
597
598 int table_set_align_percent(Table *t, TableCell *cell, unsigned percent) {
599 int r;
600
601 assert(t);
602 assert(cell);
603
604 if (percent == (unsigned) -1)
605 percent = 0;
606
607 assert(percent <= 100);
608
609 r = table_dedup_cell(t, cell);
610 if (r < 0)
611 return r;
612
613 table_get_data(t, cell)->align_percent = percent;
614 return 0;
615 }
616
617 int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent) {
618 int r;
619
620 assert(t);
621 assert(cell);
622
623 if (percent == (unsigned) -1)
624 percent = 100;
625
626 assert(percent <= 100);
627
628 r = table_dedup_cell(t, cell);
629 if (r < 0)
630 return r;
631
632 table_get_data(t, cell)->ellipsize_percent = percent;
633 return 0;
634 }
635
636 int table_set_color(Table *t, TableCell *cell, const char *color) {
637 int r;
638
639 assert(t);
640 assert(cell);
641
642 r = table_dedup_cell(t, cell);
643 if (r < 0)
644 return r;
645
646 table_get_data(t, cell)->color = empty_to_null(color);
647 return 0;
648 }
649
650 int table_set_url(Table *t, TableCell *cell, const char *url) {
651 _cleanup_free_ char *copy = NULL;
652 int r;
653
654 assert(t);
655 assert(cell);
656
657 if (url) {
658 copy = strdup(url);
659 if (!copy)
660 return -ENOMEM;
661 }
662
663 r = table_dedup_cell(t, cell);
664 if (r < 0)
665 return r;
666
667 return free_and_replace(table_get_data(t, cell)->url, copy);
668 }
669
670 int table_set_uppercase(Table *t, TableCell *cell, bool b) {
671 TableData *d;
672 int r;
673
674 assert(t);
675 assert(cell);
676
677 r = table_dedup_cell(t, cell);
678 if (r < 0)
679 return r;
680
681 assert_se(d = table_get_data(t, cell));
682
683 if (d->uppercase == b)
684 return 0;
685
686 d->formatted = mfree(d->formatted);
687 d->uppercase = b;
688 return 1;
689 }
690
691 int table_update(Table *t, TableCell *cell, TableDataType type, const void *data) {
692 _cleanup_free_ char *curl = NULL;
693 TableData *nd, *od;
694 size_t i;
695
696 assert(t);
697 assert(cell);
698
699 i = TABLE_CELL_TO_INDEX(cell);
700 if (i >= t->n_cells)
701 return -ENXIO;
702
703 assert_se(od = t->data[i]);
704
705 if (od->url) {
706 curl = strdup(od->url);
707 if (!curl)
708 return -ENOMEM;
709 }
710
711 nd = table_data_new(
712 type,
713 data,
714 od->minimum_width,
715 od->maximum_width,
716 od->weight,
717 od->align_percent,
718 od->ellipsize_percent);
719 if (!nd)
720 return -ENOMEM;
721
722 nd->color = od->color;
723 nd->url = TAKE_PTR(curl);
724 nd->uppercase = od->uppercase;
725
726 table_data_unref(od);
727 t->data[i] = nd;
728
729 return 0;
730 }
731
732 int table_add_many_internal(Table *t, TableDataType first_type, ...) {
733 TableDataType type;
734 va_list ap;
735 TableCell *last_cell = NULL;
736 int r;
737
738 assert(t);
739 assert(first_type >= 0);
740 assert(first_type < _TABLE_DATA_TYPE_MAX);
741
742 type = first_type;
743
744 va_start(ap, first_type);
745 for (;;) {
746 const void *data;
747 union {
748 uint64_t size;
749 usec_t usec;
750 int int_val;
751 int8_t int8;
752 int16_t int16;
753 int32_t int32;
754 int64_t int64;
755 unsigned uint_val;
756 uint8_t uint8;
757 uint16_t uint16;
758 uint32_t uint32;
759 uint64_t uint64;
760 int percent;
761 int ifindex;
762 bool b;
763 union in_addr_union address;
764 } buffer;
765
766 switch (type) {
767
768 case TABLE_EMPTY:
769 data = NULL;
770 break;
771
772 case TABLE_STRING:
773 case TABLE_PATH:
774 data = va_arg(ap, const char *);
775 break;
776
777 case TABLE_BOOLEAN:
778 buffer.b = va_arg(ap, int);
779 data = &buffer.b;
780 break;
781
782 case TABLE_TIMESTAMP:
783 case TABLE_TIMESTAMP_UTC:
784 case TABLE_TIMESTAMP_RELATIVE:
785 case TABLE_TIMESPAN:
786 case TABLE_TIMESPAN_MSEC:
787 buffer.usec = va_arg(ap, usec_t);
788 data = &buffer.usec;
789 break;
790
791 case TABLE_SIZE:
792 case TABLE_BPS:
793 buffer.size = va_arg(ap, uint64_t);
794 data = &buffer.size;
795 break;
796
797 case TABLE_INT:
798 buffer.int_val = va_arg(ap, int);
799 data = &buffer.int_val;
800 break;
801
802 case TABLE_INT8: {
803 int x = va_arg(ap, int);
804 assert(x >= INT8_MIN && x <= INT8_MAX);
805
806 buffer.int8 = x;
807 data = &buffer.int8;
808 break;
809 }
810
811 case TABLE_INT16: {
812 int x = va_arg(ap, int);
813 assert(x >= INT16_MIN && x <= INT16_MAX);
814
815 buffer.int16 = x;
816 data = &buffer.int16;
817 break;
818 }
819
820 case TABLE_INT32:
821 buffer.int32 = va_arg(ap, int32_t);
822 data = &buffer.int32;
823 break;
824
825 case TABLE_INT64:
826 buffer.int64 = va_arg(ap, int64_t);
827 data = &buffer.int64;
828 break;
829
830 case TABLE_UINT:
831 buffer.uint_val = va_arg(ap, unsigned);
832 data = &buffer.uint_val;
833 break;
834
835 case TABLE_UINT8: {
836 unsigned x = va_arg(ap, unsigned);
837 assert(x <= UINT8_MAX);
838
839 buffer.uint8 = x;
840 data = &buffer.uint8;
841 break;
842 }
843
844 case TABLE_UINT16: {
845 unsigned x = va_arg(ap, unsigned);
846 assert(x <= UINT16_MAX);
847
848 buffer.uint16 = x;
849 data = &buffer.uint16;
850 break;
851 }
852
853 case TABLE_UINT32:
854 buffer.uint32 = va_arg(ap, uint32_t);
855 data = &buffer.uint32;
856 break;
857
858 case TABLE_UINT64:
859 buffer.uint64 = va_arg(ap, uint64_t);
860 data = &buffer.uint64;
861 break;
862
863 case TABLE_PERCENT:
864 buffer.percent = va_arg(ap, int);
865 data = &buffer.percent;
866 break;
867
868 case TABLE_IFINDEX:
869 buffer.ifindex = va_arg(ap, int);
870 data = &buffer.ifindex;
871 break;
872
873 case TABLE_IN_ADDR:
874 buffer.address = *va_arg(ap, union in_addr_union *);
875 data = &buffer.address.in;
876 break;
877
878 case TABLE_IN6_ADDR:
879 buffer.address = *va_arg(ap, union in_addr_union *);
880 data = &buffer.address.in6;
881 break;
882
883 case TABLE_SET_MINIMUM_WIDTH: {
884 size_t w = va_arg(ap, size_t);
885
886 r = table_set_minimum_width(t, last_cell, w);
887 break;
888 }
889
890 case TABLE_SET_MAXIMUM_WIDTH: {
891 size_t w = va_arg(ap, size_t);
892 r = table_set_maximum_width(t, last_cell, w);
893 break;
894 }
895
896 case TABLE_SET_WEIGHT: {
897 unsigned w = va_arg(ap, unsigned);
898 r = table_set_weight(t, last_cell, w);
899 break;
900 }
901
902 case TABLE_SET_ALIGN_PERCENT: {
903 unsigned p = va_arg(ap, unsigned);
904 r = table_set_align_percent(t, last_cell, p);
905 break;
906 }
907
908 case TABLE_SET_ELLIPSIZE_PERCENT: {
909 unsigned p = va_arg(ap, unsigned);
910 r = table_set_ellipsize_percent(t, last_cell, p);
911 break;
912 }
913
914 case TABLE_SET_COLOR: {
915 const char *c = va_arg(ap, const char*);
916 r = table_set_color(t, last_cell, c);
917 break;
918 }
919
920 case TABLE_SET_URL: {
921 const char *u = va_arg(ap, const char*);
922 r = table_set_url(t, last_cell, u);
923 break;
924 }
925
926 case TABLE_SET_UPPERCASE: {
927 int u = va_arg(ap, int);
928 r = table_set_uppercase(t, last_cell, u);
929 break;
930 }
931
932 case _TABLE_DATA_TYPE_MAX:
933 /* Used as end marker */
934 va_end(ap);
935 return 0;
936
937 default:
938 assert_not_reached("Uh? Unexpected data type.");
939 }
940
941 if (type < _TABLE_DATA_TYPE_MAX)
942 r = table_add_cell(t, &last_cell, type, data);
943
944 if (r < 0) {
945 va_end(ap);
946 return r;
947 }
948
949 type = va_arg(ap, TableDataType);
950 }
951 }
952
953 void table_set_header(Table *t, bool b) {
954 assert(t);
955
956 t->header = b;
957 }
958
959 void table_set_width(Table *t, size_t width) {
960 assert(t);
961
962 t->width = width;
963 }
964
965 int table_set_empty_string(Table *t, const char *empty) {
966 assert(t);
967
968 return free_and_strdup(&t->empty_string, empty);
969 }
970
971 int table_set_display(Table *t, size_t first_column, ...) {
972 size_t allocated, column;
973 va_list ap;
974
975 assert(t);
976
977 allocated = t->n_display_map;
978 column = first_column;
979
980 va_start(ap, first_column);
981 for (;;) {
982 assert(column < t->n_columns);
983
984 if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, t->n_display_map+1))) {
985 va_end(ap);
986 return -ENOMEM;
987 }
988
989 t->display_map[t->n_display_map++] = column;
990
991 column = va_arg(ap, size_t);
992 if (column == (size_t) -1)
993 break;
994
995 }
996 va_end(ap);
997
998 return 0;
999 }
1000
1001 int table_set_sort(Table *t, size_t first_column, ...) {
1002 size_t allocated, column;
1003 va_list ap;
1004
1005 assert(t);
1006
1007 allocated = t->n_sort_map;
1008 column = first_column;
1009
1010 va_start(ap, first_column);
1011 for (;;) {
1012 assert(column < t->n_columns);
1013
1014 if (!GREEDY_REALLOC(t->sort_map, allocated, MAX(t->n_columns, t->n_sort_map+1))) {
1015 va_end(ap);
1016 return -ENOMEM;
1017 }
1018
1019 t->sort_map[t->n_sort_map++] = column;
1020
1021 column = va_arg(ap, size_t);
1022 if (column == (size_t) -1)
1023 break;
1024 }
1025 va_end(ap);
1026
1027 return 0;
1028 }
1029
1030 static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t index_b) {
1031 assert(a);
1032 assert(b);
1033
1034 if (a->type == b->type) {
1035
1036 /* We only define ordering for cells of the same data type. If cells with different data types are
1037 * compared we follow the order the cells were originally added in */
1038
1039 switch (a->type) {
1040
1041 case TABLE_STRING:
1042 return strcmp(a->string, b->string);
1043
1044 case TABLE_PATH:
1045 return path_compare(a->string, b->string);
1046
1047 case TABLE_BOOLEAN:
1048 if (!a->boolean && b->boolean)
1049 return -1;
1050 if (a->boolean && !b->boolean)
1051 return 1;
1052 return 0;
1053
1054 case TABLE_TIMESTAMP:
1055 case TABLE_TIMESTAMP_UTC:
1056 case TABLE_TIMESTAMP_RELATIVE:
1057 return CMP(a->timestamp, b->timestamp);
1058
1059 case TABLE_TIMESPAN:
1060 case TABLE_TIMESPAN_MSEC:
1061 return CMP(a->timespan, b->timespan);
1062
1063 case TABLE_SIZE:
1064 case TABLE_BPS:
1065 return CMP(a->size, b->size);
1066
1067 case TABLE_INT:
1068 return CMP(a->int_val, b->int_val);
1069
1070 case TABLE_INT8:
1071 return CMP(a->int8, b->int8);
1072
1073 case TABLE_INT16:
1074 return CMP(a->int16, b->int16);
1075
1076 case TABLE_INT32:
1077 return CMP(a->int32, b->int32);
1078
1079 case TABLE_INT64:
1080 return CMP(a->int64, b->int64);
1081
1082 case TABLE_UINT:
1083 return CMP(a->uint_val, b->uint_val);
1084
1085 case TABLE_UINT8:
1086 return CMP(a->uint8, b->uint8);
1087
1088 case TABLE_UINT16:
1089 return CMP(a->uint16, b->uint16);
1090
1091 case TABLE_UINT32:
1092 return CMP(a->uint32, b->uint32);
1093
1094 case TABLE_UINT64:
1095 return CMP(a->uint64, b->uint64);
1096
1097 case TABLE_PERCENT:
1098 return CMP(a->percent, b->percent);
1099
1100 case TABLE_IFINDEX:
1101 return CMP(a->ifindex, b->ifindex);
1102
1103 case TABLE_IN_ADDR:
1104 return CMP(a->address.in.s_addr, b->address.in.s_addr);
1105
1106 case TABLE_IN6_ADDR:
1107 return memcmp(&a->address.in6, &b->address.in6, FAMILY_ADDRESS_SIZE(AF_INET6));
1108
1109 default:
1110 ;
1111 }
1112 }
1113
1114 /* Generic fallback using the original order in which the cells where added. */
1115 return CMP(index_a, index_b);
1116 }
1117
1118 static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
1119 size_t i;
1120 int r;
1121
1122 assert(t);
1123 assert(t->sort_map);
1124
1125 /* Make sure the header stays at the beginning */
1126 if (*a < t->n_columns && *b < t->n_columns)
1127 return 0;
1128 if (*a < t->n_columns)
1129 return -1;
1130 if (*b < t->n_columns)
1131 return 1;
1132
1133 /* Order other lines by the sorting map */
1134 for (i = 0; i < t->n_sort_map; i++) {
1135 TableData *d, *dd;
1136
1137 d = t->data[*a + t->sort_map[i]];
1138 dd = t->data[*b + t->sort_map[i]];
1139
1140 r = cell_data_compare(d, *a, dd, *b);
1141 if (r != 0)
1142 return t->reverse_map && t->reverse_map[t->sort_map[i]] ? -r : r;
1143 }
1144
1145 /* Order identical lines by the order there were originally added in */
1146 return CMP(*a, *b);
1147 }
1148
1149 static const char *table_data_format(Table *t, TableData *d) {
1150 assert(d);
1151
1152 if (d->formatted)
1153 return d->formatted;
1154
1155 switch (d->type) {
1156 case TABLE_EMPTY:
1157 return strempty(t->empty_string);
1158
1159 case TABLE_STRING:
1160 case TABLE_PATH:
1161 if (d->uppercase) {
1162 char *p, *q;
1163
1164 d->formatted = new(char, strlen(d->string) + 1);
1165 if (!d->formatted)
1166 return NULL;
1167
1168 for (p = d->string, q = d->formatted; *p; p++, q++)
1169 *q = (char) toupper((unsigned char) *p);
1170 *q = 0;
1171
1172 return d->formatted;
1173 }
1174
1175 return d->string;
1176
1177 case TABLE_BOOLEAN:
1178 return yes_no(d->boolean);
1179
1180 case TABLE_TIMESTAMP:
1181 case TABLE_TIMESTAMP_UTC:
1182 case TABLE_TIMESTAMP_RELATIVE: {
1183 _cleanup_free_ char *p;
1184 char *ret;
1185
1186 p = new(char, FORMAT_TIMESTAMP_MAX);
1187 if (!p)
1188 return NULL;
1189
1190 if (d->type == TABLE_TIMESTAMP)
1191 ret = format_timestamp(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
1192 else if (d->type == TABLE_TIMESTAMP_UTC)
1193 ret = format_timestamp_utc(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
1194 else
1195 ret = format_timestamp_relative(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
1196 if (!ret)
1197 return "n/a";
1198
1199 d->formatted = TAKE_PTR(p);
1200 break;
1201 }
1202
1203 case TABLE_TIMESPAN:
1204 case TABLE_TIMESPAN_MSEC: {
1205 _cleanup_free_ char *p;
1206
1207 p = new(char, FORMAT_TIMESPAN_MAX);
1208 if (!p)
1209 return NULL;
1210
1211 if (!format_timespan(p, FORMAT_TIMESPAN_MAX, d->timespan,
1212 d->type == TABLE_TIMESPAN ? 0 : USEC_PER_MSEC))
1213 return "n/a";
1214
1215 d->formatted = TAKE_PTR(p);
1216 break;
1217 }
1218
1219 case TABLE_SIZE: {
1220 _cleanup_free_ char *p;
1221
1222 p = new(char, FORMAT_BYTES_MAX);
1223 if (!p)
1224 return NULL;
1225
1226 if (!format_bytes(p, FORMAT_BYTES_MAX, d->size))
1227 return "n/a";
1228
1229 d->formatted = TAKE_PTR(p);
1230 break;
1231 }
1232
1233 case TABLE_BPS: {
1234 _cleanup_free_ char *p;
1235 size_t n;
1236
1237 p = new(char, FORMAT_BYTES_MAX+2);
1238 if (!p)
1239 return NULL;
1240
1241 if (!format_bytes_full(p, FORMAT_BYTES_MAX, d->size, 0))
1242 return "n/a";
1243
1244 n = strlen(p);
1245 strscpy(p + n, FORMAT_BYTES_MAX + 2 - n, "bps");
1246
1247 d->formatted = TAKE_PTR(p);
1248 break;
1249 }
1250
1251 case TABLE_INT: {
1252 _cleanup_free_ char *p;
1253
1254 p = new(char, DECIMAL_STR_WIDTH(d->int_val) + 1);
1255 if (!p)
1256 return NULL;
1257
1258 sprintf(p, "%i", d->int_val);
1259 d->formatted = TAKE_PTR(p);
1260 break;
1261 }
1262
1263 case TABLE_INT8: {
1264 _cleanup_free_ char *p;
1265
1266 p = new(char, DECIMAL_STR_WIDTH(d->int8) + 1);
1267 if (!p)
1268 return NULL;
1269
1270 sprintf(p, "%" PRIi8, d->int8);
1271 d->formatted = TAKE_PTR(p);
1272 break;
1273 }
1274
1275 case TABLE_INT16: {
1276 _cleanup_free_ char *p;
1277
1278 p = new(char, DECIMAL_STR_WIDTH(d->int16) + 1);
1279 if (!p)
1280 return NULL;
1281
1282 sprintf(p, "%" PRIi16, d->int16);
1283 d->formatted = TAKE_PTR(p);
1284 break;
1285 }
1286
1287 case TABLE_INT32: {
1288 _cleanup_free_ char *p;
1289
1290 p = new(char, DECIMAL_STR_WIDTH(d->int32) + 1);
1291 if (!p)
1292 return NULL;
1293
1294 sprintf(p, "%" PRIi32, d->int32);
1295 d->formatted = TAKE_PTR(p);
1296 break;
1297 }
1298
1299 case TABLE_INT64: {
1300 _cleanup_free_ char *p;
1301
1302 p = new(char, DECIMAL_STR_WIDTH(d->int64) + 1);
1303 if (!p)
1304 return NULL;
1305
1306 sprintf(p, "%" PRIi64, d->int64);
1307 d->formatted = TAKE_PTR(p);
1308 break;
1309 }
1310
1311 case TABLE_UINT: {
1312 _cleanup_free_ char *p;
1313
1314 p = new(char, DECIMAL_STR_WIDTH(d->uint_val) + 1);
1315 if (!p)
1316 return NULL;
1317
1318 sprintf(p, "%u", d->uint_val);
1319 d->formatted = TAKE_PTR(p);
1320 break;
1321 }
1322
1323 case TABLE_UINT8: {
1324 _cleanup_free_ char *p;
1325
1326 p = new(char, DECIMAL_STR_WIDTH(d->uint8) + 1);
1327 if (!p)
1328 return NULL;
1329
1330 sprintf(p, "%" PRIu8, d->uint8);
1331 d->formatted = TAKE_PTR(p);
1332 break;
1333 }
1334
1335 case TABLE_UINT16: {
1336 _cleanup_free_ char *p;
1337
1338 p = new(char, DECIMAL_STR_WIDTH(d->uint16) + 1);
1339 if (!p)
1340 return NULL;
1341
1342 sprintf(p, "%" PRIu16, d->uint16);
1343 d->formatted = TAKE_PTR(p);
1344 break;
1345 }
1346
1347 case TABLE_UINT32: {
1348 _cleanup_free_ char *p;
1349
1350 p = new(char, DECIMAL_STR_WIDTH(d->uint32) + 1);
1351 if (!p)
1352 return NULL;
1353
1354 sprintf(p, "%" PRIu32, d->uint32);
1355 d->formatted = TAKE_PTR(p);
1356 break;
1357 }
1358
1359 case TABLE_UINT64: {
1360 _cleanup_free_ char *p;
1361
1362 p = new(char, DECIMAL_STR_WIDTH(d->uint64) + 1);
1363 if (!p)
1364 return NULL;
1365
1366 sprintf(p, "%" PRIu64, d->uint64);
1367 d->formatted = TAKE_PTR(p);
1368 break;
1369 }
1370
1371 case TABLE_PERCENT: {
1372 _cleanup_free_ char *p;
1373
1374 p = new(char, DECIMAL_STR_WIDTH(d->percent) + 2);
1375 if (!p)
1376 return NULL;
1377
1378 sprintf(p, "%i%%" , d->percent);
1379 d->formatted = TAKE_PTR(p);
1380 break;
1381 }
1382
1383 case TABLE_IFINDEX: {
1384 _cleanup_free_ char *p = NULL;
1385 char name[IF_NAMESIZE + 1];
1386
1387 if (format_ifname(d->ifindex, name)) {
1388 p = strdup(name);
1389 if (!p)
1390 return NULL;
1391 } else {
1392 if (asprintf(&p, "%i" , d->ifindex) < 0)
1393 return NULL;
1394 }
1395
1396 d->formatted = TAKE_PTR(p);
1397 break;
1398 }
1399
1400 case TABLE_IN_ADDR:
1401 case TABLE_IN6_ADDR: {
1402 _cleanup_free_ char *p = NULL;
1403
1404 if (in_addr_to_string(d->type == TABLE_IN_ADDR ? AF_INET : AF_INET6,
1405 &d->address, &p) < 0)
1406 return NULL;
1407
1408 d->formatted = TAKE_PTR(p);
1409 break;
1410 }
1411
1412 default:
1413 assert_not_reached("Unexpected type?");
1414 }
1415
1416 return d->formatted;
1417 }
1418
1419 static int table_data_requested_width(Table *table, TableData *d, size_t *ret) {
1420 const char *t;
1421 size_t l;
1422
1423 t = table_data_format(table, d);
1424 if (!t)
1425 return -ENOMEM;
1426
1427 l = utf8_console_width(t);
1428 if (l == (size_t) -1)
1429 return -EINVAL;
1430
1431 if (d->maximum_width != (size_t) -1 && l > d->maximum_width)
1432 l = d->maximum_width;
1433
1434 if (l < d->minimum_width)
1435 l = d->minimum_width;
1436
1437 *ret = l;
1438 return 0;
1439 }
1440
1441 static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) {
1442 size_t w = 0, space, lspace, old_length, clickable_length;
1443 _cleanup_free_ char *clickable = NULL;
1444 const char *p;
1445 char *ret;
1446 size_t i;
1447 int r;
1448
1449 /* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
1450
1451 assert(str);
1452 assert(percent <= 100);
1453
1454 old_length = strlen(str);
1455
1456 if (url) {
1457 r = terminal_urlify(url, str, &clickable);
1458 if (r < 0)
1459 return NULL;
1460
1461 clickable_length = strlen(clickable);
1462 } else
1463 clickable_length = old_length;
1464
1465 /* Determine current width on screen */
1466 p = str;
1467 while (p < str + old_length) {
1468 char32_t c;
1469
1470 if (utf8_encoded_to_unichar(p, &c) < 0) {
1471 p++, w++; /* count invalid chars as 1 */
1472 continue;
1473 }
1474
1475 p = utf8_next_char(p);
1476 w += unichar_iswide(c) ? 2 : 1;
1477 }
1478
1479 /* Already wider than the target, if so, don't do anything */
1480 if (w >= new_length)
1481 return clickable ? TAKE_PTR(clickable) : strdup(str);
1482
1483 /* How much spaces shall we add? An how much on the left side? */
1484 space = new_length - w;
1485 lspace = space * percent / 100U;
1486
1487 ret = new(char, space + clickable_length + 1);
1488 if (!ret)
1489 return NULL;
1490
1491 for (i = 0; i < lspace; i++)
1492 ret[i] = ' ';
1493 memcpy(ret + lspace, clickable ?: str, clickable_length);
1494 for (i = lspace + clickable_length; i < space + clickable_length; i++)
1495 ret[i] = ' ';
1496
1497 ret[space + clickable_length] = 0;
1498 return ret;
1499 }
1500
1501 static const char* table_data_color(TableData *d) {
1502 assert(d);
1503
1504 if (d->color)
1505 return d->color;
1506
1507 /* Let's implicitly color all "empty" cells in grey, in case an "empty_string" is set that is not empty */
1508 if (d->type == TABLE_EMPTY)
1509 return ansi_grey();
1510
1511 return NULL;
1512 }
1513
1514 int table_print(Table *t, FILE *f) {
1515 size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
1516 i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
1517 *width;
1518 _cleanup_free_ size_t *sorted = NULL;
1519 uint64_t *column_weight, weight_sum;
1520 int r;
1521
1522 assert(t);
1523
1524 if (!f)
1525 f = stdout;
1526
1527 /* Ensure we have no incomplete rows */
1528 assert(t->n_cells % t->n_columns == 0);
1529
1530 n_rows = t->n_cells / t->n_columns;
1531 assert(n_rows > 0); /* at least the header row must be complete */
1532
1533 if (t->sort_map) {
1534 /* If sorting is requested, let's calculate an index table we use to lookup the actual index to display with. */
1535
1536 sorted = new(size_t, n_rows);
1537 if (!sorted)
1538 return -ENOMEM;
1539
1540 for (i = 0; i < n_rows; i++)
1541 sorted[i] = i * t->n_columns;
1542
1543 typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
1544 }
1545
1546 if (t->display_map)
1547 display_columns = t->n_display_map;
1548 else
1549 display_columns = t->n_columns;
1550
1551 assert(display_columns > 0);
1552
1553 minimum_width = newa(size_t, display_columns);
1554 maximum_width = newa(size_t, display_columns);
1555 requested_width = newa(size_t, display_columns);
1556 width = newa(size_t, display_columns);
1557 column_weight = newa0(uint64_t, display_columns);
1558
1559 for (j = 0; j < display_columns; j++) {
1560 minimum_width[j] = 1;
1561 maximum_width[j] = (size_t) -1;
1562 requested_width[j] = (size_t) -1;
1563 }
1564
1565 /* First pass: determine column sizes */
1566 for (i = t->header ? 0 : 1; i < n_rows; i++) {
1567 TableData **row;
1568
1569 /* Note that we don't care about ordering at this time, as we just want to determine column sizes,
1570 * hence we don't care for sorted[] during the first pass. */
1571 row = t->data + i * t->n_columns;
1572
1573 for (j = 0; j < display_columns; j++) {
1574 TableData *d;
1575 size_t req;
1576
1577 assert_se(d = row[t->display_map ? t->display_map[j] : j]);
1578
1579 r = table_data_requested_width(t, d, &req);
1580 if (r < 0)
1581 return r;
1582
1583 /* Determine the biggest width that any cell in this column would like to have */
1584 if (requested_width[j] == (size_t) -1 ||
1585 requested_width[j] < req)
1586 requested_width[j] = req;
1587
1588 /* Determine the minimum width any cell in this column needs */
1589 if (minimum_width[j] < d->minimum_width)
1590 minimum_width[j] = d->minimum_width;
1591
1592 /* Determine the maximum width any cell in this column needs */
1593 if (d->maximum_width != (size_t) -1 &&
1594 (maximum_width[j] == (size_t) -1 ||
1595 maximum_width[j] > d->maximum_width))
1596 maximum_width[j] = d->maximum_width;
1597
1598 /* Determine the full columns weight */
1599 column_weight[j] += d->weight;
1600 }
1601 }
1602
1603 /* One space between each column */
1604 table_requested_width = table_minimum_width = table_maximum_width = display_columns - 1;
1605
1606 /* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
1607 weight_sum = 0;
1608 for (j = 0; j < display_columns; j++) {
1609 weight_sum += column_weight[j];
1610
1611 table_minimum_width += minimum_width[j];
1612
1613 if (maximum_width[j] == (size_t) -1)
1614 table_maximum_width = (size_t) -1;
1615 else
1616 table_maximum_width += maximum_width[j];
1617
1618 table_requested_width += requested_width[j];
1619 }
1620
1621 /* Calculate effective table width */
1622 if (t->width != (size_t) -1)
1623 table_effective_width = t->width;
1624 else if (pager_have() || !isatty(STDOUT_FILENO))
1625 table_effective_width = table_requested_width;
1626 else
1627 table_effective_width = MIN(table_requested_width, columns());
1628
1629 if (table_maximum_width != (size_t) -1 && table_effective_width > table_maximum_width)
1630 table_effective_width = table_maximum_width;
1631
1632 if (table_effective_width < table_minimum_width)
1633 table_effective_width = table_minimum_width;
1634
1635 if (table_effective_width >= table_requested_width) {
1636 size_t extra;
1637
1638 /* We have extra room, let's distribute it among columns according to their weights. We first provide
1639 * each column with what it asked for and the distribute the rest. */
1640
1641 extra = table_effective_width - table_requested_width;
1642
1643 for (j = 0; j < display_columns; j++) {
1644 size_t delta;
1645
1646 if (weight_sum == 0)
1647 width[j] = requested_width[j] + extra / (display_columns - j); /* Avoid division by zero */
1648 else
1649 width[j] = requested_width[j] + (extra * column_weight[j]) / weight_sum;
1650
1651 if (maximum_width[j] != (size_t) -1 && width[j] > maximum_width[j])
1652 width[j] = maximum_width[j];
1653
1654 if (width[j] < minimum_width[j])
1655 width[j] = minimum_width[j];
1656
1657 assert(width[j] >= requested_width[j]);
1658 delta = width[j] - requested_width[j];
1659
1660 /* Subtract what we just added from the rest */
1661 if (extra > delta)
1662 extra -= delta;
1663 else
1664 extra = 0;
1665
1666 assert(weight_sum >= column_weight[j]);
1667 weight_sum -= column_weight[j];
1668 }
1669
1670 } else {
1671 /* We need to compress the table, columns can't get what they asked for. We first provide each column
1672 * with the minimum they need, and then distribute anything left. */
1673 bool finalize = false;
1674 size_t extra;
1675
1676 extra = table_effective_width - table_minimum_width;
1677
1678 for (j = 0; j < display_columns; j++)
1679 width[j] = (size_t) -1;
1680
1681 for (;;) {
1682 bool restart = false;
1683
1684 for (j = 0; j < display_columns; j++) {
1685 size_t delta, w;
1686
1687 /* Did this column already get something assigned? If so, let's skip to the next */
1688 if (width[j] != (size_t) -1)
1689 continue;
1690
1691 if (weight_sum == 0)
1692 w = minimum_width[j] + extra / (display_columns - j); /* avoid division by zero */
1693 else
1694 w = minimum_width[j] + (extra * column_weight[j]) / weight_sum;
1695
1696 if (w >= requested_width[j]) {
1697 /* Never give more than requested. If we hit a column like this, there's more
1698 * space to allocate to other columns which means we need to restart the
1699 * iteration. However, if we hit a column like this, let's assign it the space
1700 * it wanted for good early.*/
1701
1702 w = requested_width[j];
1703 restart = true;
1704
1705 } else if (!finalize)
1706 continue;
1707
1708 width[j] = w;
1709
1710 assert(w >= minimum_width[j]);
1711 delta = w - minimum_width[j];
1712
1713 assert(delta <= extra);
1714 extra -= delta;
1715
1716 assert(weight_sum >= column_weight[j]);
1717 weight_sum -= column_weight[j];
1718
1719 if (restart && !finalize)
1720 break;
1721 }
1722
1723 if (finalize)
1724 break;
1725
1726 if (!restart)
1727 finalize = true;
1728 }
1729 }
1730
1731 /* Second pass: show output */
1732 for (i = t->header ? 0 : 1; i < n_rows; i++) {
1733 TableData **row;
1734
1735 if (sorted)
1736 row = t->data + sorted[i];
1737 else
1738 row = t->data + i * t->n_columns;
1739
1740 for (j = 0; j < display_columns; j++) {
1741 _cleanup_free_ char *buffer = NULL;
1742 const char *field;
1743 TableData *d;
1744 size_t l;
1745
1746 assert_se(d = row[t->display_map ? t->display_map[j] : j]);
1747
1748 field = table_data_format(t, d);
1749 if (!field)
1750 return -ENOMEM;
1751
1752 l = utf8_console_width(field);
1753 if (l > width[j]) {
1754 /* Field is wider than allocated space. Let's ellipsize */
1755
1756 buffer = ellipsize(field, width[j], d->ellipsize_percent);
1757 if (!buffer)
1758 return -ENOMEM;
1759
1760 field = buffer;
1761
1762 } else if (l < width[j]) {
1763 /* Field is shorter than allocated space. Let's align with spaces */
1764
1765 buffer = align_string_mem(field, d->url, width[j], d->align_percent);
1766 if (!buffer)
1767 return -ENOMEM;
1768
1769 field = buffer;
1770 }
1771
1772 if (l >= width[j] && d->url) {
1773 _cleanup_free_ char *clickable = NULL;
1774
1775 r = terminal_urlify(d->url, field, &clickable);
1776 if (r < 0)
1777 return r;
1778
1779 free_and_replace(buffer, clickable);
1780 field = buffer;
1781 }
1782
1783 if (row == t->data) /* underline header line fully, including the column separator */
1784 fputs(ansi_underline(), f);
1785
1786 if (j > 0)
1787 fputc(' ', f); /* column separator */
1788
1789 if (table_data_color(d) && colors_enabled()) {
1790 if (row == t->data) /* first undo header underliner */
1791 fputs(ANSI_NORMAL, f);
1792
1793 fputs(table_data_color(d), f);
1794 }
1795
1796 fputs(field, f);
1797
1798 if (colors_enabled() && (table_data_color(d) || row == t->data))
1799 fputs(ANSI_NORMAL, f);
1800 }
1801
1802 fputc('\n', f);
1803 }
1804
1805 return fflush_and_check(f);
1806 }
1807
1808 int table_format(Table *t, char **ret) {
1809 _cleanup_fclose_ FILE *f = NULL;
1810 char *buf = NULL;
1811 size_t sz = 0;
1812 int r;
1813
1814 f = open_memstream_unlocked(&buf, &sz);
1815 if (!f)
1816 return -ENOMEM;
1817
1818 r = table_print(t, f);
1819 if (r < 0)
1820 return r;
1821
1822 f = safe_fclose(f);
1823
1824 *ret = buf;
1825
1826 return 0;
1827 }
1828
1829 size_t table_get_rows(Table *t) {
1830 if (!t)
1831 return 0;
1832
1833 assert(t->n_columns > 0);
1834 return t->n_cells / t->n_columns;
1835 }
1836
1837 size_t table_get_columns(Table *t) {
1838 if (!t)
1839 return 0;
1840
1841 assert(t->n_columns > 0);
1842 return t->n_columns;
1843 }
1844
1845 int table_set_reverse(Table *t, size_t column, bool b) {
1846 assert(t);
1847 assert(column < t->n_columns);
1848
1849 if (!t->reverse_map) {
1850 if (!b)
1851 return 0;
1852
1853 t->reverse_map = new0(bool, t->n_columns);
1854 if (!t->reverse_map)
1855 return -ENOMEM;
1856 }
1857
1858 t->reverse_map[column] = b;
1859 return 0;
1860 }
1861
1862 TableCell *table_get_cell(Table *t, size_t row, size_t column) {
1863 size_t i;
1864
1865 assert(t);
1866
1867 if (column >= t->n_columns)
1868 return NULL;
1869
1870 i = row * t->n_columns + column;
1871 if (i >= t->n_cells)
1872 return NULL;
1873
1874 return TABLE_INDEX_TO_CELL(i);
1875 }
1876
1877 const void *table_get(Table *t, TableCell *cell) {
1878 TableData *d;
1879
1880 assert(t);
1881
1882 d = table_get_data(t, cell);
1883 if (!d)
1884 return NULL;
1885
1886 return d->data;
1887 }
1888
1889 const void* table_get_at(Table *t, size_t row, size_t column) {
1890 TableCell *cell;
1891
1892 cell = table_get_cell(t, row, column);
1893 if (!cell)
1894 return NULL;
1895
1896 return table_get(t, cell);
1897 }
1898
1899 static int table_data_to_json(TableData *d, JsonVariant **ret) {
1900
1901 switch (d->type) {
1902
1903 case TABLE_EMPTY:
1904 return json_variant_new_null(ret);
1905
1906 case TABLE_STRING:
1907 case TABLE_PATH:
1908 return json_variant_new_string(ret, d->string);
1909
1910 case TABLE_BOOLEAN:
1911 return json_variant_new_boolean(ret, d->boolean);
1912
1913 case TABLE_TIMESTAMP:
1914 case TABLE_TIMESTAMP_UTC:
1915 case TABLE_TIMESTAMP_RELATIVE:
1916 if (d->timestamp == USEC_INFINITY)
1917 return json_variant_new_null(ret);
1918
1919 return json_variant_new_unsigned(ret, d->timestamp);
1920
1921 case TABLE_TIMESPAN:
1922 case TABLE_TIMESPAN_MSEC:
1923 if (d->timespan == USEC_INFINITY)
1924 return json_variant_new_null(ret);
1925
1926 return json_variant_new_unsigned(ret, d->timespan);
1927
1928 case TABLE_SIZE:
1929 case TABLE_BPS:
1930 if (d->size == (size_t) -1)
1931 return json_variant_new_null(ret);
1932
1933 return json_variant_new_unsigned(ret, d->size);
1934
1935 case TABLE_INT:
1936 return json_variant_new_integer(ret, d->int_val);
1937
1938 case TABLE_INT8:
1939 return json_variant_new_integer(ret, d->int8);
1940
1941 case TABLE_INT16:
1942 return json_variant_new_integer(ret, d->int16);
1943
1944 case TABLE_INT32:
1945 return json_variant_new_integer(ret, d->int32);
1946
1947 case TABLE_INT64:
1948 return json_variant_new_integer(ret, d->int64);
1949
1950 case TABLE_UINT:
1951 return json_variant_new_unsigned(ret, d->uint_val);
1952
1953 case TABLE_UINT8:
1954 return json_variant_new_unsigned(ret, d->uint8);
1955
1956 case TABLE_UINT16:
1957 return json_variant_new_unsigned(ret, d->uint16);
1958
1959 case TABLE_UINT32:
1960 return json_variant_new_unsigned(ret, d->uint32);
1961
1962 case TABLE_UINT64:
1963 return json_variant_new_unsigned(ret, d->uint64);
1964
1965 case TABLE_PERCENT:
1966 return json_variant_new_integer(ret, d->percent);
1967
1968 case TABLE_IFINDEX:
1969 return json_variant_new_integer(ret, d->ifindex);
1970
1971 case TABLE_IN_ADDR:
1972 return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET));
1973
1974 case TABLE_IN6_ADDR:
1975 return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
1976
1977 default:
1978 return -EINVAL;
1979 }
1980 }
1981
1982 int table_to_json(Table *t, JsonVariant **ret) {
1983 JsonVariant **rows = NULL, **elements = NULL;
1984 _cleanup_free_ size_t *sorted = NULL;
1985 size_t n_rows, i, j, display_columns;
1986 int r;
1987
1988 assert(t);
1989
1990 /* Ensure we have no incomplete rows */
1991 assert(t->n_cells % t->n_columns == 0);
1992
1993 n_rows = t->n_cells / t->n_columns;
1994 assert(n_rows > 0); /* at least the header row must be complete */
1995
1996 if (t->sort_map) {
1997 /* If sorting is requested, let's calculate an index table we use to lookup the actual index to display with. */
1998
1999 sorted = new(size_t, n_rows);
2000 if (!sorted) {
2001 r = -ENOMEM;
2002 goto finish;
2003 }
2004
2005 for (i = 0; i < n_rows; i++)
2006 sorted[i] = i * t->n_columns;
2007
2008 typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
2009 }
2010
2011 if (t->display_map)
2012 display_columns = t->n_display_map;
2013 else
2014 display_columns = t->n_columns;
2015 assert(display_columns > 0);
2016
2017 elements = new0(JsonVariant*, display_columns * 2);
2018 if (!elements) {
2019 r = -ENOMEM;
2020 goto finish;
2021 }
2022
2023 for (j = 0; j < display_columns; j++) {
2024 TableData *d;
2025
2026 assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
2027
2028 r = table_data_to_json(d, elements + j*2);
2029 if (r < 0)
2030 goto finish;
2031 }
2032
2033 rows = new0(JsonVariant*, n_rows-1);
2034 if (!rows) {
2035 r = -ENOMEM;
2036 goto finish;
2037 }
2038
2039 for (i = 1; i < n_rows; i++) {
2040 TableData **row;
2041
2042 if (sorted)
2043 row = t->data + sorted[i];
2044 else
2045 row = t->data + i * t->n_columns;
2046
2047 for (j = 0; j < display_columns; j++) {
2048 TableData *d;
2049 size_t k;
2050
2051 assert_se(d = row[t->display_map ? t->display_map[j] : j]);
2052
2053 k = j*2+1;
2054 elements[k] = json_variant_unref(elements[k]);
2055
2056 r = table_data_to_json(d, elements + k);
2057 if (r < 0)
2058 goto finish;
2059 }
2060
2061 r = json_variant_new_object(rows + i - 1, elements, display_columns * 2);
2062 if (r < 0)
2063 goto finish;
2064 }
2065
2066 r = json_variant_new_array(ret, rows, n_rows - 1);
2067
2068 finish:
2069 if (rows) {
2070 json_variant_unref_many(rows, n_rows-1);
2071 free(rows);
2072 }
2073
2074 if (elements) {
2075 json_variant_unref_many(elements, display_columns*2);
2076 free(elements);
2077 }
2078
2079 return r;
2080 }
2081
2082 int table_print_json(Table *t, FILE *f, JsonFormatFlags flags) {
2083 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2084 int r;
2085
2086 assert(t);
2087
2088 if (!f)
2089 f = stdout;
2090
2091 r = table_to_json(t, &v);
2092 if (r < 0)
2093 return r;
2094
2095 json_variant_dump(v, flags, f, NULL);
2096
2097 return fflush_and_check(f);
2098 }