]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
journalctl: add highlighting for matched substring 7881/head
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 27 Jan 2018 12:00:09 +0000 (13:00 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sun, 28 Jan 2018 13:50:01 +0000 (14:50 +0100)
Red is used for highligting, the same as grep does. Except when the line is
highlighted red already, because it has high priority, in which case plain ansi
highlight is used for the matched substring.

Coloring is implemented for short and cat outputs, and not for other types.
I guess we could also add it for verbose output in the future.

src/basic/string-util.c
src/basic/string-util.h
src/journal-remote/journal-gatewayd.c
src/journal/journalctl.c
src/shared/logs-show.c
src/shared/logs-show.h
src/test/test-strip-tab-ansi.c

index 7e2f596edcbe743fa907aa462bc62578bbd3e507..9f2c01d8649f41ca9fae02c3db6701d469beaaff 100644 (file)
@@ -30,6 +30,7 @@
 #include "gunicode.h"
 #include "macro.h"
 #include "string-util.h"
+#include "terminal-util.h"
 #include "utf8.h"
 #include "util.h"
 
@@ -648,7 +649,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
         return ret;
 }
 
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
+        if (!offsets)
+                return;
+
+        if ((size_t) diff < offsets[0])
+                shift[0] += size;
+        if ((size_t) diff < offsets[1])
+                shift[1] += size;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
         const char *i, *begin = NULL;
         enum {
                 STATE_OTHER,
@@ -656,7 +667,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                 STATE_BRACKET
         } state = STATE_OTHER;
         char *obuf = NULL;
-        size_t osz = 0, isz;
+        size_t osz = 0, isz, shift[2] = {};
         FILE *f;
 
         assert(ibuf);
@@ -684,15 +695,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                                 break;
                         else if (*i == '\x1B')
                                 state = STATE_ESCAPE;
-                        else if (*i == '\t')
+                        else if (*i == '\t') {
                                 fputs("        ", f);
-                        else
+                                advance_offsets(i - *ibuf, highlight, shift, 7);
+                        } else
                                 fputc(*i, f);
+
                         break;
 
                 case STATE_ESCAPE:
                         if (i >= *ibuf + isz) { /* EOT */
                                 fputc('\x1B', f);
+                                advance_offsets(i - *ibuf, highlight, shift, 1);
                                 break;
                         } else if (*i == '[') {
                                 state = STATE_BRACKET;
@@ -700,6 +714,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                         } else {
                                 fputc('\x1B', f);
                                 fputc(*i, f);
+                                advance_offsets(i - *ibuf, highlight, shift, 1);
                                 state = STATE_OTHER;
                         }
 
@@ -711,6 +726,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                             (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
                                 fputc('\x1B', f);
                                 fputc('[', f);
+                                advance_offsets(i - *ibuf, highlight, shift, 2);
                                 state = STATE_OTHER;
                                 i = begin-1;
                         } else if (*i == 'm')
@@ -732,6 +748,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
         if (_isz)
                 *_isz = osz;
 
+        if (highlight) {
+                highlight[0] += shift[0];
+                highlight[1] += shift[1];
+        }
+
         return obuf;
 }
 
index d8f97e697bbf1ce7b50640bd2108d75829d4ccbb..08eda4fce02be9b0fcdcea9a49af2e60312c3658 100644 (file)
@@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
 
 char *strreplace(const char *text, const char *old_string, const char *new_string);
 
-char *strip_tab_ansi(char **p, size_t *l);
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
 
 char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
 
index 82c70cfbe31bf324f70c7e22ecadb1491d81930b..5a437ce031b8dce5f481528cbf745f81c7c6602b 100644 (file)
@@ -226,7 +226,8 @@ static ssize_t request_reader_entries(
                         return MHD_CONTENT_READER_END_WITH_ERROR;
                 }
 
