git: ce4f512dc004 - stable/14 - diff: Tweak range of -C and -U arguments

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 18 Feb 2026 00:25:58 UTC
The branch stable/14 has been updated by des:

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

commit ce4f512dc004bae5b4b9ec22b449c785e1bf5297
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-02-13 20:18:24 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-02-18 00:18:37 +0000

    diff: Tweak range of -C and -U arguments
    
    POSIX uses the terms “positive decimal integer” for -C and “non-negative
    decimal integer” for -U, which translates into lower bounds of 1 for -C
    and 0 for -U.
    
    POSIX does not specify a minimum upper bound for either mode, but as of
    5fc739eb5949 both our backends support context sizes up to and including
    INT_MAX, so use that.
    
    Having had the opportunity to consult the Unix System Test Suite, the
    diff test cases found therein happen to precisely match these bounds.
    
    While here, switch to using strtonum() to parse numerical arguments, and
    try to be more consistent in how we report usage errors.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D55261
    
    (cherry picked from commit 790f1d1cc5fa892ba59fd7f239b22064c8ab14c7)
---
 usr.bin/diff/diff.c             | 46 ++++++++++++++++++++++--------------
 usr.bin/diff/tests/diff_test.sh | 52 +++++++++++++++++++++++++----------------
 2 files changed, 61 insertions(+), 37 deletions(-)

diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c
index ba88964ca131..53b62766ba3e 100644
--- a/usr.bin/diff/diff.c
+++ b/usr.bin/diff/diff.c
@@ -126,10 +126,9 @@ static bool do_color(void);
 int
 main(int argc, char **argv)
 {
-	const char *errstr = NULL;
-	char *ep, **oargv;
-	long  l;
-	int   ch, dflags, lastch, gotstdin, prevoptind, newarg;
+	const char *errstr;
+	char **oargv;
+	int ch, dflags, lastch, gotstdin, prevoptind, newarg;
 
 	oargv = argv;
 	gotstdin = 0;
@@ -166,10 +165,13 @@ main(int argc, char **argv)
 			cflag = true;
 			diff_format = D_CONTEXT;
 			if (optarg != NULL) {
-				l = strtol(optarg, &ep, 10);
-				if (*ep != '\0' || l < 0 || l >= INT_MAX)
+				diff_context = (int) strtonum(optarg,
+				    1, INT_MAX, &errstr);
+				if (errstr != NULL) {
+					warnx("context size is %s: %s",
+					    errstr, optarg);
 					usage();
-				diff_context = (int)l;
+				}
 			}
 			break;
 		case 'd':
@@ -265,10 +267,13 @@ main(int argc, char **argv)
 				conflicting_format();
 			diff_format = D_UNIFIED;
 			if (optarg != NULL) {
-				l = strtol(optarg, &ep, 10);
-				if (*ep != '\0' || l < 0 || l >= INT_MAX)
+				diff_context = (int) strtonum(optarg,
+				    0, INT_MAX, &errstr);
+				if (errstr != NULL) {
+					warnx("context size is %s: %s",
+					    errstr, optarg);
 					usage();
-				diff_context = (int)l;
+				}
 			}
 			break;
 		case 'w':
@@ -276,8 +281,10 @@ main(int argc, char **argv)
 			break;
 		case 'W':
 			width = (int) strtonum(optarg, 1, INT_MAX, &errstr);
-			if (errstr)
-				errx(1, "width is %s: %s", errstr, optarg);
+			if (errstr != NULL) {
+				warnx("width is %s: %s", errstr, optarg);
+				usage();
+			}
 			break;
 		case 'X':
 			read_excludes_file(optarg);
@@ -315,8 +322,10 @@ main(int argc, char **argv)
 			break;
 		case OPT_TSIZE:
 			tabsize = (int) strtonum(optarg, 1, INT_MAX, &errstr);
-			if (errstr)
-				errx(1, "tabsize is %s: %s", errstr, optarg);
+			if (errstr != NULL) {
+				warnx("tabsize is %s: %s", errstr, optarg);
+				usage();
+			}
 			break;
 		case OPT_STRIPCR:
 			dflags |= D_STRIPCR;
@@ -331,9 +340,12 @@ main(int argc, char **argv)
 				colorflag = COLORFLAG_ALWAYS;
 			else if (strncmp(optarg, "never", 5) == 0)
 				colorflag = COLORFLAG_NEVER;
-			else
-				errx(2, "unsupported --color value '%s' (must be always, auto, or never)",
-					optarg);
+			else {
+				warnx("unsupported --color value "
+				    "(must be always, auto, or never): "
+				    "%s", optarg);
+				usage();
+			}
 			break;
 		case OPT_NO_DEREFERENCE:
 			noderef = true;
diff --git a/usr.bin/diff/tests/diff_test.sh b/usr.bin/diff/tests/diff_test.sh
index 61fa618b8d2c..07e019aafc4e 100755
--- a/usr.bin/diff/tests/diff_test.sh
+++ b/usr.bin/diff/tests/diff_test.sh
@@ -24,8 +24,8 @@ atf_test_case functionname
 atf_test_case noderef
 atf_test_case ignorecase
 atf_test_case dirloop
-atf_test_case bigc
-atf_test_case bigu
+atf_test_case crange
+atf_test_case urange
 atf_test_case prleak
 atf_test_case same
 
@@ -385,30 +385,42 @@ dirloop_body()
 	atf_check diff -r a b
 }
 
