git: 11715600e626 - main - uniq: Fix interactive use.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Fri, 12 Jan 2024 15:44:28 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=11715600e626cf6cc4b4f564af97f6ae1e5fb0be

commit 11715600e626cf6cc4b4f564af97f6ae1e5fb0be
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-01-12 15:40:33 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-01-12 15:43:55 +0000

    uniq: Fix interactive use.
    
    Output a line as soon as it is possible to determine that it will have
    to be output.  For the basic case, this means output each line as it is
    read unless it is identical to the previous one.  For the -d case, it
    means output the first instance as soon as the second is read, unless
    the -c option was also given.  The -D and -u cases were already fine.
    
    Add test cases for interactive use with no options and with -d.
    
    Explicitly ignore -d when -D is also specified.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    rew, kevans
    Differential Revision:  https://reviews.freebsd.org/D43382
---
 usr.bin/uniq/tests/uniq_test.sh | 26 ++++++++++++++++++++++
 usr.bin/uniq/uniq.1             |  5 ++++-
 usr.bin/uniq/uniq.c             | 49 ++++++++++++++++++++++++-----------------
 3 files changed, 59 insertions(+), 21 deletions(-)

diff --git a/usr.bin/uniq/tests/uniq_test.sh b/usr.bin/uniq/tests/uniq_test.sh
index ddd9ec9881dd..8dc2015734f6 100755
--- a/usr.bin/uniq/tests/uniq_test.sh
+++ b/usr.bin/uniq/tests/uniq_test.sh
@@ -133,6 +133,30 @@ count_unique_body() {
 	atf_check_uniq --count --unique
 }
 
+atf_test_case interactive
+interactive_head() {
+	atf_set descr "test interactive use"
+}
+interactive_body() {
+	sh -c 'yes | stdbuf -oL uniq >actual' &
+	pid=$!
+	sleep 1
+	kill $!
+	atf_check -o inline:"y\n" cat actual
+}
+
+atf_test_case interactive_repeated
+interactive_repeated_head() {
+	atf_set descr "test interactive use with -d"
+}
+interactive_repeated_body() {
+	sh -c 'yes | stdbuf -oL uniq -d >actual' &
+	pid=$!
+	sleep 1
+	kill $!
+	atf_check -o inline:"y\n" cat actual
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case basic
@@ -146,4 +170,6 @@ atf_init_test_cases()
 	atf_add_test_case skip_chars
 	atf_add_test_case unique
 	atf_add_test_case count_unique
+	atf_add_test_case interactive
+	atf_add_test_case interactive_repeated
 }
diff --git a/usr.bin/uniq/uniq.1 b/usr.bin/uniq/uniq.1
index e01c84328575..30dee856d772 100644
--- a/usr.bin/uniq/uniq.1
+++ b/usr.bin/uniq/uniq.1
@@ -28,7 +28,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 7, 2020
+.Dd January 12, 2024
 .Dt UNIQ 1
 .Os
 .Sh NAME
@@ -72,6 +72,9 @@ Precede each output line with the count of the number of times the line
 occurred in the input, followed by a single space.
 .It Fl d , Fl -repeated
 Output a single copy of each line that is repeated in the input.
