cut: with -f, process each line independently
authorPádraig Brady <P@draigBrady.com>
Mon, 21 Jan 2013 15:37:37 +0000 (15:37 +0000)
committerPádraig Brady <P@draigBrady.com>
Sat, 26 Jan 2013 02:31:53 +0000 (02:31 +0000)
Previously line N+1 was inspected before line N was fully output,
which causes output ordering issues at the terminal or delays
from intermittent sources like tail -f.

* src/cut.c (cut_fields): Adjust so that we record the
previous output character so we can use that info to
determine wether to output a '\n' or not.
* tests/misc/cut.pl: Add tests to ensure existing
functionality isn't broken.
* NEWS: Mention the fix.
Fixes bug http://bugs.gnu.org/13498

NEWS
src/cut.c
tests/misc/cut.pl

diff --git a/NEWS b/NEWS
index 46d1aba..34754bb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   another range.  Before, "echo 123|cut --output-delim=: -b2-,3" would print
   "2:3".  Now it prints "23".  [bug introduced in 5.3.0]
 
+  cut -f no longer inspects input line N+1 before fully outputting line N,
+  which avoids delayed output for intermittent input.
+  [bug introduced in TEXTUTILS-1_8b]
+
   factor no longer loops infinitely on 32 bit powerpc or sparc systems.
   [bug introduced in coreutils-8.20]
 
index 4059ecc..416591c 100644 (file)
--- a/src/cut.c
+++ b/src/cut.c
@@ -601,6 +601,7 @@ cut_fields (FILE *stream)
     return;
 
   ungetc (c, stream);
+  c = 0;
 
   /* To support the semantics of the -s flag, we may have to buffer
      all of the first field to determine whether it is 'delimited.'
@@ -631,6 +632,8 @@ cut_fields (FILE *stream)
           n_bytes = len;
           assert (n_bytes != 0);
 
+          c = 0;
+
           /* If the first field extends to the end of line (it is not
              delimited) and we are printing all non-delimited lines,
              print this one.  */
@@ -646,6 +649,7 @@ cut_fields (FILE *stream)
                   /* Make sure the output line is newline terminated.  */
                   if (field_1_buffer[n_bytes - 1] != '\n')
                     putchar ('\n');
+                  c = '\n';
                 }
               continue;
             }
@@ -658,38 +662,28 @@ cut_fields (FILE *stream)
           ++field_idx;
         }
 
-      if (c != EOF)
+      int prev_c = c;
+
+      if (print_kth (field_idx, NULL))
         {
-          if (print_kth (field_idx, NULL))
+          if (found_any_selected_field)
             {
-              if (found_any_selected_field)
-                {
-                  fwrite (output_delimiter_string, sizeof (char),
-                          output_delimiter_length, stdout);
-                }
-              found_any_selected_field = true;
-
-              while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
-                {
-                  putchar (c);
-                }
+              fwrite (output_delimiter_string, sizeof (char),
+                      output_delimiter_length, stdout);
             }
-          else
+          found_any_selected_field = true;
+
+          while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
             {
-              while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
-                {
-                  /* Empty.  */
-                }
+              putchar (c);
+              prev_c = c;
             }
         }
-
-      if (c == '\n')
+      else
         {
-          c = getc (stream);
-          if (c != EOF)
+          while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
             {
-              ungetc (c, stream);
-              c = '\n';
+              prev_c = c;
             }
         }
 
@@ -699,7 +693,10 @@ cut_fields (FILE *stream)
         {
           if (found_any_selected_field
               || !(suppress_non_delimited && field_idx == 1))
-            putchar ('\n');
+            {
+              if (c == '\n' || prev_c != '\n')
+                putchar ('\n');
+            }
           if (c == EOF)
             break;
           field_idx = 1;
index c9cff1a..3ce09bb 100755 (executable)
@@ -129,6 +129,21 @@ my @Tests =
   ['8bit-delim', '-d', "\255", '--out=_', '-f2,3', {IN=>"a\255b\255c\n"},
    {OUT=>"b_c\n"}],
 
+  # newline processing for fields
+  ['newline-1', '-f1-', {IN=>"a\nb"}, {OUT=>"a\nb\n"}],
+  ['newline-2', '-f1-', {IN=>""}, {OUT=>""}],
+  ['newline-3', '-d:', '-f1', {IN=>"a:1\nb:2\n"}, {OUT=>"a\nb\n"}],
+  ['newline-4', '-d:', '-f1', {IN=>"a:1\nb:2"}, {OUT=>"a\nb\n"}],
+  ['newline-5', '-d:', '-f2', {IN=>"a:1\nb:2\n"}, {OUT=>"1\n2\n"}],
+  ['newline-6', '-d:', '-f2', {IN=>"a:1\nb:2"}, {OUT=>"1\n2\n"}],
+  ['newline-7', '-s', '-d:', '-f1', {IN=>"a:1\nb:2"}, {OUT=>"a\nb\n"}],
+  ['newline-8', '-s', '-d:', '-f1', {IN=>"a:1\nb:2\n"}, {OUT=>"a\nb\n"}],
+  ['newline-9', '-s', '-d:', '-f1', {IN=>"a1\nb2"}, {OUT=>""}],
+  ['newline-10', '-s', '-d:', '-f1,2', {IN=>"a:1\nb:2"}, {OUT=>"a:1\nb:2\n"}],
+  ['newline-11', '-s', '-d:', '-f1,2', {IN=>"a:1\nb:2\n"}, {OUT=>"a:1\nb:2\n"}],
+  ['newline-12', '-s', '-d:', '-f1', {IN=>"a:1\nb:"}, {OUT=>"a\nb\n"}],
+  ['newline-13', '-d:', '-f1-', {IN=>"a1:\n:"}, {OUT=>"a1:\n:\n"}],
+
   # New functionality:
   ['out-delim1', '-c1-3,5-', '--output-d=:', {IN=>"abcdefg\n"},
    {OUT=>"abc:efg\n"}],