ansi-color: in 256 mode, always set the fallback color first

Linux console is very weird when it comes to ANSI color sequences.
Not only that it isn't aware of ':' separator (c.f.
https://github.com/systemd/systemd/pull/40878#issuecomment-3979826739),
it even skips the whole CSI-m sequence if it contains parts it cannot
parse. Hence when color mode is set to 256 (i.e. default when no
extra info is available) let's always emit two distinct CSI-m sequences,
and set the fallback 16 color first in case the terminal doesn't have
complete support for the 256 one.

Replaces #40905
This commit is contained in:
Mike Yuan
2026-03-03 23:56:05 +01:00
parent 085f92730a
commit 36d30330f7
+54 -43
View File
@@ -111,41 +111,20 @@ bool looks_like_ansi_color_code(const char *str);
return colors_enabled() ? ANSI_##NAME : ""; \
}
#define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
static inline const char* ansi_##name(void) { \
switch (get_color_mode()) { \
case COLOR_OFF: return ""; \
case COLOR_16: return ANSI_##FALLBACK; \
default : return ANSI_##NAME; \
} \
}
/* NB: in 256 mode we always emit the fallback color first, in order to deal with terminals with
* incomplete 256 color support (most notably Linux console, which a) lacks support for ":"
* subcommand separator and b) skips over the whole CSI-m sequence if it sees an "invalid" command).
* In 24-bit mode we don't bother with this however, under the assumption that $COLORTERM and friends
* reflect the correct status. */
static inline const char* ansi_underline(void) {
return underline_enabled() ? ANSI_UNDERLINE : "";
}
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 : \
colors_enabled() ? ANSI_##NAME : ""; \
}
#define DEFINE_ANSI_FUNC_UNDERLINE_256(name, NAME, FALLBACK) \
static inline const char* ansi_##name(void) { \
switch (get_color_mode()) { \
case COLOR_OFF: return ""; \
case COLOR_16: return underline_enabled() ? ANSI_##FALLBACK##_UNDERLINE : ANSI_##FALLBACK; \
default : return underline_enabled() ? ANSI_##NAME##_UNDERLINE: ANSI_##NAME; \
} \
#define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
static inline const char* ansi_##name(void) { \
switch (get_color_mode()) { \
case COLOR_OFF: return ""; \
case COLOR_16: return ANSI_##FALLBACK; \
case COLOR_256: return ANSI_##FALLBACK ANSI_##NAME; \
default: return ANSI_##NAME; \
} \
}
DEFINE_ANSI_FUNC(normal, NORMAL);
@@ -184,15 +163,47 @@ static inline const char* _ansi_highlight_yellow(void) {
return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
}
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT);
DEFINE_ANSI_FUNC_UNDERLINE_256(grey_underline, GREY, BRIGHT_BLACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow_underline, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_grey_underline, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
static inline const char* ansi_highlight_green_red(bool b) {
return b ? ansi_highlight_green() : ansi_highlight_red();
}
static inline const char* ansi_underline(void) {
return underline_enabled() ? ANSI_UNDERLINE : "";
}
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##_underline(void) { \
return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \
ansi_##name(); \
}
#define DEFINE_ANSI_FUNC_UNDERLINE_256(name, NAME, FALLBACK) \
static inline const char* ansi_##name##_underline(void) { \
if (!underline_enabled()) \
return ansi_##name(); \
\
switch (get_color_mode()) { \
case COLOR_OFF: return ""; \
case COLOR_16: return ANSI_##FALLBACK##_UNDERLINE; \
case COLOR_256: return ANSI_##FALLBACK##_UNDERLINE ANSI_##NAME##_UNDERLINE; \
default: return ANSI_##NAME##_UNDERLINE; \
} \
}
DEFINE_ANSI_FUNC_UNDERLINE(highlight, HIGHLIGHT);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red, HIGHLIGHT_RED);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green, HIGHLIGHT_GREEN);
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC_UNDERLINE_256(grey, GREY, BRIGHT_BLACK);
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_grey, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);