mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-26 10:03:40 +03:00
Merge pull request #17324 from keszybz/resolvectl-compat-output
resolvectl compat output
This commit is contained in:
commit
e1da60e430
@ -138,7 +138,17 @@
|
|||||||
and follow the same overriding rules. They are text files with the
|
and follow the same overriding rules. They are text files with the
|
||||||
<filename>.negative</filename> suffix. Empty lines and lines whose first character is
|
<filename>.negative</filename> suffix. Empty lines and lines whose first character is
|
||||||
<literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS
|
<literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS
|
||||||
subtree where validation shall be disabled.</para>
|
subtree where validation shall be disabled. For example:</para>
|
||||||
|
|
||||||
|
<programlisting># Reverse IPv4 mappings
|
||||||
|
10.in-addr.arpa
|
||||||
|
16.172.in-addr.arpa
|
||||||
|
168.192.in-addr.arpa
|
||||||
|
...
|
||||||
|
# Some custom domains
|
||||||
|
prod
|
||||||
|
stag
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
<para>Negative trust anchors are useful to support private DNS
|
<para>Negative trust anchors are useful to support private DNS
|
||||||
subtrees that are not referenced from the Internet DNS hierarchy,
|
subtrees that are not referenced from the Internet DNS hierarchy,
|
||||||
|
@ -650,4 +650,8 @@ static inline int __coverity_check_and_return__(int condition) {
|
|||||||
_copy; \
|
_copy; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
static inline size_t size_add(size_t x, size_t y) {
|
||||||
|
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
|
||||||
|
}
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -59,6 +59,10 @@ static inline const char* true_false(bool b) {
|
|||||||
return b ? "true" : "false";
|
return b ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char* plus_minus(bool b) {
|
||||||
|
return b ? "+" : "-";
|
||||||
|
}
|
||||||
|
|
||||||
static inline const char* one_zero(bool b) {
|
static inline const char* one_zero(bool b) {
|
||||||
return b ? "1" : "0";
|
return b ? "1" : "0";
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
|
#include "utf8.h"
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
|
|
||||||
static int arg_family = AF_UNSPEC;
|
static int arg_family = AF_UNSPEC;
|
||||||
@ -1296,24 +1297,46 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
strv_sort(*l);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
|
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
|
||||||
|
const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
|
||||||
|
int pos1, pos2;
|
||||||
|
|
||||||
|
if (ifname)
|
||||||
|
printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal());
|
||||||
|
else
|
||||||
|
printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
|
||||||
|
|
||||||
|
size_t cols = columns(), position = pos2 - pos1 + 2;
|
||||||
char **i;
|
char **i;
|
||||||
|
|
||||||
printf("%sLink %i (%s)%s:",
|
STRV_FOREACH(i, p) {
|
||||||
ansi_highlight(), ifindex, ifname, ansi_normal());
|
size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
|
||||||
|
* If that happens, we'll just print one item per line. */
|
||||||
|
|
||||||
STRV_FOREACH(i, p)
|
if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
|
||||||
printf(" %s", *i);
|
printf(" %s", *i);
|
||||||
|
position = size_add(size_add(position, 1), our_len);
|
||||||
|
} else {
|
||||||
|
printf("\n%*s%s", indent, "", *i);
|
||||||
|
position = size_add(our_len, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct link_info {
|
static int status_print_strv_global(char **p) {
|
||||||
|
return status_print_strv_ifindex(0, NULL, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct LinkInfo {
|
||||||
uint64_t scopes_mask;
|
uint64_t scopes_mask;
|
||||||
const char *llmnr;
|
const char *llmnr;
|
||||||
const char *mdns;
|
const char *mdns;
|
||||||
@ -1327,9 +1350,26 @@ struct link_info {
|
|||||||
char **ntas;
|
char **ntas;
|
||||||
bool dnssec_supported;
|
bool dnssec_supported;
|
||||||
bool default_route;
|
bool default_route;
|
||||||
};
|
} LinkInfo;
|
||||||
|
|
||||||
static void link_info_clear(struct link_info *p) {
|
typedef struct GlobalInfo {
|
||||||
|
char *current_dns;
|
||||||
|
char *current_dns_ex;
|
||||||
|
char **dns;
|
||||||
|
char **dns_ex;
|
||||||
|
char **fallback_dns;
|
||||||
|
char **fallback_dns_ex;
|
||||||
|
char **domains;
|
||||||
|
char **ntas;
|
||||||
|
const char *llmnr;
|
||||||
|
const char *mdns;
|
||||||
|
const char *dns_over_tls;
|
||||||
|
const char *dnssec;
|
||||||
|
const char *resolv_conf_mode;
|
||||||
|
bool dnssec_supported;
|
||||||
|
} GlobalInfo;
|
||||||
|
|
||||||
|
static void link_info_clear(LinkInfo *p) {
|
||||||
free(p->current_dns);
|
free(p->current_dns);
|
||||||
free(p->current_dns_ex);
|
free(p->current_dns_ex);
|
||||||
strv_free(p->dns);
|
strv_free(p->dns);
|
||||||
@ -1338,6 +1378,17 @@ static void link_info_clear(struct link_info *p) {
|
|||||||
strv_free(p->ntas);
|
strv_free(p->ntas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void global_info_clear(GlobalInfo *p) {
|
||||||
|
free(p->current_dns);
|
||||||
|
free(p->current_dns_ex);
|
||||||
|
strv_free(p->dns);
|
||||||
|
strv_free(p->dns_ex);
|
||||||
|
strv_free(p->fallback_dns);
|
||||||
|
strv_free(p->fallback_dns_ex);
|
||||||
|
strv_free(p->domains);
|
||||||
|
strv_free(p->ntas);
|
||||||
|
}
|
||||||
|
|
||||||
static int dump_list(Table *table, const char *prefix, char * const *l) {
|
static int dump_list(Table *table, const char *prefix, char * const *l) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -1346,33 +1397,88 @@ static int dump_list(Table *table, const char *prefix, char * const *l) {
|
|||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, prefix,
|
TABLE_STRING, prefix,
|
||||||
TABLE_STRV, l);
|
TABLE_STRV_WRAPPED, l);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
r = parse_boolean(value);
|
||||||
|
if (r >= 0)
|
||||||
|
return strv_extendf(strv, "%s%s", plus_minus(r), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strv_extendf(strv, "%s=%s", name, value ?: "???");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** link_protocol_status(const LinkInfo *info) {
|
||||||
|
_cleanup_strv_free_ char **s = NULL;
|
||||||
|
|
||||||
|
if (strv_extendf(&s, "%sDefaultRoute", plus_minus(info->default_route)) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extendf(&s, "DNSSEC=%s/%s",
|
||||||
|
info->dnssec ?: "???",
|
||||||
|
info->dnssec_supported ? "supported" : "unsupported") < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return TAKE_PTR(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** global_protocol_status(const GlobalInfo *info) {
|
||||||
|
_cleanup_strv_free_ char **s = NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extendf(&s, "DNSSEC=%s/%s",
|
||||||
|
info->dnssec ?: "???",
|
||||||
|
info->dnssec_supported ? "supported" : "unsupported") < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return TAKE_PTR(s);
|
||||||
|
}
|
||||||
|
|
||||||
static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
|
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[] = {
|
static const struct bus_properties_map property_map[] = {
|
||||||
{ "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
|
{ "ScopesMask", "t", NULL, offsetof(LinkInfo, scopes_mask) },
|
||||||
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
|
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(LinkInfo, dns) },
|
||||||
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(struct link_info, dns_ex) },
|
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(LinkInfo, dns_ex) },
|
||||||
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
|
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(LinkInfo, current_dns) },
|
||||||
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex) },
|
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(LinkInfo, current_dns_ex) },
|
||||||
{ "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
|
{ "Domains", "a(sb)", map_link_domains, offsetof(LinkInfo, domains) },
|
||||||
{ "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
|
{ "DefaultRoute", "b", NULL, offsetof(LinkInfo, default_route) },
|
||||||
{ "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
|
{ "LLMNR", "s", NULL, offsetof(LinkInfo, llmnr) },
|
||||||
{ "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
|
{ "MulticastDNS", "s", NULL, offsetof(LinkInfo, mdns) },
|
||||||
{ "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
|
{ "DNSOverTLS", "s", NULL, offsetof(LinkInfo, dns_over_tls) },
|
||||||
{ "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
|
{ "DNSSEC", "s", NULL, offsetof(LinkInfo, dnssec) },
|
||||||
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
|
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(LinkInfo, ntas) },
|
||||||
{ "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
|
{ "DNSSECSupported", "b", NULL, offsetof(LinkInfo, dnssec_supported) },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
_cleanup_(link_info_clear) struct link_info link_info = {};
|
_cleanup_(link_info_clear) LinkInfo link_info = {};
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *p = NULL;
|
_cleanup_free_ char *p = NULL;
|
||||||
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
|
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
|
||||||
@ -1496,19 +1602,13 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **pstatus = link_protocol_status(&link_info);
|
||||||
|
if (!pstatus)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, "DefaultRoute setting:",
|
TABLE_STRING, "Protocols:",
|
||||||
TABLE_BOOLEAN, link_info.default_route,
|
TABLE_STRV_WRAPPED, pstatus);
|
||||||
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)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
@ -1631,71 +1731,32 @@ static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
return 0;
|
strv_sort(*l);
|
||||||
}
|
|
||||||
|
|
||||||
static int status_print_strv_global(char **p) {
|
|
||||||
char **i;
|
|
||||||
|
|
||||||
printf("%sGlobal%s:", ansi_highlight(), ansi_normal());
|
|
||||||
|
|
||||||
STRV_FOREACH(i, p)
|
|
||||||
printf(" %s", *i);
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct global_info {
|
|
||||||
char *current_dns;
|
|
||||||
char *current_dns_ex;
|
|
||||||
char **dns;
|
|
||||||
char **dns_ex;
|
|
||||||
char **fallback_dns;
|
|
||||||
char **fallback_dns_ex;
|
|
||||||
char **domains;
|
|
||||||
char **ntas;
|
|
||||||
const char *llmnr;
|
|
||||||
const char *mdns;
|
|
||||||
const char *dns_over_tls;
|
|
||||||
const char *dnssec;
|
|
||||||
const char *resolv_conf_mode;
|
|
||||||
bool dnssec_supported;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void global_info_clear(struct global_info *p) {
|
|
||||||
free(p->current_dns);
|
|
||||||
free(p->current_dns_ex);
|
|
||||||
strv_free(p->dns);
|
|
||||||
strv_free(p->dns_ex);
|
|
||||||
strv_free(p->fallback_dns);
|
|
||||||
strv_free(p->fallback_dns_ex);
|
|
||||||
strv_free(p->domains);
|
|
||||||
strv_free(p->ntas);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
|
static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
|
||||||
static const struct bus_properties_map property_map[] = {
|
static const struct bus_properties_map property_map[] = {
|
||||||
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
|
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, dns) },
|
||||||
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, dns_ex) },
|
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, dns_ex) },
|
||||||
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
|
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, fallback_dns) },
|
||||||
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, fallback_dns_ex) },
|
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, fallback_dns_ex) },
|
||||||
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
|
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(GlobalInfo, current_dns) },
|
||||||
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex) },
|
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(GlobalInfo, current_dns_ex) },
|
||||||
{ "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
|
{ "Domains", "a(isb)", map_global_domains, offsetof(GlobalInfo, domains) },
|
||||||
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
|
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(GlobalInfo, ntas) },
|
||||||
{ "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) },
|
{ "LLMNR", "s", NULL, offsetof(GlobalInfo, llmnr) },
|
||||||
{ "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) },
|
{ "MulticastDNS", "s", NULL, offsetof(GlobalInfo, mdns) },
|
||||||
{ "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) },
|
{ "DNSOverTLS", "s", NULL, offsetof(GlobalInfo, dns_over_tls) },
|
||||||
{ "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) },
|
{ "DNSSEC", "s", NULL, offsetof(GlobalInfo, dnssec) },
|
||||||
{ "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
|
{ "DNSSECSupported", "b", NULL, offsetof(GlobalInfo, dnssec_supported) },
|
||||||
{ "ResolvConfMode", "s", NULL, offsetof(struct global_info, resolv_conf_mode) },
|
{ "ResolvConfMode", "s", NULL, offsetof(GlobalInfo, resolv_conf_mode) },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
_cleanup_(global_info_clear) struct global_info global_info = {};
|
_cleanup_(global_info_clear) GlobalInfo global_info = {};
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -1760,18 +1821,14 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
|
|||||||
|
|
||||||
table_set_header(table, false);
|
table_set_header(table, false);
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
|
||||||
|
if (!pstatus)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, "LLMNR setting:",
|
TABLE_STRING, "Protocols:",
|
||||||
TABLE_SET_ALIGN_PERCENT, 100,
|
TABLE_SET_ALIGN_PERCENT, 100,
|
||||||
TABLE_STRING, strna(global_info.llmnr),
|
TABLE_STRV_WRAPPED, pstatus);
|
||||||
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)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
|
@ -25,6 +25,23 @@ int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_err
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bus_map_strv_sort(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
char ***p = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = bus_message_read_strv_extend(m, &l);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = strv_extend_strv(p, l, false);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
strv_sort(*p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
|
static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
|
||||||
char type;
|
char type;
|
||||||
int r;
|
int r;
|
||||||
|
@ -18,6 +18,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
|
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
|
||||||
|
int bus_map_strv_sort(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
|
||||||
|
|
||||||
int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
|
int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
|
||||||
int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
|
int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
|
||||||
|
@ -66,6 +66,7 @@ typedef struct TableData {
|
|||||||
|
|
||||||
size_t minimum_width; /* minimum width for the column */
|
size_t minimum_width; /* minimum width for the column */
|
||||||
size_t maximum_width; /* maximum width for the column */
|
size_t maximum_width; /* maximum width for the column */
|
||||||
|
size_t formatted_for_width; /* the width we tried to format for */
|
||||||
unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
|
unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
|
||||||
unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
|
unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
|
||||||
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
|
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
|
||||||
@ -164,7 +165,6 @@ Table *table_new_raw(size_t n_columns) {
|
|||||||
Table *table_new_internal(const char *first_header, ...) {
|
Table *table_new_internal(const char *first_header, ...) {
|
||||||
_cleanup_(table_unrefp) Table *t = NULL;
|
_cleanup_(table_unrefp) Table *t = NULL;
|
||||||
size_t n_columns = 1;
|
size_t n_columns = 1;
|
||||||
const char *h;
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -172,8 +172,7 @@ Table *table_new_internal(const char *first_header, ...) {
|
|||||||
|
|
||||||
va_start(ap, first_header);
|
va_start(ap, first_header);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
h = va_arg(ap, const char*);
|
if (!va_arg(ap, const char*))
|
||||||
if (!h)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
n_columns++;
|
n_columns++;
|
||||||
@ -185,7 +184,7 @@ Table *table_new_internal(const char *first_header, ...) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
va_start(ap, first_header);
|
va_start(ap, first_header);
|
||||||
for (h = first_header; h; h = va_arg(ap, const char*)) {
|
for (const char *h = first_header; h; h = va_arg(ap, const char*)) {
|
||||||
TableCell *cell;
|
TableCell *cell;
|
||||||
|
|
||||||
r = table_add_cell(t, &cell, TABLE_STRING, h);
|
r = table_add_cell(t, &cell, TABLE_STRING, h);
|
||||||
@ -213,7 +212,7 @@ static TableData *table_data_free(TableData *d) {
|
|||||||
free(d->formatted);
|
free(d->formatted);
|
||||||
free(d->url);
|
free(d->url);
|
||||||
|
|
||||||
if (d->type == TABLE_STRV)
|
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED))
|
||||||
strv_free(d->strv);
|
strv_free(d->strv);
|
||||||
|
|
||||||
return mfree(d);
|
return mfree(d);
|
||||||
@ -223,12 +222,10 @@ DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(TableData, table_data, table_data_free);
|
|||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
|
||||||
|
|
||||||
Table *table_unref(Table *t) {
|
Table *table_unref(Table *t) {
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!t)
|
if (!t)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < t->n_cells; i++)
|
for (size_t i = 0; i < t->n_cells; i++)
|
||||||
table_data_unref(t->data[i]);
|
table_data_unref(t->data[i]);
|
||||||
|
|
||||||
free(t->data);
|
free(t->data);
|
||||||
@ -252,6 +249,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
|
|||||||
return strlen(data) + 1;
|
return strlen(data) + 1;
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
return sizeof(char **);
|
return sizeof(char **);
|
||||||
|
|
||||||
case TABLE_BOOLEAN:
|
case TABLE_BOOLEAN:
|
||||||
@ -376,7 +374,7 @@ static TableData *table_data_new(
|
|||||||
d->align_percent = align_percent;
|
d->align_percent = align_percent;
|
||||||
d->ellipsize_percent = ellipsize_percent;
|
d->ellipsize_percent = ellipsize_percent;
|
||||||
|
|
||||||
if (type == TABLE_STRV) {
|
if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) {
|
||||||
d->strv = strv_copy(data);
|
d->strv = strv_copy(data);
|
||||||
if (!d->strv)
|
if (!d->strv)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -817,6 +815,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
data = va_arg(ap, char * const *);
|
data = va_arg(ap, char * const *);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1047,11 +1046,9 @@ int table_set_empty_string(Table *t, const char *empty) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int table_set_display_all(Table *t) {
|
int table_set_display_all(Table *t) {
|
||||||
size_t allocated;
|
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
|
|
||||||
allocated = t->n_display_map;
|
size_t allocated = t->n_display_map;
|
||||||
|
|
||||||
if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, allocated)))
|
if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, allocated)))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -1124,7 +1121,6 @@ int table_set_sort(Table *t, size_t first_column, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int table_hide_column_from_display(Table *t, size_t column) {
|
int table_hide_column_from_display(Table *t, size_t column) {
|
||||||
size_t allocated, cur = 0;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
@ -1137,7 +1133,7 @@ int table_hide_column_from_display(Table *t, size_t column) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
allocated = t->n_display_map;
|
size_t allocated = t->n_display_map, cur = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < allocated; i++) {
|
for (size_t i = 0; i < allocated; i++) {
|
||||||
if (t->display_map[i] == column)
|
if (t->display_map[i] == column)
|
||||||
@ -1169,6 +1165,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
|
|||||||
return path_compare(a->string, b->string);
|
return path_compare(a->string, b->string);
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
return strv_compare(a->strv, b->strv);
|
return strv_compare(a->strv, b->strv);
|
||||||
|
|
||||||
case TABLE_BOOLEAN:
|
case TABLE_BOOLEAN:
|
||||||
@ -1247,7 +1244,6 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
||||||
size_t i;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
@ -1262,7 +1258,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Order other lines by the sorting map */
|
/* Order other lines by the sorting map */
|
||||||
for (i = 0; i < t->n_sort_map; i++) {
|
for (size_t i = 0; i < t->n_sort_map; i++) {
|
||||||
TableData *d, *dd;
|
TableData *d, *dd;
|
||||||
|
|
||||||
d = t->data[*a + t->sort_map[i]];
|
d = t->data[*a + t->sort_map[i]];
|
||||||
@ -1277,10 +1273,46 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
|||||||
return CMP(*a, *b);
|
return CMP(*a, *b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) {
|
static char* format_strv_width(char **strv, size_t column_width) {
|
||||||
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
|
size_t sz = 0;
|
||||||
|
_cleanup_free_ char *buf = NULL;
|
||||||
|
|
||||||
|
f = open_memstream_unlocked(&buf, &sz);
|
||||||
|
if (!f)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t position = 0;
|
||||||
|
char **p;
|
||||||
|
STRV_FOREACH(p, strv) {
|
||||||
|
size_t our_len = utf8_console_width(*p); /* This returns -1 on invalid utf-8 (which shouldn't happen).
|
||||||
|
* If that happens, we'll just print one item per line. */
|
||||||
|
|
||||||
|
if (position == 0) {
|
||||||
|
fputs(*p, f);
|
||||||
|
position = our_len;
|
||||||
|
} else if (size_add(size_add(position, 1), our_len) <= column_width) {
|
||||||
|
fprintf(f, " %s", *p);
|
||||||
|
position = size_add(size_add(position, 1), our_len);
|
||||||
|
} else {
|
||||||
|
fprintf(f, "\n%s", *p);
|
||||||
|
position = our_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fflush_and_check(f) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
f = safe_fclose(f);
|
||||||
|
return TAKE_PTR(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing, size_t column_width, bool *have_soft) {
|
||||||
assert(d);
|
assert(d);
|
||||||
|
|
||||||
if (d->formatted)
|
if (d->formatted &&
|
||||||
|
/* Only TABLE_STRV_WRAPPED adjust based on column_width so far… */
|
||||||
|
(d->type != TABLE_STRV_WRAPPED || d->formatted_for_width == column_width))
|
||||||
return d->formatted;
|
return d->formatted;
|
||||||
|
|
||||||
switch (d->type) {
|
switch (d->type) {
|
||||||
@ -1290,13 +1322,12 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
|
|||||||
case TABLE_STRING:
|
case TABLE_STRING:
|
||||||
case TABLE_PATH:
|
case TABLE_PATH:
|
||||||
if (d->uppercase && !avoid_uppercasing) {
|
if (d->uppercase && !avoid_uppercasing) {
|
||||||
char *p, *q;
|
|
||||||
|
|
||||||
d->formatted = new(char, strlen(d->string) + 1);
|
d->formatted = new(char, strlen(d->string) + 1);
|
||||||
if (!d->formatted)
|
if (!d->formatted)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (p = d->string, q = d->formatted; *p; p++, q++)
|
char *q = d->formatted;
|
||||||
|
for (char *p = d->string; *p; p++, q++)
|
||||||
*q = (char) toupper((unsigned char) *p);
|
*q = (char) toupper((unsigned char) *p);
|
||||||
*q = 0;
|
*q = 0;
|
||||||
|
|
||||||
@ -1305,17 +1336,28 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
|
|||||||
|
|
||||||
return d->string;
|
return d->string;
|
||||||
|
|
||||||
case TABLE_STRV: {
|
case TABLE_STRV:
|
||||||
char *p;
|
|
||||||
|
|
||||||
if (strv_isempty(d->strv))
|
if (strv_isempty(d->strv))
|
||||||
return strempty(t->empty_string);
|
return strempty(t->empty_string);
|
||||||
|
|
||||||
p = strv_join(d->strv, "\n");
|
d->formatted = strv_join(d->strv, "\n");
|
||||||
if (!p)
|
if (!d->formatted)
|
||||||
|
return NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TABLE_STRV_WRAPPED: {
|
||||||
|
if (strv_isempty(d->strv))
|
||||||
|
return strempty(t->empty_string);
|
||||||
|
|
||||||
|
char *buf = format_strv_width(d->strv, column_width);
|
||||||
|
if (!buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
d->formatted = p;
|
free_and_replace(d->formatted, buf);
|
||||||
|
d->formatted_for_width = column_width;
|
||||||
|
if (have_soft)
|
||||||
|
*have_soft = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1632,16 +1674,19 @@ static int console_width_height(
|
|||||||
static int table_data_requested_width_height(
|
static int table_data_requested_width_height(
|
||||||
Table *table,
|
Table *table,
|
||||||
TableData *d,
|
TableData *d,
|
||||||
|
size_t available_width,
|
||||||
size_t *ret_width,
|
size_t *ret_width,
|
||||||
size_t *ret_height) {
|
size_t *ret_height,
|
||||||
|
bool *have_soft) {
|
||||||
|
|
||||||
_cleanup_free_ char *truncated = NULL;
|
_cleanup_free_ char *truncated = NULL;
|
||||||
bool truncation_applied = false;
|
bool truncation_applied = false;
|
||||||
size_t width, height;
|
size_t width, height;
|
||||||
const char *t;
|
const char *t;
|
||||||
int r;
|
int r;
|
||||||
|
bool soft = false;
|
||||||
|
|
||||||
t = table_data_format(table, d, false);
|
t = table_data_format(table, d, false, available_width, &soft);
|
||||||
if (!t)
|
if (!t)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -1669,6 +1714,8 @@ static int table_data_requested_width_height(
|
|||||||
*ret_width = width;
|
*ret_width = width;
|
||||||
if (ret_height)
|
if (ret_height)
|
||||||
*ret_height = height;
|
*ret_height = height;
|
||||||
|
if (have_soft && soft)
|
||||||
|
*have_soft = true;
|
||||||
|
|
||||||
return truncation_applied;
|
return truncation_applied;
|
||||||
}
|
}
|
||||||
@ -1678,7 +1725,6 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
|
|||||||
_cleanup_free_ char *clickable = NULL;
|
_cleanup_free_ char *clickable = NULL;
|
||||||
const char *p;
|
const char *p;
|
||||||
char *ret;
|
char *ret;
|
||||||
size_t i;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
|
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
|
||||||
@ -1723,10 +1769,10 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < lspace; i++)
|
for (size_t i = 0; i < lspace; i++)
|
||||||
ret[i] = ' ';
|
ret[i] = ' ';
|
||||||
memcpy(ret + lspace, clickable ?: str, clickable_length);
|
memcpy(ret + lspace, clickable ?: str, clickable_length);
|
||||||
for (i = lspace + clickable_length; i < space + clickable_length; i++)
|
for (size_t i = lspace + clickable_length; i < space + clickable_length; i++)
|
||||||
ret[i] = ' ';
|
ret[i] = ' ';
|
||||||
|
|
||||||
ret[space + clickable_length] = 0;
|
ret[space + clickable_length] = 0;
|
||||||
@ -1740,7 +1786,7 @@ static bool table_data_isempty(TableData *d) {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Let's also consider an empty strv as truly empty. */
|
/* Let's also consider an empty strv as truly empty. */
|
||||||
if (d->type == TABLE_STRV)
|
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED))
|
||||||
return strv_isempty(d->strv);
|
return strv_isempty(d->strv);
|
||||||
|
|
||||||
/* Note that an empty string we do not consider empty here! */
|
/* Note that an empty string we do not consider empty here! */
|
||||||
@ -1771,8 +1817,8 @@ static const char* table_data_rgap_color(TableData *d) {
|
|||||||
|
|
||||||
int table_print(Table *t, FILE *f) {
|
int table_print(Table *t, FILE *f) {
|
||||||
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
|
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
|
||||||
i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
|
table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
|
||||||
*width;
|
*width = NULL;
|
||||||
_cleanup_free_ size_t *sorted = NULL;
|
_cleanup_free_ size_t *sorted = NULL;
|
||||||
uint64_t *column_weight, weight_sum;
|
uint64_t *column_weight, weight_sum;
|
||||||
int r;
|
int r;
|
||||||
@ -1795,7 +1841,7 @@ int table_print(Table *t, FILE *f) {
|
|||||||
if (!sorted)
|
if (!sorted)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < n_rows; i++)
|
for (size_t i = 0; i < n_rows; i++)
|
||||||
sorted[i] = i * t->n_columns;
|
sorted[i] = i * t->n_columns;
|
||||||
|
|
||||||
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
||||||
@ -1811,205 +1857,225 @@ int table_print(Table *t, FILE *f) {
|
|||||||
minimum_width = newa(size_t, display_columns);
|
minimum_width = newa(size_t, display_columns);
|
||||||
maximum_width = newa(size_t, display_columns);
|
maximum_width = newa(size_t, display_columns);
|
||||||
requested_width = newa(size_t, display_columns);
|
requested_width = newa(size_t, display_columns);
|
||||||
width = newa(size_t, display_columns);
|
|
||||||
column_weight = newa0(uint64_t, display_columns);
|
column_weight = newa0(uint64_t, display_columns);
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
minimum_width[j] = 1;
|
minimum_width[j] = 1;
|
||||||
maximum_width[j] = (size_t) -1;
|
maximum_width[j] = (size_t) -1;
|
||||||
requested_width[j] = (size_t) -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First pass: determine column sizes */
|
for (unsigned pass = 0; pass < 2; pass++) {
|
||||||
for (i = t->header ? 0 : 1; i < n_rows; i++) {
|
/* First pass: determine column sizes */
|
||||||
TableData **row;
|
|
||||||
|
|
||||||
/* Note that we don't care about ordering at this time, as we just want to determine column sizes,
|
for (size_t j = 0; j < display_columns; j++)
|
||||||
* hence we don't care for sorted[] during the first pass. */
|
requested_width[j] = (size_t) -1;
|
||||||
row = t->data + i * t->n_columns;
|
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
bool any_soft = false;
|
||||||
TableData *d;
|
|
||||||
size_t req_width, req_height;
|
|
||||||
|
|
||||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) {
|
||||||
|
TableData **row;
|
||||||
|
|
||||||
r = table_data_requested_width_height(t, d, &req_width, &req_height);
|
/* Note that we don't care about ordering at this time, as we just want to determine column sizes,
|
||||||
if (r < 0)
|
* hence we don't care for sorted[] during the first pass. */
|
||||||
return r;
|
row = t->data + i * t->n_columns;
|
||||||
if (r > 0) { /* Truncated because too many lines? */
|
|
||||||
_cleanup_free_ char *last = NULL;
|
|
||||||
const char *field;
|
|
||||||
|
|
||||||
/* If we are going to show only the first few lines of a cell that has
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
* multiple make sure that we have enough space horizontally to show an
|
TableData *d;
|
||||||
* ellipsis. Hence, let's figure out the last line, and account for its
|
size_t req_width, req_height;
|
||||||
* length plus ellipsis. */
|
|
||||||
|
|
||||||
field = table_data_format(t, d, false);
|
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||||
if (!field)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
assert_se(t->cell_height_max > 0);
|
r = table_data_requested_width_height(t, d,
|
||||||
r = string_extract_line(field, t->cell_height_max-1, &last);
|
width ? width[j] : SIZE_MAX,
|
||||||
|
&req_width, &req_height, &any_soft);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
if (r > 0) { /* Truncated because too many lines? */
|
||||||
|
_cleanup_free_ char *last = NULL;
|
||||||
|
const char *field;
|
||||||
|
|
||||||
req_width = MAX(req_width,
|
/* If we are going to show only the first few lines of a cell that has
|
||||||
utf8_console_width(last) +
|
* multiple make sure that we have enough space horizontally to show an
|
||||||
utf8_console_width(special_glyph(SPECIAL_GLYPH_ELLIPSIS)));
|
* ellipsis. Hence, let's figure out the last line, and account for its
|
||||||
|
* length plus ellipsis. */
|
||||||
|
|
||||||
|
field = table_data_format(t, d, false,
|
||||||
|
width ? width[j] : SIZE_MAX,
|
||||||
|
&any_soft);
|
||||||
|
if (!field)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
assert_se(t->cell_height_max > 0);
|
||||||
|
r = string_extract_line(field, t->cell_height_max-1, &last);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
req_width = MAX(req_width,
|
||||||
|
utf8_console_width(last) +
|
||||||
|
utf8_console_width(special_glyph(SPECIAL_GLYPH_ELLIPSIS)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine the biggest width that any cell in this column would like to have */
|
||||||
|
if (requested_width[j] == (size_t) -1 ||
|
||||||
|
requested_width[j] < req_width)
|
||||||
|
requested_width[j] = req_width;
|
||||||
|
|
||||||
|
/* Determine the minimum width any cell in this column needs */
|
||||||
|
if (minimum_width[j] < d->minimum_width)
|
||||||
|
minimum_width[j] = d->minimum_width;
|
||||||
|
|
||||||
|
/* Determine the maximum width any cell in this column needs */
|
||||||
|
if (d->maximum_width != (size_t) -1 &&
|
||||||
|
(maximum_width[j] == (size_t) -1 ||
|
||||||
|
maximum_width[j] > d->maximum_width))
|
||||||
|
maximum_width[j] = d->maximum_width;
|
||||||
|
|
||||||
|
/* Determine the full columns weight */
|
||||||
|
column_weight[j] += d->weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine the biggest width that any cell in this column would like to have */
|
|
||||||
if (requested_width[j] == (size_t) -1 ||
|
|
||||||
requested_width[j] < req_width)
|
|
||||||
requested_width[j] = req_width;
|
|
||||||
|
|
||||||
/* Determine the minimum width any cell in this column needs */
|
|
||||||
if (minimum_width[j] < d->minimum_width)
|
|
||||||
minimum_width[j] = d->minimum_width;
|
|
||||||
|
|
||||||
/* Determine the maximum width any cell in this column needs */
|
|
||||||
if (d->maximum_width != (size_t) -1 &&
|
|
||||||
(maximum_width[j] == (size_t) -1 ||
|
|
||||||
maximum_width[j] > d->maximum_width))
|
|
||||||
maximum_width[j] = d->maximum_width;
|
|
||||||
|
|
||||||
/* Determine the full columns weight */
|
|
||||||
column_weight[j] += d->weight;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* One space between each column */
|
/* One space between each column */
|
||||||
table_requested_width = table_minimum_width = table_maximum_width = display_columns - 1;
|
table_requested_width = table_minimum_width = table_maximum_width = display_columns - 1;
|
||||||
|
|
||||||
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
|
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
|
||||||
weight_sum = 0;
|
weight_sum = 0;
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
weight_sum += column_weight[j];
|
weight_sum += column_weight[j];
|
||||||
|
|
||||||
table_minimum_width += minimum_width[j];
|
table_minimum_width += minimum_width[j];
|
||||||
|
|
||||||
if (maximum_width[j] == (size_t) -1)
|
if (maximum_width[j] == (size_t) -1)
|
||||||
table_maximum_width = (size_t) -1;
|
table_maximum_width = (size_t) -1;
|
||||||
|
else
|
||||||
|
table_maximum_width += maximum_width[j];
|
||||||
|
|
||||||
|
table_requested_width += requested_width[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate effective table width */
|
||||||
|
if (t->width != 0 && t->width != (size_t) -1)
|
||||||
|
table_effective_width = t->width;
|
||||||
|
else if (t->width == 0 ||
|
||||||
|
((pass > 0 || !any_soft) && (pager_have() || !isatty(STDOUT_FILENO))))
|
||||||
|
table_effective_width = table_requested_width;
|
||||||
else
|
else
|
||||||
table_maximum_width += maximum_width[j];
|
table_effective_width = MIN(table_requested_width, columns());
|
||||||
|
|
||||||
table_requested_width += requested_width[j];
|
if (table_maximum_width != (size_t) -1 && table_effective_width > table_maximum_width)
|
||||||
}
|
table_effective_width = table_maximum_width;
|
||||||
|
|
||||||
/* Calculate effective table width */
|
if (table_effective_width < table_minimum_width)
|
||||||
if (t->width != 0 && t->width != (size_t) -1)
|
table_effective_width = table_minimum_width;
|
||||||
table_effective_width = t->width;
|
|
||||||
else if (t->width == 0 || pager_have() || !isatty(STDOUT_FILENO))
|
|
||||||
table_effective_width = table_requested_width;
|
|
||||||
else
|
|
||||||
table_effective_width = MIN(table_requested_width, columns());
|
|
||||||
|
|
||||||
if (table_maximum_width != (size_t) -1 && table_effective_width > table_maximum_width)
|
if (!width)
|
||||||
table_effective_width = table_maximum_width;
|
width = newa(size_t, display_columns);
|
||||||
|
|
||||||
if (table_effective_width < table_minimum_width)
|
if (table_effective_width >= table_requested_width) {
|
||||||
table_effective_width = table_minimum_width;
|
size_t extra;
|
||||||
|
|
||||||
if (table_effective_width >= table_requested_width) {
|
/* We have extra room, let's distribute it among columns according to their weights. We first provide
|
||||||
size_t extra;
|
* each column with what it asked for and the distribute the rest. */
|
||||||
|
|
||||||
/* We have extra room, let's distribute it among columns according to their weights. We first provide
|
extra = table_effective_width - table_requested_width;
|
||||||
* each column with what it asked for and the distribute the rest. */
|
|
||||||
|
|
||||||
extra = table_effective_width - table_requested_width;
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
|
size_t delta;
|
||||||
for (j = 0; j < display_columns; j++) {
|
|
||||||
size_t delta;
|
|
||||||
|
|
||||||
if (weight_sum == 0)
|
|
||||||
width[j] = requested_width[j] + extra / (display_columns - j); /* Avoid division by zero */
|
|
||||||
else
|
|
||||||
width[j] = requested_width[j] + (extra * column_weight[j]) / weight_sum;
|
|
||||||
|
|
||||||
if (maximum_width[j] != (size_t) -1 && width[j] > maximum_width[j])
|
|
||||||
width[j] = maximum_width[j];
|
|
||||||
|
|
||||||
if (width[j] < minimum_width[j])
|
|
||||||
width[j] = minimum_width[j];
|
|
||||||
|
|
||||||
assert(width[j] >= requested_width[j]);
|
|
||||||
delta = width[j] - requested_width[j];
|
|
||||||
|
|
||||||
/* Subtract what we just added from the rest */
|
|
||||||
if (extra > delta)
|
|
||||||
extra -= delta;
|
|
||||||
else
|
|
||||||
extra = 0;
|
|
||||||
|
|
||||||
assert(weight_sum >= column_weight[j]);
|
|
||||||
weight_sum -= column_weight[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* We need to compress the table, columns can't get what they asked for. We first provide each column
|
|
||||||
* with the minimum they need, and then distribute anything left. */
|
|
||||||
bool finalize = false;
|
|
||||||
size_t extra;
|
|
||||||
|
|
||||||
extra = table_effective_width - table_minimum_width;
|
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++)
|
|
||||||
width[j] = (size_t) -1;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
bool restart = false;
|
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
|
||||||
size_t delta, w;
|
|
||||||
|
|
||||||
/* Did this column already get something assigned? If so, let's skip to the next */
|
|
||||||
if (width[j] != (size_t) -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (weight_sum == 0)
|
if (weight_sum == 0)
|
||||||
w = minimum_width[j] + extra / (display_columns - j); /* avoid division by zero */
|
width[j] = requested_width[j] + extra / (display_columns - j); /* Avoid division by zero */
|
||||||
else
|
else
|
||||||
w = minimum_width[j] + (extra * column_weight[j]) / weight_sum;
|
width[j] = requested_width[j] + (extra * column_weight[j]) / weight_sum;
|
||||||
|
|
||||||
if (w >= requested_width[j]) {
|
if (maximum_width[j] != (size_t) -1 && width[j] > maximum_width[j])
|
||||||
/* Never give more than requested. If we hit a column like this, there's more
|
width[j] = maximum_width[j];
|
||||||
* space to allocate to other columns which means we need to restart the
|
|
||||||
* iteration. However, if we hit a column like this, let's assign it the space
|
|
||||||
* it wanted for good early.*/
|
|
||||||
|
|
||||||
w = requested_width[j];
|
if (width[j] < minimum_width[j])
|
||||||
restart = true;
|
width[j] = minimum_width[j];
|
||||||
|
|
||||||
} else if (!finalize)
|
assert(width[j] >= requested_width[j]);
|
||||||
continue;
|
delta = width[j] - requested_width[j];
|
||||||
|
|
||||||
width[j] = w;
|
/* Subtract what we just added from the rest */
|
||||||
|
if (extra > delta)
|
||||||
assert(w >= minimum_width[j]);
|
extra -= delta;
|
||||||
delta = w - minimum_width[j];
|
else
|
||||||
|
extra = 0;
|
||||||
assert(delta <= extra);
|
|
||||||
extra -= delta;
|
|
||||||
|
|
||||||
assert(weight_sum >= column_weight[j]);
|
assert(weight_sum >= column_weight[j]);
|
||||||
weight_sum -= column_weight[j];
|
weight_sum -= column_weight[j];
|
||||||
|
|
||||||
if (restart && !finalize)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalize)
|
break; /* Every column should be happy, no need to repeat calculations. */
|
||||||
break;
|
} else {
|
||||||
|
/* We need to compress the table, columns can't get what they asked for. We first provide each column
|
||||||
|
* with the minimum they need, and then distribute anything left. */
|
||||||
|
bool finalize = false;
|
||||||
|
size_t extra;
|
||||||
|
|
||||||
if (!restart)
|
extra = table_effective_width - table_minimum_width;
|
||||||
finalize = true;
|
|
||||||
|
for (size_t j = 0; j < display_columns; j++)
|
||||||
|
width[j] = (size_t) -1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
bool restart = false;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
|
size_t delta, w;
|
||||||
|
|
||||||
|
/* Did this column already get something assigned? If so, let's skip to the next */
|
||||||
|
if (width[j] != (size_t) -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (weight_sum == 0)
|
||||||
|
w = minimum_width[j] + extra / (display_columns - j); /* avoid division by zero */
|
||||||
|
else
|
||||||
|
w = minimum_width[j] + (extra * column_weight[j]) / weight_sum;
|
||||||
|
|
||||||
|
if (w >= requested_width[j]) {
|
||||||
|
/* Never give more than requested. If we hit a column like this, there's more
|
||||||
|
* space to allocate to other columns which means we need to restart the
|
||||||
|
* iteration. However, if we hit a column like this, let's assign it the space
|
||||||
|
* it wanted for good early.*/
|
||||||
|
|
||||||
|
w = requested_width[j];
|
||||||
|
restart = true;
|
||||||
|
|
||||||
|
} else if (!finalize)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
width[j] = w;
|
||||||
|
|
||||||
|
assert(w >= minimum_width[j]);
|
||||||
|
delta = w - minimum_width[j];
|
||||||
|
|
||||||
|
assert(delta <= extra);
|
||||||
|
extra -= delta;
|
||||||
|
|
||||||
|
assert(weight_sum >= column_weight[j]);
|
||||||
|
weight_sum -= column_weight[j];
|
||||||
|
|
||||||
|
if (restart && !finalize)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalize)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!restart)
|
||||||
|
finalize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!any_soft) /* Some columns got less than requested. If some cells were "soft",
|
||||||
|
* let's try to reformat them with the new widths. Otherwise, let's
|
||||||
|
* move on. */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Second pass: show output */
|
/* Second pass: show output */
|
||||||
for (i = t->header ? 0 : 1; i < n_rows; i++) {
|
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) {
|
||||||
size_t n_subline = 0;
|
size_t n_subline = 0;
|
||||||
bool more_sublines;
|
bool more_sublines;
|
||||||
TableData **row;
|
TableData **row;
|
||||||
@ -2023,7 +2089,7 @@ int table_print(Table *t, FILE *f) {
|
|||||||
const char *gap_color = NULL;
|
const char *gap_color = NULL;
|
||||||
more_sublines = false;
|
more_sublines = false;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
_cleanup_free_ char *buffer = NULL, *extracted = NULL;
|
_cleanup_free_ char *buffer = NULL, *extracted = NULL;
|
||||||
bool lines_truncated = false;
|
bool lines_truncated = false;
|
||||||
const char *field, *color = NULL;
|
const char *field, *color = NULL;
|
||||||
@ -2032,7 +2098,7 @@ int table_print(Table *t, FILE *f) {
|
|||||||
|
|
||||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||||
|
|
||||||
field = table_data_format(t, d, false);
|
field = table_data_format(t, d, false, width[j], NULL);
|
||||||
if (!field)
|
if (!field)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -2247,6 +2313,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
|
|||||||
return json_variant_new_string(ret, d->string);
|
return json_variant_new_string(ret, d->string);
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
return json_variant_new_array_strv(ret, d->strv);
|
return json_variant_new_array_strv(ret, d->strv);
|
||||||
|
|
||||||
case TABLE_BOOLEAN:
|
case TABLE_BOOLEAN:
|
||||||
@ -2332,17 +2399,15 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char* string_to_json_field_name(const char *f) {
|
static char* string_to_json_field_name(const char *f) {
|
||||||
char *c, *x;
|
|
||||||
|
|
||||||
/* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
|
/* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
|
||||||
* field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
|
* field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
|
||||||
* underscores and leave everything as is. */
|
* underscores and leave everything as is. */
|
||||||
|
|
||||||
c = strdup(f);
|
char *c = strdup(f);
|
||||||
if (!c)
|
if (!c)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (x = c; *x; x++)
|
for (char *x = c; *x; x++)
|
||||||
if (isspace(*x))
|
if (isspace(*x))
|
||||||
*x = '_';
|
*x = '_';
|
||||||
|
|
||||||
@ -2352,7 +2417,7 @@ static char* string_to_json_field_name(const char *f) {
|
|||||||
int table_to_json(Table *t, JsonVariant **ret) {
|
int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
JsonVariant **rows = NULL, **elements = NULL;
|
JsonVariant **rows = NULL, **elements = NULL;
|
||||||
_cleanup_free_ size_t *sorted = NULL;
|
_cleanup_free_ size_t *sorted = NULL;
|
||||||
size_t n_rows, i, j, display_columns;
|
size_t n_rows, display_columns;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
@ -2372,7 +2437,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
|||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < n_rows; i++)
|
for (size_t i = 0; i < n_rows; i++)
|
||||||
sorted[i] = i * t->n_columns;
|
sorted[i] = i * t->n_columns;
|
||||||
|
|
||||||
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
||||||
@ -2390,7 +2455,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
|||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
_cleanup_free_ char *mangled = NULL;
|
_cleanup_free_ char *mangled = NULL;
|
||||||
const char *formatted;
|
const char *formatted;
|
||||||
TableData *d;
|
TableData *d;
|
||||||
@ -2398,7 +2463,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
|||||||
assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
|
assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
|
||||||
|
|
||||||
/* Field names must be strings, hence format whatever we got here as a string first */
|
/* Field names must be strings, hence format whatever we got here as a string first */
|
||||||
formatted = table_data_format(t, d, true);
|
formatted = table_data_format(t, d, true, SIZE_MAX, NULL);
|
||||||
if (!formatted) {
|
if (!formatted) {
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto finish;
|
goto finish;
|
||||||
@ -2422,7 +2487,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
|||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i < n_rows; i++) {
|
for (size_t i = 1; i < n_rows; i++) {
|
||||||
TableData **row;
|
TableData **row;
|
||||||
|
|
||||||
if (sorted)
|
if (sorted)
|
||||||
@ -2430,7 +2495,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
|||||||
else
|
else
|
||||||
row = t->data + i * t->n_columns;
|
row = t->data + i * t->n_columns;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
TableData *d;
|
TableData *d;
|
||||||
size_t k;
|
size_t k;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ typedef enum TableDataType {
|
|||||||
TABLE_EMPTY,
|
TABLE_EMPTY,
|
||||||
TABLE_STRING,
|
TABLE_STRING,
|
||||||
TABLE_STRV,
|
TABLE_STRV,
|
||||||
|
TABLE_STRV_WRAPPED,
|
||||||
TABLE_PATH,
|
TABLE_PATH,
|
||||||
TABLE_BOOLEAN,
|
TABLE_BOOLEAN,
|
||||||
TABLE_TIMESTAMP,
|
TABLE_TIMESTAMP,
|
||||||
|
@ -12,6 +12,8 @@ static void test_issue_9549(void) {
|
|||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified"));
|
assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified"));
|
||||||
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0);
|
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0);
|
||||||
assert_se(table_add_many(table,
|
assert_se(table_add_many(table,
|
||||||
@ -36,6 +38,8 @@ static void test_multiline(void) {
|
|||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
assert_se(table = table_new("foo", "bar"));
|
assert_se(table = table_new("foo", "bar"));
|
||||||
|
|
||||||
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
||||||
@ -148,6 +152,8 @@ static void test_strv(void) {
|
|||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
assert_se(table = table_new("foo", "bar"));
|
assert_se(table = table_new("foo", "bar"));
|
||||||
|
|
||||||
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
||||||
@ -256,8 +262,111 @@ static void test_strv(void) {
|
|||||||
formatted = mfree(formatted);
|
formatted = mfree(formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
static void test_strv_wrapped(void) {
|
||||||
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
|
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_WRAPPED, STRV_MAKE("three", "different", "lines"),
|
||||||
|
TABLE_STRV_WRAPPED, 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 different lines two lines\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 different lines two 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 different lines two 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 different lines two lines\n"));
|
||||||
|
formatted = mfree(formatted);
|
||||||
|
|
||||||
|
assert_se(table_add_many(table,
|
||||||
|
TABLE_STRING, "short",
|
||||||
|
TABLE_STRV_WRAPPED, STRV_MAKE("a", "pair")) >= 0);
|
||||||
|
|
||||||
|
assert_se(table_add_many(table,
|
||||||
|
TABLE_STRV_WRAPPED, STRV_MAKE("short2"),
|
||||||
|
TABLE_STRV_WRAPPED, STRV_MAKE("a", "eight", "line", "ćęłł",
|
||||||
|
"___5___", "___6___", "___7___", "___8___")) >= 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 different… two lines\n"
|
||||||
|
"short a pair\n"
|
||||||
|
"short2 a eight line ćęłł…\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 different two lines\n"
|
||||||
|
"lines \n"
|
||||||
|
"short a pair\n"
|
||||||
|
"short2 a eight line ćęłł\n"
|
||||||
|
" ___5___ ___6___…\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 different two lines\n"
|
||||||
|
"lines \n"
|
||||||
|
"short a pair\n"
|
||||||
|
"short2 a eight line ćęłł\n"
|
||||||
|
" ___5___ ___6___\n"
|
||||||
|
" ___7___ ___8___\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 different two lines\n"
|
||||||
|
"lines \n"
|
||||||
|
"short a pair\n"
|
||||||
|
"short2 a eight line ćęłł\n"
|
||||||
|
" ___5___ ___6___\n"
|
||||||
|
" ___7___ ___8___\n"));
|
||||||
|
formatted = mfree(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
_cleanup_(table_unrefp) Table *t = NULL;
|
_cleanup_(table_unrefp) Table *t = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
@ -399,6 +508,7 @@ int main(int argc, char *argv[]) {
|
|||||||
test_issue_9549();
|
test_issue_9549();
|
||||||
test_multiline();
|
test_multiline();
|
||||||
test_strv();
|
test_strv();
|
||||||
|
test_strv_wrapped();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user