ArgumentParser: Add support for version information
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2023-04-28 16:33:05 +02:00
parent 0c1d33f2ec
commit 80914f0bb9
Signed by: apio
GPG Key ID: B8A7D06E42258954
15 changed files with 100 additions and 2 deletions

View File

@ -10,6 +10,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Encode or decode Base64 data. If not given a file, reads from standard input.");
parser.add_system_program_info("base64"_sv);
parser.add_positional_argument(path, "file", "-"_sv);
parser.add_switch_argument(decode, 'd', "decode", "decode data");
parser.add_switch_argument(allow_garbage, 'i', "ignore-garbage", "when decoding, ignore non-base64 characters");

View File

@ -27,6 +27,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Concatenate files to standard output."_sv);
parser.add_system_program_info("cat"_sv);
parser.add_positional_argument(filename, "file"_sv, "-"_sv);
Vector<StringView> extra_files = TRY(parser.parse(argc, argv));

View File

@ -10,6 +10,7 @@ int main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Change the permissions of a file."_sv);
parser.add_system_program_info("chmod"_sv);
parser.add_positional_argument(mode_string, "mode"_sv, true);
parser.add_positional_argument(path, "path"_sv, true);
parser.parse(argc, argv);

View File

@ -12,6 +12,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Change the owner and group of a file."_sv);
parser.add_system_program_info("chown"_sv);
parser.add_positional_argument(user_and_group, "owner"_sv, true);
parser.add_positional_argument(path, "path"_sv, true);
parser.parse(argc, argv);

View File

@ -11,6 +11,7 @@ int main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Display the current (or another) date and time."_sv);
parser.add_system_program_info("date"_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);

View File

@ -10,6 +10,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Edit a file using basic line-based shell editing."_sv);
parser.add_system_program_info("edit"_sv);
parser.add_positional_argument(pathname, "path"_sv, true);
parser.parse(argc, argv);

View File

@ -13,6 +13,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("List files contained in a directory (defaults to '.', the current directory)"_sv);
parser.add_system_program_info("ls"_sv);
parser.add_positional_argument(pathname, "directory"_sv, "."_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, "list all files except '.' and '..'"_sv);

View File

