]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
format-table: introduce TABLE_HEADER cell type
authorLennart Poettering <lennart@poettering.net>
Fri, 11 Nov 2022 13:25:51 +0000 (14:25 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 13 Nov 2022 08:36:22 +0000 (17:36 +0900)
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).

src/shared/format-table.c
src/shared/format-table.h

index 2a3defeb7c7fa35a4ca136bde1e71165f702aac0..82744a3f96a18ab9854a6b313efb99d240378db1 100644 (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;
-
-                                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;
                         }
index e90a7b7a6962e1ebefcbef4545f6fe9a93f604f3..c1fada77c75dace29c1b916b3eff223b4225d65d 100644 (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,