Bug Summary

File:build-scan/../src/basic/format-table.c
Warning:line 597, column 24
Potential leak of memory pointed to by 'copy'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name format-table.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/basic/libbasic.a.p -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/basic/format-table.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <ctype.h>
4#include <stdio_ext.h>
5
6#include "alloc-util.h"
7#include "fd-util.h"
8#include "fileio.h"
9#include "format-table.h"
10#include "gunicode.h"
11#include "pager.h"
12#include "parse-util.h"
13#include "string-util.h"
14#include "terminal-util.h"
15#include "time-util.h"
16#include "utf8.h"
17#include "util.h"
18
19#define DEFAULT_WEIGHT100 100
20
21/*
22 A few notes on implementation details:
23
24 - TableCell is a 'fake' structure, it's just used as data type to pass references to specific cell positions in the
25 table. It can be easily converted to an index number and back.
26
27 - TableData is where the actual data is stored: it encapsulates the data and formatting for a specific cell. It's
28 'pseudo-immutable' and ref-counted. When a cell's data's formatting is to be changed, we duplicate the object if the
29 ref-counting is larger than 1. Note that TableData and its ref-counting is mostly not visible to the outside. The
30 outside only sees Table and TableCell.
31
32 - The Table object stores a simple one-dimensional array of references to TableData objects, one row after the
33 previous one.
34
35 - There's no special concept of a "row" or "column" in the table, and no special concept of the "header" row. It's all
36 derived from the cell index: we know how many cells are to be stored in a row, and can determine the rest from
37 that. The first row is always the header row. If header display is turned off we simply skip outputting the first
38 row. Also, when sorting rows we always leave the first row where it is, as the header shouldn't move.
39
40 - Note because there's no row and no column object some properties that might be approproate as row/column properties
41 are exposed as cell properties instead. For example, the "weight" of a column (which is used to determine where to
42 add/remove space preferable when expanding/compressing tables horizontally) is actually made the "weight" of a
43 cell. Given that we usually need it per-column though we will calculate the average across every cell of the column
44 instead.
45
46 - To make things easy, when cells are added without any explicit configured formatting, then we'll copy the formatting
47 from the same cell in the previous cell. This is particularly useful for the "weight" of the cell (see above), as
48 this means setting the weight of the cells of the header row will nicely propagate to all cells in the other rows.
49*/
50
51typedef struct TableData {
52 unsigned n_ref;
53 TableDataType type;
54
55 size_t minimum_width; /* minimum width for the column */
56 size_t maximum_width; /* maximum width for the column */
57 unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
58 unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
59 unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
60
61 bool_Bool uppercase; /* Uppercase string on display */
62
63 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 */
64 char *url; /* A URL to use for a clickable hyperlink */
65 char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */
66
67 union {
68 uint8_t data[0]; /* data is generic array */
69 bool_Bool boolean;
70 usec_t timestamp;
71 usec_t timespan;
72 uint64_t size;
73 char string[0];
74 uint32_t uint32;
75 uint64_t uint64;
76 int percent; /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
77 /* … add more here as we start supporting more cell data types … */
78 };
79} TableData;
80
81static size_t TABLE_CELL_TO_INDEX(TableCell *cell) {
82 unsigned i;
83
84 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 84
, __PRETTY_FUNCTION__); } while (0)
;
85
86 i = PTR_TO_UINT(cell)((unsigned int) ((uintptr_t) (cell)));
87 assert(i > 0)do { if ((__builtin_expect(!!(!(i > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("i > 0"), "../src/basic/format-table.c"
, 87, __PRETTY_FUNCTION__); } while (0)
;
88
89 return i-1;
90}
91
92static TableCell* TABLE_INDEX_TO_CELL(size_t index) {
93 assert(index != (size_t) -1)do { if ((__builtin_expect(!!(!(index != (size_t) -1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("index != (size_t) -1"), "../src/basic/format-table.c"
, 93, __PRETTY_FUNCTION__); } while (0)
;
94 return UINT_TO_PTR((unsigned) (index + 1))((void *) ((uintptr_t) ((unsigned) (index + 1))));
95}
96
97struct Table {
98 size_t n_columns;
99 size_t n_cells;
100
101 bool_Bool header; /* Whether to show the header row? */
102 size_t width; /* If != (size_t) -1 the width to format this table in */
103
104 TableData **data;
105 size_t n_allocated;
106
107 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 */
108 size_t n_display_map;
109
110 size_t *sort_map; /* The columns to order rows by, in order of preference. */
111 size_t n_sort_map;
112
113 bool_Bool *reverse_map;
114};
115
116Table *table_new_raw(size_t n_columns) {
117 _cleanup_(table_unrefp)__attribute__((cleanup(table_unrefp))) Table *t = NULL((void*)0);
118
119 assert(n_columns > 0)do { if ((__builtin_expect(!!(!(n_columns > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("n_columns > 0"), "../src/basic/format-table.c"
, 119, __PRETTY_FUNCTION__); } while (0)
;
120
121 t = new(Table, 1)((Table*) malloc_multiply(sizeof(Table), (1)));
122 if (!t)
123 return NULL((void*)0);
124
125 *t = (struct Table) {
126 .n_columns = n_columns,
127 .header = true1,
128 .width = (size_t) -1,
129 };
130
131 return TAKE_PTR(t)({ typeof(t) _ptr_ = (t); (t) = ((void*)0); _ptr_; });
132}
133
134Table *table_new_internal(const char *first_header, ...) {
135 _cleanup_(table_unrefp)__attribute__((cleanup(table_unrefp))) Table *t = NULL((void*)0);
136 size_t n_columns = 1;
137 const char *h;
138 va_list ap;
139 int r;
140
141 assert(first_header)do { if ((__builtin_expect(!!(!(first_header)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("first_header"), "../src/basic/format-table.c"
, 141, __PRETTY_FUNCTION__); } while (0)
;
142
143 va_start(ap, first_header)__builtin_va_start(ap, first_header);
144 for (;;) {
145 h = va_arg(ap, const char*)__builtin_va_arg(ap, const char*);
146 if (!h)
147 break;
148
149 n_columns++;
150 }
151 va_end(ap)__builtin_va_end(ap);
152
153 t = table_new_raw(n_columns);
154 if (!t)
155 return NULL((void*)0);
156
157 va_start(ap, first_header)__builtin_va_start(ap, first_header);
158 for (h = first_header; h; h = va_arg(ap, const char*)__builtin_va_arg(ap, const char*)) {
159 TableCell *cell;
160
161 r = table_add_cell(t, &cell, TABLE_STRING, h);
162 if (r < 0) {
163 va_end(ap)__builtin_va_end(ap);
164 return NULL((void*)0);
165 }
166
167 /* Make the table header uppercase */
168 r = table_set_uppercase(t, cell, true1);
169 if (r < 0) {
170 va_end(ap)__builtin_va_end(ap);
171 return NULL((void*)0);
172 }
173 }
174 va_end(ap)__builtin_va_end(ap);
175
176 assert(t->n_columns == t->n_cells)do { if ((__builtin_expect(!!(!(t->n_columns == t->n_cells
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t->n_columns == t->n_cells"
), "../src/basic/format-table.c", 176, __PRETTY_FUNCTION__); }
while (0)
;
177 return TAKE_PTR(t)({ typeof(t) _ptr_ = (t); (t) = ((void*)0); _ptr_; });
178}
179
180static TableData *table_data_unref(TableData *d) {
181 if (!d)
182 return NULL((void*)0);
183
184 assert(d->n_ref > 0)do { if ((__builtin_expect(!!(!(d->n_ref > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d->n_ref > 0"), "../src/basic/format-table.c"
, 184, __PRETTY_FUNCTION__); } while (0)
;
185 d->n_ref--;
186
187 if (d->n_ref > 0)
188 return NULL((void*)0);
189
190 free(d->formatted);
191 free(d->url);
192
193 return mfree(d);
194}
195
196DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref)static inline void table_data_unrefp(TableData* *p) { if (*p)
table_data_unref(*p); }
;
197
198static TableData *table_data_ref(TableData *d) {
199 if (!d)
200 return NULL((void*)0);
201
202 assert(d->n_ref > 0)do { if ((__builtin_expect(!!(!(d->n_ref > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d->n_ref > 0"), "../src/basic/format-table.c"
, 202, __PRETTY_FUNCTION__); } while (0)
;
203 d->n_ref++;
204
205 return d;
206}
207
208Table *table_unref(Table *t) {
209 size_t i;
210
211 if (!t)
212 return NULL((void*)0);
213
214 for (i = 0; i < t->n_cells; i++)
215 table_data_unref(t->data[i]);
216
217 free(t->data);
218 free(t->display_map);
219 free(t->sort_map);
220 free(t->reverse_map);
221
222 return mfree(t);
223}
224
225static size_t table_data_size(TableDataType type, const void *data) {
226
227 switch (type) {
228
229 case TABLE_EMPTY:
230 return 0;
231
232 case TABLE_STRING:
233 return strlen(data) + 1;
234
235 case TABLE_BOOLEAN:
236 return sizeof(bool_Bool);
237
238 case TABLE_TIMESTAMP:
239 case TABLE_TIMESPAN:
240 return sizeof(usec_t);
241
242 case TABLE_SIZE:
243 case TABLE_UINT64:
244 return sizeof(uint64_t);
245
246 case TABLE_UINT32:
247 return sizeof(uint32_t);
248
249 case TABLE_PERCENT:
250 return sizeof(int);
251
252 default:
253 assert_not_reached("Uh? Unexpected cell type")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, (
"Uh? Unexpected cell type"), "../src/basic/format-table.c", 253
, __PRETTY_FUNCTION__); } while (0)
;
254 }
255}
256
257static bool_Bool table_data_matches(
258 TableData *d,
259 TableDataType type,
260 const void *data,
261 size_t minimum_width,
262 size_t maximum_width,
263 unsigned weight,
264 unsigned align_percent,
265 unsigned ellipsize_percent) {
266
267 size_t k, l;
268 assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d"), "../src/basic/format-table.c", 268
, __PRETTY_FUNCTION__); } while (0)
;
269
270 if (d->type != type)
271 return false0;
272
273 if (d->minimum_width != minimum_width)
274 return false0;
275
276 if (d->maximum_width != maximum_width)
277 return false0;
278
279 if (d->weight != weight)
280 return false0;
281
282 if (d->align_percent != align_percent)
283 return false0;
284
285 if (d->ellipsize_percent != ellipsize_percent)
286 return false0;
287
288 /* If a color/url/uppercase flag is set, refuse to merge */
289 if (d->color)
290 return false0;
291 if (d->url)
292 return false0;
293 if (d->uppercase)
294 return false0;
295
296 k = table_data_size(type, data);
297 l = table_data_size(d->type, d->data);
298
299 if (k != l)
300 return false0;
301
302 return memcmp_safe(data, d->data, l) == 0;
303}
304
305static TableData *table_data_new(
306 TableDataType type,
307 const void *data,
308 size_t minimum_width,
309 size_t maximum_width,
310 unsigned weight,
311 unsigned align_percent,
312 unsigned ellipsize_percent) {
313
314 size_t data_size;
315 TableData *d;
316
317 data_size = table_data_size(type, data);
318
319 d = malloc0(offsetof(TableData, data) + data_size)(calloc(1, (__builtin_offsetof(TableData, data) + data_size))
)
;
320 if (!d)
321 return NULL((void*)0);
322
323 d->n_ref = 1;
324 d->type = type;
325 d->minimum_width = minimum_width;
326 d->maximum_width = maximum_width;
327 d->weight = weight;
328 d->align_percent = align_percent;
329 d->ellipsize_percent = ellipsize_percent;
330 memcpy_safe(d->data, data, data_size);
331
332 return d;
333}
334
335int table_add_cell_full(
336 Table *t,
337 TableCell **ret_cell,
338 TableDataType type,
339 const void *data,
340 size_t minimum_width,
341 size_t maximum_width,
342 unsigned weight,
343 unsigned align_percent,
344 unsigned ellipsize_percent) {
345
346 _cleanup_(table_data_unrefp)__attribute__((cleanup(table_data_unrefp))) TableData *d = NULL((void*)0);
347 TableData *p;
348
349 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 349
, __PRETTY_FUNCTION__); } while (0)
;
350 assert(type >= 0)do { if ((__builtin_expect(!!(!(type >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("type >= 0"), "../src/basic/format-table.c"
, 350, __PRETTY_FUNCTION__); } while (0)
;
351 assert(type < _TABLE_DATA_TYPE_MAX)do { if ((__builtin_expect(!!(!(type < _TABLE_DATA_TYPE_MAX
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("type < _TABLE_DATA_TYPE_MAX"
), "../src/basic/format-table.c", 351, __PRETTY_FUNCTION__); }
while (0)
;
352
353 /* Determine the cell adjacent to the current one, but one row up */
354 if (t->n_cells >= t->n_columns)
355 assert_se(p = t->data[t->n_cells - t->n_columns])do { if ((__builtin_expect(!!(!(p = t->data[t->n_cells -
t->n_columns])),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("p = t->data[t->n_cells - t->n_columns]"), "../src/basic/format-table.c"
, 355, __PRETTY_FUNCTION__); } while (0)
;
356 else
357 p = NULL((void*)0);
358
359 /* If formatting parameters are left unspecified, copy from the previous row */
360 if (minimum_width == (size_t) -1)
361 minimum_width = p ? p->minimum_width : 1;
362
363 if (weight == (unsigned) -1)
364 weight = p ? p->weight : DEFAULT_WEIGHT100;
365
366 if (align_percent == (unsigned) -1)
367 align_percent = p ? p->align_percent : 0;
368
369 if (ellipsize_percent == (unsigned) -1)
370 ellipsize_percent = p ? p->ellipsize_percent : 100;
371
372 assert(align_percent <= 100)do { if ((__builtin_expect(!!(!(align_percent <= 100)),0))
) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("align_percent <= 100"
), "../src/basic/format-table.c", 372, __PRETTY_FUNCTION__); }
while (0)
;
373 assert(ellipsize_percent <= 100)do { if ((__builtin_expect(!!(!(ellipsize_percent <= 100))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("ellipsize_percent <= 100"
), "../src/basic/format-table.c", 373, __PRETTY_FUNCTION__); }
while (0)
;
374
375 /* Small optimization: Pretty often adjacent cells in two subsequent lines have the same data and
376 * formatting. Let's see if we can reuse the cell data and ref it once more. */
377
378 if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent))
379 d = table_data_ref(p);
380 else {
381 d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent);
382 if (!d)
383 return -ENOMEM12;
384 }
385
386 if (!GREEDY_REALLOC(t->data, t->n_allocated, MAX(t->n_cells + 1, t->n_columns))greedy_realloc((void**) &(t->data), &(t->n_allocated
), (__extension__ ({ const typeof((t->n_cells + 1)) __unique_prefix_A50
= ((t->n_cells + 1)); const typeof((t->n_columns)) __unique_prefix_B51
= ((t->n_columns)); __unique_prefix_A50 > __unique_prefix_B51
? __unique_prefix_A50 : __unique_prefix_B51; })), sizeof((t->
data)[0]))
)
387 return -ENOMEM12;
388
389 if (ret_cell)
390 *ret_cell = TABLE_INDEX_TO_CELL(t->n_cells);
391
392 t->data[t->n_cells++] = TAKE_PTR(d)({ typeof(d) _ptr_ = (d); (d) = ((void*)0); _ptr_; });
393
394 return 0;
395}
396
397int table_dup_cell(Table *t, TableCell *cell) {
398 size_t i;
399
400 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 400
, __PRETTY_FUNCTION__); } while (0)
;
401
402 /* Add the data of the specified cell a second time as a new cell to the end. */
403
404 i = TABLE_CELL_TO_INDEX(cell);
405 if (i >= t->n_cells)
406 return -ENXIO6;
407
408 if (!GREEDY_REALLOC(t->data, t->n_allocated, MAX(t->n_cells + 1, t->n_columns))greedy_realloc((void**) &(t->data), &(t->n_allocated
), (__extension__ ({ const typeof((t->n_cells + 1)) __unique_prefix_A52
= ((t->n_cells + 1)); const typeof((t->n_columns)) __unique_prefix_B53
= ((t->n_columns)); __unique_prefix_A52 > __unique_prefix_B53
? __unique_prefix_A52 : __unique_prefix_B53; })), sizeof((t->
data)[0]))
)
409 return -ENOMEM12;
410
411 t->data[t->n_cells++] = table_data_ref(t->data[i]);
412 return 0;
413}
414
415static int table_dedup_cell(Table *t, TableCell *cell) {
416 _cleanup_free___attribute__((cleanup(freep))) char *curl = NULL((void*)0);
417 TableData *nd, *od;
418 size_t i;
419
420 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 420
, __PRETTY_FUNCTION__); } while (0)
;
421
422 /* Helper call that ensures the specified cell's data object has a ref count of 1, which we can use before
423 * changing a cell's formatting without effecting every other cell's formatting that shares the same data */
424
425 i = TABLE_CELL_TO_INDEX(cell);
426 if (i >= t->n_cells)
427 return -ENXIO6;
428
429 assert_se(od = t->data[i])do { if ((__builtin_expect(!!(!(od = t->data[i])),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("od = t->data[i]"), "../src/basic/format-table.c"
, 429, __PRETTY_FUNCTION__); } while (0)
;
430 if (od->n_ref == 1)
431 return 0;
432
433 assert(od->n_ref > 1)do { if ((__builtin_expect(!!(!(od->n_ref > 1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("od->n_ref > 1"), "../src/basic/format-table.c"
, 433, __PRETTY_FUNCTION__); } while (0)
;
434
435 if (od->url) {
436 curl = strdup(od->url);
437 if (!curl)
438 return -ENOMEM12;
439 }
440
441 nd = table_data_new(
442 od->type,
443 od->data,
444 od->minimum_width,
445 od->maximum_width,
446 od->weight,
447 od->align_percent,
448 od->ellipsize_percent);
449 if (!nd)
450 return -ENOMEM12;
451
452 nd->color = od->color;
453 nd->url = TAKE_PTR(curl)({ typeof(curl) _ptr_ = (curl); (curl) = ((void*)0); _ptr_; }
)
;
454 nd->uppercase = od->uppercase;
455
456 table_data_unref(od);
457 t->data[i] = nd;
458
459 assert(nd->n_ref == 1)do { if ((__builtin_expect(!!(!(nd->n_ref == 1)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("nd->n_ref == 1"), "../src/basic/format-table.c"
, 459, __PRETTY_FUNCTION__); } while (0)
;
460
461 return 1;
462}
463
464static TableData *table_get_data(Table *t, TableCell *cell) {
465 size_t i;
466
467 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 467
, __PRETTY_FUNCTION__); } while (0)
;
468 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 468
, __PRETTY_FUNCTION__); } while (0)
;
469
470 /* Get the data object of the specified cell, or NULL if it doesn't exist */
471
472 i = TABLE_CELL_TO_INDEX(cell);
473 if (i >= t->n_cells)
474 return NULL((void*)0);
475
476 assert(t->data[i])do { if ((__builtin_expect(!!(!(t->data[i])),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t->data[i]"), "../src/basic/format-table.c"
, 476, __PRETTY_FUNCTION__); } while (0)
;
477 assert(t->data[i]->n_ref > 0)do { if ((__builtin_expect(!!(!(t->data[i]->n_ref > 0
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t->data[i]->n_ref > 0"
), "../src/basic/format-table.c", 477, __PRETTY_FUNCTION__); }
while (0)
;
478
479 return t->data[i];
480}
481
482int table_set_minimum_width(Table *t, TableCell *cell, size_t minimum_width) {
483 int r;
484
485 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 485
, __PRETTY_FUNCTION__); } while (0)
;
486 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 486
, __PRETTY_FUNCTION__); } while (0)
;
487
488 if (minimum_width == (size_t) -1)
489 minimum_width = 1;
490
491 r = table_dedup_cell(t, cell);
492 if (r < 0)
493 return r;
494
495 table_get_data(t, cell)->minimum_width = minimum_width;
496 return 0;
497}
498
499int table_set_maximum_width(Table *t, TableCell *cell, size_t maximum_width) {
500 int r;
501
502 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 502
, __PRETTY_FUNCTION__); } while (0)
;
503 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 503
, __PRETTY_FUNCTION__); } while (0)
;
504
505 r = table_dedup_cell(t, cell);
506 if (r < 0)
507 return r;
508
509 table_get_data(t, cell)->maximum_width = maximum_width;
510 return 0;
511}
512
513int table_set_weight(Table *t, TableCell *cell, unsigned weight) {
514 int r;
515
516 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 516
, __PRETTY_FUNCTION__); } while (0)
;
517 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 517
, __PRETTY_FUNCTION__); } while (0)
;
518
519 if (weight == (unsigned) -1)
520 weight = DEFAULT_WEIGHT100;
521
522 r = table_dedup_cell(t, cell);
523 if (r < 0)
524 return r;
525
526 table_get_data(t, cell)->weight = weight;
527 return 0;
528}
529
530int table_set_align_percent(Table *t, TableCell *cell, unsigned percent) {
531 int r;
532
533 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 533
, __PRETTY_FUNCTION__); } while (0)
;
534 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 534
, __PRETTY_FUNCTION__); } while (0)
;
535
536 if (percent == (unsigned) -1)
537 percent = 0;
538
539 assert(percent <= 100)do { if ((__builtin_expect(!!(!(percent <= 100)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("percent <= 100"), "../src/basic/format-table.c"
, 539, __PRETTY_FUNCTION__); } while (0)
;
540
541 r = table_dedup_cell(t, cell);
542 if (r < 0)
543 return r;
544
545 table_get_data(t, cell)->align_percent = percent;
546 return 0;
547}
548
549int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent) {
550 int r;
551
552 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 552
, __PRETTY_FUNCTION__); } while (0)
;
553 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 553
, __PRETTY_FUNCTION__); } while (0)
;
554
555 if (percent == (unsigned) -1)
556 percent = 100;
557
558 assert(percent <= 100)do { if ((__builtin_expect(!!(!(percent <= 100)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("percent <= 100"), "../src/basic/format-table.c"
, 558, __PRETTY_FUNCTION__); } while (0)
;
559
560 r = table_dedup_cell(t, cell);
561 if (r < 0)
562 return r;
563
564 table_get_data(t, cell)->ellipsize_percent = percent;
565 return 0;
566}
567
568int table_set_color(Table *t, TableCell *cell, const char *color) {
569 int r;
570
571 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 571
, __PRETTY_FUNCTION__); } while (0)
;
572 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 572
, __PRETTY_FUNCTION__); } while (0)
;
573
574 r = table_dedup_cell(t, cell);
575 if (r < 0)
576 return r;
577
578 table_get_data(t, cell)->color = empty_to_null(color);
579 return 0;
580}
581
582int table_set_url(Table *t, TableCell *cell, const char *url) {
583 _cleanup_free___attribute__((cleanup(freep))) char *copy = NULL((void*)0);
584 int r;
585
586 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 586
, __PRETTY_FUNCTION__); } while (0)
;
1
Assuming 't' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
587 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 587
, __PRETTY_FUNCTION__); } while (0)
;
4
Assuming 'cell' is non-null
5
Taking false branch
6
Loop condition is false. Exiting loop
588
589 if (url) {
7
Assuming 'url' is non-null
8
Taking true branch
590 copy = strdup(url);
9
Memory is allocated
591 if (!copy)
10
Assuming 'copy' is non-null
11
Taking false branch
592 return -ENOMEM12;
593 }
594
595 r = table_dedup_cell(t, cell);
596 if (r
11.1
'r' is < 0
< 0)
12
Taking true branch
597 return r;
13
Potential leak of memory pointed to by 'copy'
598
599 return free_and_replace(table_get_data(t, cell)->url, copy)({ free(table_get_data(t, cell)->url); (table_get_data(t, cell
)->url) = (copy); (copy) = ((void*)0); 0; })
;
600}
601
602int table_set_uppercase(Table *t, TableCell *cell, bool_Bool b) {
603 TableData *d;
604 int r;
605
606 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 606
, __PRETTY_FUNCTION__); } while (0)
;
607 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 607
, __PRETTY_FUNCTION__); } while (0)
;
608
609 r = table_dedup_cell(t, cell);
610 if (r < 0)
611 return r;
612
613 assert_se(d = table_get_data(t, cell))do { if ((__builtin_expect(!!(!(d = table_get_data(t, cell)))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("d = table_get_data(t, cell)"
), "../src/basic/format-table.c", 613, __PRETTY_FUNCTION__); }
while (0)
;
614
615 if (d->uppercase == b)
616 return 0;
617
618 d->formatted = mfree(d->formatted);
619 d->uppercase = b;
620 return 1;
621}
622
623int table_update(Table *t, TableCell *cell, TableDataType type, const void *data) {
624 _cleanup_free___attribute__((cleanup(freep))) char *curl = NULL((void*)0);
625 TableData *nd, *od;
626 size_t i;
627
628 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 628
, __PRETTY_FUNCTION__); } while (0)
;
629 assert(cell)do { if ((__builtin_expect(!!(!(cell)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cell"), "../src/basic/format-table.c", 629
, __PRETTY_FUNCTION__); } while (0)
;
630
631 i = TABLE_CELL_TO_INDEX(cell);
632 if (i >= t->n_cells)
633 return -ENXIO6;
634
635 assert_se(od = t->data[i])do { if ((__builtin_expect(!!(!(od = t->data[i])),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("od = t->data[i]"), "../src/basic/format-table.c"
, 635, __PRETTY_FUNCTION__); } while (0)
;
636
637 if (od->url) {
638 curl = strdup(od->url);
639 if (!curl)
640 return -ENOMEM12;
641 }
642
643 nd = table_data_new(
644 type,
645 data,
646 od->minimum_width,
647 od->maximum_width,
648 od->weight,
649 od->align_percent,
650 od->ellipsize_percent);
651 if (!nd)
652 return -ENOMEM12;
653
654 nd->color = od->color;
655 nd->url = TAKE_PTR(curl)({ typeof(curl) _ptr_ = (curl); (curl) = ((void*)0); _ptr_; }
)
;
656 nd->uppercase = od->uppercase;
657
658 table_data_unref(od);
659 t->data[i] = nd;
660
661 return 0;
662}
663
664int table_add_many_internal(Table *t, TableDataType first_type, ...) {
665 TableDataType type;
666 va_list ap;
667 int r;
668
669 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 669
, __PRETTY_FUNCTION__); } while (0)
;
670 assert(first_type >= 0)do { if ((__builtin_expect(!!(!(first_type >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("first_type >= 0"), "../src/basic/format-table.c"
, 670, __PRETTY_FUNCTION__); } while (0)
;
671 assert(first_type < _TABLE_DATA_TYPE_MAX)do { if ((__builtin_expect(!!(!(first_type < _TABLE_DATA_TYPE_MAX
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("first_type < _TABLE_DATA_TYPE_MAX"
), "../src/basic/format-table.c", 671, __PRETTY_FUNCTION__); }
while (0)
;
672
673 type = first_type;
674
675 va_start(ap, first_type)__builtin_va_start(ap, first_type);
676 for (;;) {
677 const void *data;
678 union {
679 uint64_t size;
680 usec_t usec;
681 uint32_t uint32;
682 uint64_t uint64;
683 int percent;
684 bool_Bool b;
685 } buffer;
686
687 switch (type) {
688
689 case TABLE_EMPTY:
690 data = NULL((void*)0);
691 break;
692
693 case TABLE_STRING:
694 data = va_arg(ap, const char *)__builtin_va_arg(ap, const char *);
695 break;
696
697 case TABLE_BOOLEAN:
698 buffer.b = va_arg(ap, int)__builtin_va_arg(ap, int);
699 data = &buffer.b;
700 break;
701
702 case TABLE_TIMESTAMP:
703 case TABLE_TIMESPAN:
704 buffer.usec = va_arg(ap, usec_t)__builtin_va_arg(ap, usec_t);
705 data = &buffer.usec;
706 break;
707
708 case TABLE_SIZE:
709 buffer.size = va_arg(ap, uint64_t)__builtin_va_arg(ap, uint64_t);
710 data = &buffer.size;
711 break;
712
713 case TABLE_UINT32:
714 buffer.uint32 = va_arg(ap, uint32_t)__builtin_va_arg(ap, uint32_t);
715 data = &buffer.uint32;
716 break;
717
718 case TABLE_UINT64:
719 buffer.uint64 = va_arg(ap, uint64_t)__builtin_va_arg(ap, uint64_t);
720 data = &buffer.uint64;
721 break;
722
723 case TABLE_PERCENT:
724 buffer.percent = va_arg(ap, int)__builtin_va_arg(ap, int);
725 data = &buffer.percent;
726 break;
727
728 case _TABLE_DATA_TYPE_MAX:
729 /* Used as end marker */
730 va_end(ap)__builtin_va_end(ap);
731 return 0;
732
733 default:
734 assert_not_reached("Uh? Unexpected data type.")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, (
"Uh? Unexpected data type."), "../src/basic/format-table.c", 734
, __PRETTY_FUNCTION__); } while (0)
;
735 }
736
737 r = table_add_cell(t, NULL((void*)0), type, data);
738 if (r < 0) {
739 va_end(ap)__builtin_va_end(ap);
740 return r;
741 }
742
743 type = va_arg(ap, TableDataType)__builtin_va_arg(ap, TableDataType);
744 }
745}
746
747void table_set_header(Table *t, bool_Bool b) {
748 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 748
, __PRETTY_FUNCTION__); } while (0)
;
749
750 t->header = b;
751}
752
753void table_set_width(Table *t, size_t width) {
754 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 754
, __PRETTY_FUNCTION__); } while (0)
;
755
756 t->width = width;
757}
758
759int table_set_display(Table *t, size_t first_column, ...) {
760 size_t allocated, column;
761 va_list ap;
762
763 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 763
, __PRETTY_FUNCTION__); } while (0)
;
764
765 allocated = t->n_display_map;
766 column = first_column;
767
768 va_start(ap, first_column)__builtin_va_start(ap, first_column);
769 for (;;) {
770 assert(column < t->n_columns)do { if ((__builtin_expect(!!(!(column < t->n_columns))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("column < t->n_columns"
), "../src/basic/format-table.c", 770, __PRETTY_FUNCTION__); }
while (0)
;
771
772 if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, t->n_display_map+1))greedy_realloc((void**) &(t->display_map), &(allocated
), (__extension__ ({ const typeof((t->n_columns)) __unique_prefix_A54
= ((t->n_columns)); const typeof((t->n_display_map+1))
__unique_prefix_B55 = ((t->n_display_map+1)); __unique_prefix_A54
> __unique_prefix_B55 ? __unique_prefix_A54 : __unique_prefix_B55
; })), sizeof((t->display_map)[0]))
) {
773 va_end(ap)__builtin_va_end(ap);
774 return -ENOMEM12;
775 }
776
777 t->display_map[t->n_display_map++] = column;
778
779 column = va_arg(ap, size_t)__builtin_va_arg(ap, size_t);
780 if (column == (size_t) -1)
781 break;
782
783 }
784 va_end(ap)__builtin_va_end(ap);
785
786 return 0;
787}
788
789int table_set_sort(Table *t, size_t first_column, ...) {
790 size_t allocated, column;
791 va_list ap;
792
793 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 793
, __PRETTY_FUNCTION__); } while (0)
;
794
795 allocated = t->n_sort_map;
796 column = first_column;
797
798 va_start(ap, first_column)__builtin_va_start(ap, first_column);
799 for (;;) {
800 assert(column < t->n_columns)do { if ((__builtin_expect(!!(!(column < t->n_columns))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("column < t->n_columns"
), "../src/basic/format-table.c", 800, __PRETTY_FUNCTION__); }
while (0)
;
801
802 if (!GREEDY_REALLOC(t->sort_map, allocated, MAX(t->n_columns, t->n_sort_map+1))greedy_realloc((void**) &(t->sort_map), &(allocated
), (__extension__ ({ const typeof((t->n_columns)) __unique_prefix_A56
= ((t->n_columns)); const typeof((t->n_sort_map+1)) __unique_prefix_B57
= ((t->n_sort_map+1)); __unique_prefix_A56 > __unique_prefix_B57
? __unique_prefix_A56 : __unique_prefix_B57; })), sizeof((t->
sort_map)[0]))
) {
803 va_end(ap)__builtin_va_end(ap);
804 return -ENOMEM12;
805 }
806
807 t->sort_map[t->n_sort_map++] = column;
808
809 column = va_arg(ap, size_t)__builtin_va_arg(ap, size_t);
810 if (column == (size_t) -1)
811 break;
812 }
813 va_end(ap)__builtin_va_end(ap);
814
815 return 0;
816}
817
818static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t index_b) {
819 assert(a)do { if ((__builtin_expect(!!(!(a)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("a"), "../src/basic/format-table.c", 819
, __PRETTY_FUNCTION__); } while (0)
;
820 assert(b)do { if ((__builtin_expect(!!(!(b)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("b"), "../src/basic/format-table.c", 820
, __PRETTY_FUNCTION__); } while (0)
;
821
822 if (a->type == b->type) {
823
824 /* We only define ordering for cells of the same data type. If cells with different data types are
825 * compared we follow the order the cells were originally added in */
826
827 switch (a->type) {
828
829 case TABLE_STRING:
830 return strcmp(a->string, b->string);
831
832 case TABLE_BOOLEAN:
833 if (!a->boolean && b->boolean)
834 return -1;
835 if (a->boolean && !b->boolean)
836 return 1;
837 return 0;
838
839 case TABLE_TIMESTAMP:
840 if (a->timestamp < b->timestamp)
841 return -1;
842 if (a->timestamp > b->timestamp)
843 return 1;
844 return 0;
845
846 case TABLE_TIMESPAN:
847 if (a->timespan < b->timespan)
848 return -1;
849 if (a->timespan > b->timespan)
850 return 1;
851 return 0;
852
853 case TABLE_SIZE:
854 if (a->size < b->size)
855 return -1;
856 if (a->size > b->size)
857 return 1;
858 return 0;
859
860 case TABLE_UINT32:
861 if (a->uint32 < b->uint32)
862 return -1;
863 if (a->uint32 > b->uint32)
864 return 1;
865 return 0;
866
867 case TABLE_UINT64:
868 return CMP(a->uint64, b->uint64)({ const typeof((a->uint64)) __unique_prefix_A58 = ((a->
uint64)); const typeof((b->uint64)) __unique_prefix_B59 = (
(b->uint64)); __unique_prefix_A58 < __unique_prefix_B59
? -1 : __unique_prefix_A58 > __unique_prefix_B59 ? 1 : 0;
})
;
869
870 case TABLE_PERCENT:
871 return CMP(a->percent, b->percent)({ const typeof((a->percent)) __unique_prefix_A60 = ((a->
percent)); const typeof((b->percent)) __unique_prefix_B61 =
((b->percent)); __unique_prefix_A60 < __unique_prefix_B61
? -1 : __unique_prefix_A60 > __unique_prefix_B61 ? 1 : 0;
})
;
872
873 default:
874 ;
875 }
876 }
877
878 /* Generic fallback using the orginal order in which the cells where added. */
879 if (index_a < index_b)
880 return -1;
881 if (index_a > index_b)
882 return 1;
883
884 return 0;
885}
886
887static int table_data_compare(const void *x, const void *y, void *userdata) {
888 const size_t *a = x, *b = y;
889 Table *t = userdata;
890 size_t i;
891 int r;
892
893 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 893
, __PRETTY_FUNCTION__); } while (0)
;
894 assert(t->sort_map)do { if ((__builtin_expect(!!(!(t->sort_map)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t->sort_map"), "../src/basic/format-table.c"
, 894, __PRETTY_FUNCTION__); } while (0)
;
895
896 /* Make sure the header stays at the beginning */
897 if (*a < t->n_columns && *b < t->n_columns)
898 return 0;
899 if (*a < t->n_columns)
900 return -1;
901 if (*b < t->n_columns)
902 return 1;
903
904 /* Order other lines by the sorting map */
905 for (i = 0; i < t->n_sort_map; i++) {
906 TableData *d, *dd;
907
908 d = t->data[*a + t->sort_map[i]];
909 dd = t->data[*b + t->sort_map[i]];
910
911 r = cell_data_compare(d, *a, dd, *b);
912 if (r != 0)
913 return t->reverse_map && t->reverse_map[t->sort_map[i]] ? -r : r;
914 }
915
916 /* Order identical lines by the order there were originally added in */
917 if (*a < *b)
918 return -1;
919 if (*a > *b)
920 return 1;
921
922 return 0;
923}
924
925static const char *table_data_format(TableData *d) {
926 assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("d"), "../src/basic/format-table.c", 926
, __PRETTY_FUNCTION__); } while (0)
;
927
928 if (d->formatted)
929 return d->formatted;
930
931 switch (d->type) {
932 case TABLE_EMPTY:
933 return "";
934
935 case TABLE_STRING:
936 if (d->uppercase) {
937 char *p, *q;
938
939 d->formatted = new(char, strlen(d->string) + 1)((char*) malloc_multiply(sizeof(char), (strlen(d->string) +
1)))
;
940 if (!d->formatted)
941 return NULL((void*)0);
942
943 for (p = d->string, q = d->formatted; *p; p++, q++)
944 *q = (char) toupper((unsigned char) *p);
945 *q = 0;
946
947 return d->formatted;
948 }
949
950 return d->string;
951
952 case TABLE_BOOLEAN:
953 return yes_no(d->boolean);
954
955 case TABLE_TIMESTAMP: {
956 _cleanup_free___attribute__((cleanup(freep))) char *p;
957
958 p = new(char, FORMAT_TIMESTAMP_MAX)((char*) malloc_multiply(sizeof(char), ((3+1+10+1+8+1+6+1+6+1
))))
;
959 if (!p)
960 return NULL((void*)0);
961
962 if (!format_timestamp(p, FORMAT_TIMESTAMP_MAX(3+1+10+1+8+1+6+1+6+1), d->timestamp))
963 return "n/a";
964
965 d->formatted = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
966 break;
967 }
968
969 case TABLE_TIMESPAN: {
970 _cleanup_free___attribute__((cleanup(freep))) char *p;
971
972 p = new(char, FORMAT_TIMESPAN_MAX)((char*) malloc_multiply(sizeof(char), (64)));
973 if (!p)
974 return NULL((void*)0);
975
976 if (!format_timespan(p, FORMAT_TIMESPAN_MAX64, d->timespan, 0))
977 return "n/a";
978
979 d->formatted = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
980 break;
981 }
982
983 case TABLE_SIZE: {
984 _cleanup_free___attribute__((cleanup(freep))) char *p;
985
986 p = new(char, FORMAT_BYTES_MAX)((char*) malloc_multiply(sizeof(char), (8)));
987 if (!p)
988 return NULL((void*)0);
989
990 if (!format_bytes(p, FORMAT_BYTES_MAX8, d->size))
991 return "n/a";
992
993 d->formatted = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
994 break;
995 }
996
997 case TABLE_UINT32: {
998 _cleanup_free___attribute__((cleanup(freep))) char *p;
999
1000 p = new(char, DECIMAL_STR_WIDTH(d->uint32) + 1)((char*) malloc_multiply(sizeof(char), (({ typeof(d->uint32
) _x_ = (d->uint32); unsigned ans = 1; while ((_x_ /= 10) !=
0) ans++; ans; }) + 1)))
;
1001 if (!p)
1002 return NULL((void*)0);
1003
1004 sprintf(p, "%" PRIu32"u", d->uint32);
1005 d->formatted = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
1006 break;
1007 }
1008
1009 case TABLE_UINT64: {
1010 _cleanup_free___attribute__((cleanup(freep))) char *p;
1011
1012 p = new(char, DECIMAL_STR_WIDTH(d->uint64) + 1)((char*) malloc_multiply(sizeof(char), (({ typeof(d->uint64
) _x_ = (d->uint64); unsigned ans = 1; while ((_x_ /= 10) !=
0) ans++; ans; }) + 1)))
;
1013 if (!p)
1014 return NULL((void*)0);
1015
1016 sprintf(p, "%" PRIu64"l" "u", d->uint64);
1017 d->formatted = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
1018 break;
1019 }
1020
1021 case TABLE_PERCENT: {
1022 _cleanup_free___attribute__((cleanup(freep))) char *p;
1023
1024 p = new(char, DECIMAL_STR_WIDTH(d->percent) + 2)((char*) malloc_multiply(sizeof(char), (({ typeof(d->percent
) _x_ = (d->percent); unsigned ans = 1; while ((_x_ /= 10)
!= 0) ans++; ans; }) + 2)))
;
1025 if (!p)
1026 return NULL((void*)0);
1027
1028 sprintf(p, "%i%%" , d->percent);
1029 d->formatted = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
1030 break;
1031 }
1032
1033 default:
1034 assert_not_reached("Unexpected type?")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, (
"Unexpected type?"), "../src/basic/format-table.c", 1034, __PRETTY_FUNCTION__
); } while (0)
;
1035 }
1036
1037 return d->formatted;
1038}
1039
1040static int table_data_requested_width(TableData *d, size_t *ret) {
1041 const char *t;
1042 size_t l;
1043
1044 t = table_data_format(d);
1045 if (!t)
1046 return -ENOMEM12;
1047
1048 l = utf8_console_width(t);
1049 if (l == (size_t) -1)
1050 return -EINVAL22;
1051
1052 if (d->maximum_width != (size_t) -1 && l > d->maximum_width)
1053 l = d->maximum_width;
1054
1055 if (l < d->minimum_width)
1056 l = d->minimum_width;
1057
1058 *ret = l;
1059 return 0;
1060}
1061
1062static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) {
1063 size_t w = 0, space, lspace, old_length, clickable_length;
1064 _cleanup_free___attribute__((cleanup(freep))) char *clickable = NULL((void*)0);
1065 const char *p;
1066 char *ret;
1067 size_t i;
1068 int r;
1069
1070 /* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
1071
1072 assert(str)do { if ((__builtin_expect(!!(!(str)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("str"), "../src/basic/format-table.c", 1072
, __PRETTY_FUNCTION__); } while (0)
;
1073 assert(percent <= 100)do { if ((__builtin_expect(!!(!(percent <= 100)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("percent <= 100"), "../src/basic/format-table.c"
, 1073, __PRETTY_FUNCTION__); } while (0)
;
1074
1075 old_length = strlen(str);
1076
1077 if (url) {
1078 r = terminal_urlify(url, str, &clickable);
1079 if (r < 0)
1080 return NULL((void*)0);
1081
1082 clickable_length = strlen(clickable);
1083 } else
1084 clickable_length = old_length;
1085
1086 /* Determine current width on screen */
1087 p = str;
1088 while (p < str + old_length) {
1089 char32_t c;
1090
1091 if (utf8_encoded_to_unichar(p, &c) < 0) {
1092 p++, w++; /* count invalid chars as 1 */
1093 continue;
1094 }
1095
1096 p = utf8_next_char(p)(char *)((p) + utf8_skip_data[*(const unsigned char *)(p)]);
1097 w += unichar_iswide(c) ? 2 : 1;
1098 }
1099
1100 /* Already wider than the target, if so, don't do anything */
1101 if (w >= new_length)
1102 return clickable ? TAKE_PTR(clickable)({ typeof(clickable) _ptr_ = (clickable); (clickable) = ((void
*)0); _ptr_; })
: strdup(str);
1103
1104 /* How much spaces shall we add? An how much on the left side? */
1105 space = new_length - w;
1106 lspace = space * percent / 100U;
1107
1108 ret = new(char, space + clickable_length + 1)((char*) malloc_multiply(sizeof(char), (space + clickable_length
+ 1)))
;
1109 if (!ret)
1110 return NULL((void*)0);
1111
1112 for (i = 0; i < lspace; i++)
1113 ret[i] = ' ';
1114 memcpy(ret + lspace, clickable ?: str, clickable_length);
1115 for (i = lspace + clickable_length; i < space + clickable_length; i++)
1116 ret[i] = ' ';
1117
1118 ret[space + clickable_length] = 0;
1119 return ret;
1120}
1121
1122int table_print(Table *t, FILE *f) {
1123 size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
1124 i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
1125 *width;
1126 _cleanup_free___attribute__((cleanup(freep))) size_t *sorted = NULL((void*)0);
1127 uint64_t *column_weight, weight_sum;
1128 int r;
1129
1130 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 1130
, __PRETTY_FUNCTION__); } while (0)
;
1131
1132 if (!f)
1133 f = stdoutstdout;
1134
1135 /* Ensure we have no incomplete rows */
1136 assert(t->n_cells % t->n_columns == 0)do { if ((__builtin_expect(!!(!(t->n_cells % t->n_columns
== 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t->n_cells % t->n_columns == 0"
), "../src/basic/format-table.c", 1136, __PRETTY_FUNCTION__);
} while (0)
;
1137
1138 n_rows = t->n_cells / t->n_columns;
1139 assert(n_rows > 0)do { if ((__builtin_expect(!!(!(n_rows > 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("n_rows > 0"), "../src/basic/format-table.c"
, 1139, __PRETTY_FUNCTION__); } while (0)
; /* at least the header row must be complete */
1140
1141 if (t->sort_map) {
1142 /* If sorting is requested, let's calculate an index table we use to lookup the actual index to display with. */
1143
1144 sorted = new(size_t, n_rows)((size_t*) malloc_multiply(sizeof(size_t), (n_rows)));
1145 if (!sorted)
1146 return -ENOMEM12;
1147
1148 for (i = 0; i < n_rows; i++)
1149 sorted[i] = i * t->n_columns;
1150
1151 qsort_r_safe(sorted, n_rows, sizeof(size_t), table_data_compare, t);
1152 }
1153
1154 if (t->display_map)
1155 display_columns = t->n_display_map;
1156 else
1157 display_columns = t->n_columns;
1158
1159 assert(display_columns > 0)do { if ((__builtin_expect(!!(!(display_columns > 0)),0)))
log_assert_failed_realm(LOG_REALM_SYSTEMD, ("display_columns > 0"
), "../src/basic/format-table.c", 1159, __PRETTY_FUNCTION__);
} while (0)
;
1160
1161 minimum_width = newa(size_t, display_columns)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(size_t), display_columns))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(size_t), display_columns)"
), "../src/basic/format-table.c", 1161, __PRETTY_FUNCTION__);
} while (0); (size_t*) __builtin_alloca (sizeof(size_t)*(display_columns
)); })
;
1162 maximum_width = newa(size_t, display_columns)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(size_t), display_columns))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(size_t), display_columns)"
), "../src/basic/format-table.c", 1162, __PRETTY_FUNCTION__);
} while (0); (size_t*) __builtin_alloca (sizeof(size_t)*(display_columns
)); })
;
1163 requested_width = newa(size_t, display_columns)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(size_t), display_columns))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(size_t), display_columns)"
), "../src/basic/format-table.c", 1163, __PRETTY_FUNCTION__);
} while (0); (size_t*) __builtin_alloca (sizeof(size_t)*(display_columns
)); })
;
1164 width = newa(size_t, display_columns)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(size_t), display_columns))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(size_t), display_columns)"
), "../src/basic/format-table.c", 1164, __PRETTY_FUNCTION__);
} while (0); (size_t*) __builtin_alloca (sizeof(size_t)*(display_columns
)); })
;
1165 column_weight = newa0(uint64_t, display_columns)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(uint64_t), display_columns))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(uint64_t), display_columns)"
), "../src/basic/format-table.c", 1165, __PRETTY_FUNCTION__);
} while (0); (uint64_t*) ({ char *_new_; size_t _len_ = sizeof
(uint64_t)*(display_columns); _new_ = __builtin_alloca (_len_
); (void *) memset(_new_, 0, _len_); }); })
;
1166
1167 for (j = 0; j < display_columns; j++) {
1168 minimum_width[j] = 1;
1169 maximum_width[j] = (size_t) -1;
1170 requested_width[j] = (size_t) -1;
1171 }
1172
1173 /* First pass: determine column sizes */
1174 for (i = t->header ? 0 : 1; i < n_rows; i++) {
1175 TableData **row;
1176
1177 /* Note that we don't care about ordering at this time, as we just want to determine column sizes,
1178 * hence we don't care for sorted[] during the first pass. */
1179 row = t->data + i * t->n_columns;
1180
1181 for (j = 0; j < display_columns; j++) {
1182 TableData *d;
1183 size_t req;
1184
1185 assert_se(d = row[t->display_map ? t->display_map[j] : j])do { if ((__builtin_expect(!!(!(d = row[t->display_map ? t
->display_map[j] : j])),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("d = row[t->display_map ? t->display_map[j] : j]"), "../src/basic/format-table.c"
, 1185, __PRETTY_FUNCTION__); } while (0)
;
1186
1187 r = table_data_requested_width(d, &req);
1188 if (r < 0)
1189 return r;
1190
1191 /* Determine the biggest width that any cell in this column would like to have */
1192 if (requested_width[j] == (size_t) -1 ||
1193 requested_width[j] < req)
1194 requested_width[j] = req;
1195
1196 /* Determine the minimum width any cell in this column needs */
1197 if (minimum_width[j] < d->minimum_width)
1198 minimum_width[j] = d->minimum_width;
1199
1200 /* Determine the maximum width any cell in this column needs */
1201 if (d->maximum_width != (size_t) -1 &&
1202 (maximum_width[j] == (size_t) -1 ||
1203 maximum_width[j] > d->maximum_width))
1204 maximum_width[j] = d->maximum_width;
1205
1206 /* Determine the full columns weight */
1207 column_weight[j] += d->weight;
1208 }
1209 }
1210
1211 /* One space between each column */
1212 table_requested_width = table_minimum_width = table_maximum_width = display_columns - 1;
1213
1214 /* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
1215 weight_sum = 0;
1216 for (j = 0; j < display_columns; j++) {
1217 weight_sum += column_weight[j];
1218
1219 table_minimum_width += minimum_width[j];
1220
1221 if (maximum_width[j] == (size_t) -1)
1222 table_maximum_width = (size_t) -1;
1223 else
1224 table_maximum_width += maximum_width[j];
1225
1226 table_requested_width += requested_width[j];
1227 }
1228
1229 /* Calculate effective table width */
1230 if (t->width == (size_t) -1)
1231 table_effective_width = pager_have() ? table_requested_width : MIN(table_requested_width, columns())__extension__ ({ const typeof((table_requested_width)) __unique_prefix_A62
= ((table_requested_width)); const typeof((columns())) __unique_prefix_B63
= ((columns())); __unique_prefix_A62 < __unique_prefix_B63
? __unique_prefix_A62 : __unique_prefix_B63; })
;
1232 else
1233 table_effective_width = t->width;
1234
1235 if (table_maximum_width != (size_t) -1 && table_effective_width > table_maximum_width)
1236 table_effective_width = table_maximum_width;
1237
1238 if (table_effective_width < table_minimum_width)
1239 table_effective_width = table_minimum_width;
1240
1241 if (table_effective_width >= table_requested_width) {
1242 size_t extra;
1243
1244 /* We have extra room, let's distribute it among columns according to their weights. We first provide
1245 * each column with what it asked for and the distribute the rest. */
1246
1247 extra = table_effective_width - table_requested_width;
1248
1249 for (j = 0; j < display_columns; j++) {
1250 size_t delta;
1251
1252 if (weight_sum == 0)
1253 width[j] = requested_width[j] + extra / (display_columns - j); /* Avoid division by zero */
1254 else
1255 width[j] = requested_width[j] + (extra * column_weight[j]) / weight_sum;
1256
1257 if (maximum_width[j] != (size_t) -1 && width[j] > maximum_width[j])
1258 width[j] = maximum_width[j];
1259
1260 if (width[j] < minimum_width[j])
1261 width[j] = minimum_width[j];
1262
1263 assert(width[j] >= requested_width[j])do { if ((__builtin_expect(!!(!(width[j] >= requested_width
[j])),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("width[j] >= requested_width[j]"
), "../src/basic/format-table.c", 1263, __PRETTY_FUNCTION__);
} while (0)
;
1264 delta = width[j] - requested_width[j];
1265
1266 /* Subtract what we just added from the rest */
1267 if (extra > delta)
1268 extra -= delta;
1269 else
1270 extra = 0;
1271
1272 assert(weight_sum >= column_weight[j])do { if ((__builtin_expect(!!(!(weight_sum >= column_weight
[j])),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("weight_sum >= column_weight[j]"
), "../src/basic/format-table.c", 1272, __PRETTY_FUNCTION__);
} while (0)
;
1273 weight_sum -= column_weight[j];
1274 }
1275
1276 } else {
1277 /* We need to compress the table, columns can't get what they asked for. We first provide each column
1278 * with the minimum they need, and then distribute anything left. */
1279 bool_Bool finalize = false0;
1280 size_t extra;
1281
1282 extra = table_effective_width - table_minimum_width;
1283
1284 for (j = 0; j < display_columns; j++)
1285 width[j] = (size_t) -1;
1286
1287 for (;;) {
1288 bool_Bool restart = false0;
1289
1290 for (j = 0; j < display_columns; j++) {
1291 size_t delta, w;
1292
1293 /* Did this column already get something assigned? If so, let's skip to the next */
1294 if (width[j] != (size_t) -1)
1295 continue;
1296
1297 if (weight_sum == 0)
1298 w = minimum_width[j] + extra / (display_columns - j); /* avoid division by zero */
1299 else
1300 w = minimum_width[j] + (extra * column_weight[j]) / weight_sum;
1301
1302 if (w >= requested_width[j]) {
1303 /* Never give more than requested. If we hit a column like this, there's more
1304 * space to allocate to other columns which means we need to restart the
1305 * iteration. However, if we hit a column like this, let's assign it the space
1306 * it wanted for good early.*/
1307
1308 w = requested_width[j];
1309 restart = true1;
1310
1311 } else if (!finalize)
1312 continue;
1313
1314 width[j] = w;
1315
1316 assert(w >= minimum_width[j])do { if ((__builtin_expect(!!(!(w >= minimum_width[j])),0)
)) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("w >= minimum_width[j]"
), "../src/basic/format-table.c", 1316, __PRETTY_FUNCTION__);
} while (0)
;
1317 delta = w - minimum_width[j];
1318
1319 assert(delta <= extra)do { if ((__builtin_expect(!!(!(delta <= extra)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("delta <= extra"), "../src/basic/format-table.c"
, 1319, __PRETTY_FUNCTION__); } while (0)
;
1320 extra -= delta;
1321
1322 assert(weight_sum >= column_weight[j])do { if ((__builtin_expect(!!(!(weight_sum >= column_weight
[j])),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("weight_sum >= column_weight[j]"
), "../src/basic/format-table.c", 1322, __PRETTY_FUNCTION__);
} while (0)
;
1323 weight_sum -= column_weight[j];
1324
1325 if (restart)
1326 break;
1327 }
1328
1329 if (finalize) {
1330 assert(!restart)do { if ((__builtin_expect(!!(!(!restart)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("!restart"), "../src/basic/format-table.c"
, 1330, __PRETTY_FUNCTION__); } while (0)
;
1331 break;
1332 }
1333
1334 if (!restart)
1335 finalize = true1;
1336 }
1337 }
1338
1339 /* Second pass: show output */
1340 for (i = t->header ? 0 : 1; i < n_rows; i++) {
1341 TableData **row;
1342
1343 if (sorted)
1344 row = t->data + sorted[i];
1345 else
1346 row = t->data + i * t->n_columns;
1347
1348 for (j = 0; j < display_columns; j++) {
1349 _cleanup_free___attribute__((cleanup(freep))) char *buffer = NULL((void*)0);
1350 const char *field;
1351 TableData *d;
1352 size_t l;
1353
1354 assert_se(d = row[t->display_map ? t->display_map[j] : j])do { if ((__builtin_expect(!!(!(d = row[t->display_map ? t
->display_map[j] : j])),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("d = row[t->display_map ? t->display_map[j] : j]"), "../src/basic/format-table.c"
, 1354, __PRETTY_FUNCTION__); } while (0)
;
1355
1356 field = table_data_format(d);
1357 if (!field)
1358 return -ENOMEM12;
1359
1360 l = utf8_console_width(field);
1361 if (l > width[j]) {
1362 /* Field is wider than allocated space. Let's ellipsize */
1363
1364 buffer = ellipsize(field, width[j], d->ellipsize_percent);
1365 if (!buffer)
1366 return -ENOMEM12;
1367
1368 field = buffer;
1369
1370 } else if (l < width[j]) {
1371 /* Field is shorter than allocated space. Let's align with spaces */
1372
1373 buffer = align_string_mem(field, d->url, width[j], d->align_percent);
1374 if (!buffer)
1375 return -ENOMEM12;
1376
1377 field = buffer;
1378 }
1379
1380 if (l >= width[j] && d->url) {
1381 _cleanup_free___attribute__((cleanup(freep))) char *clickable = NULL((void*)0);
1382
1383 r = terminal_urlify(d->url, field, &clickable);
1384 if (r < 0)
1385 return r;
1386
1387 free_and_replace(buffer, clickable)({ free(buffer); (buffer) = (clickable); (clickable) = ((void
*)0); 0; })
;
1388 field = buffer;
1389 }
1390
1391 if (row == t->data) /* underline header line fully, including the column separator */
1392 fputs(ansi_underline(), f);
1393
1394 if (j > 0)
1395 fputc(' ', f); /* column separator */
1396
1397 if (d->color && colors_enabled()) {
1398 if (row == t->data) /* first undo header underliner */
1399 fputs(ANSI_NORMAL"\x1B[0m", f);
1400
1401 fputs(d->color, f);
1402 }
1403
1404 fputs(field, f);
1405
1406 if (colors_enabled() && (d->color || row == t->data))
1407 fputs(ANSI_NORMAL"\x1B[0m", f);
1408 }
1409
1410 fputc('\n', f);
1411 }
1412
1413 return fflush_and_check(f);
1414}
1415
1416int table_format(Table *t, char **ret) {
1417 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
1418 char *buf = NULL((void*)0);
1419 size_t sz = 0;
1420 int r;
1421
1422 f = open_memstream(&buf, &sz);
1423 if (!f)
1424 return -ENOMEM12;
1425
1426 (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER);
1427
1428 r = table_print(t, f);
1429 if (r < 0)
1430 return r;
1431
1432 f = safe_fclose(f);
1433
1434 *ret = buf;
1435
1436 return 0;
1437}
1438
1439size_t table_get_rows(Table *t) {
1440 if (!t)
1441 return 0;
1442
1443 assert(t->n_columns > 0)do { if ((__builtin_expect(!!(!(t->n_columns > 0)),0)))
log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t->n_columns > 0"
), "../src/basic/format-table.c", 1443, __PRETTY_FUNCTION__);
} while (0)
;
1444 return t->n_cells / t->n_columns;
1445}
1446
1447size_t table_get_columns(Table *t) {
1448 if (!t)
1449 return 0;
1450
1451 assert(t->n_columns > 0)do { if ((__builtin_expect(!!(!(t->n_columns > 0)),0)))
log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t->n_columns > 0"
), "../src/basic/format-table.c", 1451, __PRETTY_FUNCTION__);
} while (0)
;
1452 return t->n_columns;
1453}
1454
1455int table_set_reverse(Table *t, size_t column, bool_Bool b) {
1456 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 1456
, __PRETTY_FUNCTION__); } while (0)
;
1457 assert(column < t->n_columns)do { if ((__builtin_expect(!!(!(column < t->n_columns))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("column < t->n_columns"
), "../src/basic/format-table.c", 1457, __PRETTY_FUNCTION__);
} while (0)
;
1458
1459 if (!t->reverse_map) {
1460 if (!b)
1461 return 0;
1462
1463 t->reverse_map = new0(bool, t->n_columns)((_Bool*) calloc((t->n_columns), sizeof(_Bool)));
1464 if (!t->reverse_map)
1465 return -ENOMEM12;
1466 }
1467
1468 t->reverse_map[column] = b;
1469 return 0;
1470}
1471
1472TableCell *table_get_cell(Table *t, size_t row, size_t column) {
1473 size_t i;
1474
1475 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 1475
, __PRETTY_FUNCTION__); } while (0)
;
1476
1477 if (column >= t->n_columns)
1478 return NULL((void*)0);
1479
1480 i = row * t->n_columns + column;
1481 if (i >= t->n_cells)
1482 return NULL((void*)0);
1483
1484 return TABLE_INDEX_TO_CELL(i);
1485}
1486
1487const void *table_get(Table *t, TableCell *cell) {
1488 TableData *d;
1489
1490 assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("t"), "../src/basic/format-table.c", 1490
, __PRETTY_FUNCTION__); } while (0)
;
1491
1492 d = table_get_data(t, cell);
1493 if (!d)
1494 return NULL((void*)0);
1495
1496 return d->data;
1497}
1498
1499const void* table_get_at(Table *t, size_t row, size_t column) {
1500 TableCell *cell;
1501
1502 cell = table_get_cell(t, row, column);
1503 if (!cell)
1504 return NULL((void*)0);
1505
1506 return table_get(t, cell);
1507}