mirror of
https://github.com/systemd/systemd.git
synced 2026-07-26 19:29:58 +00:00
Merge pull request #30101 from poettering/underline-rework
systemctl: "list-units" table tweaks
This commit is contained in:
@@ -59,6 +59,8 @@
|
||||
|
||||
/* Other ANSI codes */
|
||||
#define ANSI_UNDERLINE "\x1B[0;4m"
|
||||
#define ANSI_ADD_UNDERLINE "\x1B[4m"
|
||||
#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58;5;245m"
|
||||
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
|
||||
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
|
||||
|
||||
@@ -189,6 +191,15 @@ static inline const char *ansi_underline(void) {
|
||||
return underline_enabled() ? ANSI_UNDERLINE : ANSI_NORMAL;
|
||||
}
|
||||
|
||||
static inline const char *ansi_add_underline(void) {
|
||||
return underline_enabled() ? ANSI_ADD_UNDERLINE : "";
|
||||
}
|
||||
|
||||
static inline const char *ansi_add_underline_grey(void) {
|
||||
return underline_enabled() ?
|
||||
(colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : "";
|
||||
}
|
||||
|
||||
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
|
||||
static inline const char *ansi_##name(void) { \
|
||||
return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \
|
||||
|
||||
@@ -77,7 +77,9 @@ typedef struct TableData {
|
||||
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 */
|
||||
|
||||
bool uppercase; /* Uppercase string on display */
|
||||
bool uppercase:1; /* Uppercase string on display */
|
||||
bool underline:1;
|
||||
bool rgap_underline:1;
|
||||
|
||||
const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */
|
||||
const char *rgap_color; /* The ANSI color to use for the gap right of this cell. Usually used to underline entire rows in a gapless fashion */
|
||||
@@ -401,7 +403,7 @@ static bool table_data_matches(
|
||||
return false;
|
||||
|
||||
/* If a color/url is set, refuse to merge */
|
||||
if (d->color || d->rgap_color)
|
||||
if (d->color || d->rgap_color || d->underline || d->rgap_underline)
|
||||
return false;
|
||||
if (d->url)
|
||||
return false;
|
||||
@@ -617,6 +619,8 @@ static int table_dedup_cell(Table *t, TableCell *cell) {
|
||||
|
||||
nd->color = od->color;
|
||||
nd->rgap_color = od->rgap_color;
|
||||
nd->underline = od->underline;
|
||||
nd->rgap_underline = od->rgap_underline;
|
||||
nd->url = TAKE_PTR(curl);
|
||||
|
||||
table_data_unref(od);
|
||||
@@ -759,6 +763,46 @@ int table_set_rgap_color(Table *t, TableCell *cell, const char *color) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int table_set_underline(Table *t, TableCell *cell, bool b) {
|
||||
TableData *d;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(cell);
|
||||
|
||||
r = table_dedup_cell(t, cell);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert_se(d = table_get_data(t, cell));
|
||||
|
||||
if (d->underline == b)
|
||||
return 0;
|
||||
|
||||
d->underline = b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int table_set_rgap_underline(Table *t, TableCell *cell, bool b) {
|
||||
TableData *d;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(cell);
|
||||
|
||||
r = table_dedup_cell(t, cell);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert_se(d = table_get_data(t, cell));
|
||||
|
||||
if (d->rgap_underline == b)
|
||||
return 0;
|
||||
|
||||
d->rgap_underline = b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int table_set_url(Table *t, TableCell *cell, const char *url) {
|
||||
_cleanup_free_ char *copy = NULL;
|
||||
int r;
|
||||
@@ -834,6 +878,8 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data
|
||||
|
||||
nd->color = od->color;
|
||||
nd->rgap_color = od->rgap_color;
|
||||
nd->underline = od->underline;
|
||||
nd->rgap_underline = od->rgap_underline;
|
||||
nd->url = TAKE_PTR(curl);
|
||||
|
||||
table_data_unref(od);
|
||||
@@ -1101,6 +1147,31 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
|
||||
goto check;
|
||||
}
|
||||
|
||||
case TABLE_SET_UNDERLINE: {
|
||||
int u = va_arg(ap, int);
|
||||
r = table_set_underline(t, last_cell, u);
|
||||
goto check;
|
||||
}
|
||||
|
||||
case TABLE_SET_RGAP_UNDERLINE: {
|
||||
int u = va_arg(ap, int);
|
||||
r = table_set_rgap_underline(t, last_cell, u);
|
||||
goto check;
|
||||
}
|
||||
|
||||
case TABLE_SET_BOTH_UNDERLINES: {
|
||||
int u = va_arg(ap, int);
|
||||
|
||||
r = table_set_underline(t, last_cell, u);
|
||||
if (r < 0) {
|
||||
va_end(ap);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = table_set_rgap_underline(t, last_cell, u);
|
||||
goto check;
|
||||
}
|
||||
|
||||
case TABLE_SET_URL: {
|
||||
const char *u = va_arg(ap, const char*);
|
||||
r = table_set_url(t, last_cell, u);
|
||||
@@ -2130,8 +2201,6 @@ static const char* table_data_color(TableData *d) {
|
||||
|
||||
if (d->type == TABLE_FIELD)
|
||||
return ansi_bright_blue();
|
||||
if (d->type == TABLE_HEADER)
|
||||
return ansi_underline();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2139,11 +2208,29 @@ static const char* table_data_color(TableData *d) {
|
||||
static const char* table_data_rgap_color(TableData *d) {
|
||||
assert(d);
|
||||
|
||||
if (d->rgap_color)
|
||||
return d->rgap_color;
|
||||
return d->rgap_color ?: d->rgap_color;
|
||||
}
|
||||
|
||||
static const char* table_data_underline(TableData *d) {
|
||||
assert(d);
|
||||
|
||||
if (d->underline)
|
||||
return /* cescape( */ansi_add_underline_grey()/* ) */;
|
||||
|
||||
if (d->type == TABLE_HEADER)
|
||||
return ansi_underline();
|
||||
return ansi_add_underline();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* table_data_rgap_underline(TableData *d) {
|
||||
assert(d);
|
||||
|
||||
if (d->rgap_underline)
|
||||
return ansi_add_underline_grey();
|
||||
|
||||
if (d->type == TABLE_HEADER)
|
||||
return ansi_add_underline();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2418,13 +2505,13 @@ int table_print(Table *t, FILE *f) {
|
||||
row = t->data + i * t->n_columns;
|
||||
|
||||
do {
|
||||
const char *gap_color = NULL;
|
||||
const char *gap_color = NULL, *gap_underline = NULL;
|
||||
more_sublines = false;
|
||||
|
||||
for (size_t j = 0; j < display_columns; j++) {
|
||||
_cleanup_free_ char *buffer = NULL, *extracted = NULL;
|
||||
bool lines_truncated = false;
|
||||
const char *field, *color = NULL;
|
||||
const char *field, *color = NULL, *underline = NULL;
|
||||
TableData *d;
|
||||
size_t l;
|
||||
|
||||
@@ -2490,6 +2577,7 @@ int table_print(Table *t, FILE *f) {
|
||||
/* Drop trailing white spaces of last column when no cosmetics is set. */
|
||||
if (j == display_columns - 1 &&
|
||||
(!colors_enabled() || !table_data_color(d)) &&
|
||||
(!underline_enabled() || !table_data_underline(d)) &&
|
||||
(!urlify_enabled() || !d->url))
|
||||
delete_trailing_chars(aligned, NULL);
|
||||
|
||||
@@ -2511,27 +2599,36 @@ int table_print(Table *t, FILE *f) {
|
||||
|
||||
if (colors_enabled() && gap_color)
|
||||
fputs(gap_color, f);
|
||||
if (underline_enabled() && gap_underline)
|
||||
fputs(gap_underline, f);
|
||||
|
||||
if (j > 0)
|
||||
fputc(' ', f); /* column separator left of cell */
|
||||
|
||||
/* Undo gap color/underline */
|
||||
if ((colors_enabled() && gap_color) ||
|
||||
(underline_enabled() && gap_underline))
|
||||
fputs(ANSI_NORMAL, f);
|
||||
|
||||
if (colors_enabled()) {
|
||||
color = table_data_color(d);
|
||||
|
||||
/* Undo gap color */
|
||||
if (gap_color)
|
||||
fputs(ANSI_NORMAL, f);
|
||||
|
||||
if (color)
|
||||
fputs(color, f);
|
||||
}
|
||||
|
||||
if (underline_enabled()) {
|
||||
underline = table_data_underline(d);
|
||||
if (underline)
|
||||
fputs(underline, f);
|
||||
}
|
||||
|
||||
fputs(field, f);
|
||||
|
||||
if (colors_enabled() && color)
|
||||
if (color || underline)
|
||||
fputs(ANSI_NORMAL, f);
|
||||
|
||||
gap_color = table_data_rgap_color(d);
|
||||
gap_underline = table_data_rgap_underline(d);
|
||||
}
|
||||
|
||||
fputc('\n', f);
|
||||
|
||||
@@ -68,6 +68,9 @@ typedef enum TableDataType {
|
||||
TABLE_SET_COLOR,
|
||||
TABLE_SET_RGAP_COLOR,
|
||||
TABLE_SET_BOTH_COLORS,
|
||||
TABLE_SET_UNDERLINE,
|
||||
TABLE_SET_RGAP_UNDERLINE,
|
||||
TABLE_SET_BOTH_UNDERLINES,
|
||||
TABLE_SET_URL,
|
||||
TABLE_SET_UPPERCASE,
|
||||
|
||||
@@ -111,6 +114,8 @@ int table_set_align_percent(Table *t, TableCell *cell, unsigned percent);
|
||||
int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent);
|
||||
int table_set_color(Table *t, TableCell *cell, const char *color);
|
||||
int table_set_rgap_color(Table *t, TableCell *cell, const char *color);
|
||||
int table_set_underline(Table *t, TableCell *cell, bool b);
|
||||
int table_set_rgap_underline(Table *t, TableCell *cell, bool b);
|
||||
int table_set_url(Table *t, TableCell *cell, const char *url);
|
||||
int table_set_uppercase(Table *t, TableCell *cell, bool b);
|
||||
|
||||
|
||||
@@ -127,29 +127,45 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
|
||||
table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
|
||||
|
||||
FOREACH_ARRAY(u, unit_infos, c) {
|
||||
const char *on_loaded = NULL, *on_active = NULL, *on_sub = NULL, *on_circle = NULL;
|
||||
_cleanup_free_ char *id = NULL;
|
||||
const char *on_underline = "", *on_loaded = "", *on_active = "", *on_circle = "";
|
||||
bool circle = false, underline = false;
|
||||
bool circle = false, underline;
|
||||
|
||||
if (u + 1 < unit_infos + c &&
|
||||
!streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) {
|
||||
on_underline = ansi_underline();
|
||||
underline = true;
|
||||
underline = u + 1 < unit_infos + c && !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id));
|
||||
|
||||
if (streq(u->load_state, "not-found")) {
|
||||
on_circle = on_loaded = ansi_highlight_yellow();
|
||||
on_circle = ansi_highlight_yellow();
|
||||
circle = true;
|
||||
} else if (STR_IN_SET(u->load_state, "bad-setting", "error", "masked")) {
|
||||
on_loaded = ansi_highlight_red();
|
||||
on_circle = ansi_highlight_yellow();
|
||||
circle = true;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) {
|
||||
on_circle = underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow();
|
||||
if (streq(u->active_state, "failed")) {
|
||||
on_sub = on_active = ansi_highlight_red();
|
||||
|
||||
/* Here override any load_state highlighting */
|
||||
on_circle = ansi_highlight_red();
|
||||
circle = true;
|
||||
on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
|
||||
} else if (streq(u->active_state, "failed") && !arg_plain) {
|
||||
on_circle = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
|
||||
circle = true;
|
||||
on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
|
||||
} else {
|
||||
on_circle = on_underline;
|
||||
on_active = on_underline;
|
||||
on_loaded = on_underline;
|
||||
}
|
||||
} else if (STR_IN_SET(u->active_state, "reloading", "activating", "maintenance", "deactivating")) {
|
||||
on_sub = on_active = ansi_highlight();
|
||||
|
||||
if (!circle) { /* Here we let load_state highlighting win */
|
||||
on_circle = ansi_highlight();
|
||||
circle = true;
|
||||
}
|
||||
} else if (streq(u->active_state, "inactive"))
|
||||
on_sub = on_active = ansi_grey();
|
||||
|
||||
/* As a special case, when this is a service which has not process running, let's grey out
|
||||
* its state, to highlight that a bit */
|
||||
if (!on_sub && endswith(u->id, ".service") && streq(u->sub_state, "exited"))
|
||||
on_sub = ansi_grey();
|
||||
|
||||
if (arg_plain)
|
||||
circle = false;
|
||||
|
||||
id = format_unit_id(u->id, u->machine);
|
||||
if (!id)
|
||||
@@ -157,19 +173,24 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
|
||||
TABLE_SET_BOTH_COLORS, on_circle,
|
||||
TABLE_SET_COLOR, on_circle,
|
||||
TABLE_SET_BOTH_UNDERLINES, underline,
|
||||
TABLE_STRING, id,
|
||||
TABLE_SET_BOTH_COLORS, on_active,
|
||||
TABLE_SET_COLOR, on_active,
|
||||
TABLE_SET_BOTH_UNDERLINES, underline,
|
||||
TABLE_STRING, u->load_state,
|
||||
TABLE_SET_BOTH_COLORS, on_loaded,
|
||||
TABLE_SET_COLOR, on_loaded,
|
||||
TABLE_SET_BOTH_UNDERLINES, underline,
|
||||
TABLE_STRING, u->active_state,
|
||||
TABLE_SET_BOTH_COLORS, on_active,
|
||||
TABLE_SET_COLOR, on_active,
|
||||
TABLE_SET_BOTH_UNDERLINES, underline,
|
||||
TABLE_STRING, u->sub_state,
|
||||
TABLE_SET_BOTH_COLORS, on_active,
|
||||
TABLE_SET_COLOR, on_sub,
|
||||
TABLE_SET_BOTH_UNDERLINES, underline,
|
||||
TABLE_STRING, u->job_id ? u->job_type: "",
|
||||
TABLE_SET_BOTH_COLORS, on_underline,
|
||||
TABLE_SET_BOTH_UNDERLINES, underline,
|
||||
TABLE_STRING, u->description,
|
||||
TABLE_SET_BOTH_COLORS, on_underline);
|
||||
TABLE_SET_BOTH_UNDERLINES, underline);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
@@ -214,12 +235,14 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
|
||||
|
||||
if (arg_all || strv_contains(arg_states, "inactive"))
|
||||
printf("%s%zu loaded units listed.%s\n"
|
||||
"To show all installed unit files use 'systemctl list-unit-files'.\n",
|
||||
on, records, off);
|
||||
"%sTo show all installed unit files use 'systemctl list-unit-files'.%s\n",
|
||||
on, records, off,
|
||||
ansi_grey(), ansi_normal());
|
||||
else if (!arg_states)
|
||||
printf("%s%zu loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
|
||||
"To show all installed unit files use 'systemctl list-unit-files'.\n",
|
||||
on, records, off);
|
||||
printf("%s%zu loaded units listed.%s %sPass --all to see loaded but inactive units, too.%s\n"
|
||||
"%sTo show all installed unit files use 'systemctl list-unit-files'.%s\n",
|
||||
on, records, off,
|
||||
ansi_grey(), ansi_normal(), ansi_grey(), ansi_normal());
|
||||
else
|
||||
printf("%zu loaded units listed.\n", records);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user