git: b3cef2d2f538 - stable/14 - split: Further option parsing improvements.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Thu, 05 Oct 2023 00:01:39 UTC
The branch stable/14 has been updated by des:

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

commit b3cef2d2f538671551b30c918267bfbe246d225c
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-09-28 16:29:52 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2023-10-04 23:59:44 +0000

    split: Further option parsing improvements.
    
    * Whenever possible, use strtonum() to parse numeric arguments.
    * Improve usefulness and consistency of error messages.
    * While here, fix some type and style issues.
    
    Sponsored by:   Klara, Inc.
    Reviewed by:    christos, kevans
    Differential Revision:  https://reviews.freebsd.org/D42010
    
    (cherry picked from commit d7399551b02861b910b15c7b7a78a2835644aa45)
    
    split: Improve GNU compatibility.
    
    * Like GNU split, turn autoextend back on if given -a0.
    * Add a test case that verifies that -a<non-zero> turns autoextend off.
    * Add a test case that verifies that -a0 turns autoextend back on.
    
    Sponsored by:   Klara, Inc.
    Reviewed by:    christos, kevans
    Differential Revision:  https://reviews.freebsd.org/D42011
    
    (cherry picked from commit d9d69a6f6fc1f8d80df9ce6493e8ab9388781615)
---
 usr.bin/split/split.c             | 89 ++++++++++++++++++++++-----------------
 usr.bin/split/tests/split_test.sh | 19 +++++++++
 2 files changed, 70 insertions(+), 38 deletions(-)

diff --git a/usr.bin/split/split.c b/usr.bin/split/split.c
index af1ed69c9482..e246a0d4adfc 100644
--- a/usr.bin/split/split.c
+++ b/usr.bin/split/split.c
@@ -63,7 +63,7 @@ static const char sccsid[] = "@(#)split.c	8.2 (Berkeley) 4/16/94";
 #define DEFLINE	1000			/* Default num lines per file. */
 
 static off_t	 bytecnt;		/* Byte count to split on. */
-static off_t	 chunks = 0;		/* Chunks count to split into. */
+static long	 chunks;		/* Chunks count to split into. */
 static bool      clobber = true;        /* Whether to overwrite existing output files. */
 static long	 numlines;		/* Line count to split on. */
 static int	 file_open;		/* If a file open. */
@@ -73,7 +73,7 @@ static regex_t	 rgx;
 static int	 pflag;
 static bool	 dflag;
 static long	 sufflen = 2;		/* File name suffix length. */
-static int	 autosfx = 1;		/* Whether to auto-extend the suffix length. */
+static bool	 autosfx = true;	/* Whether to auto-extend the suffix length. */
 
 static void newfile(void);
 static void split1(void);