-bigc_head()
+crange_head()
 {
-	atf_set "descr" "Context diff with very large context"
+	atf_set "descr" "Context diff context length range"
 }
-bigc_body()
+crange_body()
 {
-	echo $'x\na\ny' >a
-	echo $'x\nb\ny' >b
-	atf_check -s exit:2 -e ignore diff -C$(((1<<31)-1)) a b
-	atf_check -s exit:1 -o match:'--- 1,3 ---' \
-	    diff -C$(((1<<31)-2)) a b
+	echo $'x\nx\na\ny\ny' >a
+	echo $'x\nx\nb\ny\ny' >b
+	atf_check -s exit:2 -e match:'too small' \
+	    diff -C-1 a b
+	atf_check -s exit:2 -e match:'too small' \
+	    diff -C0 a b
+	atf_check -s exit:1 -o match:'--- 2,4 ---' \
+	    diff -C1 a b
+	atf_check -s exit:2 -e match:'too large' \
+	    diff -C$((1<<31)) a b
+	atf_check -s exit:1 -o match:'--- 1,5 ---' \
+	    diff -C$(((1<<31)-1)) a b
 }
 
-bigu_head()
+urange_head()
 {
-	atf_set "descr" "Unified diff with very large context"
+	atf_set "descr" "Unified diff context length range"
 }
-bigu_body()
+urange_body()
 {
-	echo $'x\na\ny' >a
-	echo $'x\nb\ny' >b
-	atf_check -s exit:2 -e ignore diff -U$(((1<<31)-1)) a b
-	atf_check -s exit:1 -o match:'^@@ -1,3 \+1,3 @@$' \
-	    diff -U$(((1<<31)-2)) a b
+	echo $'x\nx\na\ny\ny' >a
+	echo $'x\nx\nb\ny\ny' >b
+	atf_check -s exit:2 -e match:'too small' \
+	    diff -U-1 a b
+	atf_check -s exit:1 -o match:'^@@ -3 \+3 @@$' \
+	    diff -U0 a b
+	atf_check -s exit:2 -e match:'too large' \
+	    diff -U$((1<<31)) a b
+	atf_check -s exit:1 -o match:'^@@ -1,5 \+1,5 @@$' \
+	    diff -U$(((1<<31)-1)) a b
 }
 
 prleak_head()
@@ -476,8 +488,8 @@ atf_init_test_cases()
 	atf_add_test_case noderef
 	atf_add_test_case ignorecase
 	atf_add_test_case dirloop
-	atf_add_test_case bigc
-	atf_add_test_case bigu
+	atf_add_test_case crange
+	atf_add_test_case urange
 	atf_add_test_case prleak
 	atf_add_test_case same
 }