]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ansi-color: in 256 mode, always set the fallback color first
authorMike Yuan <me@yhndnzj.com>
Sun, 1 Mar 2026 13:20:53 +0000 (14:20 +0100)
committerMike Yuan <me@yhndnzj.com>
Tue, 3 Mar 2026 22:56:05 +0000 (23:56 +0100)
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

src/basic/ansi-color.h

index e686e1167b14eb1a01db4b68f51589cce4755596..1ddb9c6681c874f5388a5d981bd54922c8d0f1bc 100644 (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;          \
-                }                                              \
-        }
-
-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;                \
-                }                                                                                                   \
+/* 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. */
+
+#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);