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