git: d2cfdf4c12a7 - main - cat: add -A, -E and -T flags

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Mon, 31 Aug 2026 12:38:02 UTC
The branch main has been updated by bapt:

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

commit d2cfdf4c12a76db3612c83575403150811f5aa9a
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2026-08-21 14:41:54 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2026-08-31 12:35:21 +0000

    cat: add -A, -E and -T flags
    
    Support cat -A, -E and -T, which are commonly used by Linux shell
    scripts.  -E prints a "$" at the end of each line, -T renders tabs
    as ^I, and -A is equivalent to -vET.
    
    MFC After:      1 week
    Discussed with: jrtc27
    Reviewed by:    jrtc27, ziaee
    Differential Revision:  https://reviews.freebsd.org/D59250
---
 bin/cat/cat.1                   | 32 ++++++++++++++++------
 bin/cat/cat.c                   | 25 +++++++++++------
 bin/cat/tests/Makefile          |  1 +
 bin/cat/tests/cat_extra_test.sh | 59 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 16 deletions(-)

diff --git a/bin/cat/cat.1 b/bin/cat/cat.1
index 59e0dff80ed0..7112b8167eef 100644
--- a/bin/cat/cat.1
+++ b/bin/cat/cat.1
@@ -29,7 +29,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd January 29, 2013
+.Dd August 21, 2026
 .Dt CAT 1
 .Os
 .Sh NAME
@@ -37,7 +37,7 @@
 .Nd concatenate and print files
 .Sh SYNOPSIS
 .Nm
-.Op Fl belnstuv
+.Op Fl AbeElnstTuv
 .Op Ar
 .Sh DESCRIPTION
 The
@@ -68,12 +68,24 @@ domain binding capability available in
 .Pp
 The options are as follows:
 .Bl -tag -width indent
+.It Fl A
+Equivalent to specifying the
+.Fl v
+,
+.Fl E
+and
+.Fl T
+options.
 .It Fl b
 Number the non-blank output lines, starting at 1.
 .It Fl e
-Display non-printing characters (see the
+Equivalent to specifying the
+.Fl E
+and
 .Fl v
-option), and display a dollar sign
+options.
+.It Fl E
+Display a dollar sign
 .Pq Ql \&$
 at the end of each line.
 .It Fl l
@@ -91,11 +103,15 @@ Number the output lines, starting at 1.
 .It Fl s
 Squeeze multiple adjacent empty lines, causing the output to be
 single spaced.
+.It Fl T
+Display tab characters as
+.Ql ^I .
 .It Fl t
-Display non-printing characters (see the
+Equivalent to specifying the
+.Fl T
+and
 .Fl v
-option), and display tab characters as
-.Ql ^I .
+options.
 .It Fl u
 Disable output buffering.
 .It Fl v
@@ -183,7 +199,7 @@ utility is compliant with the
 specification.
 .Pp
 The flags
-.Op Fl belnstv
+.Op Fl AbeElnstTv
 are extensions to the specification.
 .Sh HISTORY
 A
diff --git a/bin/cat/cat.c b/bin/cat/cat.c
index 8c10faf7cff9..20a20b44d9f7 100644
--- a/bin/cat/cat.c
+++ b/bin/cat/cat.c
@@ -58,7 +58,7 @@
 #include <casper/cap_fileargs.h>
 #include <casper/cap_net.h>
 
-static int bflag, eflag, lflag, nflag, sflag, tflag, vflag;
+static int bflag, Eflag, lflag, nflag, sflag, Tflag, vflag;
 static int rval;
 static const char *filename;
 static fileargs_t *fa;
@@ -103,7 +103,7 @@ static int udom_open(const char *path, int flags);
 #ifdef BOOTSTRAP_CAT
 #define SUPPORTED_FLAGS "lu"
 #else
-#define SUPPORTED_FLAGS "belnstuv"
+#define SUPPORTED_FLAGS "belnstuvAET"
 #endif
 
 #ifndef NO_UDOM_SUPPORT
@@ -166,8 +166,14 @@ main(int argc, char *argv[])
 		case 'b':
 			bflag = nflag = 1;	/* -b implies -n */
 			break;
+		case 'A':
+			Eflag = Tflag = vflag = 1;	/* -A implies -v -E -T */
+			break;
 		case 'e':
