summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJelle Raaijmakers <jelle@gmta.nl>2021-06-13 16:35:04 +0200
committerLinus Groh <mail@linusgroh.de>2021-06-13 17:05:03 +0100
commit10e8b990384a23eedf6f2d972964623df0998fdb (patch)
tree6be85792cb144e195be5d73d6cf4b4c78627e6a1
parentd72aeb2e1a6970ed56ed28abd88ea8225027ff8c (diff)
downloadserenity-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.cpp9
-rw-r--r--Userland/Libraries/LibC/getopt.cpp18
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()