static volatile int cached_on_tty = -1;
static volatile int cached_colors_enabled = -1;
+static volatile int cached_color_mode = _COLOR_INVALID;
static volatile int cached_underline_enabled = -1;
int chvt(int vt) {
cached_lines = 0;
cached_colors_enabled = -1;
+ cached_color_mode = _COLOR_INVALID;
cached_underline_enabled = -1;
cached_on_tty = -1;
}
return getenv_terminal_is_dumb();
}
-bool colors_enabled(void) {
-
- /* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
- * (which is the explicit way to turn colors on/off). 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. */
-
- if (cached_colors_enabled < 0) {
- int val;
+static ColorMode parse_systemd_colors(void) {
+ const char *e;
+ int r;
- val = getenv_bool("SYSTEMD_COLORS");
- if (val >= 0)
- cached_colors_enabled = val;
+ 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_ON : COLOR_OFF;
+ return _COLOR_INVALID;
+}
+ColorMode get_color_mode(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_ON 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;
+
+ 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_colors_enabled = false;
+ cached_color_mode = COLOR_OFF;
else if (getpid_cached() == 1)
- /* PID1 outputs to the console without holding it open all the time */
- cached_colors_enabled = !getenv_terminal_is_dumb();
+ /* PID1 outputs to the console without holding it open all the time.
+ * Also note the Linux console can only handle 16 colors.
+ */
+ cached_color_mode = getenv_terminal_is_dumb() ? COLOR_OFF : COLOR_16;
else
- cached_colors_enabled = !terminal_is_dumb();
+ cached_color_mode = terminal_is_dumb() ? COLOR_OFF : COLOR_256;
}
+ return cached_color_mode;
+}
+
+bool colors_enabled(void) {
+
+ /* Returns true if colors are considered supported on our stdout. */
+
+ if (cached_colors_enabled < 0)
+ cached_colors_enabled = get_color_mode() != COLOR_OFF;
+
return cached_colors_enabled;
}
bool dev_console_colors_enabled(void) {
_cleanup_free_ char *s = NULL;
- int b;
+ ColorMode m;
/* Returns true if we assume that color is supported on /dev/console.
*
* line. If we find $TERM set we assume color if it's not set to "dumb", similarly to how regular
* colors_enabled() operates. */
- b = getenv_bool("SYSTEMD_COLORS");
- if (b >= 0)
- return b;
+ m = parse_systemd_colors();
+ if (m >= 0)
+ return m;
if (getenv("NO_COLOR"))
return false;
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
+/* Fallback colors: 256 -> 16 */
+#define ANSI_HIGHLIGHT_GREY_FALLBACK "\x1B[0;1;90m"
+#define ANSI_HIGHLIGHT_YELLOW_FALLBACK "\x1B[0;1;33m"
+
/* Reset/clear ANSI styles */
#define ANSI_NORMAL "\x1B[0m"
ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2,
} AcquireTerminalFlags;
+/* Limits the use of ANSI colors to a subset. */
+typedef enum ColorMode {
+ /* No colors, monochrome output. */
+ COLOR_OFF = 0,
+
+ /* All colors, no restrictions. */
+ COLOR_ON = 1,
+
+ /* Only the base 16 colors. */
+ COLOR_16 = 16,
+
+ /* Only 256 colors. */
+ COLOR_256 = 256,
+
+ _COLOR_INVALID = -1,
+} ColorMode;
+
int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
int release_terminal(void);
bool on_tty(void);
bool terminal_is_dumb(void);
bool colors_enabled(void);
+ColorMode get_color_mode(void);
bool underline_enabled(void);
bool dev_console_colors_enabled(void);
return colors_enabled() ? ANSI_##NAME : ""; \
}
-#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME, REPLACEMENT) \
- static inline const char *ansi_##name(void) { \
- return underline_enabled() ? ANSI_##NAME : \
- colors_enabled() ? ANSI_##REPLACEMENT : ""; \
+#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; \
+ } \
+ }
+
+#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
+ static inline const char *ansi_##name(void) { \
+ return underline_enabled() ? ANSI_##NAME ANSI_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 ANSI_UNDERLINE : ANSI_##FALLBACK; \
+ default : return underline_enabled() ? ANSI_##NAME ANSI_UNDERLINE: ANSI_##NAME; \
+ } \
}
DEFINE_ANSI_FUNC(normal, NORMAL);
DEFINE_ANSI_FUNC(magenta, MAGENTA);
DEFINE_ANSI_FUNC(cyan, CYAN);
DEFINE_ANSI_FUNC(white, WHITE);
-DEFINE_ANSI_FUNC(grey, GREY);
+DEFINE_ANSI_FUNC_256(grey, GREY, BRIGHT_BLACK);
DEFINE_ANSI_FUNC(bright_black, BRIGHT_BLACK);
DEFINE_ANSI_FUNC(bright_red, BRIGHT_RED);
DEFINE_ANSI_FUNC(bright_cyan, BRIGHT_CYAN);
DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE);
-DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
-DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
-DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
-DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
-DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
-DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
-DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
-DEFINE_ANSI_FUNC(highlight_grey, HIGHLIGHT_GREY);
-DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
+DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
+DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
+DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
+DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
+DEFINE_ANSI_FUNC_256(highlight_grey, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
+DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
static inline const char* _ansi_highlight_yellow(void) {
return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
}
-DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT);
-DEFINE_ANSI_FUNC_UNDERLINE(grey_underline, GREY_UNDERLINE, GREY);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA_UNDERLINE, HIGHLIGHT_MAGENTA);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_grey_underline, HIGHLIGHT_GREY_UNDERLINE, HIGHLIGHT_GREY);
+DEFINE_ANSI_FUNC_UNDERLINE(underline, NORMAL);
+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);
int get_ctty_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r);