feat(libc): implement getopt_long_only

This commit is contained in:
ArjenS-LA committed 2026-10-15 11:27:50 -07:00
1 parent b7cedd900d
commit 725ad5649c
2 files changed
+95 -8

No files matched your search

+28 -8
View File
@@ -38,6 +38,7 @@ constexpr int OptionalArgument = 2;
constexpr int FlagPermute = 1;
constexpr int FlagAllArguments = 2;
constexpr int FlagLongOnly = 4;
constexpr int BadCharacter = '?';
constexpr int InOrder = 1;
@@ -112,7 +113,7 @@ void PermuteArguments(int nonOptionStart, int nonOptionEnd, int optionEnd, char*
}
}
int ParseLongOptions(char* const* arguments, const char* options, const GuestOption* longOptions, int* index, const char* dash) {
int ParseLongOptions(char* const* arguments, const char* options, const GuestOption* longOptions, int* index, const char* dash, bool shortToo, int flags) {
char* const current = g_longPlace;
int match = -1;
bool exactMatch = false;
@@ -136,8 +137,9 @@ int ParseLongOptions(char* const* arguments, const char* options, const GuestOpt
exactMatch = true;
break;
}
if (shortToo && length == 1) continue;
if (match == -1) match = i;
else if (longOptions[i].hasArgument != longOptions[match].hasArgument ||
else if ((flags & FlagLongOnly) || longOptions[i].hasArgument != longOptions[match].hasArgument ||
longOptions[i].flag != longOptions[match].flag ||
longOptions[i].value != longOptions[match].value)
secondPartialMatch = true;
@@ -149,6 +151,10 @@ int ParseLongOptions(char* const* arguments, const char* options, const GuestOpt
return BadCharacter;
}
if (match == -1) {
if (shortToo) {
--optind_nid_postfix;
return -1;
}
if (PrintError(options)) Warn(std::string("unrecognized option `") + dash + current + "'");
optopt_nid_postfix = 0;
return BadCharacter;
@@ -237,11 +243,21 @@ int GetoptInternal(int count, char* const* arguments, const char* options, const
break;
}
if (longOptions && *g_longPlace == '-' && g_longPlace != arguments[optind_nid_postfix]) {
++g_longPlace;
const int result = ParseLongOptions(arguments, options, longOptions, index, "--");
g_longPlace = g_emptyMessage;
return result;
if (longOptions && g_longPlace != arguments[optind_nid_postfix] &&
(*g_longPlace == '-' || (flags & FlagLongOnly))) {
bool shortToo = false;
const char* dash = "-";
if (*g_longPlace == '-') {
++g_longPlace;
dash = "--";
} else if (*g_longPlace != ':' && std::strchr(options, *g_longPlace)) {
shortToo = true;
}
const int result = ParseLongOptions(arguments, options, longOptions, index, dash, shortToo, flags);
if (result != -1) {
g_longPlace = g_emptyMessage;
return result;
}
}
const int character = OptionCharacter(g_longPlace++);
@@ -265,7 +281,7 @@ int GetoptInternal(int count, char* const* arguments, const char* options, const
}
g_longPlace = Argument(arguments, optind_nid_postfix, "getopt_long");
}
const int result = ParseLongOptions(arguments, options, longOptions, index, "-W ");
const int result = ParseLongOptions(arguments, options, longOptions, index, "-W ", false, flags);
g_longPlace = g_emptyMessage;
return result;
}
@@ -355,4 +371,8 @@ int APS5_VABI getopt_long_nid_postfix(int count, char* const* arguments, const c
return GetoptInternal(count, arguments, options, longOptions, index, FlagPermute);
}
int APS5_VABI getopt_long_only_nid_postfix(int count, char* const* arguments, const char* options, const GuestOption* longOptions, int* index) {
return GetoptInternal(count, arguments, options, longOptions, index, FlagPermute | FlagLongOnly);
}
}
+67
View File
@@ -17,6 +17,7 @@ struct GuestOption {
extern "C" {
int APS5_VABI getopt_nid_postfix(int, char* const*, const char*);
int APS5_VABI getopt_long_nid_postfix(int, char* const*, const char*, const GuestOption*, int*);
int APS5_VABI getopt_long_only_nid_postfix(int, char* const*, const char*, const GuestOption*, int*);
int APS5_VABI setenv_nid_postfix(const char*, const char*, int);
int APS5_VABI unsetenv_nid_postfix(const char*);
extern int opterr_nid_postfix;
@@ -244,6 +245,71 @@ void ShortOptions() {
ExpectText(RunGetopt("bc", permuted), "|3", "getopt does not permute");
}
void LongOnlyOptions() {
int flag = 0;
const GuestOption options[] = {
{"alpha", 0, nullptr, 100},
{"alpine", 0, nullptr, 100},
{"b", 0, nullptr, 101},
{"required", 1, nullptr, 102},
{"optional", 2, nullptr, 103},
{"flag", 0, &flag, 7},
{nullptr, 0, nullptr, 0},
};
struct Case {
const char* arguments;
const char* shortOptions;
std::vector<int> results;
std::vector<std::string> values;
int finalIndex;
};
const Case cases[] = {
{"foo -alpha --alpha -b", "ab", {100, 100, 101}, {"", "", ""}, 4},
{"foo -a -acvalue", "ac:", {'a', 'a', 'c'}, {"", "", "value"}, 3},
{"foo -required=one -req two -optional=three -optional four", "", {102, 102, 103, 103}, {"one", "two", "three", ""}, 6},
{"foo operand -alpha tail -b", "ab", {100, 101}, {"", ""}, 3},
{"foo -al --al", "a", {'?', '?'}, {"", ""}, 3},
{"foo -unknown", "", {'?'}, {""}, 2},
{"foo -required", ":", {':'}, {""}, 2},
{"foo -alpha=yes", "", {'?'}, {""}, 2},
{"foo -flag", "", {0}, {""}, 2},
{"foo -- -alpha", "", {}, {}, 2},
{"foo operand -alpha", "+", {}, {}, 1},
{"foo operand -alpha", "-", {1, 100}, {"operand", ""}, 3},
{"foo -W alpha", "W;", {100}, {""}, 3},
};
for (const auto& test : cases) {
ArgumentVector arguments(test.arguments);
Reset();
opterr_nid_postfix = 0;
for (std::size_t i = 0; i < test.results.size(); ++i) {
const int result = getopt_long_only_nid_postfix(arguments.Count(), arguments.Values(), test.shortOptions, options, nullptr);
Expect(result == test.results[i], std::string("getopt_long_only result: ") + test.arguments);
ExpectText(optarg_nid_postfix ? optarg_nid_postfix : "", test.values[i], "getopt_long_only argument");
}
Expect(getopt_long_only_nid_postfix(arguments.Count(), arguments.Values(), test.shortOptions, options, nullptr) == -1,
std::string("getopt_long_only termination: ") + test.arguments);
Expect(optind_nid_postfix == test.finalIndex, std::string("getopt_long_only optind: ") + test.arguments);
}
Expect(flag == 7, "getopt_long_only flag pointer");
{
ArgumentVector arguments("foo -al");
StderrCapture capture;
Reset();
int index = -1;
Expect(getopt_long_only_nid_postfix(arguments.Count(), arguments.Values(), "a", options, &index) == '?' &&
optopt_nid_postfix == 0 && index == -1, "ambiguous aliases keep index unchanged");
ExpectText(capture.Text(), "eboot.bin: option `-al' is ambiguous\n", "single dash diagnostic");
}
ArgumentVector arguments("foo -alpha");
Reset();
int index = -1;
Expect(getopt_long_only_nid_postfix(arguments.Count(), arguments.Values(), "a", options, &index) == 100 && index == 0,
"long-only index");
optind_nid_postfix = 0;
Expect(getopt_long_only_nid_postfix(arguments.Count(), arguments.Values(), "a", options, nullptr) == 100, "long-only restart");
}
void LongOptions() {
int flag = 0;
const GuestOption options[] = {
@@ -364,4 +430,5 @@ int main() {
FreeBsdGetoptLongCases();
ShortOptions();
LongOptions();
LongOnlyOptions();
}