-                r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL);
+                r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
+                                   NULL, NULL, NULL);
                 if (r < 0) {
                         log_error_errno(r, "Failed to serialize item: %m");
                         return MHD_CONTENT_READER_END_WITH_ERROR;
index 757e9df5ba4cb4868ac91bd3dbd6b67ce8dd2f25..17782688d9e6c7d55f0af6c26459215ac9288f04 100644 (file)
@@ -2516,6 +2516,7 @@ int main(int argc, char *argv[]) {
         for (;;) {
                 while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
                         int flags;
+                        size_t highlight[2] = {};
 
                         if (need_seek) {
                                 if (!arg_reverse)
@@ -2574,6 +2575,7 @@ int main(int argc, char *argv[]) {
                                 _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
                                 const void *message;
                                 size_t len;
+                                PCRE2_SIZE *ovec;
 
                                 md = pcre2_match_data_create(1, NULL);
                                 if (!md)
@@ -2613,6 +2615,10 @@ int main(int argc, char *argv[]) {
                                         r = -EINVAL;
                                         goto finish;
                                 }
+
+                                ovec = pcre2_get_ovector_pointer(md);
+                                highlight[0] = ovec[0];
+                                highlight[1] = ovec[1];
                         }
 #endif
 
@@ -2624,7 +2630,8 @@ int main(int argc, char *argv[]) {
                                 arg_utc * OUTPUT_UTC |
                                 arg_no_hostname * OUTPUT_NO_HOSTNAME;
 
-                        r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
+                        r = output_journal(stdout, j, arg_output, 0, flags,
+                                           arg_output_fields, highlight, &ellipsized);
                         need_seek = true;
                         if (r == -EADDRNOTAVAIL)
                                 break;
index a861be7b324786a4c57dc6ffeb654d583dfc84cb..3d0be7ab001a0a20bfb4bc2c3c152bd47bc8b372 100644 (file)
@@ -166,8 +166,17 @@ static bool shall_print(const char *p, size_t l, OutputFlags flags) {
         return true;
 }
 
-static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
-        const char *color_on = "", *color_off = "";
+static bool print_multiline(
+                FILE *f,
+                unsigned prefix,
+                unsigned n_columns,
+                OutputFlags flags,
+                int priority,
+                const char* message,
+                size_t message_len,
+                size_t highlight[2]) {
+
+        const char *color_on = "", *color_off = "", *highlight_on = "";
         const char *pos, *end;
         bool ellipsized = false;
         int line = 0;
@@ -176,9 +185,11 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
                 if (priority <= LOG_ERR) {
                         color_on = ANSI_HIGHLIGHT_RED;
                         color_off = ANSI_NORMAL;
+                        highlight_on = ANSI_HIGHLIGHT;
                 } else if (priority <= LOG_NOTICE) {
                         color_on = ANSI_HIGHLIGHT;
                         color_off = ANSI_NORMAL;
+                        highlight_on = ANSI_HIGHLIGHT_RED;
                 }
         }
 
@@ -209,9 +220,28 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
 
                 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
                     (prefix + len + 1 < n_columns && !tail_line)) {
-                        fprintf(f, "%*s%s%.*s%s\n",
-                                continuation * prefix, "",
-                                color_on, len, pos, color_off);
+                        if (highlight &&
+                            (size_t) (pos - message) <= highlight[0] &&
+                            highlight[0] < (size_t) len) {
+
+                                fprintf(f, "%*s%s%.*s",
+                                        continuation * prefix, "",
+                                        color_on, (int) highlight[0], pos);
+                                fprintf(f, "%s%.*s",
+                                        highlight_on,
+                                        (int) (MIN((size_t) len, highlight[1]) - highlight[0]),
+                                        pos + highlight[0]);
+                                if ((size_t) len > highlight[1])
+                                        fprintf(f, "%s%.*s",
+                                                color_on,
+                                                (int) (len - highlight[1]),
+                                                pos + highlight[1]);
+                                fprintf(f, "%s\n", color_off);
+
+                        } else
+                                fprintf(f, "%*s%s%.*s%s\n",
+                                        continuation * prefix, "",
+                                        color_on, len, pos, color_off);
                         continue;
                 }
 
@@ -369,7 +399,8 @@ static int output_short(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields) {
+                Set *output_fields,
+                size_t highlight[2]) {
 
         int r;
         const void *data;
@@ -390,6 +421,7 @@ static int output_short(
                 PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
                 PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
         };
+        size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
 
         assert(f);
         assert(j);
@@ -421,7 +453,7 @@ static int output_short(
         }
 
         if (!(flags & OUTPUT_SHOW_ALL))
-                strip_tab_ansi(&message, &message_len);
+                strip_tab_ansi(&message, &message_len, highlight_shifted);
 
         if (priority_len == 1 && *priority >= '0' && *priority <= '7')
                 p = *priority - '0';
@@ -468,7 +500,9 @@ static int output_short(
         } else {
                 fputs(": ", f);
                 ellipsized |=
-                        print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
+                        print_multiline(f, n + 2, n_columns, flags, p,
+                                        message, message_len,
+                                        highlight_shifted);
         }
 
         if (flags & OUTPUT_CATALOG)
@@ -483,7 +517,8 @@ static int output_verbose(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields) {
+                Set *output_fields,
+                size_t highlight[2]) {
 
         const void *data;
         size_t length;
@@ -561,7 +596,7 @@ static int output_verbose(
                     (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
                      && utf8_is_printable(data, length))) {
                         fprintf(f, "    %s%.*s=", on, fieldlen, (const char*)data);
-                        print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
+                        print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1, NULL);
                         fputs(off, f);
                 } else {
                         char bytes[FORMAT_BYTES_MAX];
@@ -590,7 +625,8 @@ static int output_export(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields) {
+                Set *output_fields,
+                size_t highlight[2]) {
 
         sd_id128_t boot_id;
         char sid[33];
@@ -728,7 +764,8 @@ static int output_json(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields) {
+                Set *output_fields,
+                size_t highlight[2]) {
 
         uint64_t realtime, monotonic;
         _cleanup_free_ char *cursor = NULL;
@@ -952,15 +989,22 @@ static int output_cat(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields) {
+                Set *output_fields,
+                size_t highlight[2]) {
 
         const void *data;
         size_t l;
         int r;
+        const char *highlight_on = "", *highlight_off = "";
 
         assert(j);
         assert(f);
 
+        if (flags & OUTPUT_COLOR) {
+                highlight_on = ANSI_HIGHLIGHT_RED;
+                highlight_off = ANSI_NORMAL;
+        }
+
         sd_journal_set_data_threshold(j, 0);
 
         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
@@ -974,7 +1018,17 @@ static int output_cat(
 
         assert(l >= 8);
 
-        fwrite((const char*) data + 8, 1, l - 8, f);
+        if (highlight && (flags & OUTPUT_COLOR)) {
+                assert(highlight[0] <= highlight[1]);
+                assert(highlight[1] <= l - 8);
+
+                fwrite((const char*) data + 8, 1, highlight[0], f);
+                fwrite(highlight_on, 1, strlen(highlight_on), f);
+                fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f);
+                fwrite(highlight_off, 1, strlen(highlight_off), f);
+                fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f);
+        } else
+                fwrite((const char*) data + 8, 1, l - 8, f);
         fputc('\n', f);
 
         return 0;
@@ -986,7 +1040,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields) = {
+                Set *output_fields,
+                size_t highlight[2]) = {
 
         [OUTPUT_SHORT] = output_short,
         [OUTPUT_SHORT_ISO] = output_short,
@@ -1010,6 +1065,7 @@ int output_journal(
                 unsigned n_columns,
                 OutputFlags flags,
                 char **output_fields,
+                size_t highlight[2],
                 bool *ellipsized) {
 
         int ret;
@@ -1030,7 +1086,7 @@ int output_journal(
                         return ret;
         }
 
-        ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
+        ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
 
         if (ellipsized && ret > 0)
                 *ellipsized = true;
@@ -1112,7 +1168,7 @@ static int show_journal(FILE *f,
                         line++;
                         maybe_print_begin_newline(f, &flags);
 
-                        r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
+                        r = output_journal(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
                         if (r < 0)
                                 return r;
                 }
index eaa69b6e9060eedacb8f7b4587afd352e0d40364..c876dcc46aef0dcaacc53d21356a4bb6877e7b35 100644 (file)
@@ -39,6 +39,7 @@ int output_journal(
                 unsigned n_columns,
                 OutputFlags flags,
                 char **output_fields,
+                size_t highlight[2],
                 bool *ellipsized);
 
 int add_match_this_boot(sd_journal *j, const char *machine);
index aabb7c40e70762e8f99e950b222c49ba08fdd66d..838a6e4db66c326556c53ed73af46d12953731af 100644 (file)
@@ -28,24 +28,24 @@ int main(int argc, char *argv[]) {
         char *p;
 
         assert_se(p = strdup("\tFoobar\tbar\twaldo\t"));
-        assert_se(strip_tab_ansi(&p, NULL));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
         fprintf(stdout, "<%s>\n", p);
         assert_se(streq(p, "        Foobar        bar        waldo        "));
         free(p);
 
         assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
-        assert_se(strip_tab_ansi(&p, NULL));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
         fprintf(stdout, "<%s>\n", p);
         assert_se(streq(p, "Hello world!"));
         free(p);
 
         assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
-        assert_se(strip_tab_ansi(&p, NULL));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
         assert_se(streq(p, "\x1B[\x1B[        \x1B[\x1B[Hello world!"));
         free(p);
 
         assert_se(p = strdup("\x1B[waldo"));
-        assert_se(strip_tab_ansi(&p, NULL));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
         assert_se(streq(p, "\x1B[waldo"));
         free(p);