From: Lennart Poettering Date: Wed, 22 Apr 2026 21:43:17 +0000 (+0200) Subject: format-table: add tristate field X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=f0abcd5e0fd5ba309e91a2fe589727b814e2967b;p=thirdparty%2Fsystemd.git format-table: add tristate field --- diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 200890d3cdc..432e054d4a7 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -87,6 +87,7 @@ typedef struct TableData { union { uint8_t data[0]; /* data is generic array */ bool boolean; + int tristate; usec_t timestamp; usec_t timespan; uint64_t size; @@ -342,6 +343,7 @@ static size_t table_data_size(TableDataType type, const void *data) { case TABLE_PERCENT: case TABLE_IFINDEX: case TABLE_SIGNAL: + case TABLE_TRISTATE: return sizeof(int); case TABLE_IN_ADDR: @@ -935,6 +937,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { uint64_t uint64; int percent; int ifindex; + int tristate; bool b; union in_addr_union address; sd_id128_t id128; @@ -972,6 +975,11 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { data = &buffer.b; break; + case TABLE_TRISTATE: + buffer.tristate = va_arg(ap, int); + data = &buffer.tristate; + break; + case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: @@ -1434,12 +1442,25 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t return strv_compare(a->strv, b->strv); case TABLE_BOOLEAN: + case TABLE_BOOLEAN_CHECKMARK: if (!a->boolean && b->boolean) return -1; if (a->boolean && !b->boolean) return 1; return 0; + case TABLE_TRISTATE: + /* NB: we do not use CMP() here, since we want to collapse all negative and all + * positive into one bucket each. */ + if ((a->tristate < 0 && b->tristate >= 0) || + (a->tristate == 0 && b->tristate > 0)) + return -1; + + if ((b->tristate < 0 && a->tristate >= 0) || + (b->tristate == 0 && a->tristate > 0)) + return 1; + return 0; + case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: @@ -1691,6 +1712,12 @@ static const char* table_data_format( case TABLE_BOOLEAN_CHECKMARK: return glyph(d->boolean ? GLYPH_CHECK_MARK : GLYPH_CROSS_MARK); + case TABLE_TRISTATE: + if (d->tristate < 0) + return table_ersatz_string(t); + + return yes_no(d->tristate); + case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: @@ -2776,6 +2803,12 @@ static int table_data_to_json(TableData *d, sd_json_variant **ret) { case TABLE_BOOLEAN: return sd_json_variant_new_boolean(ret, d->boolean); + case TABLE_TRISTATE: + if (d->tristate < 0) + return sd_json_variant_new_null(ret); + + return sd_json_variant_new_boolean(ret, d->tristate); + case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 7665c93e593..5b98d490175 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -20,6 +20,7 @@ typedef enum TableDataType { TABLE_VERSION, /* just like TABLE_STRING, but uses version comparison when sorting */ TABLE_BOOLEAN, TABLE_BOOLEAN_CHECKMARK, + TABLE_TRISTATE, TABLE_TIMESTAMP, TABLE_TIMESTAMP_UTC, TABLE_TIMESTAMP_RELATIVE, diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c index 677adaa9c96..9aae79e2239 100644 --- a/src/test/test-format-table.c +++ b/src/test/test-format-table.c @@ -581,6 +581,72 @@ TEST(table) { "5min 5min \n"); } +TEST(tristate) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL; + _cleanup_(table_unrefp) Table *t = NULL; + _cleanup_free_ char *formatted = NULL; + + ASSERT_NOT_NULL((t = table_new("name", "flag"))); + + ASSERT_OK(table_add_many(t, + TABLE_STRING, "neg", + TABLE_TRISTATE, -1)); + ASSERT_OK(table_add_many(t, + TABLE_STRING, "zero", + TABLE_TRISTATE, 0)); + ASSERT_OK(table_add_many(t, + TABLE_STRING, "pos", + TABLE_TRISTATE, 1)); + + ASSERT_OK(table_format(t, &formatted)); + printf("%s\n", formatted); + ASSERT_STREQ(formatted, + "NAME FLAG\n" + "neg \n" + "zero no\n" + "pos yes\n"); + formatted = mfree(formatted); + + /* Try a non-default ersatz string. */ + table_set_ersatz_string(t, TABLE_ERSATZ_DASH); + ASSERT_OK(table_format(t, &formatted)); + printf("%s\n", formatted); + ASSERT_STREQ(formatted, + "NAME FLAG\n" + "neg -\n" + "zero no\n" + "pos yes\n"); + formatted = mfree(formatted); + + /* Sorting: -1 < 0 < 1 */ + ASSERT_OK(table_set_sort(t, (size_t) 1, SIZE_MAX)); + ASSERT_OK(table_format(t, &formatted)); + printf("%s\n", formatted); + ASSERT_STREQ(formatted, + "NAME FLAG\n" + "neg -\n" + "zero no\n" + "pos yes\n"); + formatted = mfree(formatted); + + /* JSON: -1 → null, 0 → false, positive → true */ + ASSERT_OK(table_to_json(t, &v)); + + ASSERT_OK(sd_json_build(&w, + SD_JSON_BUILD_ARRAY( + SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("name", JSON_BUILD_CONST_STRING("neg")), + SD_JSON_BUILD_PAIR("flag", SD_JSON_BUILD_NULL)), + SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("name", JSON_BUILD_CONST_STRING("zero")), + SD_JSON_BUILD_PAIR_BOOLEAN("flag", false)), + SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("name", JSON_BUILD_CONST_STRING("pos")), + SD_JSON_BUILD_PAIR_BOOLEAN("flag", true))))); + + ASSERT_TRUE(sd_json_variant_equal(v, w)); +} + TEST(signed_integers) { _cleanup_(table_unrefp) Table *t = NULL; _cleanup_free_ char *formatted = NULL;