Compare commits
6 Commits
89f3ba7995
...
ab1b472062
Author | SHA1 | Date |
---|---|---|
Yu Watanabe | ab1b472062 | |
Lennart Poettering | 03f9228e7c | |
Lennart Poettering | fc6eb08e74 | |
Lennart Poettering | d91614e717 | |
Lennart Poettering | f6857fa601 | |
Lennart Poettering | 8dd6491ef9 |
|
@ -812,15 +812,11 @@
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--max-addresses=</option></term>
|
<term><option>--max-addresses=</option></term>
|
||||||
|
|
||||||
<listitem><para>When used with the <option>list-machines</option>
|
<listitem><para>When used with the <option>list-machines</option> command, limits the number of ip
|
||||||
command, limits the number of ip addresses output for every machine.
|
addresses output for every machine. Defaults to 1. All addresses can be requested with
|
||||||
Defaults to 1. All addresses can be requested with <literal>all</literal>
|
<literal>all</literal> as argument to <option>--max-addresses=</option>. If the argument to
|
||||||
as argument to <option>--max-addresses</option> . If the argument to
|
<option>--max-addresses=</option> is less than the actual number of addresses,
|
||||||
<option>--max-addresses</option> is less than the actual number
|
<literal>…</literal>follows the last address.</para></listitem>
|
||||||
of addresses, <literal>...</literal>follows the last address.
|
|
||||||
If multiple addresses are to be written for a given machine, every
|
|
||||||
address except the first one is on a new line and is followed by
|
|
||||||
<literal>,</literal> if another address will be output afterwards. </para></listitem>
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|
|
@ -1074,3 +1074,121 @@ char* string_erase(char *x) {
|
||||||
explicit_bzero_safe(x, strlen(x));
|
explicit_bzero_safe(x, strlen(x));
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
|
||||||
|
const char *p = s, *e = s;
|
||||||
|
bool truncation_applied = false;
|
||||||
|
char *copy;
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
/* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if
|
||||||
|
* there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not
|
||||||
|
* generated either. */
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
k = strcspn(p, "\n");
|
||||||
|
|
||||||
|
if (p[k] == 0) {
|
||||||
|
if (k == 0) /* final empty line */
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (n >= n_lines) /* above threshold */
|
||||||
|
break;
|
||||||
|
|
||||||
|
e = p + k; /* last line to include */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(p[k] == '\n');
|
||||||
|
|
||||||
|
if (n >= n_lines)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (k > 0)
|
||||||
|
e = p + k;
|
||||||
|
|
||||||
|
p += k + 1;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* e points after the last character we want to keep */
|
||||||
|
if (isempty(e))
|
||||||
|
copy = strdup(s);
|
||||||
|
else {
|
||||||
|
if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that
|
||||||
|
* isn't a new-line or a series of them */
|
||||||
|
truncation_applied = true;
|
||||||
|
|
||||||
|
copy = strndup(s, e - s);
|
||||||
|
}
|
||||||
|
if (!copy)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = copy;
|
||||||
|
return truncation_applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_extract_line(const char *s, size_t i, char **ret) {
|
||||||
|
const char *p = s;
|
||||||
|
size_t c = 0;
|
||||||
|
|
||||||
|
/* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that,
|
||||||
|
* and == 0 if we are looking at the last line or already beyond the last line. As special
|
||||||
|
* optimization, if the first line is requested and the string only consists of one line we return
|
||||||
|
* NULL, indicating the input string should be used as is, and avoid a memory allocation for a very
|
||||||
|
* common case. */
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *q;
|
||||||
|
|
||||||
|
q = strchr(p, '\n');
|
||||||
|
if (i == c) {
|
||||||
|
/* The line we are looking for! */
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
char *m;
|
||||||
|
|
||||||
|
m = strndup(p, q - p);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = m;
|
||||||
|
return !isempty(q + 1); /* more coming? */
|
||||||
|
} else {
|
||||||
|
if (p == s)
|
||||||
|
*ret = NULL; /* Just use the input string */
|
||||||
|
else {
|
||||||
|
char *m;
|
||||||
|
|
||||||
|
m = strdup(p);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; /* The end */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!q) {
|
||||||
|
char *m;
|
||||||
|
|
||||||
|
/* No more lines, return empty line */
|
||||||
|
|
||||||
|
m = strdup("");
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = m;
|
||||||
|
return 0; /* The end */
|
||||||
|
}
|
||||||
|
|
||||||
|
p = q + 1;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -280,3 +280,6 @@ static inline char* str_realloc(char **p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char* string_erase(char *x);
|
char* string_erase(char *x);
|
||||||
|
|
||||||
|
int string_truncate_lines(const char *s, size_t n_lines, char **ret);
|
||||||
|
int string_extract_line(const char *s, size_t i, char **ret);
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
#include "web-util.h"
|
#include "web-util.h"
|
||||||
|
|
||||||
#define ALL_IP_ADDRESSES -1
|
#define ALL_ADDRESSES -1
|
||||||
|
|
||||||
static char **arg_property = NULL;
|
static char **arg_property = NULL;
|
||||||
static bool arg_all = false;
|
static bool arg_all = false;
|
||||||
|
@ -79,7 +79,7 @@ static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
|
||||||
static const char* arg_format = NULL;
|
static const char* arg_format = NULL;
|
||||||
static const char *arg_uid = NULL;
|
static const char *arg_uid = NULL;
|
||||||
static char **arg_setenv = NULL;
|
static char **arg_setenv = NULL;
|
||||||
static int arg_addrs = 1;
|
static int arg_max_addresses = 1;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
|
||||||
|
@ -160,12 +160,17 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2, int n_addr, char **ret) {
|
static int call_get_addresses(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *name,
|
||||||
|
int ifi,
|
||||||
|
const char *prefix,
|
||||||
|
const char *prefix2,
|
||||||
|
char **ret) {
|
||||||
|
|
||||||
_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 *reply = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
_cleanup_free_ char *addresses = NULL;
|
_cleanup_free_ char *addresses = NULL;
|
||||||
bool truncate = false;
|
|
||||||
unsigned n = 0;
|
unsigned n = 0;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -208,16 +213,13 @@ static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
if (n_addr != 0) {
|
if (family == AF_INET6 && ifi > 0)
|
||||||
if (family == AF_INET6 && ifi > 0)
|
xsprintf(buf_ifi, "%%%i", ifi);
|
||||||
xsprintf(buf_ifi, "%%%i", ifi);
|
else
|
||||||
else
|
strcpy(buf_ifi, "");
|
||||||
strcpy(buf_ifi, "");
|
|
||||||
|
|
||||||
if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
|
if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
|
||||||
return log_oom();
|
return log_oom();
|
||||||
} else
|
|
||||||
truncate = true;
|
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(reply);
|
r = sd_bus_message_exit_container(reply);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -225,9 +227,6 @@ static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char
|
||||||
|
|
||||||
prefix = prefix2;
|
prefix = prefix2;
|
||||||
|
|
||||||
if (n_addr > 0)
|
|
||||||
n_addr --;
|
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -237,13 +236,6 @@ static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
if (truncate) {
|
|
||||||
|
|
||||||
if (!strextend(&addresses, special_glyph(SPECIAL_GLYPH_ELLIPSIS), NULL))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(addresses);
|
*ret = TAKE_PTR(addresses);
|
||||||
return (int) n;
|
return (int) n;
|
||||||
}
|
}
|
||||||
|
@ -306,6 +298,10 @@ static int list_machines(int argc, char *argv[], void *userdata) {
|
||||||
if (!table)
|
if (!table)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
table_set_empty_string(table, "-");
|
||||||
|
if (!arg_full && arg_max_addresses != ALL_ADDRESSES)
|
||||||
|
table_set_cell_height_max(table, arg_max_addresses);
|
||||||
|
|
||||||
if (arg_full)
|
if (arg_full)
|
||||||
table_set_width(table, 0);
|
table_set_width(table, 0);
|
||||||
|
|
||||||
|
@ -340,17 +336,16 @@ static int list_machines(int argc, char *argv[], void *userdata) {
|
||||||
name,
|
name,
|
||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
" ",
|
"\n",
|
||||||
arg_full ? ALL_IP_ADDRESSES : arg_addrs,
|
|
||||||
&addresses);
|
&addresses);
|
||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, name,
|
TABLE_STRING, empty_to_null(name),
|
||||||
TABLE_STRING, class,
|
TABLE_STRING, empty_to_null(class),
|
||||||
TABLE_STRING, empty_to_dash(service),
|
TABLE_STRING, empty_to_null(service),
|
||||||
TABLE_STRING, empty_to_dash(os),
|
TABLE_STRING, empty_to_null(os),
|
||||||
TABLE_STRING, empty_to_dash(version_id),
|
TABLE_STRING, empty_to_null(version_id),
|
||||||
TABLE_STRING, empty_to_dash(addresses));
|
TABLE_STRING, empty_to_null(addresses));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
}
|
}
|
||||||
|
@ -612,7 +607,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (call_get_addresses(bus, i->name, ifi,
|
if (call_get_addresses(bus, i->name, ifi,
|
||||||
"\t Address: ", "\n\t ", ALL_IP_ADDRESSES,
|
"\t Address: ", "\n\t ",
|
||||||
&addresses) > 0) {
|
&addresses) > 0) {
|
||||||
fputs(addresses, stdout);
|
fputs(addresses, stdout);
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
|
@ -2777,7 +2772,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_FORCE,
|
ARG_FORCE,
|
||||||
ARG_FORMAT,
|
ARG_FORMAT,
|
||||||
ARG_UID,
|
ARG_UID,
|
||||||
ARG_NUMBER_IPS,
|
ARG_MAX_ADDRESSES,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -2804,7 +2799,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "format", required_argument, NULL, ARG_FORMAT },
|
{ "format", required_argument, NULL, ARG_FORMAT },
|
||||||
{ "uid", required_argument, NULL, ARG_UID },
|
{ "uid", required_argument, NULL, ARG_UID },
|
||||||
{ "setenv", required_argument, NULL, 'E' },
|
{ "setenv", required_argument, NULL, 'E' },
|
||||||
{ "max-addresses", required_argument, NULL, ARG_NUMBER_IPS },
|
{ "max-addresses", required_argument, NULL, ARG_MAX_ADDRESSES },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3007,15 +3002,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
return log_oom();
|
return log_oom();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_NUMBER_IPS:
|
case ARG_MAX_ADDRESSES:
|
||||||
if (streq(optarg, "all"))
|
if (streq(optarg, "all"))
|
||||||
arg_addrs = ALL_IP_ADDRESSES;
|
arg_max_addresses = ALL_ADDRESSES;
|
||||||
else if (safe_atoi(optarg, &arg_addrs) < 0)
|
else if (safe_atoi(optarg, &arg_max_addresses) < 0)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Invalid number of IPs");
|
"Invalid number of addresses: %s", optarg);
|
||||||
else if (arg_addrs < 0)
|
else if (arg_max_addresses <= 0)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Number of IPs cannot be negative");
|
"Number of IPs cannot be negative or zero: %s", optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "gunicode.h"
|
#include "gunicode.h"
|
||||||
#include "in-addr-util.h"
|
#include "in-addr-util.h"
|
||||||
|
#include "locale-util.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
@ -119,6 +120,7 @@ struct Table {
|
||||||
bool header; /* Whether to show the header row? */
|
bool header; /* Whether to show the header row? */
|
||||||
size_t width; /* If == 0 format this as wide as necessary. If (size_t) -1 format this to console
|
size_t width; /* If == 0 format this as wide as necessary. If (size_t) -1 format this to console
|
||||||
* width or less wide, but not wider. Otherwise the width to format this table in. */
|
* width or less wide, but not wider. Otherwise the width to format this table in. */
|
||||||
|
size_t cell_height_max; /* Maximum number of lines per cell. (If there are more, ellipsis is shown. If (size_t) -1 then no limit is set, the default. == 0 is not allowed.) */
|
||||||
|
|
||||||
TableData **data;
|
TableData **data;
|
||||||
size_t n_allocated;
|
size_t n_allocated;
|
||||||
|
@ -147,6 +149,7 @@ Table *table_new_raw(size_t n_columns) {
|
||||||
.n_columns = n_columns,
|
.n_columns = n_columns,
|
||||||
.header = true,
|
.header = true,
|
||||||
.width = (size_t) -1,
|
.width = (size_t) -1,
|
||||||
|
.cell_height_max = (size_t) -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
return TAKE_PTR(t);
|
return TAKE_PTR(t);
|
||||||
|
@ -963,6 +966,13 @@ void table_set_width(Table *t, size_t width) {
|
||||||
t->width = width;
|
t->width = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void table_set_cell_height_max(Table *t, size_t height) {
|
||||||
|
assert(t);
|
||||||
|
assert(height >= 1 || height == (size_t) -1);
|
||||||
|
|
||||||
|
t->cell_height_max = height;
|
||||||
|
}
|
||||||
|
|
||||||
int table_set_empty_string(Table *t, const char *empty) {
|
int table_set_empty_string(Table *t, const char *empty) {
|
||||||
assert(t);
|
assert(t);
|
||||||
|
|
||||||
|
@ -1417,26 +1427,94 @@ static const char *table_data_format(Table *t, TableData *d) {
|
||||||
return d->formatted;
|
return d->formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int table_data_requested_width(Table *table, TableData *d, size_t *ret) {
|
static int console_width_height(
|
||||||
|
const char *s,
|
||||||
|
size_t *ret_width,
|
||||||
|
size_t *ret_height) {
|
||||||
|
|
||||||
|
size_t max_width = 0, height = 0;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
/* Determine the width and height in console character cells the specified string needs. */
|
||||||
|
|
||||||
|
do {
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
p = strchr(s, '\n');
|
||||||
|
if (p) {
|
||||||
|
_cleanup_free_ char *c = NULL;
|
||||||
|
|
||||||
|
c = strndup(s, p - s);
|
||||||
|
if (!c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
k = utf8_console_width(c);
|
||||||
|
s = p + 1;
|
||||||
|
} else {
|
||||||
|
k = utf8_console_width(s);
|
||||||
|
s = NULL;
|
||||||
|
}
|
||||||
|
if (k == (size_t) -1)
|
||||||
|
return -EINVAL;
|
||||||
|
if (k > max_width)
|
||||||
|
max_width = k;
|
||||||
|
|
||||||
|
height++;
|
||||||
|
} while (!isempty(s));
|
||||||
|
|
||||||
|
if (ret_width)
|
||||||
|
*ret_width = max_width;
|
||||||
|
|
||||||
|
if (ret_height)
|
||||||
|
*ret_height = height;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int table_data_requested_width_height(
|
||||||
|
Table *table,
|
||||||
|
TableData *d,
|
||||||
|
size_t *ret_width,
|
||||||
|
size_t *ret_height) {
|
||||||
|
|
||||||
|
_cleanup_free_ char *truncated = NULL;
|
||||||
|
bool truncation_applied = false;
|
||||||
|
size_t width, height;
|
||||||
const char *t;
|
const char *t;
|
||||||
size_t l;
|
int r;
|
||||||
|
|
||||||
t = table_data_format(table, d);
|
t = table_data_format(table, d);
|
||||||
if (!t)
|
if (!t)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
l = utf8_console_width(t);
|
if (table->cell_height_max != (size_t) -1) {
|
||||||
if (l == (size_t) -1)
|
r = string_truncate_lines(t, table->cell_height_max, &truncated);
|
||||||
return -EINVAL;
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r > 0)
|
||||||
|
truncation_applied = true;
|
||||||
|
|
||||||
if (d->maximum_width != (size_t) -1 && l > d->maximum_width)
|
t = truncated;
|
||||||
l = d->maximum_width;
|
}
|
||||||
|
|
||||||
if (l < d->minimum_width)
|
r = console_width_height(t, &width, &height);
|
||||||
l = d->minimum_width;
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
*ret = l;
|
if (d->maximum_width != (size_t) -1 && width > d->maximum_width)
|
||||||
return 0;
|
width = d->maximum_width;
|
||||||
|
|
||||||
|
if (width < d->minimum_width)
|
||||||
|
width = d->minimum_width;
|
||||||
|
|
||||||
|
if (ret_width)
|
||||||
|
*ret_width = width;
|
||||||
|
if (ret_height)
|
||||||
|
*ret_height = height;
|
||||||
|
|
||||||
|
return truncation_applied;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) {
|
static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) {
|
||||||
|
@ -1573,18 +1651,40 @@ int table_print(Table *t, FILE *f) {
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (j = 0; j < display_columns; j++) {
|
||||||
TableData *d;
|
TableData *d;
|
||||||
size_t req;
|
size_t req_width, req_height;
|
||||||
|
|
||||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||||
|
|
||||||
r = table_data_requested_width(t, d, &req);
|
r = table_data_requested_width_height(t, d, &req_width, &req_height);
|
||||||
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;
|
||||||
|
|
||||||
|
/* If we are going to show only the first few lines of a cell that has
|
||||||
|
* multiple make sure that we have enough space horizontally to show an
|
||||||
|
* ellipsis. Hence, let's figure out the last line, and account for its
|
||||||
|
* length plus ellipsis. */
|
||||||
|
|
||||||
|
field = table_data_format(t, d);
|
||||||
|
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 */
|
/* Determine the biggest width that any cell in this column would like to have */
|
||||||
if (requested_width[j] == (size_t) -1 ||
|
if (requested_width[j] == (size_t) -1 ||
|
||||||
requested_width[j] < req)
|
requested_width[j] < req_width)
|
||||||
requested_width[j] = req;
|
requested_width[j] = req_width;
|
||||||
|
|
||||||
/* Determine the minimum width any cell in this column needs */
|
/* Determine the minimum width any cell in this column needs */
|
||||||
if (minimum_width[j] < d->minimum_width)
|
if (minimum_width[j] < d->minimum_width)
|
||||||
|
@ -1731,6 +1831,8 @@ int table_print(Table *t, FILE *f) {
|
||||||
|
|
||||||
/* Second pass: show output */
|
/* Second pass: show output */
|
||||||
for (i = t->header ? 0 : 1; i < n_rows; i++) {
|
for (i = t->header ? 0 : 1; i < n_rows; i++) {
|
||||||
|
size_t n_subline = 0;
|
||||||
|
bool more_sublines;
|
||||||
TableData **row;
|
TableData **row;
|
||||||
|
|
||||||
if (sorted)
|
if (sorted)
|
||||||
|
@ -1738,69 +1840,113 @@ int table_print(Table *t, FILE *f) {
|
||||||
else
|
else
|
||||||
row = t->data + i * t->n_columns;
|
row = t->data + i * t->n_columns;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
do {
|
||||||
_cleanup_free_ char *buffer = NULL;
|
more_sublines = false;
|
||||||
const char *field;
|
|
||||||
TableData *d;
|
|
||||||
size_t l;
|
|
||||||
|
|
||||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
for (j = 0; j < display_columns; j++) {
|
||||||
|
_cleanup_free_ char *buffer = NULL, *extracted = NULL;
|
||||||
|
bool lines_truncated = false;
|
||||||
|
const char *field;
|
||||||
|
TableData *d;
|
||||||
|
size_t l;
|
||||||
|
|
||||||
field = table_data_format(t, d);
|
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||||
if (!field)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
l = utf8_console_width(field);
|
field = table_data_format(t, d);
|
||||||
if (l > width[j]) {
|
if (!field)
|
||||||
/* Field is wider than allocated space. Let's ellipsize */
|
|
||||||
|
|
||||||
buffer = ellipsize(field, width[j], d->ellipsize_percent);
|
|
||||||
if (!buffer)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
field = buffer;
|
r = string_extract_line(field, n_subline, &extracted);
|
||||||
|
|
||||||
} else if (l < width[j]) {
|
|
||||||
/* Field is shorter than allocated space. Let's align with spaces */
|
|
||||||
|
|
||||||
buffer = align_string_mem(field, d->url, width[j], d->align_percent);
|
|
||||||
if (!buffer)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
field = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l >= width[j] && d->url) {
|
|
||||||
_cleanup_free_ char *clickable = NULL;
|
|
||||||
|
|
||||||
r = terminal_urlify(d->url, field, &clickable);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
if (r > 0) {
|
||||||
|
/* There are more lines to come */
|
||||||
|
if ((t->cell_height_max == (size_t) -1 || n_subline + 1 < t->cell_height_max))
|
||||||
|
more_sublines = true; /* There are more lines to come */
|
||||||
|
else
|
||||||
|
lines_truncated = true;
|
||||||
|
}
|
||||||
|
if (extracted)
|
||||||
|
field = extracted;
|
||||||
|
|
||||||
free_and_replace(buffer, clickable);
|
l = utf8_console_width(field);
|
||||||
field = buffer;
|
if (l > width[j]) {
|
||||||
}
|
/* Field is wider than allocated space. Let's ellipsize */
|
||||||
|
|
||||||
if (row == t->data) /* underline header line fully, including the column separator */
|
buffer = ellipsize(field, width[j], /* ellipsize at the end if we truncated coming lines, otherwise honour configuration */
|
||||||
fputs(ansi_underline(), f);
|
lines_truncated ? 100 : d->ellipsize_percent);
|
||||||
|
if (!buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (j > 0)
|
field = buffer;
|
||||||
fputc(' ', f); /* column separator */
|
} else {
|
||||||
|
if (lines_truncated) {
|
||||||
|
_cleanup_free_ char *padded = NULL;
|
||||||
|
|
||||||
if (table_data_color(d) && colors_enabled()) {
|
/* We truncated more lines of this cell, let's add an
|
||||||
if (row == t->data) /* first undo header underliner */
|
* ellipsis. We first append it, but thta might make our
|
||||||
|
* string grow above what we have space for, hence ellipsize
|
||||||
|
* right after. This will truncate the ellipsis and add a new
|
||||||
|
* one. */
|
||||||
|
|
||||||
|
padded = strjoin(field, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
|
||||||
|
if (!padded)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
buffer = ellipsize(padded, width[j], 100);
|
||||||
|
if (!buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
field = buffer;
|
||||||
|
l = utf8_console_width(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l < width[j]) {
|
||||||
|
_cleanup_free_ char *aligned = NULL;
|
||||||
|
/* Field is shorter than allocated space. Let's align with spaces */
|
||||||
|
|
||||||
|
aligned = align_string_mem(field, d->url, width[j], d->align_percent);
|
||||||
|
if (!aligned)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
free_and_replace(buffer, aligned);
|
||||||
|
field = buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l >= width[j] && d->url) {
|
||||||
|
_cleanup_free_ char *clickable = NULL;
|
||||||
|
|
||||||
|
r = terminal_urlify(d->url, field, &clickable);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
free_and_replace(buffer, clickable);
|
||||||
|
field = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row == t->data) /* underline header line fully, including the column separator */
|
||||||
|
fputs(ansi_underline(), f);
|
||||||
|
|
||||||
|
if (j > 0)
|
||||||
|
fputc(' ', f); /* column separator */
|
||||||
|
|
||||||
|
if (table_data_color(d) && colors_enabled()) {
|
||||||
|
if (row == t->data) /* first undo header underliner */
|
||||||
|
fputs(ANSI_NORMAL, f);
|
||||||
|
|
||||||
|
fputs(table_data_color(d), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(field, f);
|
||||||
|
|
||||||
|
if (colors_enabled() && (table_data_color(d) || row == t->data))
|
||||||
fputs(ANSI_NORMAL, f);
|
fputs(ANSI_NORMAL, f);
|
||||||
|
|
||||||
fputs(table_data_color(d), f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fputs(field, f);
|
fputc('\n', f);
|
||||||
|
n_subline ++;
|
||||||
if (colors_enabled() && (table_data_color(d) || row == t->data))
|
} while (more_sublines);
|
||||||
fputs(ANSI_NORMAL, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
fputc('\n', f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fflush_and_check(f);
|
return fflush_and_check(f);
|
||||||
|
|
|
@ -96,6 +96,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...);
|
||||||
|
|
||||||
void table_set_header(Table *table, bool b);
|
void table_set_header(Table *table, bool b);
|
||||||
void table_set_width(Table *t, size_t width);
|
void table_set_width(Table *t, size_t width);
|
||||||
|
void table_set_cell_height_max(Table *t, size_t height);
|
||||||
int table_set_empty_string(Table *t, const char *empty);
|
int table_set_empty_string(Table *t, const char *empty);
|
||||||
int table_set_display(Table *t, size_t first_column, ...);
|
int table_set_display(Table *t, size_t first_column, ...);
|
||||||
int table_set_sort(Table *t, size_t first_column, ...);
|
int table_set_sort(Table *t, size_t first_column, ...);
|
||||||
|
|
|
@ -31,6 +31,118 @@ static void test_issue_9549(void) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_multiline(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_STRING, "three\ndifferent\nlines",
|
||||||
|
TABLE_STRING, "two\nlines\n") >= 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_STRING, "a\npair") >= 0);
|
||||||
|
|
||||||
|
assert_se(table_add_many(table,
|
||||||
|
TABLE_STRING, "short2\n",
|
||||||
|
TABLE_STRING, "a\nfour\nline\ncell") >= 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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
_cleanup_(table_unrefp) Table *t = NULL;
|
_cleanup_(table_unrefp) Table *t = NULL;
|
||||||
|
@ -172,6 +284,7 @@ int main(int argc, char *argv[]) {
|
||||||
"5min 5min \n"));
|
"5min 5min \n"));
|
||||||
|
|
||||||
test_issue_9549();
|
test_issue_9549();
|
||||||
|
test_multiline();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,6 +563,153 @@ static void test_memory_startswith_no_case(void) {
|
||||||
assert_se(memory_startswith_no_case((char[2]){'X', 'X'}, 2, "XX"));
|
assert_se(memory_startswith_no_case((char[2]){'X', 'X'}, 2, "XX"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_string_truncate_lines_one(const char *input, size_t n_lines, const char *output, bool truncation) {
|
||||||
|
_cleanup_free_ char *b = NULL;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
assert_se((k = string_truncate_lines(input, n_lines, &b)) >= 0);
|
||||||
|
assert_se(streq(b, output));
|
||||||
|
assert_se(!!k == truncation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_string_truncate_lines(void) {
|
||||||
|
test_string_truncate_lines_one("", 0, "", false);
|
||||||
|
test_string_truncate_lines_one("", 1, "", false);
|
||||||
|
test_string_truncate_lines_one("", 2, "", false);
|
||||||
|
test_string_truncate_lines_one("", 3, "", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("x", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("x", 1, "x", false);
|
||||||
|
test_string_truncate_lines_one("x", 2, "x", false);
|
||||||
|
test_string_truncate_lines_one("x", 3, "x", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("x\n", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("x\n", 1, "x", false);
|
||||||
|
test_string_truncate_lines_one("x\n", 2, "x", false);
|
||||||
|
test_string_truncate_lines_one("x\n", 3, "x", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("x\ny", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("x\ny", 1, "x", true);
|
||||||
|
test_string_truncate_lines_one("x\ny", 2, "x\ny", false);
|
||||||
|
test_string_truncate_lines_one("x\ny", 3, "x\ny", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("x\ny\n", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\n", 1, "x", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\n", 2, "x\ny", false);
|
||||||
|
test_string_truncate_lines_one("x\ny\n", 3, "x\ny", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("x\ny\nz", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\nz", 1, "x", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\nz", 2, "x\ny", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\nz", 3, "x\ny\nz", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("x\ny\nz\n", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\nz\n", 1, "x", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\nz\n", 2, "x\ny", true);
|
||||||
|
test_string_truncate_lines_one("x\ny\nz\n", 3, "x\ny\nz", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("\n", 0, "", false);
|
||||||
|
test_string_truncate_lines_one("\n", 1, "", false);
|
||||||
|
test_string_truncate_lines_one("\n", 2, "", false);
|
||||||
|
test_string_truncate_lines_one("\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("\n\n", 0, "", false);
|
||||||
|
test_string_truncate_lines_one("\n\n", 1, "", false);
|
||||||
|
test_string_truncate_lines_one("\n\n", 2, "", false);
|
||||||
|
test_string_truncate_lines_one("\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("\n\n\n", 0, "", false);
|
||||||
|
test_string_truncate_lines_one("\n\n\n", 1, "", false);
|
||||||
|
test_string_truncate_lines_one("\n\n\n", 2, "", false);
|
||||||
|
test_string_truncate_lines_one("\n\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("\nx\n\n", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("\nx\n\n", 1, "", true);
|
||||||
|
test_string_truncate_lines_one("\nx\n\n", 2, "\nx", false);
|
||||||
|
test_string_truncate_lines_one("\nx\n\n", 3, "\nx", false);
|
||||||
|
|
||||||
|
test_string_truncate_lines_one("\n\nx\n", 0, "", true);
|
||||||
|
test_string_truncate_lines_one("\n\nx\n", 1, "", true);
|
||||||
|
test_string_truncate_lines_one("\n\nx\n", 2, "", true);
|
||||||
|
test_string_truncate_lines_one("\n\nx\n", 3, "\n\nx", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_string_extract_lines_one(const char *input, size_t i, const char *output, bool more) {
|
||||||
|
_cleanup_free_ char *b = NULL;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
assert_se((k = string_extract_line(input, i, &b)) >= 0);
|
||||||
|
assert_se(streq(b ?: input, output));
|
||||||
|
assert_se(!!k == more);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_string_extract_line(void) {
|
||||||
|
test_string_extract_lines_one("", 0, "", false);
|
||||||
|
test_string_extract_lines_one("", 1, "", false);
|
||||||
|
test_string_extract_lines_one("", 2, "", false);
|
||||||
|
test_string_extract_lines_one("", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("x", 0, "x", false);
|
||||||
|
test_string_extract_lines_one("x", 1, "", false);
|
||||||
|
test_string_extract_lines_one("x", 2, "", false);
|
||||||
|
test_string_extract_lines_one("x", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("x\n", 0, "x", false);
|
||||||
|
test_string_extract_lines_one("x\n", 1, "", false);
|
||||||
|
test_string_extract_lines_one("x\n", 2, "", false);
|
||||||
|
test_string_extract_lines_one("x\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("x\ny", 0, "x", true);
|
||||||
|
test_string_extract_lines_one("x\ny", 1, "y", false);
|
||||||
|
test_string_extract_lines_one("x\ny", 2, "", false);
|
||||||
|
test_string_extract_lines_one("x\ny", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("x\ny\n", 0, "x", true);
|
||||||
|
test_string_extract_lines_one("x\ny\n", 1, "y", false);
|
||||||
|
test_string_extract_lines_one("x\ny\n", 2, "", false);
|
||||||
|
test_string_extract_lines_one("x\ny\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("x\ny\nz", 0, "x", true);
|
||||||
|
test_string_extract_lines_one("x\ny\nz", 1, "y", true);
|
||||||
|
test_string_extract_lines_one("x\ny\nz", 2, "z", false);
|
||||||
|
test_string_extract_lines_one("x\ny\nz", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\n", 0, "", false);
|
||||||
|
test_string_extract_lines_one("\n", 1, "", false);
|
||||||
|
test_string_extract_lines_one("\n", 2, "", false);
|
||||||
|
test_string_extract_lines_one("\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\n\n", 0, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n", 1, "", false);
|
||||||
|
test_string_extract_lines_one("\n\n", 2, "", false);
|
||||||
|
test_string_extract_lines_one("\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\n\n\n", 0, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\n", 1, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\n", 2, "", false);
|
||||||
|
test_string_extract_lines_one("\n\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\n\n\n\n", 0, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\n\n", 1, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\n\n", 2, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\nx\n\n\n", 0, "", true);
|
||||||
|
test_string_extract_lines_one("\nx\n\n\n", 1, "x", true);
|
||||||
|
test_string_extract_lines_one("\nx\n\n\n", 2, "", true);
|
||||||
|
test_string_extract_lines_one("\nx\n\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\n\nx\n\n", 0, "", true);
|
||||||
|
test_string_extract_lines_one("\n\nx\n\n", 1, "", true);
|
||||||
|
test_string_extract_lines_one("\n\nx\n\n", 2, "x", true);
|
||||||
|
test_string_extract_lines_one("\n\nx\n\n", 3, "", false);
|
||||||
|
|
||||||
|
test_string_extract_lines_one("\n\n\nx\n", 0, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\nx\n", 1, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\nx\n", 2, "", true);
|
||||||
|
test_string_extract_lines_one("\n\n\nx\n", 3, "x", false);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
test_setup_logging(LOG_DEBUG);
|
test_setup_logging(LOG_DEBUG);
|
||||||
|
|
||||||
|
@ -595,6 +742,8 @@ int main(int argc, char *argv[]) {
|
||||||
test_strlen_ptr();
|
test_strlen_ptr();
|
||||||
test_memory_startswith();
|
test_memory_startswith();
|
||||||
test_memory_startswith_no_case();
|
test_memory_startswith_no_case();
|
||||||
|
test_string_truncate_lines();
|
||||||
|
test_string_extract_line();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue