diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2020-01-15 13:59:11 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-15 13:59:11 +0100 |
commit | 98f44b97bb49673b32a3bc824067950c23321840 (patch) | |
tree | d4a0b1dc247156fa35387730bca9055535d14797 /src | |
parent | Merge pull request #14578 from keszybz/docs-index (diff) | |
parent | test-format-table: add tests for TABLE_STRV (diff) | |
download | systemd-98f44b97bb49673b32a3bc824067950c23321840.tar.xz systemd-98f44b97bb49673b32a3bc824067950c23321840.zip |
Merge pull request #14562 from yuwata/table-strv
introduce TABLE_STRV and use it in networkctl and resolvectl
Diffstat (limited to 'src')
-rw-r--r-- | src/basic/strv.c | 23 | ||||
-rw-r--r-- | src/basic/strv.h | 5 | ||||
-rw-r--r-- | src/network/networkctl.c | 137 | ||||
-rw-r--r-- | src/resolve/resolvectl.c | 285 | ||||
-rw-r--r-- | src/shared/format-table.c | 40 | ||||
-rw-r--r-- | src/shared/format-table.h | 1 | ||||
-rw-r--r-- | src/test/test-format-table.c | 114 | ||||
-rw-r--r-- | src/test/test-strv.c | 26 |
8 files changed, 418 insertions, 213 deletions
diff --git a/src/basic/strv.c b/src/basic/strv.c index 303d979859..74d20a9a95 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -730,19 +730,26 @@ char **strv_sort(char **l) { return l; } -bool strv_equal(char * const *a, char * const *b) { +int strv_compare(char * const *a, char * const *b) { + int r; - if (strv_isempty(a)) - return strv_isempty(b); + if (strv_isempty(a)) { + if (strv_isempty(b)) + return 0; + else + return -1; + } if (strv_isempty(b)) - return false; + return 1; - for ( ; *a || *b; ++a, ++b) - if (!streq_ptr(*a, *b)) - return false; + for ( ; *a || *b; ++a, ++b) { + r = strcmp_ptr(*a, *b); + if (r != 0) + return r; + } - return true; + return 0; } void strv_print(char * const *l) { diff --git a/src/basic/strv.h b/src/basic/strv.h index 16faf9e7f2..85a49ab5c3 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -51,7 +51,10 @@ char **strv_remove(char **l, const char *s); char **strv_uniq(char **l); bool strv_is_uniq(char * const *l); -bool strv_equal(char * const *a, char * const *b); +int strv_compare(char * const *a, char * const *b); +static inline bool strv_equal(char * const *a, char * const *b) { + return strv_compare(a, b) == 0; +} #define strv_contains(l, s) (!!strv_find((l), (s))) diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 621aa2703e..629bce29b9 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -742,29 +742,41 @@ static int get_gateway_description( return -ENODATA; } +static int dump_list(Table *table, const char *prefix, char * const *l) { + int r; + + if (strv_isempty(l)) + return 0; + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, prefix, + TABLE_STRV, l); + if (r < 0) + return table_log_add_error(r); + + return 0; +} + static int dump_gateways( sd_netlink *rtnl, sd_hwdb *hwdb, Table *table, int ifindex) { _cleanup_free_ struct local_address *local = NULL; + _cleanup_strv_free_ char **buf = NULL; int r, n, i; assert(rtnl); assert(table); n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local); - if (n < 0) + if (n <= 0) return n; for (i = 0; i < n; i++) { _cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL; - - r = table_add_many(table, - TABLE_EMPTY, - TABLE_STRING, i == 0 ? "Gateway:" : ""); - if (r < 0) - return table_log_add_error(r); + char name[IF_NAMESIZE+1]; r = in_addr_to_string(local[i].family, &local[i].address, &gateway); if (r < 0) @@ -772,28 +784,24 @@ static int dump_gateways( r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description); if (r < 0) - log_debug_errno(r, "Could not get description of gateway: %m"); + log_debug_errno(r, "Could not get description of gateway, ignoring: %m"); if (description) { with_description = strjoin(gateway, " (", description, ")"); if (!with_description) - return -ENOMEM; + return log_oom(); } - /* Show interface name for the entry if we show - * entries for all interfaces */ - if (ifindex <= 0) { - char name[IF_NAMESIZE+1]; - - r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway, - format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT)); - } else - r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway); + /* Show interface name for the entry if we show entries for all interfaces */ + r = strv_extendf(&buf, "%s%s%s", + with_description ?: gateway, + ifindex <= 0 ? " on " : "", + ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : ""); if (r < 0) - return table_log_add_error(r); + return log_oom(); } - return 0; + return dump_list(table, "Gateway:", buf); } static int dump_addresses( @@ -803,25 +811,21 @@ static int dump_addresses( _cleanup_free_ struct local_address *local = NULL; _cleanup_free_ char *dhcp4_address = NULL; + _cleanup_strv_free_ char **buf = NULL; int r, n, i; assert(rtnl); assert(table); n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local); - if (n < 0) + if (n <= 0) return n; (void) sd_network_link_get_dhcp4_address(ifindex, &dhcp4_address); for (i = 0; i < n; i++) { _cleanup_free_ char *pretty = NULL; - - r = table_add_many(table, - TABLE_EMPTY, - TABLE_STRING, i == 0 ? "Address:" : ""); - if (r < 0) - return table_log_add_error(r); + char name[IF_NAMESIZE+1]; r = in_addr_to_string(local[i].family, &local[i].address, &pretty); if (r < 0) @@ -836,18 +840,15 @@ static int dump_addresses( return log_oom(); } - if (ifindex <= 0) { - char name[IF_NAMESIZE+1]; - - r = table_add_cell_stringf(table, NULL, "%s on %s", pretty, - format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT)); - } else - r = table_add_cell(table, NULL, TABLE_STRING, pretty); + r = strv_extendf(&buf, "%s%s%s", + pretty, + ifindex <= 0 ? " on " : "", + ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : ""); if (r < 0) - return table_log_add_error(r); + return log_oom(); } - return 0; + return dump_list(table, "Address:", buf); } static int dump_address_labels(sd_netlink *rtnl) { @@ -997,8 +998,9 @@ static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) { } static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) { + _cleanup_strv_free_ char **buf = NULL; _cleanup_fclose_ FILE *f = NULL; - int r, c = 0; + int r; assert(table); assert(prefix); @@ -1020,29 +1022,21 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) { if (r == 0) break; - r = table_add_many(table, - TABLE_EMPTY, - TABLE_STRING, c == 0 ? prefix : ""); - if (r < 0) - return table_log_add_error(r); - (void) sd_lldp_neighbor_get_system_name(n, &system_name); (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); (void) sd_lldp_neighbor_get_port_description(n, &port_description); - r = table_add_cell_stringf(table, NULL, - "%s on port %s%s%s%s", - strna(system_name), strna(port_id), - isempty(port_description) ? "" : " (", - strempty(port_description), - isempty(port_description) ? "" : ")"); + r = strv_extendf(&buf, "%s on port %s%s%s%s", + strna(system_name), + strna(port_id), + isempty(port_description) ? "" : " (", + strempty(port_description), + isempty(port_description) ? "" : ")"); if (r < 0) - return table_log_add_error(r); - - c++; + return log_oom(); } - return c; + return dump_list(table, prefix, buf); } static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) { @@ -1066,25 +1060,6 @@ static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes return 0; } -static int dump_list(Table *table, const char *prefix, char **l) { - char **i; - int r; - - if (strv_isempty(l)) - return 0; - - STRV_FOREACH(i, l) { - r = table_add_many(table, - TABLE_EMPTY, - TABLE_STRING, i == l ? prefix : "", - TABLE_STRING, *i); - if (r < 0) - return table_log_add_error(r); - } - - return 0; -} - #define DUMP_STATS_ONE(name, val_name) \ r = table_add_many(table, \ TABLE_EMPTY, \ @@ -1197,7 +1172,6 @@ static int link_status_one( _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; _cleanup_(table_unrefp) Table *table = NULL; TableCell *cell; - char **p; int r; assert(rtnl); @@ -1282,20 +1256,9 @@ static int link_status_one( if (r < 0) return table_log_add_error(r); - STRV_FOREACH(p, info->alternative_names) { - if (p == info->alternative_names) - r = table_add_many(table, - TABLE_EMPTY, - TABLE_STRING, "Alternative Names:", - TABLE_STRING, *p); - else - r = table_add_many(table, - TABLE_EMPTY, - TABLE_EMPTY, - TABLE_STRING, *p); - if (r < 0) - return table_log_add_error(r); - } + r = dump_list(table, "Alternative Names:", info->alternative_names); + if (r < 0) + return r; if (path) { r = table_add_many(table, diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index df40349e66..f20e8c44b8 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -14,6 +14,7 @@ #include "bus-util.h" #include "dns-domain.h" #include "escape.h" +#include "format-table.h" #include "format-util.h" #include "gcrypt-util.h" #include "main-func.h" @@ -984,6 +985,7 @@ static int verb_tlsa(int argc, char **argv, void *userdata) { static int show_statistics(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(table_unrefp) Table *table = NULL; sd_bus *bus = userdata; uint64_t n_current_transactions, n_total_transactions, cache_size, n_cache_hit, n_cache_miss, @@ -1025,14 +1027,6 @@ static int show_statistics(int argc, char **argv, void *userdata) { if (r < 0) return bus_log_parse_error(r); - printf("%sTransactions%s\n" - "Current Transactions: %" PRIu64 "\n" - " Total Transactions: %" PRIu64 "\n", - ansi_highlight(), - ansi_normal(), - n_current_transactions, - n_total_transactions); - reply = sd_bus_message_unref(reply); r = sd_bus_get_property(bus, @@ -1053,16 +1047,6 @@ static int show_statistics(int argc, char **argv, void *userdata) { if (r < 0) return bus_log_parse_error(r); - printf("\n%sCache%s\n" - " Current Cache Size: %" PRIu64 "\n" - " Cache Hits: %" PRIu64 "\n" - " Cache Misses: %" PRIu64 "\n", - ansi_highlight(), - ansi_normal(), - cache_size, - n_cache_hit, - n_cache_miss); - reply = sd_bus_message_unref(reply); r = sd_bus_get_property(bus, @@ -1084,17 +1068,53 @@ static int show_statistics(int argc, char **argv, void *userdata) { if (r < 0) return bus_log_parse_error(r); - printf("\n%sDNSSEC Verdicts%s\n" - " Secure: %" PRIu64 "\n" - " Insecure: %" PRIu64 "\n" - " Bogus: %" PRIu64 "\n" - " Indeterminate: %" PRIu64 "\n", - ansi_highlight(), - ansi_normal(), - n_dnssec_secure, - n_dnssec_insecure, - n_dnssec_bogus, - n_dnssec_indeterminate); + table = table_new("key", "value"); + if (!table) + return log_oom(); + + table_set_header(table, false); + + r = table_add_many(table, + TABLE_STRING, "Transactions", + TABLE_SET_COLOR, ansi_highlight(), + TABLE_EMPTY, + TABLE_STRING, "Current Transactions:", + TABLE_SET_ALIGN_PERCENT, 100, + TABLE_UINT64, n_current_transactions, + TABLE_STRING, "Total Transactions:", + TABLE_UINT64, n_total_transactions, + TABLE_EMPTY, TABLE_EMPTY, + TABLE_STRING, "Cache", + TABLE_SET_COLOR, ansi_highlight(), + TABLE_SET_ALIGN_PERCENT, 0, + TABLE_EMPTY, + TABLE_STRING, "Current Cache Size:", + TABLE_SET_ALIGN_PERCENT, 100, + TABLE_UINT64, cache_size, + TABLE_STRING, "Cache Hits:", + TABLE_UINT64, n_cache_hit, + TABLE_STRING, "Cache Misses:", + TABLE_UINT64, n_cache_miss, + TABLE_EMPTY, TABLE_EMPTY, + TABLE_STRING, "DNSSEC Verdicts", + TABLE_SET_COLOR, ansi_highlight(), + TABLE_SET_ALIGN_PERCENT, 0, + TABLE_EMPTY, + TABLE_STRING, "Secure:", + TABLE_SET_ALIGN_PERCENT, 100, + TABLE_UINT64, n_dnssec_secure, + TABLE_STRING, "Insecure:", + TABLE_UINT64, n_dnssec_insecure, + TABLE_STRING, "Bogus:", + TABLE_UINT64, n_dnssec_bogus, + TABLE_STRING, "Indeterminate:", + TABLE_UINT64, n_dnssec_indeterminate); + if (r < 0) + table_log_add_error(r); + + r = table_print(table, NULL); + if (r < 0) + return log_error_errno(r, "Failed to print table: %m"); return 0; } @@ -1365,6 +1385,21 @@ static void link_info_clear(struct link_info *p) { strv_free(p->ntas); } +static int dump_list(Table *table, const char *prefix, char * const *l) { + int r; + + if (strv_isempty(l)) + return 0; + + r = table_add_many(table, + TABLE_STRING, prefix, + TABLE_STRV, l); + if (r < 0) + return table_log_add_error(r); + + return 0; +} + static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) { static const struct bus_properties_map property_map[] = { { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) }, @@ -1383,9 +1418,9 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(link_info_clear) struct link_info link_info = {}; + _cleanup_(table_unrefp) Table *table = NULL; _cleanup_free_ char *p = NULL; char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = ""; - char **i; int r; assert(bus); @@ -1471,49 +1506,80 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode printf("%sLink %i (%s)%s\n", ansi_highlight(), ifindex, name, ansi_normal()); + table = table_new("key", "value"); + if (!table) + return log_oom(); + + table_set_header(table, false); + + r = table_add_many(table, + TABLE_STRING, "Current Scopes:", + TABLE_SET_ALIGN_PERCENT, 100); + if (r < 0) + return table_log_add_error(r); + if (link_info.scopes_mask == 0) - printf(" Current Scopes: none\n"); - else - printf(" Current Scopes:%s%s%s%s%s\n", - link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "", - link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", - link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "", - link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "", - link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : ""); - - printf("DefaultRoute setting: %s\n" - " LLMNR setting: %s\n" - "MulticastDNS setting: %s\n" - " DNSOverTLS setting: %s\n" - " DNSSEC setting: %s\n" - " DNSSEC supported: %s\n", - yes_no(link_info.default_route), - strna(link_info.llmnr), - strna(link_info.mdns), - strna(link_info.dns_over_tls), - strna(link_info.dnssec), - yes_no(link_info.dnssec_supported)); - - if (link_info.current_dns) - printf(" Current DNS Server: %s\n", link_info.current_dns); - - STRV_FOREACH(i, link_info.dns) { - printf(" %s %s\n", - i == link_info.dns ? "DNS Servers:" : " ", - *i); - } - - STRV_FOREACH(i, link_info.domains) { - printf(" %s %s\n", - i == link_info.domains ? "DNS Domain:" : " ", - *i); - } - - STRV_FOREACH(i, link_info.ntas) { - printf(" %s %s\n", - i == link_info.ntas ? "DNSSEC NTA:" : " ", - *i); + r = table_add_cell(table, NULL, TABLE_STRING, "none"); + else { + _cleanup_free_ char *buf = NULL; + size_t len; + + if (asprintf(&buf, "%s%s%s%s%s", + link_info.scopes_mask & SD_RESOLVED_DNS ? "DNS " : "", + link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? "LLMNR/IPv4 " : "", + link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? "LLMNR/IPv6 " : "", + link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4 " : "", + link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6 " : "") < 0) + return log_oom(); + + len = strlen(buf); + assert(len > 0); + buf[len - 1] = '\0'; + + r = table_add_cell(table, NULL, TABLE_STRING, buf); } + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_STRING, "DefaultRoute setting:", + TABLE_BOOLEAN, link_info.default_route, + TABLE_STRING, "LLMNR setting:", + TABLE_STRING, strna(link_info.llmnr), + TABLE_STRING, "MulticastDNS setting:", + TABLE_STRING, strna(link_info.mdns), + TABLE_STRING, "DNSOverTLS setting:", + TABLE_STRING, strna(link_info.dns_over_tls), + TABLE_STRING, "DNSSEC setting:", + TABLE_STRING, strna(link_info.dnssec), + TABLE_STRING, "DNSSEC supported:", + TABLE_BOOLEAN, link_info.dnssec_supported); + if (r < 0) + return table_log_add_error(r); + + if (link_info.current_dns) { + r = table_add_many(table, + TABLE_STRING, "Current DNS Server:", + TABLE_STRING, link_info.current_dns); + if (r < 0) + return table_log_add_error(r); + } + + r = dump_list(table, "DNS Servers:", link_info.dns); + if (r < 0) + return r; + + r = dump_list(table, "DNS Domain:", link_info.domains); + if (r < 0) + return r; + + r = dump_list(table, "DNSSEC NTA:", link_info.ntas); + if (r < 0) + return r; + + r = table_print(table, NULL); + if (r < 0) + return log_error_errno(r, "Failed to print table: %m"); if (empty_line) *empty_line = true; @@ -1653,7 +1719,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(global_info_clear) struct global_info global_info = {}; - char **i; + _cleanup_(table_unrefp) Table *table = NULL; int r; assert(bus); @@ -1711,44 +1777,55 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { printf("%sGlobal%s\n", ansi_highlight(), ansi_normal()); - printf(" LLMNR setting: %s\n" - "MulticastDNS setting: %s\n" - " DNSOverTLS setting: %s\n" - " DNSSEC setting: %s\n" - " DNSSEC supported: %s\n", - strna(global_info.llmnr), - strna(global_info.mdns), - strna(global_info.dns_over_tls), - strna(global_info.dnssec), - yes_no(global_info.dnssec_supported)); - - if (global_info.current_dns) - printf(" Current DNS Server: %s\n", global_info.current_dns); + table = table_new("key", "value"); + if (!table) + return log_oom(); - STRV_FOREACH(i, global_info.dns) { - printf(" %s %s\n", - i == global_info.dns ? "DNS Servers:" : " ", - *i); + table_set_header(table, false); + + r = table_add_many(table, + TABLE_STRING, "LLMNR setting:", + TABLE_SET_ALIGN_PERCENT, 100, + TABLE_STRING, strna(global_info.llmnr), + TABLE_STRING, "MulticastDNS setting:", + TABLE_STRING, strna(global_info.mdns), + TABLE_STRING, "DNSOverTLS setting:", + TABLE_STRING, strna(global_info.dns_over_tls), + TABLE_STRING, "DNSSEC setting:", + TABLE_STRING, strna(global_info.dnssec), + TABLE_STRING, "DNSSEC supported:", + TABLE_BOOLEAN, global_info.dnssec_supported); + if (r < 0) + return table_log_add_error(r); + + if (global_info.current_dns) { + r = table_add_many(table, + TABLE_STRING, "Current DNS Server:", + TABLE_STRING, global_info.current_dns); + if (r < 0) + return table_log_add_error(r); } - STRV_FOREACH(i, global_info.fallback_dns) { - printf("%s %s\n", - i == global_info.fallback_dns ? "Fallback DNS Servers:" : " ", - *i); - } + r = dump_list(table, "DNS Servers:", global_info.dns); + if (r < 0) + return r; - STRV_FOREACH(i, global_info.domains) { - printf(" %s %s\n", - i == global_info.domains ? "DNS Domain:" : " ", - *i); - } + r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns); + if (r < 0) + return r; + + r = dump_list(table, "DNS Domain:", global_info.domains); + if (r < 0) + return r; strv_sort(global_info.ntas); - STRV_FOREACH(i, global_info.ntas) { - printf(" %s %s\n", - i == global_info.ntas ? "DNSSEC NTA:" : " ", - *i); - } + r = dump_list(table, "DNSSEC NTA:", global_info.ntas); + if (r < 0) + return r; + + r = table_print(table, NULL); + if (r < 0) + return log_error_errno(r, "Failed to print table: %m"); *empty_line = true; diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 178bc78cc0..d7cb976757 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -80,6 +80,7 @@ typedef struct TableData { usec_t timespan; uint64_t size; char string[0]; + char **strv; int int_val; int8_t int8; int16_t int16; @@ -207,6 +208,9 @@ static TableData *table_data_free(TableData *d) { free(d->formatted); free(d->url); + if (d->type == TABLE_STRV) + strv_free(d->strv); + return mfree(d); } @@ -242,6 +246,9 @@ static size_t table_data_size(TableDataType type, const void *data) { case TABLE_PATH: return strlen(data) + 1; + case TABLE_STRV: + return sizeof(char **); + case TABLE_BOOLEAN: return sizeof(bool); @@ -344,8 +351,8 @@ static TableData *table_data_new( unsigned align_percent, unsigned ellipsize_percent) { + _cleanup_free_ TableData *d = NULL; size_t data_size; - TableData *d; data_size = table_data_size(type, data); @@ -360,9 +367,15 @@ static TableData *table_data_new( d->weight = weight; d->align_percent = align_percent; d->ellipsize_percent = ellipsize_percent; - memcpy_safe(d->data, data, data_size); - return d; + if (type == TABLE_STRV) { + d->strv = strv_copy(data); + if (!d->strv) + return NULL; + } else + memcpy_safe(d->data, data, data_size); + + return TAKE_PTR(d); } int table_add_cell_full( @@ -778,6 +791,10 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { data = va_arg(ap, const char *); break; + case TABLE_STRV: + data = va_arg(ap, char * const *); + break; + case TABLE_BOOLEAN: buffer.b = va_arg(ap, int); data = &buffer.b; @@ -1055,6 +1072,9 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t case TABLE_PATH: return path_compare(a->string, b->string); + case TABLE_STRV: + return strv_compare(a->strv, b->strv); + case TABLE_BOOLEAN: if (!a->boolean && b->boolean) return -1; @@ -1185,6 +1205,17 @@ static const char *table_data_format(Table *t, TableData *d) { return d->string; + case TABLE_STRV: { + char *p; + + p = strv_join(d->strv, "\n"); + if (!p) + return NULL; + + d->formatted = p; + break; + } + case TABLE_BOOLEAN: return yes_no(d->boolean); @@ -2054,6 +2085,9 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { case TABLE_PATH: return json_variant_new_string(ret, d->string); + case TABLE_STRV: + return json_variant_new_array_strv(ret, d->strv); + case TABLE_BOOLEAN: return json_variant_new_boolean(ret, d->boolean); diff --git a/src/shared/format-table.h b/src/shared/format-table.h index cb9232b47a..fa7a2bd6d6 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -11,6 +11,7 @@ typedef enum TableDataType { TABLE_EMPTY, TABLE_STRING, + TABLE_STRV, TABLE_PATH, TABLE_BOOLEAN, TABLE_TIMESTAMP, diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c index 7bf62e48e2..283cf157ba 100644 --- a/src/test/test-format-table.c +++ b/src/test/test-format-table.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "format-table.h" #include "string-util.h" +#include "strv.h" #include "time-util.h" static void test_issue_9549(void) { @@ -143,6 +144,118 @@ static void test_multiline(void) { formatted = mfree(formatted); } +static void test_strv(void) { + _cleanup_(table_unrefp) Table *table = NULL; + _cleanup_free_ char *formatted = NULL; + + assert_se(table = table_new("foo", "bar")); + + assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0); + + assert_se(table_add_many(table, + TABLE_STRV, STRV_MAKE("three", "different", "lines"), + TABLE_STRV, STRV_MAKE("two", "lines")) >= 0); + + table_set_cell_height_max(table, 1); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three… two…\n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, 2); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three two\n" + "different… lines\n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, 3); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three two\n" + "different lines\n" + "lines \n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, (size_t) -1); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three two\n" + "different lines\n" + "lines \n")); + formatted = mfree(formatted); + + assert_se(table_add_many(table, + TABLE_STRING, "short", + TABLE_STRV, STRV_MAKE("a", "pair")) >= 0); + + assert_se(table_add_many(table, + TABLE_STRV, STRV_MAKE("short2"), + TABLE_STRV, STRV_MAKE("a", "four", "line", "cell")) >= 0); + + table_set_cell_height_max(table, 1); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three… two…\n" + "short a…\n" + "short2 a…\n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, 2); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three two\n" + "different… lines\n" + "short a\n" + " pair\n" + "short2 a\n" + " four…\n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, 3); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three two\n" + "different lines\n" + "lines \n" + "short a\n" + " pair\n" + "short2 a\n" + " four\n" + " line…\n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, (size_t) -1); + assert_se(table_format(table, &formatted) >= 0); + fputs(formatted, stdout); + assert_se(streq(formatted, + "FOO BAR\n" + "three two\n" + "different lines\n" + "lines \n" + "short a\n" + " pair\n" + "short2 a\n" + " four\n" + " line\n" + " cell\n")); + formatted = mfree(formatted); +} + int main(int argc, char *argv[]) { _cleanup_(table_unrefp) Table *t = NULL; @@ -285,6 +398,7 @@ int main(int argc, char *argv[]) { test_issue_9549(); test_multiline(); + test_strv(); return 0; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index b63cb4b63e..dd6233175c 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -711,27 +711,33 @@ static void test_strv_push(void) { assert_se(streq_ptr(a[3], NULL)); } -static void test_strv_equal(void) { +static void test_strv_compare(void) { _cleanup_strv_free_ char **a = NULL; _cleanup_strv_free_ char **b = NULL; _cleanup_strv_free_ char **c = NULL; + _cleanup_strv_free_ char **d = NULL; log_info("/* %s */", __func__); a = strv_new("one", "two", "three"); assert_se(a); b = strv_new("one", "two", "three"); - assert_se(a); + assert_se(b); c = strv_new("one", "two", "three", "four"); - assert_se(a); + assert_se(c); + d = strv_new(NULL); + assert_se(d); - assert_se(strv_equal(a, a)); - assert_se(strv_equal(a, b)); - assert_se(strv_equal(NULL, NULL)); + assert_se(strv_compare(a, a) == 0); + assert_se(strv_compare(a, b) == 0); + assert_se(strv_compare(d, d) == 0); + assert_se(strv_compare(d, NULL) == 0); + assert_se(strv_compare(NULL, NULL) == 0); - assert_se(!strv_equal(a, c)); - assert_se(!strv_equal(b, c)); - assert_se(!strv_equal(b, NULL)); + assert_se(strv_compare(a, c) < 0); + assert_se(strv_compare(b, c) < 0); + assert_se(strv_compare(b, d) == 1); + assert_se(strv_compare(b, NULL) == 1); } static void test_strv_is_uniq(void) { @@ -990,7 +996,7 @@ int main(int argc, char *argv[]) { test_strv_insert(); test_strv_push_prepend(); test_strv_push(); - test_strv_equal(); + test_strv_compare(); test_strv_is_uniq(); test_strv_reverse(); test_strv_shell_escape(); |