git: 3c37828ee187 - main - cmp: Check the status of stdout.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 09 Oct 2024 12:11:54 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=3c37828ee1874754e1c5e96268016113c1e02ba2

commit 3c37828ee1874754e1c5e96268016113c1e02ba2
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-10-09 12:08:47 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-10-09 12:11:43 +0000

    cmp: Check the status of stdout.
    
    POSIX requires us to print an error message and exit non-zero if
    writing to stdout fails.  This can only happen if sflag is unset.
    
    MFC after:      3 days
    Sponsored by:   Klara, Inc.
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D47020
---
 usr.bin/cmp/cmp.c              | 25 +++++++++++++++----------
 usr.bin/cmp/extern.h           |  6 +++---
 usr.bin/cmp/link.c             | 14 ++++++++------
 usr.bin/cmp/misc.c             | 10 +++-------
 usr.bin/cmp/regular.c          | 35 +++++++++++++++++++----------------
 usr.bin/cmp/special.c          | 18 +++++++++++-------
 usr.bin/cmp/tests/cmp_test2.sh | 30 ++++++++++++++++++++++++++++++
 7 files changed, 89 insertions(+), 49 deletions(-)

diff --git a/usr.bin/cmp/cmp.c b/usr.bin/cmp/cmp.c
index 01750aa66da7..746616c0c20b 100644
--- a/usr.bin/cmp/cmp.c
+++ b/usr.bin/cmp/cmp.c
@@ -100,8 +100,9 @@ main(int argc, char *argv[])
 	int ch, fd1, fd2, oflag;
 	bool special;
 	const char *file1, *file2;
+	int ret;
 
-	limit = skip1 = skip2 = 0;
+	limit = skip1 = skip2 = ret = 0;
 	oflag = O_RDONLY;
 	while ((ch = getopt_long(argc, argv, "+bhi:ln:sxz", long_opts, NULL)) != -1)
 		switch (ch) {
@@ -199,8 +200,8 @@ main(int argc, char *argv[])
 
 	if (fd1 == -1) {
 		if (fd2 == -1) {
-			c_link(file1, skip1, file2, skip2, limit);
-			exit(0);
+			ret = c_link(file1, skip1, file2, skip2, limit);
+			goto end;
 		} else if (!sflag)
 			errx(ERR_EXIT, "%s: Not a symbolic link", file2);
 		else
@@ -239,19 +240,23 @@ main(int argc, char *argv[])
 #ifdef SIGINFO
 	(void)signal(SIGINFO, siginfo);
 #endif
-	if (special)
-		c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
-	else {
+	if (special) {
+		ret = c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
+	} else {
 		if (zflag && sb1.st_size != sb2.st_size) {
 			if (!sflag)
 				(void)printf("%s %s differ: size\n",
 				    file1, file2);
-			exit(DIFF_EXIT);
+			ret = DIFF_EXIT;
+		} else {
+			ret = c_regular(fd1, file1, skip1, sb1.st_size,
+			    fd2, file2, skip2, sb2.st_size, limit);
 		}
-		c_regular(fd1, file1, skip1, sb1.st_size,
-		    fd2, file2, skip2, sb2.st_size, limit);
 	}
-	exit(0);
+end:
+	if (!sflag && fflush(stdout) != 0)
+		err(ERR_EXIT, "stdout");
+	exit(ret);
 }
 
 static void
diff --git a/usr.bin/cmp/extern.h b/usr.bin/cmp/extern.h
index a7cb1fe5330b..4671b34653fa 100644
--- a/usr.bin/cmp/extern.h
+++ b/usr.bin/cmp/extern.h
@@ -34,10 +34,10 @@
 #define DIFF_EXIT	1
 #define ERR_EXIT	2	/* error exit code */
 
-void	c_link(const char *, off_t, const char *, off_t, off_t);
-void	c_regular(int, const char *, off_t, off_t, int, const char *, off_t,
+int	c_link(const char *, off_t, const char *, off_t, off_t);
+int	c_regular(int, const char *, off_t, off_t, int, const char *, off_t,
 	    off_t, off_t);
-void	c_special(int, const char *, off_t, int, const char *, off_t, off_t);
+int	c_special(int, const char *, off_t, int, const char *, off_t, off_t);
 void	diffmsg(const char *, const char *, off_t, off_t, int, int);
 void	eofmsg(const char *);
 
diff --git a/usr.bin/cmp/link.c b/usr.bin/cmp/link.c
index a08f4dcf9973..dfa2f957d829 100644
--- a/usr.bin/cmp/link.c
+++ b/usr.bin/cmp/link.c
@@ -37,7 +37,7 @@
 
 #include "extern.h"
 
-void
+int
 c_link(const char *file1, off_t skip1, const char *file2, off_t skip2,
     off_t limit)
 {
@@ -87,15 +87,17 @@ c_link(const char *file1, off_t skip1, const char *file2, off_t skip2,
 				else
 					(void)printf("%6lld %3o %3o\n",
 					    (long long)byte, ch, *p2);
-			} else
+			} else {
 				diffmsg(file1, file2, byte, 1, ch, *p2);
-				/* NOTREACHED */
+				return (DIFF_EXIT);
+			}
 		}
 		byte++;
 	}
 
-	if (*p1 || *p2)
+	if (*p1 || *p2) {
 		eofmsg (*p1 ? file2 : file1);
-	if (dfound)
-		exit(DIFF_EXIT);
+		return (DIFF_EXIT);
+	}
+	return (dfound ? DIFF_EXIT : 0);
 }