+Ignored if
+.Fl D
+is also specified.
 .It Fl D , Fl -all-repeated Op Ar septype
 Output all lines that are repeated (like
 .Fl d ,
diff --git a/usr.bin/uniq/uniq.c b/usr.bin/uniq/uniq.c
index 55766f7e987e..0bc9b2b86af3 100644
--- a/usr.bin/uniq/uniq.c
+++ b/usr.bin/uniq/uniq.c
@@ -42,6 +42,7 @@
 #include <limits.h>
 #include <locale.h>
 #include <nl_types.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -51,14 +52,9 @@
 #include <wchar.h>
 #include <wctype.h>
 
-static int Dflag, cflag, dflag, uflag, iflag;
-static int numchars, numfields, repeats;
-
-/* Dflag values */
-#define	DF_NONE		0
-#define	DF_NOSEP	1
-#define	DF_PRESEP	2
-#define	DF_POSTSEP	3
+static enum { DF_NONE, DF_NOSEP, DF_PRESEP, DF_POSTSEP } Dflag;
+static bool cflag, dflag, uflag, iflag;
+static long long numchars, numfields, repeats;
 
 static const struct option long_opts[] =
 {
@@ -88,7 +84,7 @@ main (int argc, char *argv[])
 	int ch, comp;
 	size_t prevbuflen, thisbuflen, b1;
 	char *prevline, *thisline, *p;
-	const char *ifn, *errstr;;
+	const char *errstr, *ifn;
 	cap_rights_t rights;
 
 	(void) setlocale(LC_ALL, "");
@@ -108,13 +104,13 @@ main (int argc, char *argv[])
 				usage();
 			break;
 		case 'c':
-			cflag = 1;
+			cflag = true;
 			break;
 		case 'd':
-			dflag = 1;
+			dflag = true;
 			break;
 		case 'i':
-			iflag = 1;
+			iflag = true;
 			break;
 		case 'f':
 			numfields = strtonum(optarg, 0, INT_MAX, &errstr);
@@ -127,7 +123,7 @@ main (int argc, char *argv[])
 				errx(1, "character skip value is %s: %s", errstr, optarg);
 			break;
 		case 'u':
-			uflag = 1;
+			uflag = true;
 			break;
 		case '?':
 		default:
@@ -140,6 +136,9 @@ main (int argc, char *argv[])
 	if (argc > 2)
 		usage();
 
+	if (Dflag && dflag)
+		dflag = false;
+
 	ifp = stdin;
 	ifn = "stdin";
 	ofp = stdout;
@@ -180,6 +179,8 @@ main (int argc, char *argv[])
 			err(1, "%s", ifn);
 		exit(0);
 	}
+	if (!cflag && !Dflag && !dflag && !uflag)
+		show(ofp, prevline);
 	tprev = convert(prevline);
 
 	tthis = NULL;
@@ -199,7 +200,11 @@ main (int argc, char *argv[])
 			/* If different, print; set previous to new value. */
 			if (Dflag == DF_POSTSEP && repeats > 0)
 				fputc('\n', ofp);
-			if (!Dflag)
+			if (!cflag && !Dflag && !dflag && !uflag)
+				show(ofp, thisline);
+			else if (!Dflag &&
+			    (!dflag || (cflag && repeats > 0)) &&
+			    (!uflag || repeats == 0))
 				show(ofp, prevline);
 			p = prevline;
 			b1 = prevbuflen;
@@ -220,13 +225,20 @@ main (int argc, char *argv[])
 					show(ofp, prevline);
 				}
 				show(ofp, thisline);
+			} else if (dflag && !cflag) {
+				if (repeats == 0)
+					show(ofp, prevline);
 			}
 			++repeats;
 		}
 	}
 	if (ferror(ifp))
 		err(1, "%s", ifn);
-	if (!Dflag)
+	if (!cflag && !Dflag && !dflag && !uflag)
+		/* already printed */ ;
+	else if (!Dflag &&
+	    (!dflag || (cflag && repeats > 0)) &&
+	    (!uflag || repeats == 0))
 		show(ofp, prevline);
 	exit(0);
 }
@@ -291,11 +303,8 @@ inlcmp(const char *s1, const char *s2)
 static void
 show(FILE *ofp, const char *str)
 {
-
-	if ((!Dflag && dflag && repeats == 0) || (uflag && repeats > 0))
-		return;
 	if (cflag)
-		(void)fprintf(ofp, "%4d %s", repeats + 1, str);
+		(void)fprintf(ofp, "%4lld %s", repeats + 1, str);
 	else
 		(void)fprintf(ofp, "%s", str);
 }
@@ -303,7 +312,7 @@ show(FILE *ofp, const char *str)
 static wchar_t *
 skip(wchar_t *str)
 {
-	int nchars, nfields;
+	long long nchars, nfields;
 
 	for (nfields = 0; *str != L'\0' && nfields++ != numfields; ) {
 		while (iswblank(*str))