libos: Add support for --help to ArgumentParser
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
0058df9f01
commit
eb58b4acc8
@ -30,6 +30,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
StringView filename;
|
StringView filename;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Concatenate files to standard output."_sv);
|
||||||
parser.add_positional_argument(filename, "file"_sv, "-"_sv);
|
parser.add_positional_argument(filename, "file"_sv, "-"_sv);
|
||||||
Vector<StringView> extra_files = TRY(parser.parse(argc, argv));
|
Vector<StringView> extra_files = TRY(parser.parse(argc, argv));
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ int main(int argc, char** argv)
|
|||||||
StringView path;
|
StringView path;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Change the permissions of a file."_sv);
|
||||||
parser.add_positional_argument(mode_string, "mode"_sv, true);
|
parser.add_positional_argument(mode_string, "mode"_sv, true);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
@ -9,6 +9,7 @@ int main(int argc, char** argv)
|
|||||||
StringView path;
|
StringView path;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Change the owner and group of a file."_sv);
|
||||||
parser.add_positional_argument(user, "user"_sv, true);
|
parser.add_positional_argument(user, "user"_sv, true);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
@ -10,7 +10,9 @@ int main(int argc, char** argv)
|
|||||||
StringView date;
|
StringView date;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_value_argument(date, 'd', "date"_sv, true);
|
parser.add_description("Display the current (or another) date and time."_sv);
|
||||||
|
parser.add_value_argument(date, 'd', "date"_sv, true,
|
||||||
|
"the UNIX timestamp to display instead of the current time"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
time_t now;
|
time_t now;
|
||||||
|
@ -9,6 +9,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
StringView pathname;
|
StringView pathname;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Edit a file using basic line-based shell editing."_sv);
|
||||||
parser.add_positional_argument(pathname, "path"_sv, true);
|
parser.add_positional_argument(pathname, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
@ -12,9 +12,10 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
bool show_almost_all { false };
|
bool show_almost_all { false };
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv);
|
||||||
parser.add_positional_argument(pathname, "directory"_sv, "."_sv);
|
parser.add_positional_argument(pathname, "directory"_sv, "."_sv);
|
||||||
parser.add_switch_argument(show_all, 'a', "all"_sv);
|
parser.add_switch_argument(show_all, 'a', "all"_sv, "also list hidden files (whose filename begins with a dot)"_sv);
|
||||||
parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv);
|
parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
DIR* dp = opendir(pathname.chars());
|
DIR* dp = opendir(pathname.chars());
|
||||||
|
@ -30,9 +30,11 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
bool recursive;
|
bool recursive;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Create directories."_sv);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.add_positional_argument(mode_string, "mode"_sv, "755"_sv);
|
parser.add_positional_argument(mode_string, "mode"_sv, "755"_sv);
|
||||||
parser.add_switch_argument(recursive, 'p', "parents"_sv);
|
parser.add_switch_argument(recursive, 'p', "parents"_sv,
|
||||||
|
"if parent directories do not exist, create them as well"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
mode_t mode = (mode_t)parse_unsigned_integer(mode_string.chars(), nullptr, 8);
|
mode_t mode = (mode_t)parse_unsigned_integer(mode_string.chars(), nullptr, 8);
|
||||||
|
@ -7,8 +7,10 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
bool recursive;
|
bool recursive;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Remove a path from the file system."_sv);
|
||||||
parser.add_positional_argument(path, "path"_sv, true);
|
parser.add_positional_argument(path, "path"_sv, true);
|
||||||
parser.add_switch_argument(recursive, 'r', "recursive"_sv);
|
parser.add_switch_argument(recursive, 'r', "recursive"_sv,
|
||||||
|
"remove a directory recursively (by default, rm removes only empty directories)"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
if (!recursive) TRY(os::FileSystem::remove(path));
|
if (!recursive) TRY(os::FileSystem::remove(path));
|
||||||
|
@ -37,8 +37,9 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
SharedPtr<File> input_file;
|
SharedPtr<File> input_file;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("The Luna system's command shell."_sv);
|
||||||
parser.add_positional_argument(path, "path"_sv, "-"_sv);
|
parser.add_positional_argument(path, "path"_sv, "-"_sv);
|
||||||
parser.add_value_argument(command, 'c', "command"_sv, true);
|
parser.add_value_argument(command, 'c', "command"_sv, true, "execute a single command and then exit"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
if (!command.is_empty()) TRY(execute_command(command));
|
if (!command.is_empty()) TRY(execute_command(command));
|
||||||
|
@ -62,6 +62,7 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Switch to a different user."_sv);
|
||||||
parser.add_positional_argument(name, "name"_sv, true);
|
parser.add_positional_argument(name, "name"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
@ -7,15 +7,19 @@ namespace os
|
|||||||
class ArgumentParser
|
class ArgumentParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ArgumentParser() = default;
|
ArgumentParser(bool add_help = true);
|
||||||
|
|
||||||
|
void add_description(StringView description);
|
||||||
|
|
||||||
Result<void> add_positional_argument(StringView& out, StringView name, bool required);
|
Result<void> add_positional_argument(StringView& out, StringView name, bool required);
|
||||||
Result<void> add_positional_argument(StringView& out, StringView name, StringView fallback);
|
Result<void> add_positional_argument(StringView& out, StringView name, StringView fallback);
|
||||||
|
|
||||||
Result<void> add_switch_argument(bool& out, char short_flag, StringView long_flag);
|
Result<void> add_switch_argument(bool& out, char short_flag, StringView long_flag, StringView help = {});
|
||||||
|
|
||||||
Result<void> add_value_argument(StringView& out, char short_flag, StringView long_flag, bool value_required);
|
Result<void> add_value_argument(StringView& out, char short_flag, StringView long_flag, bool value_required,
|
||||||
Result<void> add_value_argument(StringView& out, char short_flag, StringView long_flag, StringView fallback);
|
StringView help = {});
|
||||||
|
Result<void> add_value_argument(StringView& out, char short_flag, StringView long_flag, StringView fallback,
|
||||||
|
StringView help = {});
|
||||||
|
|
||||||
Result<Vector<StringView>> parse(int argc, char* const* argv);
|
Result<Vector<StringView>> parse(int argc, char* const* argv);
|
||||||
|
|
||||||
@ -33,6 +37,7 @@ namespace os
|
|||||||
bool* out;
|
bool* out;
|
||||||
char short_flag;
|
char short_flag;
|
||||||
StringView long_flag;
|
StringView long_flag;
|
||||||
|
StringView help;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ValueArgument
|
struct ValueArgument
|
||||||
@ -42,10 +47,17 @@ namespace os
|
|||||||
StringView long_flag;
|
StringView long_flag;
|
||||||
bool required;
|
bool required;
|
||||||
StringView fallback;
|
StringView fallback;
|
||||||
|
StringView help;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Result<void> usage(StringView program_name);
|
||||||
|
void short_usage(StringView program_name);
|
||||||
|
|
||||||
Vector<PositionalArgument> m_positional_args;
|
Vector<PositionalArgument> m_positional_args;
|
||||||
Vector<SwitchArgument> m_switch_args;
|
Vector<SwitchArgument> m_switch_args;
|
||||||
Vector<ValueArgument> m_value_args;
|
Vector<ValueArgument> m_value_args;
|
||||||
|
StringView m_description = {};
|
||||||
|
bool m_add_short_help_flag { false };
|
||||||
|
bool m_add_long_help_flag { false };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
|
#include <luna/StringBuilder.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
namespace os
|
namespace os
|
||||||
{
|
{
|
||||||
|
ArgumentParser::ArgumentParser(bool add_help) : m_add_short_help_flag(add_help), m_add_long_help_flag(add_help)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgumentParser::add_description(StringView description)
|
||||||
|
{
|
||||||
|
m_description = description;
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> ArgumentParser::add_positional_argument(StringView& out, StringView name, bool required)
|
Result<void> ArgumentParser::add_positional_argument(StringView& out, StringView name, bool required)
|
||||||
{
|
{
|
||||||
PositionalArgument arg { &out, name, required, {} };
|
PositionalArgument arg { &out, name, required, {} };
|
||||||
@ -18,25 +28,34 @@ namespace os
|
|||||||
return m_positional_args.try_append(move(arg));
|
return m_positional_args.try_append(move(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> ArgumentParser::add_switch_argument(bool& out, char short_flag, StringView long_flag)
|
Result<void> ArgumentParser::add_switch_argument(bool& out, char short_flag, StringView long_flag, StringView help)
|
||||||
{
|
{
|
||||||
SwitchArgument arg { &out, short_flag, long_flag };
|
SwitchArgument arg { &out, short_flag, long_flag, help };
|
||||||
|
|
||||||
|
if (short_flag == 'h') m_add_short_help_flag = false;
|
||||||
|
if (long_flag == "help"_sv) m_add_long_help_flag = false;
|
||||||
|
|
||||||
return m_switch_args.try_append(move(arg));
|
return m_switch_args.try_append(move(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> ArgumentParser::add_value_argument(StringView& out, char short_flag, StringView long_flag,
|
Result<void> ArgumentParser::add_value_argument(StringView& out, char short_flag, StringView long_flag,
|
||||||
bool value_required)
|
bool value_required, StringView help)
|
||||||
{
|
{
|
||||||
ValueArgument arg { &out, short_flag, long_flag, value_required, {} };
|
ValueArgument arg { &out, short_flag, long_flag, value_required, {}, help };
|
||||||
|
|
||||||
|
if (short_flag == 'h') m_add_short_help_flag = false;
|
||||||
|
if (long_flag == "help"_sv) m_add_long_help_flag = false;
|
||||||
|
|
||||||
return m_value_args.try_append(move(arg));
|
return m_value_args.try_append(move(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> ArgumentParser::add_value_argument(StringView& out, char short_flag, StringView long_flag,
|
Result<void> ArgumentParser::add_value_argument(StringView& out, char short_flag, StringView long_flag,
|
||||||
StringView fallback)
|
StringView fallback, StringView help)
|
||||||
{
|
{
|
||||||
ValueArgument arg { &out, short_flag, long_flag, false, fallback };
|
ValueArgument arg { &out, short_flag, long_flag, false, fallback, help };
|
||||||
|
|
||||||
|
if (short_flag == 'h') m_add_short_help_flag = false;
|
||||||
|
if (long_flag == "help"_sv) m_add_long_help_flag = false;
|
||||||
|
|
||||||
return m_value_args.try_append(move(arg));
|
return m_value_args.try_append(move(arg));
|
||||||
}
|
}
|
||||||
@ -87,6 +106,8 @@ namespace os
|
|||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
|
if (m_add_long_help_flag && flag == "help"_sv) { TRY(usage(program_name)); }
|
||||||
|
|
||||||
for (const auto& current : m_switch_args)
|
for (const auto& current : m_switch_args)
|
||||||
{
|
{
|
||||||
if (current.long_flag == flag)
|
if (current.long_flag == flag)
|
||||||
@ -111,7 +132,7 @@ namespace os
|
|||||||
if (found) continue;
|
if (found) continue;
|
||||||
|
|
||||||
fprintf(stderr, "%s: unrecognized option '%s'\n", program_name.chars(), arg.chars());
|
fprintf(stderr, "%s: unrecognized option '%s'\n", program_name.chars(), arg.chars());
|
||||||
exit(1);
|
short_usage(program_name);
|
||||||
}
|
}
|
||||||
else if (looks_like_short_flag(arg))
|
else if (looks_like_short_flag(arg))
|
||||||
{
|
{
|
||||||
@ -122,6 +143,8 @@ namespace os
|
|||||||
char c = flags[j];
|
char c = flags[j];
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
|
if (m_add_short_help_flag && c == 'h') { TRY(usage(program_name)); }
|
||||||
|
|
||||||
// Last flag, this could be a value flag
|
// Last flag, this could be a value flag
|
||||||
if (j + 1 == flags.length())
|
if (j + 1 == flags.length())
|
||||||
{
|
{
|
||||||
@ -156,7 +179,7 @@ namespace os
|
|||||||
if (found) continue;
|
if (found) continue;
|
||||||
|
|
||||||
fprintf(stderr, "%s: invalid option -- '%c'\n", program_name.chars(), c);
|
fprintf(stderr, "%s: invalid option -- '%c'\n", program_name.chars(), c);
|
||||||
exit(1);
|
short_usage(program_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -179,7 +202,7 @@ namespace os
|
|||||||
{
|
{
|
||||||
fprintf(stderr, "%s: option '--%s' requires an argument\n", program_name.chars(),
|
fprintf(stderr, "%s: option '--%s' requires an argument\n", program_name.chars(),
|
||||||
current_value_argument->long_flag.chars());
|
current_value_argument->long_flag.chars());
|
||||||
exit(1);
|
short_usage(program_name);
|
||||||
}
|
}
|
||||||
else { *current_value_argument->out = current_value_argument->fallback; }
|
else { *current_value_argument->out = current_value_argument->fallback; }
|
||||||
}
|
}
|
||||||
@ -190,11 +213,86 @@ namespace os
|
|||||||
if (arg.required)
|
if (arg.required)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: required argument '%s' not provided\n", program_name.chars(), arg.name.chars());
|
fprintf(stderr, "%s: required argument '%s' not provided\n", program_name.chars(), arg.name.chars());
|
||||||
exit(1);
|
short_usage(program_name);
|
||||||
}
|
}
|
||||||
else { *arg.out = arg.fallback; }
|
else { *arg.out = arg.fallback; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return leftovers;
|
return leftovers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void> ArgumentParser::usage(StringView program_name)
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
TRY(sb.format("Usage: %s <options>"_sv, program_name.chars()));
|
||||||
|
|
||||||
|
for (const auto& arg : m_positional_args)
|
||||||
|
{
|
||||||
|
if (arg.required) { TRY(sb.format(" %s", arg.name.chars())); }
|
||||||
|
else { TRY(sb.format(" [%s]", arg.name.chars())); }
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(sb.add('\n'));
|
||||||
|
|
||||||
|
auto usage_line = TRY(sb.string());
|
||||||
|
fputs(usage_line.chars(), stdout);
|
||||||
|
|
||||||
|
if (!m_description.is_empty()) { puts(m_description.chars()); }
|
||||||
|
|
||||||
|
if (m_switch_args.size() || m_value_args.size())
|
||||||
|
{
|
||||||
|
putchar('\n');
|
||||||
|
puts("Options:");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& arg : m_switch_args)
|
||||||
|
{
|
||||||
|
if (arg.long_flag.is_empty())
|
||||||
|
{
|
||||||
|
if (arg.short_flag == ' ') continue;
|
||||||
|
|
||||||
|
printf(" -%-25c %s\n", arg.short_flag, arg.help.chars());
|
||||||
|
}
|
||||||
|
else if (arg.short_flag == ' ') { printf(" --%-20s %s\n", arg.long_flag.chars(), arg.help.chars()); }
|
||||||
|
else { printf(" -%c, --%-20s %s\n", arg.short_flag, arg.long_flag.chars(), arg.help.chars()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& arg : m_value_args)
|
||||||
|
{
|
||||||
|
StringView value_name;
|
||||||
|
if (arg.required) value_name = "VALUE"_sv;
|
||||||
|
else
|
||||||
|
value_name = "[VALUE]"_sv;
|
||||||
|
|
||||||
|
int field_size = 20 - (int)(arg.long_flag.length() + 1);
|
||||||
|
if (field_size < 0) field_size = 0;
|
||||||
|
|
||||||
|
if (arg.long_flag.is_empty())
|
||||||
|
{
|
||||||
|
if (arg.short_flag == ' ') continue;
|
||||||
|
|
||||||
|
printf(" -%c %-20s %s\n", arg.short_flag, value_name.chars(), arg.help.chars());
|
||||||
|
}
|
||||||
|
else if (arg.short_flag == ' ')
|
||||||
|
{
|
||||||
|
printf(" --%s %-*s %s\n", arg.long_flag.chars(), field_size, value_name.chars(), arg.help.chars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(" -%c, --%s %-*s %s\n", arg.short_flag, arg.long_flag.chars(), field_size, value_name.chars(),
|
||||||
|
arg.help.chars());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgumentParser::short_usage(StringView program_name)
|
||||||
|
{
|
||||||
|
if (m_add_short_help_flag || m_add_long_help_flag)
|
||||||
|
fprintf(stderr, "Try running '%s %s' for more information.\n", program_name.chars(),
|
||||||
|
m_add_long_help_flag ? "--help" : "-h");
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user