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;
}