diff --git a/usr.bin/cmp/misc.c b/usr.bin/cmp/misc.c
index 78b431b6f6a9..4abefff31cf9 100644
--- a/usr.bin/cmp/misc.c
+++ b/usr.bin/cmp/misc.c
@@ -43,17 +43,15 @@ eofmsg(const char *file)
 {
 	if (!sflag)
 		warnx("EOF on %s", file);
-	exit(DIFF_EXIT);
 }
 
 void
 diffmsg(const char *file1, const char *file2, off_t byte, off_t line,
     int b1, int b2)
 {
-	if (sflag)
-		goto out;
-
-	if (bflag) {
+	if (sflag) {
+		/* nothing */
+	} else if (bflag) {
 		(void)printf("%s %s differ: char %lld, line %lld is %3o %c %3o %c\n",
 		    file1, file2, (long long)byte, (long long)line, b1, b1,
 		    b2, b2);
@@ -61,6 +59,4 @@ diffmsg(const char *file1, const char *file2, off_t byte, off_t line,
 		(void)printf("%s %s differ: char %lld, line %lld\n",
 		    file1, file2, (long long)byte, (long long)line);
 	}
-out:
-	exit(DIFF_EXIT);
 }
diff --git a/usr.bin/cmp/regular.c b/usr.bin/cmp/regular.c
index 9e1db2bd8772..c4407f708e8a 100644
--- a/usr.bin/cmp/regular.c
+++ b/usr.bin/cmp/regular.c
@@ -50,7 +50,7 @@ static void segv_handler(int);
 
 #define ROUNDPAGE(i) ((i) & ~pagemask)
 
-void
+int
 c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
     int fd2, const char *file2, off_t skip2, off_t len2, off_t limit)
 {
@@ -62,15 +62,19 @@ c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
 	size_t pagesize;
 	int dfound;
 
-	if (skip1 > len1)
+	if (skip1 > len1) {
 		eofmsg(file1);
+		return (DIFF_EXIT);
+	}
 	len1 -= skip1;
-	if (skip2 > len2)
+	if (skip2 > len2) {
 		eofmsg(file2);
+		return (DIFF_EXIT);
+	}
 	len2 -= skip2;
 
 	if (sflag && len1 != len2)
-		exit(DIFF_EXIT);
+		return (DIFF_EXIT);
 
 	pagesize = getpagesize();
 	pagemask = (off_t)pagesize - 1;
@@ -82,14 +86,12 @@ c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
 		length = MIN(length, limit);
 
 	if ((m1 = remmap(NULL, fd1, off1)) == NULL) {
-		c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
-		return;
+		return (c_special(fd1, file1, skip1, fd2, file2, skip2, limit));
 	}
 
 	if ((m2 = remmap(NULL, fd2, off2)) == NULL) {
 		munmap(m1, MMAP_CHUNK);
-		c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
-		return;
+		return (c_special(fd1, file1, skip1, fd2, file2, skip2, limit));
 	}
 
 	if (caph_rights_limit(fd1, cap_rights_init(&rights, CAP_MMAP_R)) < 0)
@@ -120,21 +122,21 @@ c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
 		}
 #endif
 		if ((ch = *p1) != *p2) {
+			dfound = 1;
 			if (xflag) {
-				dfound = 1;
 				(void)printf("%08llx %02x %02x\n",
 				    (long long)byte - 1, ch, *p2);
 			} else if (lflag) {
-				dfound = 1;
 				if (bflag)
 					(void)printf("%6lld %3o %c %3o %c\n",
 					    (long long)byte, ch, ch, *p2, *p2);
 				else
 					(void)printf("%6lld %3o %3o\n",
 					    (long long)byte, ch, *p2);
-			} else
+			} else {
 				diffmsg(file1, file2, byte, line, ch, *p2);
-				/* NOTREACHED */
+				return (DIFF_EXIT);
+			}
 		}
 		if (ch == '\n')
 			++line;
