]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
terminal-util: several cleanups for ColorMode 33456/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 23 Jun 2024 19:18:04 +0000 (04:18 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 24 Jun 2024 08:57:07 +0000 (17:57 +0900)
- introduce or rename usual enum values _MAX and _INVALID,
- introduce and use string table lookup functions,
- split out implementation of get_color_mode() to _impl(),
- add tests for get_color_mode().

src/basic/terminal-util.c
src/basic/terminal-util.h
src/test/test-terminal-util.c

index 6712d7c89d835d284742a6c0b2edd48138de21a0..cf27134984e96c79f6583be752c0ff523732b0ea 100644 (file)
@@ -40,6 +40,7 @@
 #include "socket-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -51,7 +52,7 @@ static volatile unsigned cached_lines = 0;
 
 static volatile int cached_on_tty = -1;
 static volatile int cached_on_dev_null = -1;
-static volatile int cached_color_mode = _COLOR_INVALID;
+static volatile int cached_color_mode = _COLOR_MODE_INVALID;
 static volatile int cached_underline_enabled = -1;
 
 bool isatty_safe(int fd) {
@@ -959,7 +960,7 @@ void reset_terminal_feature_caches(void) {
         cached_columns = 0;
         cached_lines = 0;
 
-        cached_color_mode = _COLOR_INVALID;
+        cached_color_mode = _COLOR_MODE_INVALID;
         cached_underline_enabled = -1;
         cached_on_tty = -1;
         cached_on_dev_null = -1;
@@ -1315,69 +1316,68 @@ bool terminal_is_dumb(void) {
         return getenv_terminal_is_dumb();
 }
 
+static const char* const color_mode_table[_COLOR_MODE_MAX] = {
+        [COLOR_OFF]   = "off",
+        [COLOR_16]    = "16",
+        [COLOR_256]   = "256",
+        [COLOR_24BIT] = "24bit",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(color_mode, ColorMode, COLOR_24BIT);
+
 static ColorMode parse_systemd_colors(void) {
         const char *e;
-        int r;
 
         e = getenv("SYSTEMD_COLORS");
         if (!e)
-                return _COLOR_INVALID;
-        if (streq(e, "16"))
-                return COLOR_16;
-        if (streq(e, "256"))
-                return COLOR_256;
-        r = parse_boolean(e);
-        if (r >= 0)
-                return r > 0 ? COLOR_24BIT : COLOR_OFF;
-        return _COLOR_INVALID;
-}
+                return _COLOR_MODE_INVALID;
 
-ColorMode get_color_mode(void) {
+        ColorMode m = color_mode_from_string(e);
+        if (m < 0)
+                return log_debug_errno(m, "Failed to parse $SYSTEMD_COLORS value '%s', ignoring: %m", e);
 
+        return m;
+}
+
+static ColorMode get_color_mode_impl(void) {
         /* Returns the mode used to choose output colors. The possible modes are COLOR_OFF for no colors,
          * COLOR_16 for only the base 16 ANSI colors, COLOR_256 for more colors, and COLOR_24BIT for
-         * unrestricted color output. For that we check $SYSTEMD_COLORS first (which is the explicit way to
-         * change the mode). If that didn't work we turn colors off unless we are on a TTY. And if we are on a TTY
-         * we turn it off if $TERM is set to "dumb". There's one special tweak though: if we are PID 1 then we do not
-         * check whether we are connected to a TTY, because we don't keep /dev/console open continuously due to fear
-         * of SAK, and hence things are a bit weird. */
-        ColorMode m;
+         * unrestricted color output. */
 
-        if (cached_color_mode < 0) {
-                m = parse_systemd_colors();
-                if (m >= 0)
-                        cached_color_mode = m;
-                else if (getenv("NO_COLOR"))
-                        /* We only check for the presence of the variable; value is ignored. */
-                        cached_color_mode = COLOR_OFF;
-
-                else if (getpid_cached() == 1) {
-                        /* PID1 outputs to the console without holding it open all the time.
-                         *
-                         * Note that the Linux console can only display 16 colors. We still enable 256 color
-                         * mode even for PID1 output though (which typically goes to the Linux console),
-                         * since the Linux console is able to parse the 256 color sequences and automatically
-                         * map them to the closest color in the 16 color palette (since kernel 3.16). Doing
-                         * 256 colors is nice for people who invoke systemd in a container or via a serial
-                         * link or such, and use a true 256 color terminal to do so. */
-                        if (getenv_terminal_is_dumb())
-                                cached_color_mode = COLOR_OFF;
-                } else {
-                        if (terminal_is_dumb())
-                                cached_color_mode = COLOR_OFF;
-                }
+        /* First, we check $SYSTEMD_COLORS, which is the explicit way to change the mode. */
+        ColorMode m = parse_systemd_colors();
+        if (m >= 0)
+                return m;
 
-                if (cached_color_mode < 0) {
-                        /* We failed to figure out any reason to *disable* colors.
-                         * Let's see how many colors we shall use. */
-                        if (STRPTR_IN_SET(getenv("COLORTERM"),
-                                          "truecolor",
-                                          "24bit"))
-                                cached_color_mode = COLOR_24BIT;
-                        else
-                                cached_color_mode = COLOR_256;
-                }
-        }
+        /* Next, check for the presence of $NO_COLOR; value is ignored. */
+        if (getenv("NO_COLOR"))
+                return COLOR_OFF;
+
+        /* If the above didn't work, we turn colors off unless we are on a TTY. And if we are on a TTY we
+         * turn it off if $TERM is set to "dumb". There's one special tweak though: if we are PID 1 then we
+         * do not check whether we are connected to a TTY, because we don't keep /dev/console open
+         * continuously due to fear of SAK, and hence things are a bit weird. */
+        if (getpid_cached() == 1 ? getenv_terminal_is_dumb() : terminal_is_dumb())
+                return COLOR_OFF;
+
+        /* We failed to figure out any reason to *disable* colors. Let's see how many colors we shall use. */
+        if (STRPTR_IN_SET(getenv("COLORTERM"),
+                          "truecolor",
+                          "24bit"))
+                return COLOR_24BIT;
+
+        /* Note that the Linux console can only display 16 colors. We still enable 256 color mode
+         * even for PID1 output though (which typically goes to the Linux console), since the Linux
+         * console is able to parse the 256 color sequences and automatically map them to the closest
+         * color in the 16 color palette (since kernel 3.16). Doing 256 colors is nice for people who
+         * invoke systemd in a container or via a serial link or such, and use a true 256 color
+         * terminal to do so. */
+        return COLOR_256;
+}
+
+ColorMode get_color_mode(void) {
+        if (cached_color_mode < 0)
+                cached_color_mode = get_color_mode_impl();
 
         return cached_color_mode;
 }
index addb69f7faea91eef7fe406778a0221d51190b49..691b37aa51ed89a73184ea353d090b5a6d7bcbab 100644 (file)
@@ -119,21 +119,17 @@ typedef enum AcquireTerminalFlags {
 
 /* Limits the use of ANSI colors to a subset. */
 typedef enum ColorMode {
-        /* No colors, monochrome output. */
-        COLOR_OFF,
-
-        /* Only the base 16 colors. */
-        COLOR_16,
-
-        /* Only 256 colors. */
-        COLOR_256,
-
-        /* For truecolor or 24bit color support, no restrictions. */
-        COLOR_24BIT,
-
-        _COLOR_INVALID = -EINVAL,
+        COLOR_OFF,   /* No colors, monochrome output. */
+        COLOR_16,    /* Only the base 16 colors. */
+        COLOR_256,   /* Only 256 colors. */
+        COLOR_24BIT, /* For truecolor or 24bit color support, no restriction. */
+        _COLOR_MODE_MAX,
+        _COLOR_MODE_INVALID = -EINVAL,
 } ColorMode;
 
+const char* color_mode_to_string(ColorMode m) _const_;
+ColorMode color_mode_from_string(const char *s) _pure_;
+
 int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
 int release_terminal(void);
 
index dbd76549919f2663793debf0eb0e4c55279f92ff..9f8ca15a658dd9ae9a499f853c172448a6b06db6 100644 (file)
@@ -176,4 +176,37 @@ TEST(get_default_background_color) {
                 log_notice("R=%g G=%g B=%g", red, green, blue);
 }
 
+static void test_get_color_mode_with_env(const char *key, const char *val, ColorMode expected) {
+        ASSERT_OK(setenv(key, val, true));
+        reset_terminal_feature_caches();
+        log_info("get_color_mode($%s=%s): %s", key, val, color_mode_to_string(get_color_mode()));
+        ASSERT_EQ(get_color_mode(), expected);
+}
+
+TEST(get_color_mode) {
+        log_info("get_color_mode(default): %s", color_mode_to_string(get_color_mode()));
+        ASSERT_OK(get_color_mode());
+
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "0",     COLOR_OFF);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "no",    COLOR_OFF);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "16",    COLOR_16);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "256",   COLOR_256);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "1",     COLOR_24BIT);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "yes",   COLOR_24BIT);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "24bit", COLOR_24BIT);
+
+        ASSERT_OK(setenv("NO_COLOR", "1", true));
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "42",      COLOR_OFF);
+        test_get_color_mode_with_env("SYSTEMD_COLORS", "invalid", COLOR_OFF);
+        ASSERT_OK(unsetenv("NO_COLOR"));
+        ASSERT_OK(unsetenv("SYSTEMD_COLORS"));
+
+        test_get_color_mode_with_env("COLORTERM", "truecolor", terminal_is_dumb() ? COLOR_OFF : COLOR_24BIT);
+        test_get_color_mode_with_env("COLORTERM", "24bit",     terminal_is_dumb() ? COLOR_OFF : COLOR_24BIT);
+        test_get_color_mode_with_env("COLORTERM", "invalid",   terminal_is_dumb() ? COLOR_OFF : COLOR_256);
+        test_get_color_mode_with_env("COLORTERM", "42",        terminal_is_dumb() ? COLOR_OFF : COLOR_256);
+        unsetenv("COLORTERM");
+        reset_terminal_feature_caches();
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);