Add option_parser_print_usage

Signed-off-by: Jim Ramsay <i.am@jimramsay.com>
wayvncctl-polishing
Jim Ramsay 2023-01-11 21:31:36 -05:00
parent dcb23ebfe1
commit 104040291b
6 changed files with 158 additions and 27 deletions

View File

@ -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);

View File

@ -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))

View File

@ -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] [<address> [<port>]]\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", "<path>",
"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);

View File

@ -23,6 +23,7 @@
#include <stdbool.h>
#include <assert.h>
#include <sys/param.h>
#include <ctype.h>
#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;
}

View File

@ -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] <command> [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;
}

View File

@ -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;
}