From fad48ffa27af96ee0d9489ded88f80c1eeb238dc Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Mon, 14 Mar 2022 19:22:42 +0100 Subject: stricter error checking in file streams (input, output) This also makes the programs exit with a non-zero status when a read or write error occurs. This makes checking the exit status more reliable in scripts. A simple example to simulate a disk with no space left: curl -s 'https://codemadness.org/atom.xml' | sfeed > f /mnt/test: write failed, file system is full echo $? 0 Which now produces: curl -s 'https://codemadness.org/atom.xml' | sfeed > f /mnt/test: write failed, file system is full write error: echo $? 1 Tested with a small mfs on OpenBSD, fstab entry: swap /mnt/test mfs rw,nodev,nosuid,-s=1M 0 0 --- sfeed.c | 6 ++++++ sfeed_atom.c | 9 ++++++--- sfeed_frames.c | 15 +++++++++++---- sfeed_gopher.c | 10 +++++++--- sfeed_html.c | 11 ++++++----- sfeed_mbox.c | 9 ++++++--- sfeed_opml_import.c | 3 +++ sfeed_plain.c | 8 +++++--- sfeed_twtxt.c | 9 ++++++--- sfeed_web.c | 3 +++ sfeed_xmlenc.c | 3 +++ util.c | 10 ++++++++++ util.h | 1 + 13 files changed, 73 insertions(+), 24 deletions(-) diff --git a/sfeed.c b/sfeed.c index 4dd89c1..f2aa6b0 100644 --- a/sfeed.c +++ b/sfeed.c @@ -679,6 +679,9 @@ printfields(void) putchar(FieldSeparator); string_print_trimmed_multi(&ctx.fields[FeedFieldCategory].str); putchar('\n'); + + if (ferror(stdout)) /* check for errors but do not flush */ + checkfileerror(stdout, "", 'w'); } static int @@ -1059,5 +1062,8 @@ main(int argc, char *argv[]) /* NOTE: getnext is defined in xml.h for inline optimization */ xml_parse(&parser); + checkfileerror(stdin, "", 'r'); + checkfileerror(stdout, "", 'w'); + return 0; } diff --git a/sfeed_atom.c b/sfeed_atom.c index 2aea54e..07c34cd 100644 --- a/sfeed_atom.c +++ b/sfeed_atom.c @@ -43,7 +43,8 @@ printfeed(FILE *fp, const char *feedname) ssize_t linelen; int c; - while ((linelen = getline(&line, &linesize, fp)) > 0) { + while ((linelen = getline(&line, &linesize, fp)) > 0 && + !ferror(stdout)) { if (line[linelen - 1] == '\n') line[--linelen] = '\0'; parseline(line, fields); @@ -141,19 +142,21 @@ main(int argc, char *argv[]) if (argc == 1) { printfeed(stdin, ""); + checkfileerror(stdin, "", 'r'); } else { for (i = 1; i < argc; i++) { if (!(fp = fopen(argv[i], "r"))) err(1, "fopen: %s", argv[i]); name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i]; printfeed(fp, name); - if (ferror(fp)) - err(1, "ferror: %s", argv[i]); + checkfileerror(fp, argv[i], 'r'); fclose(fp); } } fputs("\n", stdout); + checkfileerror(stdout, "", 'w'); + return 0; } diff --git a/sfeed_frames.c b/sfeed_frames.c index d345c60..178a4a2 100644 --- a/sfeed_frames.c +++ b/sfeed_frames.c @@ -34,7 +34,8 @@ printfeed(FILE *fpitems, FILE *fpin, struct feed *f) } fputs("
\n", fpitems);
 
-	while ((linelen = getline(&line, &linesize, fpin)) > 0) {
+	while ((linelen = getline(&line, &linesize, fpin)) > 0 &&
+	       !ferror(fpitems)) {
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 		parseline(line, fields);
@@ -114,6 +115,7 @@ main(int argc, char *argv[])
 	if (argc == 1) {
 		feeds[0].name = "";
 		printfeed(fpitems, stdin, &feeds[0]);
+		checkfileerror(stdin, "", 'r');
 	} else {
 		for (i = 1; i < argc; i++) {
 			name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
@@ -122,8 +124,8 @@ main(int argc, char *argv[])
 			if (!(fp = fopen(argv[i], "r")))
 				err(1, "fopen: %s", argv[i]);
 			printfeed(fpitems, fp, &feeds[i - 1]);
-			if (ferror(fp))
-				err(1, "ferror: %s", argv[i]);
+			checkfileerror(fp, argv[i], 'r');
+			checkfileerror(fpitems, "items.html", 'w');
 			fclose(fp);
 		}
 	}
@@ -174,10 +176,15 @@ main(int argc, char *argv[])
 	      "\n"
 	      "\n", fpindex);
 
+	checkfileerror(fpindex, "index.html", 'w');
+	checkfileerror(fpitems, "items.html", 'w');
+
 	fclose(fpindex);
 	fclose(fpitems);
-	if (fpmenu)
+	if (fpmenu) {
+		checkfileerror(fpmenu, "menu.html", 'w');
 		fclose(fpmenu);
+	}
 
 	return 0;
 }
diff --git a/sfeed_gopher.c b/sfeed_gopher.c
index 0b95b3d..7da41f6 100644
--- a/sfeed_gopher.c
+++ b/sfeed_gopher.c
@@ -50,7 +50,8 @@ printfeed(FILE *fpitems, FILE *fpin, struct feed *f)
 		fprintf(fpitems, "i\t\t%s\t%s\r\n", host, port);
 	}
 
