git: cef433d3fb38 - stable/14 - uniq: Replace NetBSD's unit tests with our own.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 17 Jan 2024 18:28:38 UTC
The branch stable/14 has been updated by des:

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

commit cef433d3fb38a705f950c4a59b2439084c933139
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-01-12 15:40:26 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-01-17 18:28:23 +0000

    uniq: Replace NetBSD's unit tests with our own.
    
    These new tests cover more functionality and are easier to extend.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    emaste
    Differential Revision:  https://reviews.freebsd.org/D43381
    
    (cherry picked from commit e762fd81e253d4ae9b9f7d2e65cf448633bbe527)
    
    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
    
    (cherry picked from commit 11715600e626cf6cc4b4f564af97f6ae1e5fb0be)
    
    uniq: Clean up and test obsolete options.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D43402
    
    (cherry picked from commit e2ec8ee02a33e39b8ff86a56e8a1ef5e84ac7e62)
    
    uniq: Error out if writing to the output failed.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D43407
    
    (cherry picked from commit 899837e8f5741f9a847b63d9e7c8b76ccc033ab5)
---
 ObsoleteFiles.inc               |   8 ++
 usr.bin/uniq/tests/Makefile     |  13 +--
 usr.bin/uniq/tests/uniq_test.sh | 193 ++++++++++++++++++++++++++++++++++++++++
 usr.bin/uniq/uniq.1             |   5 +-
 usr.bin/uniq/uniq.c             |  68 +++++++-------
 5 files changed, 244 insertions(+), 43 deletions(-)

diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
index 26d6e20ab0d4..afbfb877ebfd 100644
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -51,6 +51,14 @@
 #   xargs -n1 | sort | uniq -d;
 # done
 
+# 20240117: replaced NetBSD tests for uniq with our own
+OLD_FILES+=usr/tests/usr.bin/uniq/d_basic.in
+OLD_FILES+=usr/tests/usr.bin/uniq/d_basic.out
+OLD_FILES+=usr/tests/usr.bin/uniq/d_counts.out
+OLD_FILES+=usr/tests/usr.bin/uniq/d_input.in
+OLD_FILES+=usr/tests/usr.bin/uniq/d_show_duplicates.out
+OLD_FILES+=usr/tests/usr.bin/uniq/d_show_uniques.out
+
 # 20240107: new clang import which bumps version from 16 to 17
 OLD_FILES+=usr/lib/clang/16/include/__clang_cuda_builtin_vars.h
 OLD_FILES+=usr/lib/clang/16/include/__clang_cuda_cmath.h
diff --git a/usr.bin/uniq/tests/Makefile b/usr.bin/uniq/tests/Makefile
index d9c839aa35b6..55bb98e98c34 100644
--- a/usr.bin/uniq/tests/Makefile
+++ b/usr.bin/uniq/tests/Makefile
@@ -1,15 +1,4 @@
-
 PACKAGE=		tests
-
-NETBSD_ATF_TESTS_SH=	uniq_test
-
-${PACKAGE}FILES+=	d_basic.in
-${PACKAGE}FILES+=	d_basic.out
-${PACKAGE}FILES+=	d_counts.out
-${PACKAGE}FILES+=	d_input.in
-${PACKAGE}FILES+=	d_show_duplicates.out
-${PACKAGE}FILES+=	d_show_uniques.out
-
-.include <netbsd-tests.test.mk>
+ATF_TESTS_SH=		uniq_test
 
 .include <bsd.test.mk>
