Add missing stdio error checks to head(1):

- Output errors are terminal.  The output is always stdout.

- Input errors yield a warning and cause the program to fail
  gracefully.

- Restructure the getc(3)/putchar(3) loop in head_file() to accomodate
  checking for errors.

ok?

P.S. Restructuring the loop makes head(1) a bit faster on my machine
in certain contrived benchmarks.  For example, reading the words file
five hundred times:

/usr/bin/time head -n $((1 << 30)) $(jot -b /usr/share/dict/words 500) 
>/dev/null

Not exactly a win, but at least it isn't slower.

Index: head.c
===================================================================
RCS file: /cvs/src/usr.bin/head/head.c,v
retrieving revision 1.23
diff -u -p -r1.23 head.c
--- head.c      29 Jan 2022 00:19:04 -0000      1.23
+++ head.c      6 Feb 2022 23:44:01 -0000
@@ -96,30 +96,46 @@ main(int argc, char *argv[])
 int
 head_file(const char *path, long count, int need_header)
 {
+       const char *name;
        FILE *fp;
-       int ch;
+       int ch, status = 0;
        static int first = 1;
 
        if (path != NULL) {
-               fp = fopen(path, "r");
+               name = path;
+               fp = fopen(name, "r");
                if (fp == NULL) {
-                       warn("%s", path);
+                       warn("%s", name);
                        return 1;
                }
                if (need_header) {
-                       printf("%s==> %s <==\n", first ? "" : "\n", path);
+                       printf("%s==> %s <==\n", first ? "" : "\n", name);
+                       if (ferror(stdout))
+                               err(1, "stdout");
                        first = 0;
                }
-       } else
+       } else {
+               name = "stdin";
                fp = stdin;
+       }
 
-       for (; count > 0 && !feof(fp); --count)
-               while ((ch = getc(fp)) != EOF)
-                       if (putchar(ch) == '\n')
-                               break;
-       fclose(fp);
+       while ((ch = getc(fp)) != EOF) {
+               if (putchar(ch) == EOF)
+                       err(1, "stdout");
+               if (ch == '\n' && --count == 0)
+                       break;
+       }
+       if (ferror(fp)) {
+               warn("%s", name);
+               status = 1;
+       }
 
-       return 0;
+       if (fclose(fp) == EOF) {
+               warn("%s", name);
+               status = 1;
+       }
+
+       return status;
 }
 
 

Reply via email to