@@ -161,10 +163,11 @@ c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
 	if (sigaction(SIGSEGV, &oact, NULL))
 		err(ERR_EXIT, "sigaction()");
 
-	if (len1 != len2)
-		eofmsg (len1 > len2 ? file2 : file1);
-	if (dfound)
-		exit(DIFF_EXIT);
+	if (len1 != len2) {
+		eofmsg(len1 > len2 ? file2 : file1);
+		return (DIFF_EXIT);
+	}
+	return (dfound ? DIFF_EXIT : 0);
 }
 
 static u_char *
diff --git a/usr.bin/cmp/special.c b/usr.bin/cmp/special.c
index 47082eb276ab..e25e82b17047 100644
--- a/usr.bin/cmp/special.c
+++ b/usr.bin/cmp/special.c
@@ -39,7 +39,7 @@
 
 #include "extern.h"
 
-void
+int
 c_special(int fd1, const char *file1, off_t skip1,
     int fd2, const char *file2, off_t skip2, off_t limit)
 {
@@ -98,7 +98,7 @@ c_special(int fd1, const char *file1, off_t skip1,
 					    (long long)byte, ch1, ch2);
 			} else {
 				diffmsg(file1, file2, byte, line, ch1, ch2);
-				/* NOTREACHED */
+				return (DIFF_EXIT);
 			}
 		}
 		if (ch1 == '\n')
@@ -110,13 +110,17 @@ eof:	if (ferror(fp1))
 	if (ferror(fp2))
 		err(ERR_EXIT, "%s", file2);
 	if (feof(fp1)) {
-		if (!feof(fp2))
+		if (!feof(fp2)) {
 			eofmsg(file1);
-	} else
-		if (feof(fp2))
+			return (DIFF_EXIT);
+		}
+	} else {
+		if (feof(fp2)) {
 			eofmsg(file2);
+			return (DIFF_EXIT);
+		}
+	}
 	fclose(fp2);
 	fclose(fp1);
-	if (dfound)
-		exit(DIFF_EXIT);
+	return (dfound ? DIFF_EXIT : 0);
 }
diff --git a/usr.bin/cmp/tests/cmp_test2.sh b/usr.bin/cmp/tests/cmp_test2.sh
index 80d2e663875f..2ec6071851d3 100755
--- a/usr.bin/cmp/tests/cmp_test2.sh
+++ b/usr.bin/cmp/tests/cmp_test2.sh
@@ -133,6 +133,35 @@ bflag_body()
 	    cmp -bl a b
 }
 
+# Helper for stdout test case
+atf_check_stdout()
+{
+	(
+		trap "" PIPE
+		cmp "$@" 2>stderr
+		echo $? >result
+	) | true
+	atf_check -o inline:"2\n" cat result
+	atf_check -o match:"stdout" cat stderr
+}
+
+atf_test_case stdout
+stdout_head()
+{
+	atf_set descr "Failure to write to stdout"
+}
+stdout_body()
+{
+	echo a >a
+	echo b >b
+	atf_check_stdout a b
+	atf_check_stdout - b <a
+	atf_check_stdout a - <b
+	ln -s a alnk
+	ln -s b blnk
+	atf_check_stdout -h alnk blnk
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case special
@@ -141,4 +170,5 @@ atf_init_test_cases()
 	atf_add_test_case skipsuff
 	atf_add_test_case limit
 	atf_add_test_case bflag
+	atf_add_test_case stdout
 }