Pádraig Brady <[email protected]> writes:

> On 25/07/2026 18:54, Collin Funk wrote:
>> 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.
>
> Very nice. This now matches behavior of bash and dash at least.
> One nit on the NEWS, I was a bit confused at the double mention
> of binary operators, when perhaps unary was more accurate.
> Therefore it might be best to simplify the first line to:
>
>   'test' no longer treats '-a' and '-o' as operators when given as

Right. I was trying to make the entry friendly to those who may not be
familiar with the terminology, hence the added example. While thinking
of how to word it I must have made that silly mistake.

I pushed this v2 patch with the changes in two_arguments removed. It
caused an incorrect error message, as noticed by
tests/test/test-diag.pl:

    $ ./src/test -o arg
    test: missing argument after ‘arg’

Whereas, in v2 it gives the more correct error, telling us that -o is
not a unary operator:

    $ ./src/test -o arg
    test: ‘-o’: unary operator expected

I'm not too sure why I added it in v1, to be honest. I added a check for
-a in tests/test/test-diag.pl, just because it seems nice to cover that
case as well.

One thing I found confusing is that 'bash' doesn't have error messages
in the following cases:

    $ test -o -o; echo $?
    1
    $ test -a -a; echo $?
    1

Before testing both, I tested 'test -a -a' and thought it might have
been treated as the same as:

    $ test -a -a ''

But we can see that isn't the case from this:

    $ test -o -o; echo $?
    1
    $ test -o -o ''; echo $?
    0

I probably put too much thought into these seldom used operators. :)

Collin

>From c2a2508c380402865124d6101856f489e6dd04f8 Mon Sep 17 00:00:00 2001
Message-ID: <c2a2508c380402865124d6101856f489e6dd04f8.1785096019.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Thu, 23 Jul 2026 21:12:42 -0700
Subject: [PATCH v2] 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.
* tests/test/test.pl: Add some test cases.
* tests/test/test-diag.pl: Also test the diagnostics with '-a arg'.
---
 NEWS                    |  5 +++++
 cfg.mk                  |  2 +-
 src/test.c              |  3 ++-
 tests/test/test-diag.pl |  5 ++---
 tests/test/test.pl      | 18 ++++++++++++++++++
 5 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index d47d9517e..fd0902cb7 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 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..f28fb7c9d 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
     {
diff --git a/tests/test/test-diag.pl b/tests/test/test-diag.pl
index b9929d9c5..8a2557045 100755
--- a/tests/test/test-diag.pl
+++ b/tests/test/test-diag.pl
@@ -26,9 +26,8 @@ use strict;
 my @Tests =
     (
      # In coreutils-5.93, this diagnostic lacked the newline.
-     ['o', '-o arg', {ERR => "test: '-o': unary operator expected\n"},
-      {ERR_SUBST => 's!^.*test:!test:!'},
-      {EXIT => 2}],
+     map {[$_, "-$_ arg", {ERR => "test: '-$_': unary operator expected\n"},
+           {ERR_SUBST => 's!^.*test:!test:!'}, {EXIT => 2}]} qw(o a),
     );
 
 my $save_temps = $ENV{DEBUG};
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