The attached patch should fix the issue. We were treating "-o" and "-a"
as if they could be unary operators, which they cannot be. They are
either a string operand or a binary operator.

Will push it in a bit.

Collin

>From 4150550035a3978a5c63884c497d0cd29863283e Mon Sep 17 00:00:00 2001
Message-ID: <4150550035a3978a5c63884c497d0cd29863283e.1785002034.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Thu, 23 Jul 2026 21:12:42 -0700
Subject: [PATCH] test: handle adjacent binary operators

* NEWS: Mention the bug fix.
* cfg.mk (exclude_file_name_regexp--sc_prohibit_test_minus_ao): Exclude
NEWS.
* src/test.c (term): Treat '-a' and '-o' as strings instead of expecting
them to be valid unary operators.
(two_arguments): Don't treat '-a' and '-o' as possible unary operators.
* tests/test/test.pl: Add some test cases.
---
 NEWS               |  5 +++++
 cfg.mk             |  2 +-
 src/test.c         |  8 ++++----
 tests/test/test.pl | 18 ++++++++++++++++++
 4 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index d47d9517e..059027625 100644
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,11 @@ GNU coreutils NEWS                                    -*- outline -*-
   'tee' no longer treats short writes as errors.
   [bug introduced in coreutils-9.11]
 
+  'test' no longer treats '-a' and '-o' as binary operators when given as
+  strings to a binary operator.  E.g., 'test -a -a -a' exits successfully
+  instead of exiting with an error.
+  [This bug was present in "the beginning".]
+
   'unexpand -t' no longer overflows a heap buffer, for tab values > SIZE_MAX/16,
   or with multi-byte blank characters longer than the tab value.
   [bugs introduced in coreutils-9.11]
diff --git a/cfg.mk b/cfg.mk
index 90e6a21d1..b21b7dc03 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -950,7 +950,7 @@ exclude_file_name_regexp--sc_prohibit_always_true_header_tests = \
   ^m4/stat-prog\.m4$$
 exclude_file_name_regexp--sc_prohibit_fail_0 = \
   (^.*/git-hooks/commit-msg|^tests/init\.sh|Makefile\.am|\.mk|.*\.texi)$$
-exclude_file_name_regexp--sc_prohibit_test_minus_ao = doc/.*\.texi$$
+exclude_file_name_regexp--sc_prohibit_test_minus_ao = NEWS|doc/.*\.texi$$
 exclude_file_name_regexp--sc_prohibit_atoi_atof = ^lib/euidaccess-stat\.c$$
 
 # longlong.h is maintained elsewhere.
diff --git a/src/test.c b/src/test.c
index 692c37263..3cf314276 100644
--- a/src/test.c
+++ b/src/test.c
@@ -265,7 +265,8 @@ term (void)
     value = binary_operator (false, bop);
 
   /* It might be a switch type argument.  */
-  else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
+  else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][1] != 'a'
+           && argv[pos][1] != 'o' && argv[pos][2] == '\0')
     value = unary_operator ();
   else
     {
@@ -565,9 +566,8 @@ two_arguments (void)
       advance (false);
       value = ! one_argument ();
     }
-  else if (argv[pos][0] == '-'
-           && argv[pos][1] != '\0'
-           && argv[pos][2] == '\0')
+  else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][1] != 'a'
+           && argv[pos][1] != 'o' && argv[pos][2] == '\0')
     {
       value = unary_operator ();
     }
diff --git a/tests/test/test.pl b/tests/test/test.pl
index 0f0a93d67..9b722191d 100755
--- a/tests/test/test.pl
+++ b/tests/test/test.pl
@@ -204,6 +204,24 @@ my @Tests =
   ['greater-collate-3', "'a' '>' 'b'", {EXIT=>1}],
 );
 
+# Test the behavior of multiple adjacent binary operators.
+# These would not behave correctly in coreutils-9.11 and earlier.
+my $i = 1;
+my @operands = ('-a', '-o', "''");
+for my $b ('-a', '-o')
+  {
+    for my $a (@operands)
+      {
+        for my $c (@operands)
+          {
+            push @Tests, ["multiple-binary-" . ++$i, "$a $b $c",
+                          {EXIT=>0 + (($b eq '-o')
+                                      ? ($a eq "''" && $c eq "''")
+                                      : ($a eq "''" || $c eq "''"))}];
+          }
+      }
+  }
+
 @Tests = add_inverse_op_tests \@Tests;
 @Tests = add_pn_tests \@Tests;
 
-- 
2.55.0

Reply via email to