wayvnc/src/option-parser.c

173 lines
4.0 KiB
C

/*
* Copyright (c) 2022 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "option-parser.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
static int count_options(const struct wv_option* opts)
{
int n = 0;
while (opts[n].short_opt || opts[n].long_opt)
n++;
return n;
}
void option_parser_init(struct option_parser* self,
const struct wv_option* options)
{
memset(self, 0, sizeof(*self));
self->options = options;
self->n_opts = count_options(options);
int short_opt_index = 0;
int long_opt_index = 0;
for (int i = 0; i < self->n_opts; ++i) {
assert(options[i].short_opt); // TODO: Make this optional?
self->short_opts[short_opt_index++] = options[i].short_opt;
if (options[i].schema)
self->short_opts[short_opt_index++] = ':';
if (!options[i].long_opt)
continue;
const struct wv_option* src_opt = &options[i];
struct option* dst_opt = &self->long_opts[long_opt_index++];
dst_opt->val = src_opt->short_opt;
dst_opt->name = src_opt->long_opt;
dst_opt->has_arg = src_opt->schema ?
required_argument : no_argument;
}
}
static int get_left_col_width(const struct wv_option* opts, int n)
{
int max_width = 0;
for (int i = 0; i < n; ++i) {
int width = 0;
if (opts[i].short_opt)
width += 2;
if (opts[i].long_opt)
width += 2 + strlen(opts[i].long_opt);
if (opts[i].short_opt && opts[i].long_opt)
width += 1; // for ','
if (opts[i].schema) {
width += strlen(opts[i].schema);
if (opts[i].long_opt)
width += 1; // for '='
}
if (width > max_width)
max_width = width;
}
return max_width;
}
static void reflow_text(char* dst, const char* src, int width)
{
int line_len = 0;
int last_space_pos = 0;
int dst_len = 0;
int i = 0;
while (src[i]) {
char c = src[i];
if (line_len > width) {
assert(last_space_pos > 0);
dst_len -= i - last_space_pos;
dst[dst_len++] = '\n';
i = last_space_pos + 1;
line_len = 0;
continue;
}
if (c == ' ')
last_space_pos = i;
dst[dst_len++] = c;
++i;
++line_len;
}
dst[dst_len] = '\0';
}
static void format_option(const struct wv_option* opt, int left_col_width,
FILE* stream)
{
assert(opt->help);
int n_chars = fprintf(stream, " ");
if (opt->short_opt)
n_chars += fprintf(stream, "-%c", opt->short_opt);
if (opt->long_opt)
n_chars += fprintf(stream, "%s--%s",
opt->short_opt ? "," : "", opt->long_opt);
if (opt->schema)
n_chars += fprintf(stream, "%s%s",
opt->long_opt ? "=" : "", opt->schema);
n_chars += fprintf(stream, "%*s", left_col_width - n_chars + 8, "");
int right_col_width = 80 - 8 - left_col_width;
assert(right_col_width >= 0);
char help[256];
reflow_text(help, opt->help, right_col_width);
char* line = strtok(help, "\n");
fprintf(stream, "%s\n", line);
while (true) {
line = strtok(NULL, "\n");
if (!line)
break;
fprintf(stream, "%*s%s\n", left_col_width + 8, "", line);
}
}
void option_parser_print_usage(struct option_parser* self, FILE* stream)
{
int left_col_width = get_left_col_width(self->options, self->n_opts);
for (int i = 0; i < self->n_opts; ++i) {
format_option(&self->options[i], left_col_width, stream);
}
}
int option_parser_getopt(struct option_parser* self, int argc, char* argv[])
{
return getopt_long(argc, argv, self->short_opts, self->long_opts, NULL);
}