The main loop in head(1) is obfuscated.  In particular, the path
through the loop to exit(3) is extremely clever.  Clever in a bad way.

This patch moves the open/read/write/close portions of the loop out
into a separate function, head_file().  I think the result is easier
to understand.  We only loop in main() if we have file arguments, the
argv[] loop is idiomatic and simple, we actually reach the end of
main(), etc.

In a subsequent patch I intend to switch to getline(3) and add more
error checking to head_file().  For now I have just moved the
getc/putchar loop into head_file() as-is.

ok?

Index: head.c
===================================================================
RCS file: /cvs/src/usr.bin/head/head.c,v
retrieving revision 1.22
diff -u -p -r1.22 head.c
--- head.c      10 Oct 2021 15:57:25 -0000      1.22
+++ head.c      27 Jan 2022 04:41:09 -0000
@@ -37,6 +37,7 @@
 #include <errno.h>
 #include <unistd.h>
 
+int head_file(const char *, long, int);
 static void usage(void);
 
 /*
@@ -49,9 +50,7 @@ int
 main(int argc, char *argv[])
 {
        const char *errstr;
-       FILE    *fp;
-       long    cnt;
-       int     ch, firsttime;
+       int     ch;
        long    linecnt = 10;
        int     status = 0;
 
@@ -81,33 +80,46 @@ main(int argc, char *argv[])
        }
        argc -= optind, argv += optind;
 
-       for (firsttime = 1; ; firsttime = 0) {
-               if (!*argv) {
-                       if (!firsttime)
-                               exit(status);
-                       fp = stdin;
-                       if (pledge("stdio", NULL) == -1)
-                               err(1, "pledge");
-               } else {
-                       if ((fp = fopen(*argv, "r")) == NULL) {
-                               warn("%s", *argv++);
-                               status = 1;
-                               continue;
-                       }
-                       if (argc > 1) {
-                               if (!firsttime)
-                                       putchar('\n');
-                               printf("==> %s <==\n", *argv);
-                       }
-                       ++argv;
-               }
-               for (cnt = linecnt; cnt && !feof(fp); --cnt)
-                       while ((ch = getc(fp)) != EOF)
-                               if (putchar(ch) == '\n')
-                                       break;
-               fclose(fp);
+       if (argc == 0) {
+               if (pledge("stdio", NULL) == -1)
+                       err(1, "pledge");
+
+               status = head_file(NULL, linecnt, 0);
+       } else {
+               for (; *argv != NULL; argv++)
+                       status |= head_file(*argv, linecnt, argc > 1);
        }
-       /*NOTREACHED*/
+
+       return status;
+}
+
+int
+head_file(const char *path, long count, int need_header)
+{
+       FILE *fp;
+       int ch;
+       static int first = 1;
+
+       if (path != NULL) {
+               fp = fopen(path, "r");
+               if (fp == NULL) {
+                       warn("%s", path);
+                       return 1;
+               }
+               if (need_header) {
+                       printf("%s==> %s <==\n", first ? "" : "\n", path);
+                       first = 0;
+               }
+       } else
+               fp = stdin;
+
+       for (; count > 0 && !feof(fp); --count)
+               while ((ch = getc(fp)) != EOF)
+                       if (putchar(ch) == '\n')
+                               break;
+       fclose(fp);
+
+       return 0;
 }
 
 

Reply via email to