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
This commit is contained in:
Baptiste Daroussin committed 2026-08-31 14:35:21 +02:00
1 parent 9bec8a959b
commit d2cfdf4c12
4 files changed
+102 -17

No files matched your search

+25 -9
View File
@@ -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 non-printing characters (see the
.Fl v
option), and display tab characters as
.It Fl T
Display tab characters as
.Ql ^I .
.It Fl t
Equivalent to specifying the
.Fl T
and
.Fl v
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
+17 -8
View File
@@ -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;
+1
View File
@@ -1,5 +1,6 @@
PACKAGE= tests
ATF_TESTS_SH= cat_extra_test
NETBSD_ATF_TESTS_SH= cat_test
${PACKAGE}FILES+= d_align.in
+59
View File
@@ -0,0 +1,59 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2026 Baptiste Daroussin <[email protected]>
#
#
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
}