-	while ((linelen = getline(&line, &linesize, fpin)) > 0) {
+	while ((linelen = getline(&line, &linesize, fpin)) > 0 &&
+	       !ferror(fpitems)) {
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 		parseline(line, fields);
@@ -150,6 +151,8 @@ main(int argc, char *argv[])
 	if (argc == 1) {
 		f.name = "";
 		printfeed(stdout, stdin, &f);
+		checkfileerror(stdin, "", 'r');
+		checkfileerror(stdout, "", 'w');
 	} else {
 		if ((p = getenv("SFEED_GOPHER_PATH")))
 			prefixpath = p;
@@ -172,8 +175,8 @@ main(int argc, char *argv[])
 			if (!(fpitems = fopen(path, "wb")))
 				err(1, "fopen");
 			printfeed(fpitems, fp, &f);
-			if (ferror(fp))
-				err(1, "ferror: %s", argv[i]);
+			checkfileerror(fp, argv[i], 'r');
+			checkfileerror(fpitems, path, 'w');
 			fclose(fp);
 			fclose(fpitems);
 
@@ -186,6 +189,7 @@ main(int argc, char *argv[])
 			fprintf(fpindex, "\t%s\t%s\r\n", host, port);
 		}
 		fputs(".\r\n", fpindex);
+		checkfileerror(fpindex, "index", 'w');
 		fclose(fpindex);
 	}
 
diff --git a/sfeed_html.c b/sfeed_html.c
index 240ea66..f3cff2b 100644
--- a/sfeed_html.c
+++ b/sfeed_html.c
@@ -34,7 +34,8 @@ printfeed(FILE *fp, struct feed *f)
 	}
 	fputs("
\n", stdout);
 
-	while ((linelen = getline(&line, &linesize, fp)) > 0) {
+	while ((linelen = getline(&line, &linesize, fp)) > 0 &&
+	       !ferror(stdout)) {
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 		parseline(line, fields);
@@ -109,8 +110,7 @@ main(int argc, char *argv[])
 	if (argc == 1) {
 		feeds[0].name = "";
 		printfeed(stdin, &feeds[0]);
-		if (ferror(stdin))
-			err(1, "ferror: :");
+		checkfileerror(stdin, "", 'r');
 	} else {
 		for (i = 1; i < argc; i++) {
 			name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
@@ -118,8 +118,7 @@ main(int argc, char *argv[])
 			if (!(fp = fopen(argv[i], "r")))
 				err(1, "fopen: %s", argv[i]);
 			printfeed(fp, &feeds[i - 1]);
-			if (ferror(fp))
-				err(1, "ferror: %s", argv[i]);
+			checkfileerror(fp, argv[i], 'r');
 			fclose(fp);
 		}
 	}
@@ -150,5 +149,7 @@ main(int argc, char *argv[])
 	fprintf(stdout, "\t\n\t(%lu/%lu) - Newsfeed\n\n",
 	        totalnew, total);
 
+	checkfileerror(stdout, "", 'w');
+
 	return 0;
 }
diff --git a/sfeed_mbox.c b/sfeed_mbox.c
index c2827d4..33c9ec6 100644
--- a/sfeed_mbox.c
+++ b/sfeed_mbox.c
@@ -63,7 +63,8 @@ printfeed(FILE *fp, const char *feedname)
 	ssize_t linelen;
 	int ishtml;
 
-	while ((linelen = getline(&line, &linesize, fp)) > 0) {
+	while ((linelen = getline(&line, &linesize, fp)) > 0 &&
+	       !ferror(stdout)) {
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 		hash = djb2((unsigned char *)line, 5381ULL);
@@ -163,17 +164,19 @@ main(int argc, char *argv[])
 
 	if (argc == 1) {
 		printfeed(stdin, "");
+		checkfileerror(stdin, "", 'r');
 	} else {
 		for (i = 1; i < argc; i++) {
 			if (!(fp = fopen(argv[i], "r")))
 				err(1, "fopen: %s", argv[i]);
 			name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
 			printfeed(fp, name);
-			if (ferror(fp))
-				err(1, "ferror: %s", argv[i]);
+			checkfileerror(fp, argv[i], 'r');
 			fclose(fp);
 		}
 	}
 
+	checkfileerror(stdout, "", 'w');
+
 	return 0;
 }
diff --git a/sfeed_opml_import.c b/sfeed_opml_import.c
index 0844b5c..6c0bd0e 100644
--- a/sfeed_opml_import.c
+++ b/sfeed_opml_import.c
@@ -101,5 +101,8 @@ main(void)
 	xml_parse(&parser);
 	fputs("}\n", stdout);
 
+	checkfileerror(stdin, "", 'r');
+	checkfileerror(stdout, "", 'w');
+
 	return 0;
 }
diff --git a/sfeed_plain.c b/sfeed_plain.c
index 45785e7..c954eff 100644
--- a/sfeed_plain.c
+++ b/sfeed_plain.c
@@ -19,7 +19,8 @@ printfeed(FILE *fp, const char *feedname)
 	time_t parsedtime;
 	ssize_t linelen;
 
-	while ((linelen = getline(&line, &linesize, fp)) > 0) {
+	while ((linelen = getline(&line, &linesize, fp)) > 0 &&
+	       !ferror(stdout)) {
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 		parseline(line, fields);
@@ -69,17 +70,18 @@ main(int argc, char *argv[])
 
 	if (argc == 1) {
 		printfeed(stdin, "");
+		checkfileerror(stdin, "", 'r');
 	} else {
 		for (i = 1; i < argc; i++) {
 			if (!(fp = fopen(argv[i], "r")))
 				err(1, "fopen: %s", argv[i]);
 			name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
 			printfeed(fp, name);
-			if (ferror(fp))
-				err(1, "ferror: %s", argv[i]);
+			checkfileerror(fp, argv[i], 'r');
 			fclose(fp);
 		}
 	}
+	checkfileerror(stdout, "", 'w');
 
 	return 0;
 }
diff --git a/sfeed_twtxt.c b/sfeed_twtxt.c
index 6107fc7..8514bd6 100644
--- a/sfeed_twtxt.c
+++ b/sfeed_twtxt.c
@@ -17,7 +17,8 @@ printfeed(FILE *fp, const char *feedname)
 	time_t parsedtime;
 	ssize_t linelen;
 
-	while ((linelen = getline(&line, &linesize, fp)) > 0) {
+	while ((linelen = getline(&line, &linesize, fp)) > 0 &&
+	       !ferror(stdout)) {
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 		parseline(line, fields);
@@ -54,17 +55,19 @@ main(int argc, char *argv[])
 
 	if (argc == 1) {
 		printfeed(stdin, "");
+		checkfileerror(stdin, "", 'r');
 	} else {
 		for (i = 1; i < argc; i++) {
 			if (!(fp = fopen(argv[i], "r")))
 				err(1, "fopen: %s", argv[i]);
 			name = ((name = strrchr(argv[i], '/'))) ? name + 1 : argv[i];
 			printfeed(fp, name);
-			if (ferror(fp))
-				err(1, "ferror: %s", argv[i]);
+			checkfileerror(fp, argv[i], 'r');
 			fclose(fp);
 		}
 	}
 
+	checkfileerror(stdout, "", 'w');
+
 	return 0;
 }
diff --git a/sfeed_web.c b/sfeed_web.c
index 2d77d4c..704afe8 100644
--- a/sfeed_web.c
+++ b/sfeed_web.c
@@ -136,5 +136,8 @@ main(int argc, char *argv[])
 	/* NOTE: getnext is defined in xml.h for inline optimization */
 	xml_parse(&parser);
 
+	checkfileerror(stdin, "", 'r');
+	checkfileerror(stdout, "", 'w');
+
 	return 0;
 }
diff --git a/sfeed_xmlenc.c b/sfeed_xmlenc.c
index c6a43d4..67d7b0f 100644
--- a/sfeed_xmlenc.c
+++ b/sfeed_xmlenc.c
@@ -56,5 +56,8 @@ main(void)
 	/* NOTE: getnext is defined in xml.h for inline optimization */
 	xml_parse(&parser);
 
+	checkfileerror(stdin, "", 'r');
+	checkfileerror(stdout, "", 'w');
+
 	return 0;
 }
diff --git a/util.c b/util.c
index db43e0c..ed0b5c9 100644
--- a/util.c
+++ b/util.c
@@ -46,6 +46,16 @@ errx(int exitstatus, const char *fmt, ...)
 	exit(exitstatus);
 }
 
+/* Handle read or write errors for a FILE * stream */
+void
+checkfileerror(FILE *fp, const char *name, int mode)
+{
+	if (mode == 'r' && ferror(fp))
+		errx(1, "read error: %s", name);
+	else if (mode == 'w' && (fflush(fp) || ferror(fp)))
+		errx(1, "write error: %s", name);
+}
+
 /* strcasestr() included for portability */
 char *
 strcasestr(const char *h, const char *n)
diff --git a/util.h b/util.h
index 6115dcf..8053750 100644
--- a/util.h
+++ b/util.h
@@ -64,6 +64,7 @@ int uri_hasscheme(const char *);
 int uri_makeabs(struct uri *, struct uri *, struct uri *);
 int uri_parse(const char *, struct uri *);
 
+void checkfileerror(FILE *, const char *, int);
 void parseline(char *, char *[FieldLast]);
 void printutf8pad(FILE *, const char *, size_t, int);
 int  strtotime(const char *, time_t *);
-- 
cgit v1.2.3