diff --git a/usr.bin/uniq/tests/uniq_test.sh b/usr.bin/uniq/tests/uniq_test.sh
new file mode 100755
index 000000000000..804e82ce7766
--- /dev/null
+++ b/usr.bin/uniq/tests/uniq_test.sh
@@ -0,0 +1,193 @@
+#
+# Copyright (c) 2024 Klara, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+atf_check_uniq() {
+	atf_check uniq "$@" input actual
+	atf_check diff -u actual expected
+	atf_check uniq "$@" - actual <input
+	atf_check diff -u actual expected
+	atf_check -o file:expected uniq "$@" input
+	atf_check -o file:expected uniq "$@" <input
+	atf_check -o file:expected uniq "$@" - <input
+}
+
+atf_test_case basic
+basic_head() {
+	atf_set descr "basic test without options"
+}
+basic_body() {
+	printf "a\na\nb\nb\na\na\n" >input
+	printf "a\nb\na\n" >expected
+	atf_check_uniq
+}
+
+atf_test_case count
+count_head() {
+	atf_set descr "basic test showing counts"
+}
+count_body() {
+	printf "a\na\nb\nb\nb\na\na\na\na\n" >input
+	printf "   2 a\n   3 b\n   4 a\n" >expected
+	atf_check_uniq -c
+	atf_check_uniq --count
+}
+
+atf_test_case repeated
+repeated_head() {
+	atf_set descr "print repeated lines only"
+}
+repeated_body() {
+	printf "a\na\nb\na\na\n" >input
+	printf "a\na\n" >expected
+	atf_check_uniq -d
+	atf_check_uniq --repeated
+}
+
+atf_test_case count_repeated
+count_repeated_head() {
+	atf_set descr "count and print repeated lines only"
+}
+count_repeated_body() {
+	printf "a\na\nb\nb\na\n" >input
+	printf "   2 a\n   2 b\n" >expected
+	atf_check_uniq --count --repeated
+}
+
+atf_test_case all_repeated
+all_repeated_head() {
+	atf_set descr "print every instance of repeated lines"
+}
+all_repeated_body() {
+	printf "a\na\nb\na\na\n" >input
+	printf "a\na\na\na\n" >expected
+	atf_check_uniq -D
+	atf_check_uniq --all-repeated
+}
+
+atf_test_case skip_fields
+skip_fields_head() {
+	atf_set descr "skip fields"
+}
+skip_fields_body() {
+	printf "1 a\n2 a\n3 b\n4 b\n5 a\n6 a\n" >input
+	printf "1 a\n3 b\n5 a\n" >expected
+	atf_check_uniq -1
+	atf_check_uniq -f 1
+	atf_check_uniq --skip-fields 1
+}
+
+atf_test_case skip_fields_tab
+skip_fields_tab_head() {
+	atf_set descr "skip fields (with tabs)"
+}
+skip_fields_tab_body() {
+	printf "1\ta\n2\ta\n3\tb\n4\tb\n5\ta\n6\ta\n" >input
+	printf "1\ta\n3\tb\n5\ta\n" >expected
+	atf_check_uniq -1
+	atf_check_uniq -f 1
+	atf_check_uniq --skip-fields 1
+}
+
+atf_test_case ignore_case
+ignore_case_head() {
+	atf_set descr "ignore case"
+}
+ignore_case_body() {
+	printf "a\nA\nb\nB\na\nA\n" >input
+	printf "a\nb\na\n" >expected
+	atf_check_uniq -i
+	atf_check_uniq --ignore-case
+}
+
+atf_test_case skip_chars
+skip_chars_head() {
+	atf_set descr "skip chars"
+}
+skip_chars_body() {
+	printf "1 a\n2 a\n3 b\n4 b\n5 a\n6 a\n" >input
+	printf "1 a\n3 b\n5 a\n" >expected
+	atf_check_uniq +2
+	atf_check_uniq -s 2
+	atf_check_uniq --skip-chars 2
+}
+
+atf_test_case unique
+unique_head() {
+	atf_set descr "print non-repeated lines only"
+}
+unique_body() {
+	printf "a\na\nb\na\na\n" >input
+	printf "b\n" >expected
+	atf_check_uniq -u
+	atf_check_uniq --unique
+}
+
+atf_test_case count_unique
+count_unique_head() {
+	atf_set descr "print non-repeated lines with count"
+}
+count_unique_body() {
+	printf "a\na\nb\n" >input
+	printf "   1 b\n" >expected
+	atf_check_uniq --unique --count
+	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_test_case stdout
+stdout_head() {
+	atf_set descr "error writing to stdout"
+}
+stdout_body() {
+	(
+		trap "" PIPE
+		echo a | uniq 2>stderr
+		echo $? >result
+	) | true
+	atf_check -o inline:"1\n" cat result
+	atf_check -o match:"stdout" cat stderr
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case basic
+	atf_add_test_case count
+	atf_add_test_case repeated
+	atf_add_test_case count_repeated
+	atf_add_test_case all_repeated
+	atf_add_test_case skip_fields
+	atf_add_test_case skip_fields_tab
+	atf_add_test_case ignore_case
+	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
+	atf_add_test_case stdout
+}
diff --git a/usr.bin/uniq/uniq.1 b/usr.bin/uniq/uniq.1
index 285e13d901a7..3fc1d26774ca 100644
--- a/usr.bin/uniq/uniq.1
+++ b/usr.bin/uniq/uniq.1
@@ -30,7 +30,7 @@
 .\"
 .\"     From: @(#)uniq.1	8.1 (Berkeley) 6/6/93
 .\"
-.Dd June 7, 2020
+.Dd January 12, 2024
 .Dt UNIQ 1
 .Os
 .Sh NAME
@@ -74,6 +74,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 1513ae185bcf..5b675600e56c 100644
--- a/usr.bin/uniq/uniq.c
+++ b/usr.bin/uniq/uniq.c
@@ -54,6 +54,7 @@ static char sccsid[] = "@(#)uniq.c	8.3 (Berkeley) 5/4/95";
 #include <limits.h>
 #include <locale.h>
 #include <nl_types.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -63,14 +64,9 @@ static char sccsid[] = "@(#)uniq.c	8.3 (Berkeley) 5/4/95";
 #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[] =
 {
@@ -100,7 +96,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, *ofn;
 	cap_rights_t rights;
 
 	(void) setlocale(LC_ALL, "");
@@ -120,13 +116,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);
@@ -139,7 +135,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:
@@ -152,9 +148,13 @@ main (int argc, char *argv[])
 	if (argc > 2)
 		usage();
 
+	if (Dflag && dflag)
+		dflag = false;
+
 	ifp = stdin;
 	ifn = "stdin";
 	ofp = stdout;
+	ofn = "stdout";
 	if (argc > 0 && strcmp(argv[0], "-") != 0)
 		ifp = file(ifn = argv[0], "r");
 	cap_rights_init(&rights, CAP_FSTAT, CAP_READ);
@@ -162,7 +162,7 @@ main (int argc, char *argv[])
 		err(1, "unable to limit rights for %s", ifn);
 	cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE);
 	if (argc > 1)
