diff options
author | Jelle Raaijmakers <jelle@gmta.nl> | 2021-06-13 16:35:04 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-06-13 17:05:03 +0100 |
commit | 10e8b990384a23eedf6f2d972964623df0998fdb (patch) | |
tree | 6be85792cb144e195be5d73d6cf4b4c78627e6a1 | |
parent | d72aeb2e1a6970ed56ed28abd88ea8225027ff8c (diff) | |
download | serenity-10e8b990384a23eedf6f2d972964623df0998fdb.zip |
LibC: Make `getopt` modify `argv` again
A POSIX-compatibility fix was introduced in 64740a0214 to make the
compilation of the `diffutils` port work, which expected a
`char* const* argv` signature.
And indeed, the POSIX spec does not mention permutation of `argv`:
https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html
However, most implementations do modify `argv` as evidenced by
documentation such as:
https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic
/LSB-Core-generic/libutil-getopt-3.html
"The function prototype was aligned with POSIX 1003.1-2008 (ISO/IEC
9945-2009) despite the fact that it modifies argv, and the library
maintainers are unwilling to change this."
Change the behavior back to permutate `argc` to allow for the following
command line argument order to work again:
unzip ./file.zip -o target-dir
Without this change, `./file.zip` in the example above would have been
ignored completely.
-rw-r--r-- | Tests/LibCore/TestLibCoreArgsParser.cpp | 9 | ||||
-rw-r--r-- | Userland/Libraries/LibC/getopt.cpp | 18 |
2 files changed, 13 insertions, 14 deletions
diff --git a/Tests/LibCore/TestLibCoreArgsParser.cpp b/Tests/LibCore/TestLibCoreArgsParser.cpp index 3e60a8f6ef..753dca8958 100644 --- a/Tests/LibCore/TestLibCoreArgsParser.cpp +++ b/Tests/LibCore/TestLibCoreArgsParser.cpp @@ -333,7 +333,7 @@ TEST_CASE(stop_on_first_non_option) EXPECT_EQ(positionals[0], "one"); // Do not stop on first non-option; arguments in wrong order - // Expected: parser chokes on the positional argument + // Expected: bool options are set and one positional argument is filled bool_opt1 = false; bool_opt2 = false; positionals = {}; @@ -343,7 +343,12 @@ TEST_CASE(stop_on_first_non_option) parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes); }); - EXPECT_EQ(parser_result, false); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(bool_opt1, true); + EXPECT_EQ(bool_opt2, true); + EXPECT_EQ(positionals.size(), 1u); + if (positionals.size() == 1u) + EXPECT_EQ(positionals[0], "one"); // Stop on first non-option; arguments in correct order // Expected: bool options are set and one positional argument is filled diff --git a/Userland/Libraries/LibC/getopt.cpp b/Userland/Libraries/LibC/getopt.cpp index 9683455c0d..1fd5203351 100644 --- a/Userland/Libraries/LibC/getopt.cpp +++ b/Userland/Libraries/LibC/getopt.cpp @@ -42,7 +42,6 @@ namespace { class OptionParser { public: OptionParser(int argc, char* const* argv, const StringView& short_options, const option* long_options, int* out_long_option_index = nullptr); - ~OptionParser(); int getopt(); private: @@ -56,7 +55,7 @@ private: bool find_next_option(); size_t m_argc { 0 }; - char** m_argv { nullptr }; + char* const* m_argv { nullptr }; StringView m_short_options; const option* m_long_options { nullptr }; int* m_out_long_option_index { nullptr }; @@ -68,12 +67,11 @@ private: OptionParser::OptionParser(int argc, char* const* argv, const StringView& short_options, const option* long_options, int* out_long_option_index) : m_argc(argc) + , m_argv(argv) , m_short_options(short_options) , m_long_options(long_options) , m_out_long_option_index(out_long_option_index) { - m_argv = new char*[m_argc + 1]; - memmove(m_argv, argv, sizeof(char*) * (m_argc + 1)); // In the following case: // $ foo bar -o baz // we want to parse the option (-o baz) first, and leave the argument (bar) @@ -94,11 +92,6 @@ OptionParser::OptionParser(int argc, char* const* argv, const StringView& short_ optarg = nullptr; } -OptionParser::~OptionParser() -{ - delete[] m_argv; -} - int OptionParser::getopt() { bool should_reorder_argv = !m_stop_on_first_non_option; @@ -308,10 +301,11 @@ void OptionParser::shift_argv() return; } + auto new_argv = const_cast<char**>(m_argv); char* buffer[m_consumed_args]; - memcpy(buffer, &m_argv[m_arg_index], sizeof(char*) * m_consumed_args); - memmove(&m_argv[optind + m_consumed_args], &m_argv[optind], sizeof(char*) * (m_arg_index - optind)); - memcpy(&m_argv[optind], buffer, sizeof(char*) * m_consumed_args); + memcpy(buffer, &new_argv[m_arg_index], sizeof(char*) * m_consumed_args); + memmove(&new_argv[optind + m_consumed_args], &new_argv[optind], sizeof(char*) * (m_arg_index - optind)); + memcpy(&new_argv[optind], buffer, sizeof(char*) * m_consumed_args); } bool OptionParser::find_next_option() |