Subject: bug#16559: closed (Re: bug#16329: Acknowledgement (`head --lines=-0' prints nothing if no newline at the EOF))

From: [email-masked] (GNU bug Tracking System)
Date: Wed, 29 Jan 2014 11:57:03 +0000

Your bug report

#16329: Bug report for 'head' (and 'wc' et. al.)

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
16329: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16329
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#16329: Acknowledgement (`head --lines=-0' prints nothing if no newline at the EOF)
From: Pádraig Brady <[email-masked]>
To: Алексей Шилин <[email-masked]>
Cc: [email-masked]
Date: Wed, 29 Jan 2014 11:55:53 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

On 01/03/2014 08:40 PM, Алексей Шилин wrote:
> Looks like I was overly cautious about decrementing an unsigned...
> 
> size_t n = bytes_read;
> while (n)
>   {
>     if (all_lines)
>       n -= n ? 1 : 0;        // ...here.
>     else
> 
> As it is under `while (n)' statement, n is always true here, and thus the ternary operator, though makes no
> harm, is needless, and the whole line can be replaced with just `n--;'. Sorry for that.
> 
> The fixed version of the original patch is attached.
> 

I've updated the patch to also handle the pipe case,
and I added a test. Will push soon.

thanks,
Pádraig.
>From b1387eb3e922398ab90cf6aa63cac6dea37971fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9=20=D0=A8=D0=B8=D0?=
 =?UTF-8?q?=BB=D0=B8=D0=BD?= <[email-masked]>
Date: Wed, 29 Jan 2014 01:23:46 +0000
Subject: [PATCH] head: fix --lines=-0 outputting nothing if no newline at EOF

* src/head.c (elide_tail_lines_pipe): Just output all input in
this case to avoid the issue and also avoid redundant '\n' processing.
(elide_tail_lines_seekable): Likewise.
* tests/misc/head-elide-tail.pl: Add tests for no '\n' at EOF.
* NEWS: Mention the fix.
Fixes http://bugs.gnu.org/16329
---
 NEWS                          |    4 ++++
 src/head.c                    |   27 +++++++++++++++++++++------
 tests/misc/head-elide-tail.pl |    7 +++++--
 3 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/NEWS b/NEWS
index e091d18..f86e589 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   when reading the SELinux context for a file.
   [bug introduced in coreutils-8.22]
 
+  head --lines=-0, when the input does not contain a trailing '\n',
+  now copies all input to stdout.  Previously nothing was output in this case.
+  [bug introduced with the --lines=-N feature in coreutils-5.0.1]
+
   ln -sf now replaces symbolic links whose targets can't exist.  Previously
   it would display an error, requiring --no-dereference to avoid the issue.
   [bug introduced in coreutils-5.3.0]
diff --git a/src/head.c b/src/head.c
index ddaa990..ef368d7 100644
--- a/src/head.c
+++ b/src/head.c
@@ -501,6 +501,13 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
       n_read = safe_read (fd, tmp->buffer, BUFSIZ);
       if (n_read == 0 || n_read == SAFE_READ_ERROR)
         break;
+
+      if (! n_elide)
+        {
+          fwrite (tmp->buffer, 1, n_read, stdout);
+          continue;
+        }
+
       tmp->nbytes = n_read;
       tmp->nlines = 0;
       tmp->next = NULL;
@@ -636,8 +643,11 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
       return false;
     }
 
+  /* n_lines == 0 case needs special treatment. */
+  const bool all_lines = !n_lines;
+
   /* Count the incomplete line on files that don't end with a newline.  */
-  if (bytes_read && buffer[bytes_read - 1] != '\n')
+  if (n_lines && bytes_read && buffer[bytes_read - 1] != '\n')
     --n_lines;
 
   while (1)
@@ -647,11 +657,16 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
       size_t n = bytes_read;
       while (n)
         {
-          char const *nl;
-          nl = memrchr (buffer, '\n', n);
-          if (nl == NULL)
-            break;
-          n = nl - buffer;
+          if (all_lines)
+            n -= 1;
+          else
+            {
+              char const *nl;
+              nl = memrchr (buffer, '\n', n);
+              if (nl == NULL)
+                break;
+              n = nl - buffer;
+            }
           if (n_lines-- == 0)
             {
               /* Found it.  */
diff --git a/tests/misc/head-elide-tail.pl b/tests/misc/head-elide-tail.pl
index 758e1c9..3c178d6 100755
--- a/tests/misc/head-elide-tail.pl
+++ b/tests/misc/head-elide-tail.pl
@@ -52,6 +52,8 @@ my @Tests =
    ['elide-l2', "--lines=-1", {IN=>"a"}, {OUT=>''}],
    ['elide-l3', "--lines=-1", {IN=>"a\nb"}, {OUT=>"a\n"}],
    ['elide-l4', "--lines=-1", {IN=>"a\nb\n"}, {OUT=>"a\n"}],
+   ['elide-l5', "--lines=-0", {IN=>"a\nb\n"}, {OUT=>"a\nb\n"}],
+   ['elide-l6', "--lines=-0", {IN=>"a\nb"}, {OUT=>"a\nb"}],
   );
 
 if ($ENV{RUN_EXPENSIVE_TESTS})
@@ -80,9 +82,10 @@ if ($ENV{RUN_EXPENSIVE_TESTS})
       }
 
     $s =~ s/(.)/$1\n/g;
-    for my $file_size (0..20)
+    $s .= 'u'; # test without trailing '\n'
+    for my $file_size (0..21)
       {
-        for my $n_elide (0..20)
+        for my $n_elide (0..21)
           {
             my $input = substr $s, 0, 2 * $file_size;
             my $out_len = $n_elide < $file_size ? $file_size - $n_elide : 0;
-- 
1.7.7.6

Subject: Bug report for 'head' (and 'wc' et. al.)
From: LGUC <[email-masked]>
To: [email-masked]
Date: Sun, 26 Jan 2014 10:28:50 -0430
List-Id: <debbugs-submit.debbugs.gnu.org>

Caracas, Sunday 26th, 2014

Ref: Bug report for 'head' (and 'wc' et. al.)

Dear friends:

Please find attached the text file 'head-tst.txt'

As you easily can see, the following command fails and do not print
anything, even if the file has:  6 lines,  49 words and  250 chars:

'head -n -0 head-tst.txt'

The last line on the file does NOT end with a '\n', and this seems
to be the base of the problem. If you add the last '\n', 'head' works
pretty fine.

So this seems to be a problem with the definition of a 'text line':
I guess that a line that has around 68 normal chars and 13 spaces, is
a good candidate to be considered as a line.

I found the same problem in several other core utils, being the
most remarcable 'wc'. If you executes:

'wc head-tst.txt'

you will get:

5  49 250 head-tst.txt

what is wrong, as the file has six (6) lines instead of five (5).
The last one lines is missing due to the fact that it does not
include a '\n' at the end.

In 1998 I fix 'wc', and I have attached 'wc-fix.c' including only
the most remarkable aspects, in case it could be of any help.

Best regards,

Luis G. Uribe C.

Attachments inside 'head-tst.zip':
- 'head-tst.txt': text file that makes 'head' core utility to fail
- 'wc-fix.c':     fix to same problem, applyied to 'wc' in 1998

Software ID:
GNU coreutils 8.15  January 2012  HEAD(1)
(Cygwin)