Subject: bug#11631: Head command does not position file pointer correctly for negative line count

From: Jim Meyering <[email-masked]>
Date: Tue, 05 Jun 2012 12:29:38 +0200

Anoop Sharma wrote:
> Head command does not position file pointer correctly for negative line
> count. Here is a demonstration of the problem.
>
> Step 1 - Create a file with 10 lines in it.
> $ yes "ABC" | head -c 40 >ip.txt
> $

Thank you for the report.  That is indeed a bug.
Here's a quick example of how head -n-3 should work:

    $ seq 10 > k; (./head -n-3; echo foo; cat) < k
    1
    2
    3
    4
    5
    6
    7
    foo
    8
    9
    10

Before your suggested change, it did this:

    $ seq 10 > k; (head -n-3; echo foo; cat) < k
    1
    2
    3
    4
    5
    6
    7
    foo

I note that a similar change is *not* required for the end-relative
byte-seekable case:

    $ seq 3 > k; (head -c-2; echo foo; cat) < k
    1
    2
    foo
    3

Here's the start of a proper patch.
To come: mention this in NEWS and add a test.

>From 0c156fb347dba3f499ed7b922af1ea357f5558c0 Mon Sep 17 00:00:00 2001
From: Jim Meyering <[email-masked]>
Date: Tue, 5 Jun 2012 12:24:49 +0200
Subject: [PATCH] head: with --lines=-N (-n-N) reset file pointer on seekable
 input

* src/head.c (elide_tail_lines_seekable): Reset file pointer
after printing up to an end-relative line-counted offset.
Anoop Sharma reported the problem and suggested the fix.
---
 src/head.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/head.c b/src/head.c
index d7e83b7..75a69d8 100644
--- a/src/head.c
+++ b/src/head.c
@@ -667,6 +667,14 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
                  Don't bother testing for failure for such a small amount.
                  Any failure will be detected upon close.  */
               fwrite (buffer, 1, n + 1, stdout);
+
+              /* Set file pointer to the byte after what we've output.  */
+              if (lseek (fd, start_pos + n + 1, SEEK_SET) < 0)
+                {
+                  error (0, errno, "%s: failed to reset file pointer",
+                         quote (pretty_filename));
+                  return false;
+                }
               return true;
             }
         }
--
1.7.11.rc1