From 80914f0bb93e31bbbfc79d2bbfc2d57921f9a656 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 28 Apr 2023 16:33:05 +0200 Subject: [PATCH] ArgumentParser: Add support for version information --- apps/base64.cpp | 1 + apps/cat.cpp | 1 + apps/chmod.cpp | 1 + apps/chown.cpp | 1 + apps/date.cpp | 1 + apps/edit.cpp | 1 + apps/ls.cpp | 1 + apps/mkdir.cpp | 1 + apps/rm.cpp | 1 + apps/sh.cpp | 1 + apps/stat.cpp | 1 + apps/su.cpp | 1 + apps/uname.cpp | 1 + libos/include/os/ArgumentParser.h | 23 ++++++++++- libos/src/ArgumentParser.cpp | 66 ++++++++++++++++++++++++++++++- 15 files changed, 100 insertions(+), 2 deletions(-) diff --git a/apps/base64.cpp b/apps/base64.cpp index 83390a40..8bcf2db2 100644 --- a/apps/base64.cpp +++ b/apps/base64.cpp @@ -10,6 +10,7 @@ Result 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"); diff --git a/apps/cat.cpp b/apps/cat.cpp index d5019b6d..66953631 100644 --- a/apps/cat.cpp +++ b/apps/cat.cpp @@ -27,6 +27,7 @@ Result 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 extra_files = TRY(parser.parse(argc, argv)); diff --git a/apps/chmod.cpp b/apps/chmod.cpp index e1f3f206..b1d22fcf 100644 --- a/apps/chmod.cpp +++ b/apps/chmod.cpp @@ -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); diff --git a/apps/chown.cpp b/apps/chown.cpp index c3b3ff58..16416cd3 100644 --- a/apps/chown.cpp +++ b/apps/chown.cpp @@ -12,6 +12,7 @@ Result 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); diff --git a/apps/date.cpp b/apps/date.cpp index e20a2d5b..945203a8 100644 --- a/apps/date.cpp +++ b/apps/date.cpp @@ -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); diff --git a/apps/edit.cpp b/apps/edit.cpp index 88039dcb..2af1d36b 100644 --- a/apps/edit.cpp +++ b/apps/edit.cpp @@ -10,6 +10,7 @@ Result 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); diff --git a/apps/ls.cpp b/apps/ls.cpp index 4053a9e9..e2f412ed 100644 --- a/apps/ls.cpp +++ b/apps/ls.cpp @@ -13,6 +13,7 @@ Result 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); diff --git a/apps/mkdir.cpp b/apps/mkdir.cpp index e4a1ef8d..200b6a12 100644 --- a/apps/mkdir.cpp +++ b/apps/mkdir.cpp @@ -31,6 +31,7 @@ Result 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, diff --git a/apps/rm.cpp b/apps/rm.cpp index 494c89a2..c87a02ed 100644 --- a/apps/rm.cpp +++ b/apps/rm.cpp @@ -8,6 +8,7 @@ Result 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); diff --git a/apps/sh.cpp b/apps/sh.cpp index b3ecc98e..0d6bed61 100644 --- a/apps/sh.cpp +++ b/apps/sh.cpp @@ -46,6 +46,7 @@ Result 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); diff --git a/apps/stat.cpp b/apps/stat.cpp index 49dd851f..6d88e2df 100644 --- a/apps/stat.cpp +++ b/apps/stat.cpp @@ -19,6 +19,7 @@ Result 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); diff --git a/apps/su.cpp b/apps/su.cpp index 0dae9d0f..7bb3176f 100644 --- a/apps/su.cpp +++ b/apps/su.cpp @@ -63,6 +63,7 @@ Result 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); diff --git a/apps/uname.cpp b/apps/uname.cpp index f73a92b3..110b8edc 100644 --- a/apps/uname.cpp +++ b/apps/uname.cpp @@ -24,6 +24,7 @@ Result 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); diff --git a/libos/include/os/ArgumentParser.h b/libos/include/os/ArgumentParser.h index 5f04382a..cce1d60d 100644 --- a/libos/include/os/ArgumentParser.h +++ b/libos/include/os/ArgumentParser.h @@ -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> 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 usage(StringView program_name); void short_usage(StringView program_name); + void version(); + Vector m_positional_args; Vector m_switch_args; Vector 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 }; }; } diff --git a/libos/src/ArgumentParser.cpp b/libos/src/ArgumentParser.cpp index f7a0ea0f..d13d05f6 100644 --- a/libos/src/ArgumentParser.cpp +++ b/libos/src/ArgumentParser.cpp @@ -2,10 +2,11 @@ #include #include #include +#include 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 "; + + 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); }