This patch makes find accept things like "find -PP .".
From c10a0126dd56e42a8118494cf67a6c74eb58c0db Mon Sep 17 00:00:00 2001 From: James Youngman <ja...@youngman.org> Date: Mon, 3 Jun 2024 08:44:12 +0100 Subject: [PATCH] find: Process -P -H -L options in a POSIX-compliant way. To: findutils-patc...@gnu.org
* tests/find/posix-options.sh: New test for option combinations like find -LP which previously were not recognised. * tests/local.mk(sh_tests): add tests/find/posix-options.sh. * find/util.c(process_leading_options): process leading options with getopt (instead of strcmp) for greater POSIX compliance * NEWS: mention this bugfix. --- NEWS | 4 ++ find/util.c | 106 ++++++++++++++++++++++-------------- tests/find/posix-options.sh | 97 +++++++++++++++++++++++++++++++++ tests/local.mk | 1 + 4 files changed, 167 insertions(+), 41 deletions(-) create mode 100755 tests/find/posix-options.sh diff --git a/NEWS b/NEWS index e2a8e067..40fd0309 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) * Noteworthy changes in release ?.? (????-??-??) [?] +** Bug Fixes + + Find now accepts combinations of leading options, for example "find + -PP". Previously, these were incorrectly rejected. * Noteworthy changes in release 4.10.0 (2024-06-01) [stable] diff --git a/find/util.c b/find/util.c index a1736807..709e2518 100644 --- a/find/util.c +++ b/find/util.c @@ -25,6 +25,7 @@ #include <fcntl.h> #include <limits.h> #include <string.h> +#include <unistd.h> /* for getopt() */ #include <sys/stat.h> /* for fstatat() */ #include <sys/time.h> #include <sys/utsname.h> @@ -908,56 +909,79 @@ process_optimisation_option (const char *arg) int process_leading_options (int argc, char *argv[]) { - int i, end_of_leading_options; + bool done = false; + int opt; - for (i=1; (end_of_leading_options = i) < argc; ++i) + /* We don't want getopt to print its own error messages. */ + opterr = 0; + + /* We put + as the first character of optstring in order to prevent + * getopt permuting the order of the options. Therefore we require + * GNU getopt, since this is an extension. We use ':' as the second + * character to change the way getopt handles missing + * option-arguments (as specified by POSIX). + */ + while (!done && (opt = getopt(argc, argv, "+:HLPD:O:")) != -1) { - if (0 == strcmp ("-H", argv[i])) - { + switch (opt) + { + case 'H': /* Meaning: dereference symbolic links on command line, but nowhere else. */ set_follow_state (SYMLINK_DEREF_ARGSONLY); - } - else if (0 == strcmp ("-L", argv[i])) - { + break; + + case 'L': /* Meaning: dereference all symbolic links. */ set_follow_state (SYMLINK_ALWAYS_DEREF); - } - else if (0 == strcmp ("-P", argv[i])) - { + break; + + case 'P': /* Meaning: never dereference symbolic links (default). */ set_follow_state (SYMLINK_NEVER_DEREF); - } - else if (0 == strcmp ("--", argv[i])) - { - /* -- signifies the end of options. */ - end_of_leading_options = i+1; /* Next time start with the next option */ - break; - } - else if (0 == strcmp ("-D", argv[i])) - { - if (argc <= i+1) - { - error (0, 0, _("Missing argument after the -D option.")); - usage (EXIT_FAILURE); - } - process_debug_options (argv[i+1]); - ++i; /* skip the argument too. */ - } - else if (0 == strncmp ("-O", argv[i], 2)) - { - process_optimisation_option (argv[i]+2); - } - else - { - /* Hmm, must be one of - * (a) A path name - * (b) A predicate - */ - end_of_leading_options = i; /* Next time start with this option */ - break; - } + break; + + case 'D': + process_debug_options (optarg); + break; + + case 'O': + process_optimisation_option (optarg); + break; + + case ':': + /* There is a missing option-argument. We use this switch + * statement below to ensure that the existing translations + * for the error messages are used. Once translations have + * caught up and include the error message which uses %c, we + * can remove the special cases for -E and -D and use only + * the generic error message. + */ + switch (optopt) + { + case 'E': + error (1, 0, _("Missing argument after the -E option.")); + break; + case 'D': + error (1, 0, _("Missing argument after the -D option.")); + break; + default: + /* TRANSLATORS: At the moment this case is not actually + * reached but it is included so that this message gets + * translated. Eventually this will be the only + * message. See the comment above for an explanation. + */ + error (1, 0, _("Missing argument after the -%c option."), optopt); + } + break; + + case '?': + default: + /* This is the first argument that isn't a leading option. */ + done = true; + break; + } } - return end_of_leading_options; + return optind; } static struct timespec diff --git a/tests/find/posix-options.sh b/tests/find/posix-options.sh new file mode 100755 index 00000000..055dab58 --- /dev/null +++ b/tests/find/posix-options.sh @@ -0,0 +1,97 @@ +#!/bin/sh +# Verify that -P, -L, -H are processed in a POSIX-compliant way. + +# Copyright (C) 2024 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"; fu_path_prepend_ +print_ver_ find + +# create test files. +touch empty +ln -s empty symlink +ln -s does-not-exist broken-symlink + +# Verify that we don't follow symbolic links by default. +echo "== testing: find with no options" +echo symlink >| expected +find symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that we don't follow symbolic links with -P. +echo "== testing: find -P" +echo symlink >| expected +find -P symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that we accept -PP +echo "== testing: find -PP" +echo symlink >| expected +find -PP symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that with -L we resolve the symlink. +echo "== testing: find -L" +# Expect no output since -L doesn't think symlink is a symlink +# (because it gets resolved). +true >| expected +find -L symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that -LL is accepted. +echo "== testing: find -LL" +# Expect no output since -L doesn't think symlink is a symlink +# (because it gets resolved). +true >| expected +find -LL symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that with -L a broken symlink still looks like a symlink. +echo "== testing: find -L with broken symlink" +echo broken-symlink >| expected +find -L broken-symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that -LP is erquivalent to -P +echo "== testing: find -LP" +echo symlink >| expected +find -P symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that -H resolves symlinks on the command line. +echo "== testing: find -H" +# Expect no output since -H doesn't think symlink is a symlink +# (because it gets resolved). +true >| expected +find -H symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that -PH is equivalent to -H +echo "== testing: find -PH" +# Expect no output since -H doesn't think symlink is a symlink +# (because it gets resolved). +true >| expected +find -PH symlink -type l >| out +compare expected out || { fail=1; cat out; } + +# Verify that -LH is equivalent to -H +echo "== testing: find -LH" +# Expect no output since -H doesn't think symlink is a symlink +# (because it gets resolved). +true >| expected +find -LH symlink -type l >| out +compare expected out || { fail=1; cat out; } + +Exit $fail diff --git a/tests/local.mk b/tests/local.mk index 1fe14c01..cfe2a661 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -127,6 +127,7 @@ sh_tests = \ tests/find/used.sh \ tests/find/newer.sh \ tests/find/opt-numeric-arg.sh \ + tests/find/posix-options.sh \ tests/find/user-group-max.sh \ tests/xargs/conflicting_opts.sh \ tests/xargs/verbose-quote.sh \ -- 2.39.2