]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared/logs-show: strip trailing carriage returns at EOL/EOF
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 29 Oct 2019 09:31:19 +0000 (10:31 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 29 Oct 2019 09:54:45 +0000 (10:54 +0100)
When showing logs from a container, we would fail to show various lines:
Oct 29 09:50:51 krowka systemd-nspawn[61376]: Detected architecture x86-64.
Oct 29 09:50:51 krowka systemd-nspawn[61376]: [1B blob data]
Oct 29 09:50:51 krowka systemd-nspawn[61376]: Welcome to Fedora 32 (Rawhide)!
Oct 29 09:50:51 krowka systemd-nspawn[61376]: [1B blob data]

Those are only harmless \r characters that trail the line. We already replace
tabs and strip various ansi characters that we deem inconsequential, so let's
also strip trailing carriage returns. Non-trailing ones are different, because
they change what would be displayed.

src/basic/string-util.c
src/test/test-strip-tab-ansi.c
src/test/test-utf8.c

index 9586b3940eb93d2d4af93e1e868f12f804557804..c1828687557eaed1e38d648c73a48f71633e5a41 100644 (file)
@@ -743,7 +743,7 @@ static void advance_offsets(
 }
 
 char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
-        const char *i, *begin = NULL;
+        const char *begin = NULL;
         enum {
                 STATE_OTHER,
                 STATE_ESCAPE,
@@ -751,7 +751,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                 STATE_CSO,
         } state = STATE_OTHER;
         char *obuf = NULL;
-        size_t osz = 0, isz, shift[2] = {};
+        size_t osz = 0, isz, shift[2] = {}, n_carriage_returns = 0;
         FILE *f;
 
         assert(ibuf);
@@ -762,6 +762,8 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
          * 1. Replaces TABs by 8 spaces
          * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
          * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
+         * 4. Strip trailing \r characters (since they would "move the cursor", but have no
+         *    other effect).
          *
          * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
          * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
@@ -777,14 +779,24 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
         if (!f)
                 return NULL;
 
-        for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
 
                 switch (state) {
 
                 case STATE_OTHER:
                         if (i >= *ibuf + isz) /* EOT */
                                 break;
-                        else if (*i == '\x1B')
+
+                        if (*i == '\r') {
+                                n_carriage_returns++;
+                                break;
+                        } else if (*i == '\n')
+                                /* Ignore carriage returns before new line */
+                                n_carriage_returns = 0;
+                        for (; n_carriage_returns > 0; n_carriage_returns--)
+                                fputc('\r', f);
+
+                        if (*i == '\x1B')
                                 state = STATE_ESCAPE;
                         else if (*i == '\t') {
                                 fputs("        ", f);
@@ -795,6 +807,8 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                         break;
 
                 case STATE_ESCAPE:
+                        assert(n_carriage_returns == 0);
+
                         if (i >= *ibuf + isz) { /* EOT */
                                 fputc('\x1B', f);
                                 advance_offsets(i - *ibuf, highlight, shift, 1);
@@ -815,6 +829,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                         break;
 
                 case STATE_CSI:
+                        assert(n_carriage_returns == 0);
 
                         if (i >= *ibuf + isz || /* EOT … */
                             !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
@@ -829,6 +844,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                         break;
 
                 case STATE_CSO:
+                        assert(n_carriage_returns == 0);
 
                         if (i >= *ibuf + isz || /* EOT … */
                             (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
@@ -848,7 +864,6 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                 fclose(f);
                 return mfree(obuf);
         }
-
         fclose(f);
 
         free_and_replace(*ibuf, obuf);
index fae384ef9b611354245e4b9ed9e82af43658d6f4..8e305f3791269d25ef24904807bede47b31ff3ba 100644 (file)
@@ -34,6 +34,21 @@ int main(int argc, char *argv[]) {
         assert_se(streq(p, "\x1B[waldo"));
         free(p);
 
+        assert_se(p = strdup("\r\rwaldo"));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
+        assert_se(streq(p, "\r\rwaldo"));
+        free(p);
+
+        assert_se(p = strdup("waldo\r\r"));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
+        assert_se(streq(p, "waldo"));
+        free(p);
+
+        assert_se(p = strdup("waldo\r\r\n\r\n"));
+        assert_se(strip_tab_ansi(&p, NULL, NULL));
+        assert_se(streq(p, "waldo\n\n"));
+        free(p);
+
         assert_se(terminal_urlify_path("/etc/fstab", "i am a fabulous link", &urlified) >= 0);
         assert_se(p = strjoin("something ", urlified, " something-else"));
         assert_se(q = strdup(p));
index b5c4e3dc343c5714eb19b4bf844d7f12713b3848..8937f56237f67bc3a886e55ccbde5ca427e6068e 100644 (file)
@@ -13,6 +13,9 @@ static void test_utf8_is_printable(void) {
         assert_se(utf8_is_printable("\342\204\242", 3));
         assert_se(!utf8_is_printable("\341\204", 2));
         assert_se(utf8_is_printable("ąę", 4));
+        assert_se(!utf8_is_printable("\r", 1));
+        assert_se(utf8_is_printable("\n", 1));
+        assert_se(utf8_is_printable("\t", 1));
 }
 
 static void test_utf8_is_valid(void) {