-			eflag = vflag = 1;	/* -e implies -v */
+			Eflag = vflag = 1;	/* -e implies -v */
+			break;
+		case 'E':
+			Eflag = 1;
 			break;
 		case 'l':
 			lflag = 1;
@@ -179,7 +185,10 @@ main(int argc, char *argv[])
 			sflag = 1;
 			break;
 		case 't':
-			tflag = vflag = 1;	/* -t implies -v */
+			Tflag = vflag = 1;	/* -t implies -v */
+			break;
+		case 'T':
+			Tflag = 1;
 			break;
 		case 'u':
 			setbuf(stdout, NULL);
@@ -209,7 +218,7 @@ main(int argc, char *argv[])
 	if (caph_enter_casper() != 0)
 		err(EXIT_FAILURE, "capsicum");
 
-	if (bflag || eflag || nflag || sflag || tflag || vflag)
+	if (bflag || Eflag || nflag || sflag || Tflag || vflag)
 		scanfiles(argv, 1);
 	else
 		scanfiles(argv, 0);
@@ -313,7 +322,7 @@ cook_cat(FILE *fp)
 					(void)fprintf(stdout, "%6d\t", ++line);
 					if (ferror(stdout))
 						break;
-				} else if (eflag) {
+				} else if (Eflag) {
 					(void)fprintf(stdout, "%6s\t", "");
 					if (ferror(stdout))
 						break;
@@ -321,10 +330,10 @@ cook_cat(FILE *fp)
 			}
 		}
 		if (ch == '\n') {
-			if (eflag && putchar('$') == EOF)
+			if (Eflag && putchar('$') == EOF)
 				break;
 		} else if (ch == '\t') {
-			if (tflag) {
+			if (Tflag) {
 				if (putchar('^') == EOF || putchar('I') == EOF)
 					break;
 				continue;
diff --git a/bin/cat/tests/Makefile b/bin/cat/tests/Makefile
index 347f4826ba4d..38fcff5eaf36 100644
--- a/bin/cat/tests/Makefile
+++ b/bin/cat/tests/Makefile
@@ -1,5 +1,6 @@
 PACKAGE=	tests
 
+ATF_TESTS_SH=		cat_extra_test
 NETBSD_ATF_TESTS_SH=		cat_test
 
 ${PACKAGE}FILES+=		d_align.in
diff --git a/bin/cat/tests/cat_extra_test.sh b/bin/cat/tests/cat_extra_test.sh
new file mode 100644
index 000000000000..87d617122abf
--- /dev/null
+++ b/bin/cat/tests/cat_extra_test.sh
@@ -0,0 +1,59 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org>
+#
+#
+
+atf_test_case E_output
+E_output_body()
+{
+	printf 'a\tb\n\001c\n\177\n' > input
+	atf_check -o 'inline:a\tb$\n\001c$\n\0177$\n' cat -E input
+}
+
+atf_test_case T_output
+T_output_body()
+{
+	printf 'a\tb\n\001c\n\177\n' > input
+	atf_check -o 'inline:a^Ib\n\001c\n\0177\n' cat -T input
+}
+
+atf_test_case A_output
+A_output_body()
+{
+	printf 'a\tb\n\001c\n\177\n' > input
+
+	# -A implies -v, -E and -T.
+	expected='a^Ib$\n^Ac$\n^?$\n'
+	atf_check -o "inline:${expected}" cat -A input
+	atf_check -o "inline:${expected}" cat -vET input
+	atf_check -o "inline:${expected}" cat -AET input
+}
+
+atf_test_case e_implies_v
+e_implies_v_body()
+{
+	printf 'a\tb\n\001c\n\177\n' > input
+
+	atf_check -o 'inline:a\tb$\n^Ac$\n^?$\n' cat -e input
+	atf_check -o 'inline:a\tb$\n^Ac$\n^?$\n' cat -Ev input
+}
+
+atf_test_case t_implies_v
+t_implies_v_body()
+{
+	printf 'a\tb\n\001c\n\177\n' > input
+
+	atf_check -o 'inline:a^Ib\n^Ac\n^?\n' cat -t input
+	atf_check -o 'inline:a^Ib\n^Ac\n^?\n' cat -Tv input
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case E_output
+	atf_add_test_case T_output
+	atf_add_test_case A_output
+	atf_add_test_case e_implies_v
+	atf_add_test_case t_implies_v
+}