From: Lennart Poettering Date: Fri, 11 Nov 2022 13:25:51 +0000 (+0100) Subject: format-table: introduce TABLE_HEADER cell type X-Git-Tag: v253-rc1~541^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8f6469cbf9c217a0d9c0e17b63cc49ede00ddf05;p=thirdparty%2Fsystemd.git 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). --- diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 2a3defeb7c7..82744a3f96a 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -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; - - return d->formatted; - } + } else if (d->type == TABLE_FIELD) { + d->formatted = strjoin(d->string, ":"); + if (!d->formatted) + return NULL; - 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; } diff --git a/src/shared/format-table.h b/src/shared/format-table.h index e90a7b7a696..c1fada77c75 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -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,