Hi,

while we are talking about ancient bugs in head(1), here is another one:

Without the patch:

   $ date | head /dev/null /dev/stdin                  
  ==> /dev/null <==
  
  ==> /dev/stdin <==
   $

Oops, where did stdin go?  The reopen(3) function doesn't work here,
it first closes stdin, and then it's gone for good.

With the patch:

   $ date | ./obj/head /dev/null /dev/stdin
  ==> /dev/null <==
  
  ==> /dev/stdin <==
  Tue Oct  7 23:28:21 CEST 2014
   $

Actually, it's not me who found and fixed that bug.
Here is the original CSRG SCCS commit message:

  ^As 00009/00006/00145
  ^Ad D 5.7 92/03/04 14:35:42 bostic 9 8
  ^Ac can't use freopen; example is "date | head file1 /dev/stdin"

So Keith fixed that for 4.4BSD and we neglected to merge his fix
for more than 22 years.  Meanwhile, the code diverged slightly,
but my fix follows Keith's as closely as possible.

The tail(1) and cat(1) utilities also avoid the reopen(3) idiom
and use fp = fopen(...).

While here, remove a statement that is commented since November 14,
1987 - that is, for almost 27 years, it was already commented in
4.3BSD Tahoe.

OK?
  Ingo


Index: head.c
===================================================================
RCS file: /cvs/src/usr.bin/head/head.c,v
retrieving revision 1.17
diff -u -p -r1.17 head.c
--- head.c      7 Oct 2014 19:38:57 -0000       1.17
+++ head.c      7 Oct 2014 21:16:43 -0000
@@ -48,6 +48,7 @@ static void usage(void);
 int
 main(int argc, char *argv[])
 {
+       FILE    *fp;
        long    cnt;
        int     ch, firsttime;
        long    linecnt = 10;
@@ -81,13 +82,13 @@ main(int argc, char *argv[])
                        errx(1, "line count %s: %s", errstr, p);
        }
 
-       /* setlinebuf(stdout); */
        for (firsttime = 1; ; firsttime = 0) {
                if (!*argv) {
                        if (!firsttime)
                                exit(status);
+                       fp = stdin;
                } else {
-                       if (!freopen(*argv, "r", stdin)) {
+                       if ((fp = fopen(*argv, "r")) == NULL) {
                                warn("%s", *argv++);
                                status = 1;
                                continue;
@@ -99,10 +100,11 @@ main(int argc, char *argv[])
                        }
                        ++argv;
                }
-               for (cnt = linecnt; cnt && !feof(stdin); --cnt)
-                       while ((ch = getchar()) != EOF)
+               for (cnt = linecnt; cnt && !feof(fp); --cnt)
+                       while ((ch = getc(fp)) != EOF)
                                if (putchar(ch) == '\n')
                                        break;
+               fclose(fp);
        }
        /*NOTREACHED*/
 }

Reply via email to