@@ -84,8 +84,8 @@ static void usage(void) __dead2;
 int
 main(int argc, char **argv)
 {
-	const char *p;
-	char *ep;
+	char errbuf[64];
+	const char *p, *errstr;
 	int ch, error;
 
 	setlocale(LC_ALL, "");
@@ -106,20 +106,27 @@ main(int argc, char **argv)
 			while (numlines >= 0 && *p >= '0' && *p <= '9')
 				numlines = numlines * 10 + *p++ - '0';
 			if (numlines <= 0 || *p != '\0')
-				errx(EX_USAGE, "%c%s: illegal line count", ch,
-				    optarg ? optarg : "");
+				errx(EX_USAGE, "%c%s: line count is invalid",
+				    ch, optarg ? optarg : "");
 			break;
 		case 'a':		/* Suffix length */
-			if ((sufflen = strtol(optarg, &ep, 10)) <= 0 || *ep)
-				errx(EX_USAGE,
-				    "%s: illegal suffix length", optarg);
-			autosfx = 0;
+			sufflen = strtonum(optarg, 0, INT_MAX, &errstr);
+			if (errstr != NULL) {
+				errx(EX_USAGE, "%s: suffix length is %s",
+				    optarg, errstr);
+			}
+			if (sufflen == 0) {
+				sufflen = 2;
+				autosfx = true;
+			} else {
+				autosfx = false;
+			}
 			break;
 		case 'b':		/* Byte count. */
-			errno = 0;
-			error = expand_number(optarg, &bytecnt);
-			if (error == -1)
-				errx(EX_USAGE, "%s: offset too large", optarg);
+			if (expand_number(optarg, &bytecnt) != 0) {
+				errx(EX_USAGE, "%s: byte count is invalid",
+				    optarg);
+			}
 			break;
 		case 'c':               /* Continue, don't overwrite output files. */
 			clobber = false;
@@ -130,22 +137,27 @@ main(int argc, char **argv)
 		case 'l':		/* Line count. */
 			if (numlines != 0)
 				usage();
-			if ((numlines = strtol(optarg, &ep, 10)) <= 0 || *ep)
-				errx(EX_USAGE,
-				    "%s: illegal line count", optarg);
+			numlines = strtonum(optarg, 1, LONG_MAX, &errstr);
+			if (errstr != NULL) {
+				errx(EX_USAGE, "%s: line count is %s",
+				    optarg, errstr);
+			}
 			break;
 		case 'n':		/* Chunks. */
-			if (!isdigit((unsigned char)optarg[0]) ||
-			    (chunks = (size_t)strtoul(optarg, &ep, 10)) == 0 ||
-			    *ep != '\0') {
-				errx(EX_USAGE, "%s: illegal number of chunks",
-				     optarg);
+			chunks = strtonum(optarg, 1, LONG_MAX, &errstr);
+			if (errstr != NULL) {
+				errx(EX_USAGE, "%s: number of chunks is %s",
+				    optarg, errstr);
 			}
 			break;
 
 		case 'p':		/* pattern matching. */
-			if (regcomp(&rgx, optarg, REG_EXTENDED|REG_NOSUB) != 0)
-				errx(EX_USAGE, "%s: illegal regexp", optarg);
+			error = regcomp(&rgx, optarg, REG_EXTENDED|REG_NOSUB);
+			if (error != 0) {
+				regerror(error, &rgx, errbuf, sizeof(errbuf));
+				errx(EX_USAGE, "%s: regex is invalid: %s",
+				    optarg, errbuf);
+			}
 			pflag = 1;
 			break;
 		default:
@@ -163,9 +175,10 @@ main(int argc, char **argv)
 		--argc;
 	}
 	if (argc > 0) {			/* File name prefix. */
-		if (strlcpy(fname, *argv, sizeof(fname)) >= sizeof(fname))
-			errx(EX_USAGE, "file name prefix is too long: %s",
+		if (strlcpy(fname, *argv, sizeof(fname)) >= sizeof(fname)) {
+			errx(EX_USAGE, "%s: file name prefix is too long",
 			    *argv);
+		}
 		++argv;
 		--argc;
 	}
@@ -182,16 +195,16 @@ main(int argc, char **argv)
 	else if (bytecnt != 0 || chunks != 0)
 		usage();
 
-	if (bytecnt && chunks)
+	if (bytecnt != 0 && chunks != 0)
 		usage();
 
 	if (ifd == -1)				/* Stdin by default. */
 		ifd = 0;
 
-	if (bytecnt) {
+	if (bytecnt != 0) {
 		split1();
 		exit (0);
-	} else if (chunks) {
+	} else if (chunks != 0) {
 		split3();
 		exit (0);
 	}
@@ -225,7 +238,7 @@ split1(void)
 			/* NOTREACHED */
 		default:
 			if (!file_open) {
-				if (!chunks || (nfiles < chunks)) {
+				if (chunks == 0 || nfiles < chunks) {
 					newfile();
 					nfiles++;
 				}
@@ -236,24 +249,24 @@ split1(void)
 					err(EX_IOERR, "write");
 				len -= dist;
 				for (C = bfr + dist; len >= bytecnt;
-				    len -= bytecnt, C += bytecnt) {
-					if (!chunks || (nfiles < chunks)) {
-					newfile();
+				     len -= bytecnt, C += bytecnt) {
+					if (chunks == 0 || nfiles < chunks) {
+						newfile();
 						nfiles++;
 					}
-					if (write(ofd,
-					    C, bytecnt) != bytecnt)
+					if (write(ofd, C, bytecnt) != bytecnt)
 						err(EX_IOERR, "write");
 				}
 				if (len != 0) {
-					if (!chunks || (nfiles < chunks)) {
-					newfile();
+					if (chunks == 0 || nfiles < chunks) {
+						newfile();
 						nfiles++;
 					}
 					if (write(ofd, C, len) != len)
 						err(EX_IOERR, "write");
-				} else
+				} else {
 					file_open = 0;
+				}
 				bcnt = len;
 			} else {
 				bcnt += len;
diff --git a/usr.bin/split/tests/split_test.sh b/usr.bin/split/tests/split_test.sh
index c9b87c01618c..48065719055a 100755
--- a/usr.bin/split/tests/split_test.sh
+++ b/usr.bin/split/tests/split_test.sh
@@ -209,6 +209,23 @@ autoextend_body()
 	atf_check -o inline:"$((26*25+1))\n" cat xzaaa
 }
 
+atf_test_case noautoextend
+noautoextend_body()
+{
+	seq $((26*26)) >input
+	atf_check split -a2 -l1 input
+	atf_check -o inline:"$((26*26))\n" cat xzz
+}
+
+atf_test_case reautoextend
+reautoextend_body()
+{
+	seq $((26*25+1)) >input
+	atf_check split -a2 -a0 -l1 input
+	atf_check -o inline:"$((26*25))\n" cat xyz
+	atf_check -o inline:"$((26*25+1))\n" cat xzaaa
+}
+
 atf_test_case continue
 continue_body()
 {
@@ -249,6 +266,8 @@ atf_init_test_cases()
 	atf_add_test_case larger_suffix_length
 	atf_add_test_case pattern
 	atf_add_test_case autoextend
+	atf_add_test_case noautoextend
+	atf_add_test_case reautoextend
 	atf_add_test_case continue
 	atf_add_test_case undocumented_kludge
 	atf_add_test_case duplicate_linecount