git: e5081477e6b3 - stable/14 - column(1): add tests and -l flag
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 19 May 2025 09:30:48 UTC
The branch stable/14 has been updated by ivy:
URL: https://cgit.FreeBSD.org/src/commit/?id=e5081477e6b3a7f9533c57b2bf68b6ee305614b2
commit e5081477e6b3a7f9533c57b2bf68b6ee305614b2
Author: Lexi Winter <ivy@FreeBSD.org>
AuthorDate: 2025-05-07 09:27:20 +0000
Commit: Lexi Winter <ivy@FreeBSD.org>
CommitDate: 2025-05-19 09:29:55 +0000
column(1): add tests and -l flag
column(1): add tests
Reviewed by: des
Approved by: des (mentor)
Differential Revision: https://reviews.freebsd.org/D49911
(cherry picked from commit 6f2b1b56ac3dd154bd98f5a7ea075abcb4356560)
column(1): add -l flag
the '-l <tblcols>' flag limits the number of columns that column(1) will
produce in -t mode. this is syntax-compatible with the same option in
util-linux's column(1), but due to existing differences between the two
implementations, it's not semantically compatible.
as a side-effect, fix a pre-existing bug where empty fields could cause
incorrect output:
% echo ':' | column -ts:
(null)
while here, also fix a couple of minor existing issues.
Reviewed by: des
Approved by: des (mentor)
Differential Revision: https://reviews.freebsd.org/D50290
(cherry picked from commit 313713b24c6d2a3061972c4f431515c4f1b01c77)
---
etc/mtree/BSD.tests.dist | 2 +
usr.bin/column/Makefile | 4 +
usr.bin/column/column.1 | 11 ++-
usr.bin/column/column.c | 45 +++++++---
usr.bin/column/tests/Makefile | 3 +
usr.bin/column/tests/column.sh | 200 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 254 insertions(+), 11 deletions(-)
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index a6b2eed725b8..2159b24978c0 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -1041,6 +1041,8 @@
..
cmp
..
+ column
+ ..
compress
..
cpio
diff --git a/usr.bin/column/Makefile b/usr.bin/column/Makefile
index 1c304e2b3927..23933d9ef96f 100644
--- a/usr.bin/column/Makefile
+++ b/usr.bin/column/Makefile
@@ -1,5 +1,9 @@
# @(#)Makefile 8.1 (Berkeley) 6/6/93
+.include <src.opts.mk>
PROG= column
+HAS_TESTS=
+SUBDIR.${MK_TESTS}= tests
+
.include <bsd.prog.mk>
diff --git a/usr.bin/column/column.1 b/usr.bin/column/column.1
index f9b05ccf2210..c9dff361cc9c 100644
--- a/usr.bin/column/column.1
+++ b/usr.bin/column/column.1
@@ -27,7 +27,7 @@
.\"
.\" @(#)column.1 8.1 (Berkeley) 6/6/93
.\"
-.Dd July 29, 2004
+.Dd May 13, 2025
.Dt COLUMN 1
.Os
.Sh NAME
@@ -37,6 +37,7 @@
.Nm
.Op Fl tx
.Op Fl c Ar columns
+.Op Fl l Ar tblcols
.Op Fl s Ar sep
.Op Ar
.Sh DESCRIPTION
@@ -55,6 +56,14 @@ The options are as follows:
Output is formatted for a display
.Ar columns
wide.
+.It Fl l
+When used with
+.Fl t ,
+limit the table to
+.Ar tblcols
+columns in width.
+The last column will contain the rest of the line,
+including any delimiters.
.It Fl s
Specify a set of characters to be used to delimit columns for the
.Fl t
diff --git a/usr.bin/column/column.c b/usr.bin/column/column.c
index a808113649bc..97fc83e6b589 100644
--- a/usr.bin/column/column.c
+++ b/usr.bin/column/column.c
@@ -67,6 +67,7 @@ static void usage(void);
static int width(const wchar_t *);
static int termwidth = 80; /* default terminal width */
+static int tblcols; /* number of table columns for -t */
static int entries; /* number of records */
static int eval; /* exit value */
@@ -81,7 +82,7 @@ main(int argc, char **argv)
FILE *fp;
int ch, tflag, xflag;
char *p;
- const char *src;
+ const char *errstr, *src;
wchar_t *newsep;
size_t seplen;
@@ -94,17 +95,26 @@ main(int argc, char **argv)
termwidth = win.ws_col;
tflag = xflag = 0;
- while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
+ while ((ch = getopt(argc, argv, "c:l:s:tx")) != -1)
switch(ch) {
case 'c':
- termwidth = atoi(optarg);
+ termwidth = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "invalid terminal width \"%s\": %s",
+ optarg, errstr);
+ break;
+ case 'l':
+ tblcols = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "invalid max width \"%s\": %s",
+ optarg, errstr);
break;
case 's':
src = optarg;
seplen = mbsrtowcs(NULL, &src, 0, NULL);
if (seplen == (size_t)-1)
err(1, "bad separator");
- newsep = malloc((seplen + 1) * sizeof(wchar_t));
+ newsep = calloc(seplen + 1, sizeof(wchar_t));
if (newsep == NULL)
err(1, NULL);
mbsrtowcs(newsep, &src, seplen + 1, NULL);
@@ -123,6 +133,9 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
+ if (tblcols && !tflag)
+ errx(1, "the -l flag cannot be used without the -t flag");
+
if (!*argv)
input(stdin);
else for (; *argv; ++argv)
@@ -230,7 +243,7 @@ maketbl(void)
int *lens, maxcols;
TBL *tbl;
wchar_t **cols;
- wchar_t *last;
+ wchar_t *s;
if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL)
err(1, NULL);
@@ -239,9 +252,11 @@ maketbl(void)
if ((lens = calloc(maxcols, sizeof(int))) == NULL)
err(1, NULL);
for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
- for (coloff = 0, p = *lp;
- (cols[coloff] = wcstok(p, separator, &last));
- p = NULL)
+ for (p = *lp; wcschr(separator, *p); ++p)
+ /* nothing */ ;
+ for (coloff = 0; *p;) {
+ cols[coloff] = p;
+
if (++coloff == maxcols) {
if (!(cols = realloc(cols, ((u_int)maxcols +
DEFCOLS) * sizeof(wchar_t *))) ||
@@ -252,6 +267,16 @@ maketbl(void)
0, DEFCOLS * sizeof(int));
maxcols += DEFCOLS;
}
+
+ if ((!tblcols || coloff < tblcols) &&
+ (s = wcspbrk(p, separator))) {
+ *s++ = L'\0';
+ while (*s && wcschr(separator, *s))
+ ++s;
+ p = s;
+ } else
+ break;
+ }
if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL)
err(1, NULL);
if ((t->len = calloc(coloff, sizeof(int))) == NULL)
@@ -332,8 +357,8 @@ width(const wchar_t *wcs)
static void
usage(void)
{
-
(void)fprintf(stderr,
- "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
+ "usage: column [-tx] [-c columns] [-l tblcols]"
+ " [-s sep] [file ...]\n");
exit(1);
}
diff --git a/usr.bin/column/tests/Makefile b/usr.bin/column/tests/Makefile
new file mode 100644
index 000000000000..40a7767f0dc0
--- /dev/null
+++ b/usr.bin/column/tests/Makefile
@@ -0,0 +1,3 @@
+ATF_TESTS_SH= column
+
+.include <bsd.test.mk>
diff --git a/usr.bin/column/tests/column.sh b/usr.bin/column/tests/column.sh
new file mode 100644
index 000000000000..283dc88bff1a
--- /dev/null
+++ b/usr.bin/column/tests/column.sh
@@ -0,0 +1,200 @@
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2025 Lexi Winter
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+atf_test_case "basic"
+basic_head()
+{
+ atf_set descr "Basic column(1) with default options"
+}
+
+basic_body()
+{
+ cat >input.1 <<END
+this is the first input file
+it has multiple lines
+END
+
+ cat >input.2 <<END
+here lies the second input file
+some lines
+
+are empty
+END
+
+ cat >input.3 <<END
+third of the input files am i
+and i have
+more
+lines
+than before
+END
+
+ cat >expected <<END
+this is the first input file are empty lines
+it has multiple lines third of the input files am i than before
+here lies the second input file and i have
+some lines more
+END
+
+ atf_check -o save:output column -c120 input.1 input.2 input.3
+ atf_check diff expected output
+}
+
+atf_test_case "rows"
+rows_head()
+{
+ atf_set descr "column(1) with -x (row-wise) option"
+}
+
+rows_body()
+{
+ cat >input.1 <<END
+this is the first input file
+it has multiple lines
+END
+
+ cat >input.2 <<END
+here lies the second input file
+some lines
+
+are empty
+END
+
+ cat >input.3 <<END
+third of the input files am i
+and i have
+more
+lines
+than before
+END
+
+ cat >expected <<END
+this is the first input file it has multiple lines here lies the second input file
+some lines are empty third of the input files am i
+and i have more lines
+than before
+END
+
+ atf_check -o save:output column -xc120 input.1 input.2 input.3
+ atf_check diff expected output
+}
+
+atf_test_case "basic_table"
+basic_table_head()
+{
+ atf_set descr "column(1) with -t (table) option"
+}
+
+basic_table_body()
+{
+ cat >input.1 <<END
+1 2 3 4
+foo bar baz quux
+END
+
+ cat >input.2 <<END
+fie fi fo fum
+END
+
+ cat >input.3 <<END
+where did my
+fields go
+argh
+END
+
+ cat >expected <<END
+1 2 3 4
+foo bar baz quux
+fie fi fo fum
+where did my
+fields go
+argh
+END
+
+ atf_check -o save:output column -tc120 input.1 input.2 input.3
+ atf_check diff expected output
+}
+
+atf_test_case "colonic_table"
+colonic_table_head()
+{
+ atf_set descr "column(1) with -t (table) and -s options"
+}
+
+colonic_table_body()
+{
+ cat >input <<END
+one:two.three
+four.five:six
+seven.:eight.:nine
+:ein
+::zwei
+drei..
+vier:
+:
+
+END
+
+ cat >expected <<END
+one two three
+four five six
+seven eight nine
+ein
+zwei
+drei
+vier
+END
+
+ atf_check -o save:output column -tc120 -s:. input
+ atf_check diff expected output
+}
+
+atf_test_case "ncols"
+ncols_head()
+{
+ atf_set descr "column(1) with -t (table) and -s and -l options"
+}
+
+ncols_body()
+{
+ cat >input <<END
+now we have five columns
+here there are four
+now only three
+just two
+one
+END
+
+ cat >expected <<END
+now we have five columns
+here there are four
+now only three
+just two
+one
+END
+
+ atf_check -o save:output column -tc120 -l3 input
+ atf_check diff expected output
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case basic
+ atf_add_test_case rows
+ atf_add_test_case basic_table
+ atf_add_test_case colonic_table
+ atf_add_test_case ncols
+}