From 104040291b52cfb6ddf2618698523a64e2d28412 Mon Sep 17 00:00:00 2001 From: Jim Ramsay Date: Wed, 11 Jan 2023 21:31:36 -0500 Subject: [PATCH] Add option_parser_print_usage Signed-off-by: Jim Ramsay --- include/option-parser.h | 5 +++ src/ctl-client.c | 9 ++---- src/main.c | 30 +++++++++++------- src/option-parser.c | 66 +++++++++++++++++++++++++++++++++++++-- src/wayvncctl.c | 11 +++---- test/option-parser-test.c | 64 +++++++++++++++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 27 deletions(-) diff --git a/include/option-parser.h b/include/option-parser.h index 6601f0e..bee0b92 100644 --- a/include/option-parser.h +++ b/include/option-parser.h @@ -50,11 +50,16 @@ struct option_parser { void option_parser_init(struct option_parser* self, const struct wv_option* options); +void option_parser_print_usage(struct option_parser* self, FILE* stream); + int option_parser_print_arguments(struct option_parser* self, FILE* stream); void option_parser_print_options(struct option_parser* self, FILE* stream); int option_parser_parse(struct option_parser* self, int argc, const char* const* argv); + const char* option_parser_get_value(const struct option_parser* self, const char* name); +const char* option_parser_get_value_no_default(const struct option_parser* self, + const char* name); diff --git a/src/ctl-client.c b/src/ctl-client.c index 07cf2ad..f33f870 100644 --- a/src/ctl-client.c +++ b/src/ctl-client.c @@ -738,12 +738,9 @@ static int print_command_usage(struct ctl_client* self, WARN("No such command"); return 1; } - printf("Usage: wayvncctl [options] %s ", info->name); - for (int i = 0; i < cmd_options->n_opts; ++i) - if (cmd_options->options[i].positional) - printf("<%s> ", cmd_options->options[i].positional); - - printf("[parameters]\n\n"); + printf("Usage: wayvncctl [options] %s", info->name); + option_parser_print_usage(cmd_options, stdout); + printf("\n\n"); table_printer_indent_and_reflow_text(stdout, info->description, 80, 0, 0); printf("\n"); if (option_parser_print_arguments(cmd_options, stdout)) diff --git a/src/main.c b/src/main.c index cd7b883..e89006d 100644 --- a/src/main.c +++ b/src/main.c @@ -66,6 +66,9 @@ #define DEFAULT_ADDRESS "127.0.0.1" #define DEFAULT_PORT 5900 +#define XSTR(x) STR(x) +#define STR(x) #x + #define MAYBE_UNUSED __attribute__((unused)) struct wayvnc { @@ -907,15 +910,14 @@ void on_capture_done(struct screencopy* sc) int wayvnc_usage(struct option_parser* parser, FILE* stream, int rc) { - static const char* usage = -"Usage: wayvnc [options] [
[]]\n" -"\n" -"Starts a VNC server for $WAYLAND_DISPLAY"; - fprintf(stream, "%s\n\n", usage); + fputs("Usage: wayvnc", stream); + option_parser_print_usage(parser, stream); + fputs("\n\n", stream); + fprintf(stream, "Starts a VNC server for $WAYLAND_DISPLAY\n\n"); if (option_parser_print_arguments(parser, stream)) - fprintf(stream, "\n"); + fputc('\n', stream); option_parser_print_options(parser, stream); - fprintf(stream, "\n"); + fputc('\n', stream); return rc; } @@ -1270,9 +1272,11 @@ int main(int argc, char* argv[]) static const struct wv_option opts[] = { { .positional = "address", - .help = "The IP address or unix socket path to listen on. Default: 127.0.0.1" }, + .help = "The IP address or unix socket path to listen on.", + .default_ = DEFAULT_ADDRESS}, { .positional = "port", - .help = "The TCP port to listen on. Default: 5900" }, + .help = "The TCP port to listen on.", + .default_ = XSTR(DEFAULT_PORT)}, { 'C', "config", "", "Select a config file." }, { 'g', "gpu", NULL, @@ -1305,7 +1309,7 @@ int main(int argc, char* argv[]) .default_ = "warning" }, { 'h', "help", NULL, "Get help (this text)." }, - { '\0', NULL, NULL, NULL } + {} }; struct option_parser option_parser; @@ -1344,8 +1348,10 @@ int main(int argc, char* argv[]) nvnc_set_log_level(log_level); - address = option_parser_get_value(&option_parser, "address"); - const char* port_str = option_parser_get_value(&option_parser, "port"); + // Only check for explicitly-set values here (defaults applied below) + address = option_parser_get_value_no_default(&option_parser, "address"); + const char* port_str = option_parser_get_value_no_default(&option_parser, + "port"); if (port_str) port = atoi(port_str); diff --git a/src/option-parser.c b/src/option-parser.c index 337d799..cf4bdea 100644 --- a/src/option-parser.c +++ b/src/option-parser.c @@ -23,6 +23,7 @@ #include #include #include +#include #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) @@ -105,6 +106,39 @@ void option_parser_print_options(struct option_parser* self, FILE* stream) format_option(&printer, &self->options[i]); } +static void print_string_tolower(FILE* stream, const char *src) +{ + for (const char* c = src; *c != '\0'; c++) + fprintf(stream, "%c", tolower(*c)); +} + +void option_parser_print_usage(struct option_parser* self, FILE* stream) +{ + fprintf(stream, " ["); + print_string_tolower(stream, self->name); + fprintf(stream, "]"); + int optional_paren_count = 0; + for (int i = 0; i < self->n_opts; ++i) { + const struct wv_option* opt = &self->options[i]; + if (!opt->positional) + continue; + const char* open = "<"; + const char* close = ">"; + if (opt->default_) { + open = "["; + close = ""; // Closed via optional_paren_count loop below + optional_paren_count++; + } else { + // Enforce there must be NO non-optional args after + // we've processed at least one optional arg + assert(optional_paren_count == 0); + } + fprintf(stream, " %s%s%s", open, opt->positional, close); + } + for (int i = 0; i < optional_paren_count; ++i) + fprintf(stream, "]"); +} + int option_parser_print_arguments(struct option_parser* self, FILE* stream) { size_t max_arg = 0; @@ -168,6 +202,19 @@ static const struct wv_option* find_positional_option( return NULL; } +static const struct wv_option* find_positional_option_by_name( + const struct option_parser* self, const char*name) +{ + for (int i = 0; i < self->n_opts; ++i) { + const struct wv_option* opt = &self->options[i]; + if (!opt->positional) + continue; + if (strcmp(opt->positional, name) == 0) + return opt; + } + return NULL; +} + static int append_value(struct option_parser* self, const struct wv_option* option, const char* value) { @@ -317,7 +364,7 @@ int option_parser_parse(struct option_parser* self, int argc, return 0; } -const char* option_parser_get_value(const struct option_parser* self, +const char* option_parser_get_value_no_default(const struct option_parser* self, const char* name) { const struct wv_option* opt; @@ -340,6 +387,18 @@ const char* option_parser_get_value(const struct option_parser* self, return value->value; } + return NULL; +} + +const char* option_parser_get_value(const struct option_parser* self, + const char* name) +{ + const char* value = option_parser_get_value_no_default(self, name); + if (value) + return value; + + bool is_short = name[0] && !name[1]; + const struct wv_option* opt; if (is_short) { opt = find_short_option(self, name[0]); if (opt) @@ -348,9 +407,10 @@ const char* option_parser_get_value(const struct option_parser* self, opt = find_long_option(self, name); if (opt) return opt->default_; + opt = find_positional_option_by_name(self, name); + if (opt) + return opt->default_; } - // TODO: Add positional option? - return NULL; } diff --git a/src/wayvncctl.c b/src/wayvncctl.c index f2e7b84..01d7b5e 100644 --- a/src/wayvncctl.c +++ b/src/wayvncctl.c @@ -43,13 +43,12 @@ struct wayvncctl { static int wayvncctl_usage(FILE* stream, struct option_parser* options, int rc) { - static const char* usage = -"Usage: wayvncctl [options] [parameters]\n" -"\n" -"Connects to and interacts with a running wayvnc instance."; - fprintf(stream, "%s\n\n", usage); + fputs("Usage: wayvncctl", stream); + option_parser_print_usage(options, stream); + fputs(" [parameters]\n\n", stream); + fputs("Connects to and interacts with a running wayvnc instance.\n\n", stream); option_parser_print_options(options, stream); - fprintf(stream, "\n"); + fputc('\n', stream); ctl_client_print_command_list(stream); return rc; } diff --git a/test/option-parser-test.c b/test/option-parser-test.c index 7c256d7..525bfe3 100644 --- a/test/option-parser-test.c +++ b/test/option-parser-test.c @@ -15,6 +15,13 @@ static const struct wv_option options[] = { { }, }; +static const struct wv_option default_options[] = { + { .positional = "first" }, + { .positional = "second", .default_ = "second_default" }, + { 'v', "value-option", "value", "Description of v", .default_ = "v_default" }, + { }, +}; + static int test_simple(void) { struct option_parser parser; @@ -227,6 +234,61 @@ static int test_subcommand_with_arguments(void) return 0; } +static int test_defaults_not_set(void) +{ + struct option_parser parser; + option_parser_init(&parser, default_options); + const char* argv[] = { + "executable", + "pos 1", + }; + + ASSERT_INT_EQ(0, option_parser_parse(&parser, ARRAY_SIZE(argv), argv)); + ASSERT_STR_EQ("pos 1", option_parser_get_value(&parser, "first")); + + ASSERT_STR_EQ("second_default", option_parser_get_value(&parser, "second")); + ASSERT_FALSE(option_parser_get_value_no_default(&parser, "second")); + + ASSERT_STR_EQ("v_default", option_parser_get_value(&parser, "value-option")); + ASSERT_FALSE(option_parser_get_value_no_default(&parser, "value-option")); + ASSERT_STR_EQ("v_default", option_parser_get_value(&parser, "v")); + ASSERT_FALSE(option_parser_get_value_no_default(&parser, "v")); + + ASSERT_INT_EQ(0, parser.remaining_argc); + ASSERT_FALSE(parser.remaining_argv); + + return 0; +} + +static int test_defaults_overridden(void) +{ + struct option_parser parser; + option_parser_init(&parser, default_options); + const char* argv[] = { + "executable", + "pos 1", + "pos 2", + "-v", + "v_set", + }; + + ASSERT_INT_EQ(0, option_parser_parse(&parser, ARRAY_SIZE(argv), argv)); + ASSERT_STR_EQ("pos 1", option_parser_get_value(&parser, "first")); + + ASSERT_STR_EQ("pos 2", option_parser_get_value(&parser, "second")); + ASSERT_STR_EQ("pos 2", option_parser_get_value_no_default(&parser, "second")); + + ASSERT_STR_EQ("v_set", option_parser_get_value(&parser, "value-option")); + ASSERT_STR_EQ("v_set", option_parser_get_value_no_default(&parser, "value-option")); + ASSERT_STR_EQ("v_set", option_parser_get_value(&parser, "v")); + ASSERT_STR_EQ("v_set", option_parser_get_value_no_default(&parser, "v")); + + ASSERT_INT_EQ(0, parser.remaining_argc); + ASSERT_FALSE(parser.remaining_argv); + + return 0; +} + int main() { int r = 0; @@ -246,5 +308,7 @@ int main() RUN_TEST(test_missing_long_value); RUN_TEST(test_subcommand_without_arguments); RUN_TEST(test_subcommand_with_arguments); + RUN_TEST(test_defaults_not_set); + RUN_TEST(test_defaults_overridden); return r; }