-		ofp = file(argv[1], "w");
+		ofp = file(ofn = argv[1], "w");
 	else
 		cap_rights_set(&rights, CAP_IOCTL);
 	if (caph_rights_limit(fileno(ofp), &rights) < 0) {
@@ -192,6 +192,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;
@@ -211,7 +213,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;
@@ -232,14 +238,23 @@ 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);
+	if (fflush(ofp) != 0)
+		err(1, "%s", ofn);
 	exit(0);
 }
 
@@ -303,11 +318,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);
 }
@@ -315,7 +327,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))
@@ -341,29 +353,25 @@ file(const char *name, const char *mode)
 static void
 obsolete(char *argv[])
 {
-	int len;
-	char *ap, *p, *start;
+	char *ap, *p;
 
 	while ((ap = *++argv)) {
 		/* Return if "--" or not an option of any form. */
 		if (ap[0] != '-') {
 			if (ap[0] != '+')
 				return;
-		} else if (ap[1] == '-')
+		} else if (ap[1] == '-') {
 			return;
+		}
 		if (!isdigit((unsigned char)ap[1]))
 			continue;
 		/*
 		 * Digit signifies an old-style option.  Malloc space for dash,
 		 * new option and argument.
 		 */
-		len = strlen(ap);
-		if ((start = p = malloc(len + 3)) == NULL)
+		if (asprintf(&p, "-%c%s", ap[0] == '+' ? 's' : 'f', ap + 1) < 0)
 			err(1, "malloc");
-		*p++ = '-';
-		*p++ = ap[0] == '+' ? 's' : 'f';
-		(void)strcpy(p, ap + 1);
-		*argv = start;
+		*argv = p;
 	}
 }