1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-27 18:04:05 +03:00

format-table: introduce TABLE_HEADER cell type

This rework the logic for handling the "header" cells a bit. Instead of
special casing the first row in regards to uppercasing/coloring let's
just intrduce a proper cell type TABLE_HEADER which is in most ways
identical to TABLE_STRING except that it defaults to uppercase output
and underlined coloring.

This is mostly refactoring, but I think it makes a ton of sense as it
makes the first row less special and you could in fact insert
TABLE_HEADER (and in fact TABLE_FIELD) cells wherever you like and
something sensible would happen (i.e. a string cell is displayed with
a specific formatting).
This commit is contained in:
Lennart Poettering 2022-11-11 14:25:51 +01:00 committed by Yu Watanabe
parent 1c03f7f4ba
commit 8f6469cbf9
2 changed files with 73 additions and 81 deletions

View File

@ -199,14 +199,7 @@ Table *table_new_internal(const char *first_header, ...) {
for (const char *h = first_header; h; h = va_arg(ap, const char*)) {
TableCell *cell;
r = table_add_cell(t, &cell, TABLE_STRING, h);
if (r < 0) {
va_end(ap);
return NULL;
}
/* Make the table header uppercase */
r = table_set_uppercase(t, cell, true);
r = table_add_cell(t, &cell, TABLE_HEADER, h);
if (r < 0) {
va_end(ap);
return NULL;
@ -229,19 +222,13 @@ Table *table_new_vertical(void) {
t->vertical = true;
t->header = false;
if (table_add_cell(t, &cell, TABLE_STRING, "key") < 0)
return NULL;
if (table_set_uppercase(t, cell, true) < 0)
if (table_add_cell(t, &cell, TABLE_HEADER, "key") < 0)
return NULL;
if (table_set_align_percent(t, cell, 100) < 0)
return NULL;
if (table_add_cell(t, &cell, TABLE_STRING, "value") < 0)
return NULL;
if (table_set_uppercase(t, cell, true) < 0)
if (table_add_cell(t, &cell, TABLE_HEADER, "value") < 0)
return NULL;
if (table_set_align_percent(t, cell, 0) < 0)
@ -295,6 +282,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
case TABLE_STRING:
case TABLE_PATH:
case TABLE_FIELD:
case TABLE_HEADER:
return strlen(data) + 1;
case TABLE_STRV:
@ -371,7 +359,8 @@ static bool table_data_matches(
size_t maximum_width,
unsigned weight,
unsigned align_percent,
unsigned ellipsize_percent) {
unsigned ellipsize_percent,
bool uppercase) {
size_t k, l;
assert(d);
@ -394,13 +383,14 @@ static bool table_data_matches(
if (d->ellipsize_percent != ellipsize_percent)
return false;
/* If a color/url/uppercase flag is set, refuse to merge */
if (d->uppercase != uppercase)
return false;
/* If a color/url is set, refuse to merge */
if (d->color || d->rgap_color)
return false;
if (d->url)
return false;
if (d->uppercase)
return false;
k = table_data_size(type, data);
l = table_data_size(d->type, d->data);
@ -417,7 +407,8 @@ static TableData *table_data_new(
size_t maximum_width,
unsigned weight,
unsigned align_percent,
unsigned ellipsize_percent) {
unsigned ellipsize_percent,
bool uppercase) {
_cleanup_free_ TableData *d = NULL;
size_t data_size;
@ -435,6 +426,7 @@ static TableData *table_data_new(
d->weight = weight;
d->align_percent = align_percent;
d->ellipsize_percent = ellipsize_percent;
d->uppercase = uppercase;
if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) {
d->strv = strv_copy(data);
@ -458,6 +450,7 @@ int table_add_cell_full(
unsigned ellipsize_percent) {
_cleanup_(table_data_unrefp) TableData *d = NULL;
bool uppercase;
TableData *p;
assert(t);
@ -490,13 +483,15 @@ int table_add_cell_full(
assert(align_percent <= 100);
assert(ellipsize_percent <= 100);
uppercase = type == TABLE_HEADER;
/* Small optimization: Pretty often adjacent cells in two subsequent lines have the same data and
* formatting. Let's see if we can reuse the cell data and ref it once more. */
if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent))
if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent, uppercase))
d = table_data_ref(p);
else {
d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent);
d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent, uppercase);
if (!d)
return -ENOMEM;
}
@ -598,14 +593,14 @@ static int table_dedup_cell(Table *t, TableCell *cell) {
od->maximum_width,
od->weight,
od->align_percent,
od->ellipsize_percent);
od->ellipsize_percent,
od->uppercase);
if (!nd)
return -ENOMEM;
nd->color = od->color;
nd->rgap_color = od->rgap_color;
nd->url = TAKE_PTR(curl);
nd->uppercase = od->uppercase;
table_data_unref(od);
t->data[i] = nd;
@ -815,14 +810,14 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data
od->maximum_width,
od->weight,
od->align_percent,
od->ellipsize_percent);
od->ellipsize_percent,
od->uppercase);
if (!nd)
return -ENOMEM;
nd->color = od->color;
nd->rgap_color = od->rgap_color;
nd->url = TAKE_PTR(curl);
nd->uppercase = od->uppercase;
table_data_unref(od);
t->data[i] = nd;
@ -876,6 +871,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
case TABLE_STRING:
case TABLE_PATH:
case TABLE_FIELD:
case TABLE_HEADER:
data = va_arg(ap, const char *);
break;
@ -1278,6 +1274,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
case TABLE_STRING:
case TABLE_FIELD:
case TABLE_HEADER:
return strcmp(a->string, b->string);
case TABLE_PATH:
@ -1454,6 +1451,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
case TABLE_STRING:
case TABLE_PATH:
case TABLE_FIELD:
case TABLE_HEADER:
if (d->uppercase && !avoid_uppercasing) {
d->formatted = new(char, strlen(d->string) + (d->type == TABLE_FIELD) + 1);
if (!d->formatted)
@ -1468,18 +1466,15 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
*q = 0;
return d->formatted;
} else {
if (d->type == TABLE_FIELD) {
d->formatted = strjoin(d->string, ":");
if (!d->formatted)
return NULL;
} else if (d->type == TABLE_FIELD) {
d->formatted = strjoin(d->string, ":");
if (!d->formatted)
return NULL;
return d->formatted;
}
return d->string;
return d->formatted;
}
break;
return d->string;
case TABLE_STRV:
if (strv_isempty(d->strv))
@ -2034,6 +2029,8 @@ static const char* table_data_color(TableData *d) {
if (d->type == TABLE_FIELD)
return ansi_bright_blue();
if (d->type == TABLE_HEADER)
return ansi_underline();
return NULL;
}
@ -2044,6 +2041,9 @@ static const char* table_data_rgap_color(TableData *d) {
if (d->rgap_color)
return d->rgap_color;
if (d->type == TABLE_HEADER)
return ansi_underline();
return NULL;
}
@ -2388,7 +2388,7 @@ int table_print(Table *t, FILE *f) {
/* Drop trailing white spaces of last column when no cosmetics is set. */
if (j == display_columns - 1 &&
(!colors_enabled() || (!table_data_color(d) && row != t->data)) &&
(!colors_enabled() || !table_data_color(d)) &&
(!urlify_enabled() || !d->url))
delete_trailing_chars(aligned, NULL);
@ -2408,12 +2408,8 @@ int table_print(Table *t, FILE *f) {
field = buffer;
}
if (colors_enabled()) {
if (gap_color)
fputs(gap_color, f);
else if (row == t->data) /* underline header line fully, including the column separator */
fputs(ansi_underline(), f);
}
if (colors_enabled() && gap_color)
fputs(gap_color, f);
if (j > 0)
fputc(' ', f); /* column separator left of cell */
@ -2422,18 +2418,16 @@ int table_print(Table *t, FILE *f) {
color = table_data_color(d);
/* Undo gap color */
if (gap_color || (color && row == t->data))
if (gap_color)
fputs(ANSI_NORMAL, f);
if (color)
fputs(color, f);
else if (gap_color && row == t->data) /* underline header line cell */
fputs(ansi_underline(), f);
}
fputs(field, f);
if (colors_enabled() && (color || row == t->data))
if (colors_enabled() && color)
fputs(ANSI_NORMAL, f);
gap_color = table_data_rgap_color(d);
@ -2548,6 +2542,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
case TABLE_STRING:
case TABLE_PATH:
case TABLE_FIELD:
case TABLE_HEADER:
return json_variant_new_string(ret, d->string);
case TABLE_STRV:
@ -2683,6 +2678,30 @@ static char* string_to_json_field_name(const char *f) {
return c;
}
static int table_make_json_field_name(Table *t, TableData *d, char **ret) {
_cleanup_free_ char *mangled = NULL;
const char *n;
assert(t);
assert(d);
assert(ret);
if (IN_SET(d->type, TABLE_HEADER, TABLE_FIELD))
n = d->string;
else {
n = table_data_format(t, d, /* avoid_uppercasing= */ true, SIZE_MAX, NULL);
if (!n)
return -ENOMEM;
}
mangled = string_to_json_field_name(n);
if (!mangled)
return -ENOMEM;
*ret = TAKE_PTR(mangled);
return 0;
}
static const char *table_get_json_field_name(Table *t, size_t idx) {
assert(t);
@ -2742,24 +2761,10 @@ static int table_to_json_regular(Table *t, JsonVariant **ret) {
/* Use explicitly set JSON field name, if we have one. Otherwise mangle the column field value. */
n = table_get_json_field_name(t, c);
if (!n) {
const char *formatted;
TableData *d;
assert_se(d = t->data[c]);
/* Field names must be strings, hence format whatever we got here as a string first */
formatted = table_data_format(t, d, true, SIZE_MAX, NULL);
if (!formatted) {
r = -ENOMEM;
r = table_make_json_field_name(t, ASSERT_PTR(t->data[c]), &mangled);
if (r < 0)
goto finish;
}
/* Arbitrary strings suck as field names, try to mangle them into something more suitable hence */
mangled = string_to_json_field_name(formatted);
if (!mangled) {
r = -ENOMEM;
goto finish;
}
n = mangled;
}
@ -2845,23 +2850,9 @@ static int table_to_json_vertical(Table *t, JsonVariant **ret) {
n = table_get_json_field_name(t, i / t->n_columns - 1);
if (!n) {
TableData *d = ASSERT_PTR(t->data[i]);
if (d->type == TABLE_FIELD)
n = d->string;
else {
n = table_data_format(t, d, /* avoid_uppercasing= */ true, SIZE_MAX, NULL);
if (!n) {
r = -ENOMEM;
goto finish;
}
}
mangled = string_to_json_field_name(n);
if (!mangled) {
r = -ENOMEM;
r = table_make_json_field_name(t, ASSERT_PTR(t->data[i]), &mangled);
if (r < 0)
goto finish;
}
n = mangled;
}

View File

@ -12,7 +12,8 @@
typedef enum TableDataType {
TABLE_EMPTY,
TABLE_STRING,
TABLE_FIELD, /* used in vertical mode */
TABLE_HEADER, /* in regular mode: the cells in the first row, that carry the column names */
TABLE_FIELD, /* in vertical mode: the cells in the first column, that carry the field names */
TABLE_STRV,
TABLE_STRV_WRAPPED,
TABLE_PATH,