On 10/21/18 2:09 AM, Paul Eggert wrote: > I have the opposite impression. Any scripts using this confusing -a operator > are > already broken, and we should phase it out. Not that anybody actually *uses* > coreutils "test -a".
Done with the attached 1st patch. The 2nd patch is a cleanup avoiding the redundant checking of unary operators in test_unop and unary_operator. The 3rd patch adds support for 'test -N FILE' as in bash. Please check (on various platforms / file systems if possible). Thanks & have a nice day, Berny
From 3dc474f71275d5fdd18a028ede19c507e1ecd830 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker <m...@bernhard-voelker.de> Date: Sun, 21 Oct 2018 21:17:31 +0200 Subject: [PATCH 1/3] test: remove support for the ambigous -a unary operator * src/test.c (unary_operator): Remove case 'a'. (test_unop): Likewise. * NEWS (Changes in behavior): Document the change. Discussed at https://bugs.gnu.org/33097 --- NEWS | 7 +++++++ src/test.c | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index ae9667e61..a2b733d38 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,13 @@ GNU coreutils NEWS -*- outline -*- approach is still used in situations where hard links to directories are allowed (e.g., NetBSD when superuser). +** Changes in behavior + + 'test -a FILE' is not supported anymore. Long ago, there were concerns about + the high probability of humans confusing the -a primary with the -a binary + operator, so POSIX changed this to 'test -e FILE'. Scripts using it were + already broken and non-portable; the -a unary operator was never documented. + ** New features id now supports specifying multiple users. diff --git a/src/test.c b/src/test.c index aae45012a..339a84075 100644 --- a/src/test.c +++ b/src/test.c @@ -406,8 +406,7 @@ unary_operator (void) pos right past it. This means that pos - 1 is the location of the argument. */ - case 'a': /* file exists in the file system? */ - case 'e': + case 'e': /* file exists in the file system? */ unary_advance (); return stat (argv[pos - 1], &stat_buf) == 0; @@ -586,7 +585,7 @@ test_unop (char const *op) switch (op[1]) { - case 'a': case 'b': case 'c': case 'd': case 'e': + case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'k': case 'n': case 'o': case 'p': case 'r': case 's': case 't': case 'u': case 'w': case 'x': case 'z': -- 2.19.1
From 6088beb33c64c1fad7f1ec607335bf227fd831d0 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker <m...@bernhard-voelker.de> Date: Sun, 21 Oct 2018 21:56:43 +0200 Subject: [PATCH 2/3] test: simplify redundant code Remove the function 'test_unop', as the cases therein are redundant to those handled by 'unary_operator'; exception: the cases 'o' and 'N': they had been present in test_unop and handling the commands test -N STR test -o STR and test x = x -a -N STR test x = x -a -o STR which ran into an error later on anyway. With this commit, the error diagnostic will change from ... $ /usr/bin/test -N STR /usr/bin/test: extra argument '-N' $ /usr/bin/test -o STR /usr/bin/test: extra argument '-o' ... to ... $ src/test -N STR src/test: '-N': unary operator expected $ src/test -o STR src/test: '-o': unary operator expected * src/test.c (test_unop): Remove. (unary_operator): Fail with test_syntax_error in the default case. (term): Directly call unary_operator. (two_arguments): Likewise. * tests/misc/test-diag.pl: Adjust error diagnostic. --- src/test.c | 34 +++------------------------------- tests/misc/test-diag.pl | 4 ++-- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/src/test.c b/src/test.c index 339a84075..9005b1962 100644 --- a/src/test.c +++ b/src/test.c @@ -72,7 +72,6 @@ static int pos; /* The offset of the current argument in ARGV. */ static int argc; /* The number of arguments present in ARGV. */ static char **argv; /* The argument list. */ -static bool test_unop (char const *s); static bool unary_operator (void); static bool binary_operator (bool); static bool two_arguments (void); @@ -258,12 +257,7 @@ term (void) /* It might be a switch type argument. */ else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0') - { - if (test_unop (argv[pos])) - value = unary_operator (); - else - test_syntax_error (_("%s: unary operator expected"), quote (argv[pos])); - } + value = unary_operator (); else { value = (argv[pos][0] != '\0'); @@ -399,6 +393,7 @@ unary_operator (void) switch (argv[pos][1]) { default: + test_syntax_error (_("%s: unary operator expected"), quote (argv[pos])); return false; /* All of the following unary operators use unary_advance (), which @@ -576,26 +571,6 @@ expr (void) return or (); /* Same with this. */ } -/* Return true if OP is one of the test command's unary operators. */ -static bool -test_unop (char const *op) -{ - if (op[0] != '-') - return false; - - switch (op[1]) - { - case 'b': case 'c': case 'd': case 'e': - case 'f': case 'g': case 'h': case 'k': case 'n': - case 'o': case 'p': case 'r': case 's': case 't': - case 'u': case 'w': case 'x': case 'z': - case 'G': case 'L': case 'O': case 'S': case 'N': - return true; - default: - return false; - } -} - static bool one_argument (void) { @@ -616,10 +591,7 @@ two_arguments (void) && argv[pos][1] != '\0' && argv[pos][2] == '\0') { - if (test_unop (argv[pos])) - value = unary_operator (); - else - test_syntax_error (_("%s: unary operator expected"), quote (argv[pos])); + value = unary_operator (); } else beyond (); diff --git a/tests/misc/test-diag.pl b/tests/misc/test-diag.pl index 91356ef56..f543f9f09 100755 --- a/tests/misc/test-diag.pl +++ b/tests/misc/test-diag.pl @@ -26,8 +26,8 @@ use strict; my @Tests = ( # In coreutils-5.93, this diagnostic lacked the newline. - ['o', '-o arg', {ERR => "test: extra argument '-o'\n"}, - {ERR_SUBST => 's!^.*:!test:!'}, + ['o', '-o arg', {ERR => "test: '-o': unary operator expected\n"}, + {ERR_SUBST => 's!^.*test:!test:!'}, {EXIT => 2}], ); -- 2.19.1
From ab7719540668240baf734b44a63bff270557c0a6 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker <m...@bernhard-voelker.de> Date: Mon, 22 Oct 2018 00:54:51 +0200 Subject: [PATCH 3/3] test: add -N unary operator Bash knows 'test -N FILE'. Add it to GNU 'test' as well. * src/test.c (unary_operator): Add a case for 'N'. (usage): Document it. * doc/coreutils.texi (node File characteristic tests): Likewise. * NEWS (New features): Likewise. * tests/misc/test-N.sh: Add a test. * tests/local.mk (all_tests): Reference it. --- NEWS | 3 +++ doc/coreutils.texi | 6 ++++++ src/test.c | 11 +++++++++++ tests/local.mk | 1 + tests/misc/test-N.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+) create mode 100755 tests/misc/test-N.sh diff --git a/NEWS b/NEWS index a2b733d38..a8fa5f2e4 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,9 @@ GNU coreutils NEWS -*- outline -*- id now supports specifying multiple users. + test now supports the '-N FILE' unary operator (like e.g. bash) to check + whether FILE exists and has been modified since it was last read. + * Noteworthy changes in release 8.30 (2018-07-01) [stable] diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 512443aa6..30155bb86 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -13099,6 +13099,12 @@ True if @var{file1} is older (according to modification date) than True if @var{file1} and @var{file2} have the same device and inode numbers, i.e., if they are hard links to each other. +@item -N @var{file} +@opindex -N +@cindex mtime-greater-atime file check +True if @var{file} exists and has been modified (mtime) since it was +last read (atime). + @end table diff --git a/src/test.c b/src/test.c index 9005b1962..7ed797f17 100644 --- a/src/test.c +++ b/src/test.c @@ -417,6 +417,16 @@ unary_operator (void) unary_advance (); return euidaccess (argv[pos - 1], X_OK) == 0; + case 'N': /* File exists and has been modified since it was last read? */ + { + unary_advance (); + if (stat (argv[pos - 1], &stat_buf) != 0) + return false; + struct timespec atime = get_stat_atime (&stat_buf); + struct timespec mtime = get_stat_mtime (&stat_buf); + return (timespec_cmp (mtime, atime) > 0); + } + case 'O': /* File is owned by you? */ { unary_advance (); @@ -741,6 +751,7 @@ EXPRESSION is true or false and sets exit status. It is one of:\n\ "), stdout); fputs (_("\ -L FILE FILE exists and is a symbolic link (same as -h)\n\ + -N FILE FILE exists and has been modified since it was last read\n\ -O FILE FILE exists and is owned by the effective user ID\n\ -p FILE FILE exists and is a named pipe\n\ -r FILE FILE exists and read permission is granted\n\ diff --git a/tests/local.mk b/tests/local.mk index 47c6b9537..84d346979 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -408,6 +408,7 @@ all_tests = \ tests/misc/tac-2-nonseekable.sh \ tests/misc/tail.pl \ tests/misc/tee.sh \ + tests/misc/test-N.sh \ tests/misc/test-diag.pl \ tests/misc/time-style.sh \ tests/misc/timeout.sh \ diff --git a/tests/misc/test-N.sh b/tests/misc/test-N.sh new file mode 100755 index 000000000..36eae8e8c --- /dev/null +++ b/tests/misc/test-N.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# Test 'test -N file'. + +# Copyright (C) 2018 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ test + +# For a freshly touched file, atime should equal mtime: 'test -N' returns 1. +touch file || framework_failure_ +stat file +returns_ 1 env test -N file || fail=1 + +# Set access time to yesterday at noon: 'test -N' returns 0. +touch -a -d "12:00 today -1 days" file || framework_failure_ +stat file +env test -N file || fail=1 + +# Set mtime to the day before yesterday: 'test -N' returns 1; +touch -m -d "12:00 today -2 days" file || framework_failure_ +stat file +returns_ 1 env test -N file || fail=1 + +# Now modify the file: 'test -N' returns 0. +> file || framework_failure_ +stat file +env test -N file || fail=1 + +Exit $fail -- 2.19.1