LCOV - code coverage report
Current view: top level - shared - format-table.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 479 1100 43.5 %
Date: 2019-08-22 15:41:25 Functions: 30 50 60.0 %

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

Generated by: LCOV version 1.14