From 42b71eb94e8be7873b07d001878a3d729c3df621 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 7 Jan 2025 03:59:55 +0900 Subject: [PATCH 1/4] pretty-print: cleanups for cat_file() - add one missing assertion, - always logs on error, - simplify the logic to make it easy to understand, - add several more comments. Preparation for later commits. No functional change. --- src/shared/pretty-print.c | 91 ++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 53 deletions(-) diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index d8a5537658..fe82f51a63 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -183,34 +183,20 @@ int terminal_urlify_man(const char *page, const char *section, char **ret) { return terminal_urlify(url, text, ret); } -typedef enum { - LINE_SECTION, - LINE_COMMENT, - LINE_NORMAL, -} LineType; - -static LineType classify_line_type(const char *line, CatFlags flags) { - const char *t = skip_leading_chars(line, WHITESPACE); - - if ((flags & CAT_FORMAT_HAS_SECTIONS) && *t == '[') - return LINE_SECTION; - if (IN_SET(*t, '#', ';', '\0')) - return LINE_COMMENT; - return LINE_NORMAL; -} - static int cat_file(const char *filename, bool newline, CatFlags flags) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *urlified = NULL, *section = NULL, *old_section = NULL; int r; + assert(filename); + f = fopen(filename, "re"); if (!f) - return -errno; + return log_error_errno(errno, "Failed to open \"%s\": %m", filename); r = terminal_urlify_path(filename, NULL, &urlified); if (r < 0) - return r; + return log_error_errno(r, "Failed to urlify path \"%s\": %m", filename); printf("%s%s# %s%s\n", newline ? "\n" : "", @@ -228,58 +214,57 @@ static int cat_file(const char *filename, bool newline, CatFlags flags) { if (r == 0) break; - LineType line_type = classify_line_type(line, flags); - if (FLAGS_SET(flags, CAT_TLDR)) { - if (line_type == LINE_SECTION) { - /* The start of a section, let's not print it yet. */ + const char *l = skip_leading_chars(line, WHITESPACE); + + /* comment */ + if (*l != '\0' && strchr(COMMENTS, *l)) { + if (!FLAGS_SET(flags, CAT_TLDR)) + printf("%s%s%s\n", ansi_highlight_grey(), line, ansi_normal()); + continue; + } + + /* empty line */ + if (FLAGS_SET(flags, CAT_TLDR) && isempty(l)) + continue; + + /* section */ + if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS) && *l == '[') { + if (FLAGS_SET(flags, CAT_TLDR)) + /* On TLDR, let's not print it yet. */ free_and_replace(section, line); - continue; - } + else + printf("%s%s%s\n", ansi_highlight_cyan(), line, ansi_normal()); + continue; + } - if (line_type == LINE_COMMENT) - continue; + /* normal line */ - /* Before we print the actual line, print the last section header */ - if (section) { - /* Do not print redundant section headers */ - if (!streq_ptr(section, old_section)) - printf("%s%s%s\n", - ansi_highlight_cyan(), - section, - ansi_normal()); + /* Before we print the line, print the last section header. */ + if (FLAGS_SET(flags, CAT_TLDR) && section) { + /* Do not print redundant section headers */ + if (!streq_ptr(section, old_section)) + printf("%s%s%s\n", ansi_highlight_cyan(), section, ansi_normal()); - free_and_replace(old_section, section); - } + free_and_replace(old_section, section); } /* Highlight the left side (directive) of a Foo=bar assignment */ - if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS) && line_type == LINE_NORMAL) { + if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS)) { const char *p = strchr(line, '='); if (p) { - _cleanup_free_ char *highlighted = NULL, *directive = NULL; + _cleanup_free_ char *directive = NULL; directive = strndup(line, p - line); if (!directive) return log_oom(); - highlighted = strjoin(ansi_highlight_green(), - directive, - "=", - ansi_normal(), - p + 1); - if (!highlighted) - return log_oom(); - - free_and_replace(line, highlighted); + printf("%s%s=%s%s\n", ansi_highlight_green(), directive, ansi_normal(), p + 1); + continue; } } - printf("%s%s%s\n", - line_type == LINE_SECTION ? ansi_highlight_cyan() : - line_type == LINE_COMMENT ? ansi_highlight_grey() : - "", - line, - line_type != LINE_NORMAL ? ansi_normal() : ""); + /* Otherwise, print the line as is. */ + printf("%s\n", line); } return 0; From e27fb39e07afeb130f4988782a1d8b45ae29f57c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 7 Jan 2025 04:02:53 +0900 Subject: [PATCH 2/4] pretty-print: fix handling of line continuation in cat_file() Fixes #35878. --- src/shared/pretty-print.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index fe82f51a63..e7360352b3 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -205,7 +205,7 @@ static int cat_file(const char *filename, bool newline, CatFlags flags) { ansi_normal()); fflush(stdout); - for (;;) { + for (bool continued = false;;) { _cleanup_free_ char *line = NULL; r = read_line(f, LONG_LINE_MAX, &line); @@ -224,11 +224,11 @@ static int cat_file(const char *filename, bool newline, CatFlags flags) { } /* empty line */ - if (FLAGS_SET(flags, CAT_TLDR) && isempty(l)) + if (FLAGS_SET(flags, CAT_TLDR) && (isempty(l) || streq(l, "\\"))) continue; /* section */ - if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS) && *l == '[') { + if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS) && *l == '[' && !continued) { if (FLAGS_SET(flags, CAT_TLDR)) /* On TLDR, let's not print it yet. */ free_and_replace(section, line); @@ -248,8 +248,17 @@ static int cat_file(const char *filename, bool newline, CatFlags flags) { free_and_replace(old_section, section); } + /* Check if the line ends with a backslash. */ + bool escaped = false; + for (const char *e = line; *e != '\0'; e++) { + if (escaped) + escaped = false; + else if (*e == '\\') + escaped = true; + } + /* Highlight the left side (directive) of a Foo=bar assignment */ - if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS)) { + if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS) && !continued) { const char *p = strchr(line, '='); if (p) { _cleanup_free_ char *directive = NULL; @@ -259,12 +268,14 @@ static int cat_file(const char *filename, bool newline, CatFlags flags) { return log_oom(); printf("%s%s=%s%s\n", ansi_highlight_green(), directive, ansi_normal(), p + 1); + continued = escaped; continue; } } /* Otherwise, print the line as is. */ printf("%s\n", line); + continued = escaped; } return 0; From 5e244e724f7990436d5ec099abab780d9317b6d1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 7 Jan 2025 04:06:35 +0900 Subject: [PATCH 3/4] pretty-print: make cat_file() also highlight the trailing backslash for line continuation --- src/shared/pretty-print.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index e7360352b3..4eb4858931 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -250,13 +250,23 @@ static int cat_file(const char *filename, bool newline, CatFlags flags) { /* Check if the line ends with a backslash. */ bool escaped = false; - for (const char *e = line; *e != '\0'; e++) { + char *e; + for (e = line; *e != '\0'; e++) { if (escaped) escaped = false; else if (*e == '\\') escaped = true; } + /* Highlight the trailing backslash. */ + if (escaped) { + assert(e > line); + *(e-1) = '\0'; + + if (!strextend(&line, ansi_highlight_red(), "\\", ansi_normal())) + return log_oom(); + } + /* Highlight the left side (directive) of a Foo=bar assignment */ if (FLAGS_SET(flags, CAT_FORMAT_HAS_SECTIONS) && !continued) { const char *p = strchr(line, '='); From 41a1e3012ef45a7f36816bfaa58b21e0758744fc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 9 Jan 2025 03:35:02 +0900 Subject: [PATCH 4/4] TEST-65-ANALYZE: add detailed test case for cat-config --- test/units/TEST-65-ANALYZE.sh | 99 +++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index ac39c42da4..ac4b220ed9 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -202,6 +202,105 @@ systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null systemd-analyze cat-config --tldr foo/bar +mkdir -p /run/test-analyze-cat-config/main.conf.d +cat >/run/test-analyze-cat-config/main.conf </run/test-analyze-cat-config/main.conf.d/override.conf <