mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 18:07:59 +00:00
118 lines
3.1 KiB
C
118 lines
3.1 KiB
C
|
/*
|
||
|
* Helper function for matching command-line options in the Windows
|
||
|
* auxiliary tools (PuTTYgen and Pageant).
|
||
|
*
|
||
|
* Supports basically the usual kinds of option, including GNUish
|
||
|
* --foo long options and simple -f short options. But historically
|
||
|
* those tools have also supported long options with a single dash, so
|
||
|
* we don't go full GNU and report those as syntax errors.
|
||
|
*/
|
||
|
|
||
|
#include "putty.h"
|
||
|
|
||
|
/*
|
||
|
* Call this to initialise the state structure.
|
||
|
*/
|
||
|
AuxMatchOpt aux_match_opt_init(int argc, char **argv, int start_index,
|
||
|
aux_opt_error_fn_t opt_error)
|
||
|
{
|
||
|
AuxMatchOpt amo[1];
|
||
|
|
||
|
amo->argc = argc;
|
||
|
amo->argv = argv;
|
||
|
amo->index = start_index;
|
||
|
amo->doing_opts = true;
|
||
|
amo->error = opt_error;
|
||
|
|
||
|
return amo[0];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Call this with a NULL-terminated list of synonymous option names.
|
||
|
* Point 'val' at a char * to receive the option argument, if one is
|
||
|
* expected. Set 'val' to NULL if no argument is expected.
|
||
|
*/
|
||
|
bool aux_match_opt(AuxMatchOpt *amo, char **val, const char *optname, ...)
|
||
|
{
|
||
|
assert(amo->index < amo->argc);
|
||
|
|
||
|
/* Find the end of the option name */
|
||
|
char *opt = amo->argv[amo->index];
|
||
|
ptrlen argopt;
|
||
|
argopt.ptr = opt;
|
||
|
argopt.len = strcspn(opt, "=");
|
||
|
|
||
|
/* Normalise GNU-style --foo long options to the -foo that this
|
||
|
* tool has historically used */
|
||
|
ptrlen argopt2 = make_ptrlen(NULL, 0);
|
||
|
if (ptrlen_startswith(argopt, PTRLEN_LITERAL("--"), NULL))
|
||
|
ptrlen_startswith(argopt, PTRLEN_LITERAL("-"), &argopt2);
|
||
|
|
||
|
/* See if this option matches any of our synonyms */
|
||
|
va_list ap;
|
||
|
va_start(ap, optname);
|
||
|
bool matched = false;
|
||
|
while (optname) {
|
||
|
if (ptrlen_eq_string(argopt, optname)) {
|
||
|
matched = true;
|
||
|
break;
|
||
|
}
|
||
|
if (argopt2.ptr && strlen(optname) > 2 &&
|
||
|
ptrlen_eq_string(argopt2, optname)) {
|
||
|
matched = true;
|
||
|
break;
|
||
|
}
|
||
|
optname = va_arg(ap, const char *);
|
||
|
}
|
||
|
va_end(ap);
|
||
|
if (!matched)
|
||
|
return false;
|
||
|
|
||
|
/* Check for a value */
|
||
|
if (opt[argopt.len]) {
|
||
|
if (!val)
|
||
|
amo->error("option '%s' does not expect a value", opt);
|
||
|
*val = opt + argopt.len + 1;
|
||
|
amo->index++;
|
||
|
} else if (!val) {
|
||
|
amo->index++;
|
||
|
} else {
|
||
|
if (amo->index + 1 >= amo->argc)
|
||
|
amo->error("option '%s' expects a value", opt);
|
||
|
*val = amo->argv[amo->index + 1];
|
||
|
amo->index += 2;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Call this to return a non-option argument in *val.
|
||
|
*/
|
||
|
bool aux_match_arg(AuxMatchOpt *amo, char **val)
|
||
|
{
|
||
|
assert(amo->index < amo->argc);
|
||
|
char *opt = amo->argv[amo->index];
|
||
|
|
||
|
if (!amo->doing_opts || opt[0] != '-' || !strcmp(opt, "-")) {
|
||
|
*val = opt;
|
||
|
amo->index++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* And call this to test whether we're done. Also handles '--'.
|
||
|
*/
|
||
|
bool aux_match_done(AuxMatchOpt *amo)
|
||
|
{
|
||
|
if (amo->index < amo->argc && !strcmp(amo->argv[amo->index], "--")) {
|
||
|
amo->doing_opts = false;
|
||
|
amo->index++;
|
||
|
}
|
||
|
|
||
|
return amo->index >= amo->argc;
|
||
|
}
|