@ -31,6 +31,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Create directories."_sv);
parser.add_system_program_info("mkdir"_sv);
parser.add_positional_argument(path, "path"_sv, true);
parser.add_positional_argument(mode_string, "mode"_sv, "755"_sv);
parser.add_switch_argument(recursive, 'p', "parents"_sv,

View File

@ -8,6 +8,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Remove a path from the file system."_sv);
parser.add_system_program_info("rm"_sv);
parser.add_positional_argument(path, "path"_sv, true);
parser.add_switch_argument(recursive, 'r', "recursive"_sv,
"remove a directory recursively (by default, rm removes only empty directories)"_sv);

View File

@ -46,6 +46,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("The Luna system's command shell."_sv);
parser.add_system_program_info("sh"_sv);
parser.add_positional_argument(path, "path"_sv, "-"_sv);
parser.add_value_argument(command, 'c', "command"_sv, true, "execute a single command and then exit"_sv);
parser.parse(argc, argv);

View File

@ -19,6 +19,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Display file status.");
parser.add_system_program_info("stat"_sv);
parser.add_positional_argument(path, "path", true);
parser.parse(argc, argv);

View File

@ -63,6 +63,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Switch to a different user (by default, root)."_sv);
parser.add_system_program_info("su"_sv);
parser.add_positional_argument(name, "name"_sv, "root"_sv);
parser.parse(argc, argv);

View File

@ -24,6 +24,7 @@ Result<int> luna_main(int argc, char** argv)
os::ArgumentParser parser;
parser.add_description("Print system information. If given no options, defaults to -s."_sv);
parser.add_system_program_info("uname"_sv);
parser.add_switch_argument(all, 'a', "all"_sv, "print all information"_sv);
parser.add_switch_argument(kernel_name, 's', "kernel-name"_sv, "print the kernel name"_sv);
parser.add_switch_argument(node_name, 'n', "nodename"_sv, "print the network hostname"_sv);

View File

@ -7,7 +7,7 @@ namespace os
class ArgumentParser
{
public:
ArgumentParser(bool add_help = true);
ArgumentParser();
void add_description(StringView description);
@ -23,6 +23,22 @@ namespace os
Result<Vector<StringView>> parse(int argc, char* const* argv);
struct ProgramInfo
{
StringView name;
StringView version;
StringView copyright;
StringView license;
StringView authors;
StringView package;
};
void add_program_info(ProgramInfo info);
/* Used from programs that are part of the Luna source tree, to add the same version info for all programs.
* Should not be used otherwise. */
void add_system_program_info(StringView name);
private:
struct PositionalArgument
{
@ -53,11 +69,16 @@ namespace os
Result<void> usage(StringView program_name);
void short_usage(StringView program_name);
void version();
Vector<PositionalArgument> m_positional_args;
Vector<SwitchArgument> m_switch_args;
Vector<ValueArgument> m_value_args;
ProgramInfo m_program_info;
StringView m_description = {};
bool m_add_short_help_flag { false };
bool m_add_long_help_flag { false };
bool m_add_short_version_flag { false };
bool m_add_long_version_flag { false };
};
}

View File

@ -2,10 +2,11 @@
#include <os/ArgumentParser.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
namespace os
{
ArgumentParser::ArgumentParser(bool add_help) : m_add_short_help_flag(add_help), m_add_long_help_flag(add_help)
ArgumentParser::ArgumentParser() : m_add_short_help_flag(true), m_add_long_help_flag(true)
{
}
@ -34,6 +35,8 @@ namespace os
if (short_flag == 'h') m_add_short_help_flag = false;
if (long_flag == "help"_sv) m_add_long_help_flag = false;
if (short_flag == 'v') m_add_short_version_flag = false;
if (long_flag == "version"_sv) m_add_long_version_flag = false;
return m_switch_args.try_append(move(arg));
}
@ -45,6 +48,8 @@ namespace os
if (short_flag == 'h') m_add_short_help_flag = false;
if (long_flag == "help"_sv) m_add_long_help_flag = false;
if (short_flag == 'v') m_add_short_version_flag = false;
if (long_flag == "version"_sv) m_add_long_version_flag = false;
return m_value_args.try_append(move(arg));
}
@ -56,10 +61,38 @@ namespace os
if (short_flag == 'h') m_add_short_help_flag = false;
if (long_flag == "help"_sv) m_add_long_help_flag = false;
if (short_flag == 'v') m_add_short_version_flag = false;
if (long_flag == "version"_sv) m_add_long_version_flag = false;
return m_value_args.try_append(move(arg));
}
constexpr auto copyright_text = "Copyright (C) 2023, the Luna authors.";
constexpr auto license_text = "Licensed under the BSD-2 license <https://opensource.org/license/bsd-2-clause/>";
void ArgumentParser::add_program_info(ProgramInfo info)
{
m_program_info = info;
m_add_short_version_flag = m_add_long_version_flag = true;
}
void ArgumentParser::add_system_program_info(StringView name)
{
static utsname info;
check(uname(&info) == 0);
ProgramInfo system_info {
.name = name,
.version = info.release,
.copyright = copyright_text,
.license = license_text,
.authors = {},
.package = "Luna system",
};
add_program_info(system_info);
}
static bool looks_like_short_flag(StringView arg)
{
return arg.length() > 1 && arg[0] == '-';
@ -107,6 +140,7 @@ namespace os
bool found = false;
if (m_add_long_help_flag && flag == "help"_sv) { TRY(usage(program_name)); }
if (m_add_long_version_flag && flag == "version"_sv) { version(); }
for (const auto& current : m_switch_args)
{
@ -144,6 +178,7 @@ namespace os
bool found = false;
if (m_add_short_help_flag && c == 'h') { TRY(usage(program_name)); }
if (m_add_short_version_flag && c == 'v') { version(); }
// Last flag, this could be a value flag
if (j + 1 == flags.length())
@ -284,6 +319,35 @@ namespace os
}
}
if (m_add_short_help_flag && m_add_long_help_flag)
printf(" -h, --%-20s %s\n", "help", "show this help message and exit");
else if (m_add_long_help_flag)
printf(" --%-20s %s\n", "help", "show this help message and exit");
else
printf(" -%-25c %s\n", 'h', "show this help message and exit");
if (m_add_short_version_flag && m_add_long_version_flag)
printf(" -v, --%-20s %s\n", "version", "show version information and exit");
else if (m_add_long_version_flag)
printf(" --%-20s %s\n", "version", "show version information and exit");
else
printf(" -%-25c %s\n", 'v', "show version information and exit");
exit(0);
}
void ArgumentParser::version()
{
if (m_program_info.package.is_empty())
printf("%s %s\n", m_program_info.name.chars(), m_program_info.version.chars());
else
printf("%s (%s) %s\n", m_program_info.name.chars(), m_program_info.package.chars(),
m_program_info.version.chars());
printf("%s\n%s\n", m_program_info.copyright.chars(), m_program_info.license.chars());
if (!m_program_info.authors.is_empty()) printf("\n%s\n", m_program_info.authors.chars());
exit(0);
}