mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-01 18:47:31 +00:00
696 lines
16 KiB
C
696 lines
16 KiB
C
/*
|
|
* Copyright (c), Edward Thomson <ethomson@edwardthomson.com>
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of adopt, distributed under the MIT license.
|
|
* For full terms and conditions, see the included LICENSE file.
|
|
*
|
|
* THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
|
|
*
|
|
* This file was produced by using the `rename.pl` script included with
|
|
* adopt. The command-line specified was:
|
|
*
|
|
* ./rename.pl cli_opt --filename=opt --include=common.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(__sun) || defined(__illumos__)
|
|
# include <alloca.h>
|
|
#endif
|
|
|
|
#include "common.h"
|
|
#include "opt.h"
|
|
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
#else
|
|
# include <fcntl.h>
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
# define alloca _alloca
|
|
#endif
|
|
|
|
#define spec_is_option_type(x) \
|
|
((x)->type == CLI_OPT_TYPE_BOOL || \
|
|
(x)->type == CLI_OPT_TYPE_SWITCH || \
|
|
(x)->type == CLI_OPT_TYPE_VALUE)
|
|
|
|
GIT_INLINE(const cli_opt_spec *) spec_for_long(
|
|
int *is_negated,
|
|
int *has_value,
|
|
const char **value,
|
|
const cli_opt_parser *parser,
|
|
const char *arg)
|
|
{
|
|
const cli_opt_spec *spec;
|
|
char *eql;
|
|
size_t eql_pos;
|
|
|
|
eql = strchr(arg, '=');
|
|
eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg);
|
|
|
|
for (spec = parser->specs; spec->type; ++spec) {
|
|
/* Handle -- (everything after this is literal) */
|
|
if (spec->type == CLI_OPT_TYPE_LITERAL && arg[0] == '\0')
|
|
return spec;
|
|
|
|
/* Handle --no-option arguments for bool types */
|
|
if (spec->type == CLI_OPT_TYPE_BOOL &&
|
|
strncmp(arg, "no-", 3) == 0 &&
|
|
strcmp(arg + 3, spec->name) == 0) {
|
|
*is_negated = 1;
|
|
return spec;
|
|
}
|
|
|
|
/* Handle the typical --option arguments */
|
|
if (spec_is_option_type(spec) &&
|
|
spec->name &&
|
|
strcmp(arg, spec->name) == 0)
|
|
return spec;
|
|
|
|
/* Handle --option=value arguments */
|
|
if (spec->type == CLI_OPT_TYPE_VALUE &&
|
|
spec->name && eql &&
|
|
strncmp(arg, spec->name, eql_pos) == 0 &&
|
|
spec->name[eql_pos] == '\0') {
|
|
*has_value = 1;
|
|
*value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL;
|
|
return spec;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GIT_INLINE(const cli_opt_spec *) spec_for_short(
|
|
const char **value,
|
|
const cli_opt_parser *parser,
|
|
const char *arg)
|
|
{
|
|
const cli_opt_spec *spec;
|
|
|
|
for (spec = parser->specs; spec->type; ++spec) {
|
|
/* Handle -svalue short options with a value */
|
|
if (spec->type == CLI_OPT_TYPE_VALUE &&
|
|
arg[0] == spec->alias &&
|
|
arg[1] != '\0') {
|
|
*value = &arg[1];
|
|
return spec;
|
|
}
|
|
|
|
/* Handle typical -s short options */
|
|
if (arg[0] == spec->alias) {
|
|
*value = NULL;
|
|
return spec;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GIT_INLINE(const cli_opt_spec *) spec_for_arg(cli_opt_parser *parser)
|
|
{
|
|
const cli_opt_spec *spec;
|
|
size_t args = 0;
|
|
|
|
for (spec = parser->specs; spec->type; ++spec) {
|
|
if (spec->type == CLI_OPT_TYPE_ARG) {
|
|
if (args == parser->arg_idx) {
|
|
parser->arg_idx++;
|
|
return spec;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (spec->type == CLI_OPT_TYPE_ARGS && args == parser->arg_idx)
|
|
return spec;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GIT_INLINE(int) spec_is_choice(const cli_opt_spec *spec)
|
|
{
|
|
return ((spec + 1)->type &&
|
|
((spec + 1)->usage & CLI_OPT_USAGE_CHOICE));
|
|
}
|
|
|
|
/*
|
|
* If we have a choice with switches and bare arguments, and we see
|
|
* the switch, then we no longer expect the bare argument.
|
|
*/
|
|
GIT_INLINE(void) consume_choices(const cli_opt_spec *spec, cli_opt_parser *parser)
|
|
{
|
|
/* back up to the beginning of the choices */
|
|
while (spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE))
|
|
--spec;
|
|
|
|
if (!spec_is_choice(spec))
|
|
return;
|
|
|
|
do {
|
|
if (spec->type == CLI_OPT_TYPE_ARG)
|
|
parser->arg_idx++;
|
|
++spec;
|
|
} while(spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE));
|
|
}
|
|
|
|
static cli_opt_status_t parse_long(cli_opt *opt, cli_opt_parser *parser)
|
|
{
|
|
const cli_opt_spec *spec;
|
|
char *arg = parser->args[parser->idx++];
|
|
const char *value = NULL;
|
|
int is_negated = 0, has_value = 0;
|
|
|
|
opt->arg = arg;
|
|
|
|
if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) {
|
|
opt->spec = NULL;
|
|
opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
|
|
goto done;
|
|
}
|
|
|
|
opt->spec = spec;
|
|
|
|
/* Future options parsed as literal */
|
|
if (spec->type == CLI_OPT_TYPE_LITERAL)
|
|
parser->in_literal = 1;
|
|
|
|
/* --bool or --no-bool */
|
|
else if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
|
|
*((int *)spec->value) = !is_negated;
|
|
|
|
/* --accumulate */
|
|
else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
|
|
*((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
|
|
|
|
/* --switch */
|
|
else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
|
|
*((int *)spec->value) = spec->switch_value;
|
|
|
|
/* Parse values as "--foo=bar" or "--foo bar" */
|
|
else if (spec->type == CLI_OPT_TYPE_VALUE) {
|
|
if (has_value)
|
|
opt->value = (char *)value;
|
|
else if ((parser->idx + 1) <= parser->args_len)
|
|
opt->value = parser->args[parser->idx++];
|
|
|
|
if (spec->value)
|
|
*((char **)spec->value) = opt->value;
|
|
}
|
|
|
|
/* Required argument was not provided */
|
|
if (spec->type == CLI_OPT_TYPE_VALUE &&
|
|
!opt->value &&
|
|
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
|
|
opt->status = CLI_OPT_STATUS_MISSING_VALUE;
|
|
else
|
|
opt->status = CLI_OPT_STATUS_OK;
|
|
|
|
consume_choices(opt->spec, parser);
|
|
|
|
done:
|
|
return opt->status;
|
|
}
|
|
|
|
static cli_opt_status_t parse_short(cli_opt *opt, cli_opt_parser *parser)
|
|
{
|
|
const cli_opt_spec *spec;
|
|
char *arg = parser->args[parser->idx++];
|
|
const char *value;
|
|
|
|
opt->arg = arg;
|
|
|
|
if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) {
|
|
opt->spec = NULL;
|
|
opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
|
|
goto done;
|
|
}
|
|
|
|
opt->spec = spec;
|
|
|
|
if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
|
|
*((int *)spec->value) = 1;
|
|
|
|
else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
|
|
*((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
|
|
|
|
else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
|
|
*((int *)spec->value) = spec->switch_value;
|
|
|
|
/* Parse values as "-ifoo" or "-i foo" */
|
|
else if (spec->type == CLI_OPT_TYPE_VALUE) {
|
|
if (value)
|
|
opt->value = (char *)value;
|
|
else if ((parser->idx + 1) <= parser->args_len)
|
|
opt->value = parser->args[parser->idx++];
|
|
|
|
if (spec->value)
|
|
*((char **)spec->value) = opt->value;
|
|
}
|
|
|
|
/*
|
|
* Handle compressed short arguments, like "-fbcd"; see if there's
|
|
* another character after the one we processed. If not, advance
|
|
* the parser index.
|
|
*/
|
|
if (spec->type != CLI_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') {
|
|
parser->in_short++;
|
|
parser->idx--;
|
|
} else {
|
|
parser->in_short = 0;
|
|
}
|
|
|
|
/* Required argument was not provided */
|
|
if (spec->type == CLI_OPT_TYPE_VALUE && !opt->value)
|
|
opt->status = CLI_OPT_STATUS_MISSING_VALUE;
|
|
else
|
|
opt->status = CLI_OPT_STATUS_OK;
|
|
|
|
consume_choices(opt->spec, parser);
|
|
|
|
done:
|
|
return opt->status;
|
|
}
|
|
|
|
static cli_opt_status_t parse_arg(cli_opt *opt, cli_opt_parser *parser)
|
|
{
|
|
const cli_opt_spec *spec = spec_for_arg(parser);
|
|
|
|
opt->spec = spec;
|
|
opt->arg = parser->args[parser->idx];
|
|
|
|
if (!spec) {
|
|
parser->idx++;
|
|
opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
|
|
} else if (spec->type == CLI_OPT_TYPE_ARGS) {
|
|
if (spec->value)
|
|
*((char ***)spec->value) = &parser->args[parser->idx];
|
|
|
|
/*
|
|
* We have started a list of arguments; the remainder of
|
|
* given arguments need not be examined.
|
|
*/
|
|
parser->in_args = (parser->args_len - parser->idx);
|
|
parser->idx = parser->args_len;
|
|
opt->args_len = parser->in_args;
|
|
opt->status = CLI_OPT_STATUS_OK;
|
|
} else {
|
|
if (spec->value)
|
|
*((char **)spec->value) = parser->args[parser->idx];
|
|
|
|
parser->idx++;
|
|
opt->status = CLI_OPT_STATUS_OK;
|
|
}
|
|
|
|
return opt->status;
|
|
}
|
|
|
|
static int support_gnu_style(unsigned int flags)
|
|
{
|
|
if ((flags & CLI_OPT_PARSE_FORCE_GNU) != 0)
|
|
return 1;
|
|
|
|
if ((flags & CLI_OPT_PARSE_GNU) == 0)
|
|
return 0;
|
|
|
|
/* TODO: Windows */
|
|
#if defined(_WIN32) && defined(UNICODE)
|
|
if (_wgetenv(L"POSIXLY_CORRECT") != NULL)
|
|
return 0;
|
|
#else
|
|
if (getenv("POSIXLY_CORRECT") != NULL)
|
|
return 0;
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
void cli_opt_parser_init(
|
|
cli_opt_parser *parser,
|
|
const cli_opt_spec specs[],
|
|
char **args,
|
|
size_t args_len,
|
|
unsigned int flags)
|
|
{
|
|
assert(parser);
|
|
|
|
memset(parser, 0x0, sizeof(cli_opt_parser));
|
|
|
|
parser->specs = specs;
|
|
parser->args = args;
|
|
parser->args_len = args_len;
|
|
parser->flags = flags;
|
|
|
|
parser->needs_sort = support_gnu_style(flags);
|
|
}
|
|
|
|
GIT_INLINE(const cli_opt_spec *) spec_for_sort(
|
|
int *needs_value,
|
|
const cli_opt_parser *parser,
|
|
const char *arg)
|
|
{
|
|
int is_negated, has_value = 0;
|
|
const char *value;
|
|
const cli_opt_spec *spec = NULL;
|
|
size_t idx = 0;
|
|
|
|
*needs_value = 0;
|
|
|
|
if (strncmp(arg, "--", 2) == 0) {
|
|
spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]);
|
|
*needs_value = !has_value;
|
|
}
|
|
|
|
else if (strncmp(arg, "-", 1) == 0) {
|
|
spec = spec_for_short(&value, parser, &arg[1]);
|
|
|
|
/*
|
|
* Advance through compressed short arguments to see if
|
|
* the last one has a value, eg "-xvffilename".
|
|
*/
|
|
while (spec && !value && arg[1 + ++idx] != '\0')
|
|
spec = spec_for_short(&value, parser, &arg[1 + idx]);
|
|
|
|
*needs_value = (value == NULL);
|
|
}
|
|
|
|
return spec;
|
|
}
|
|
|
|
/*
|
|
* Some parsers allow for handling arguments like "file1 --help file2";
|
|
* this is done by re-sorting the arguments in-place; emulate that.
|
|
*/
|
|
static int sort_gnu_style(cli_opt_parser *parser)
|
|
{
|
|
size_t i, j, insert_idx = parser->idx, offset;
|
|
const cli_opt_spec *spec;
|
|
char *option, *value;
|
|
int needs_value, changed = 0;
|
|
|
|
parser->needs_sort = 0;
|
|
|
|
for (i = parser->idx; i < parser->args_len; i++) {
|
|
spec = spec_for_sort(&needs_value, parser, parser->args[i]);
|
|
|
|
/* Not a "-" or "--" prefixed option. No change. */
|
|
if (!spec)
|
|
continue;
|
|
|
|
/* A "--" alone means remaining args are literal. */
|
|
if (spec->type == CLI_OPT_TYPE_LITERAL)
|
|
break;
|
|
|
|
option = parser->args[i];
|
|
|
|
/*
|
|
* If the argument is a value type and doesn't already
|
|
* have a value (eg "--foo=bar" or "-fbar") then we need
|
|
* to copy the next argument as its value.
|
|
*/
|
|
if (spec->type == CLI_OPT_TYPE_VALUE && needs_value) {
|
|
/*
|
|
* A required value is not provided; set parser
|
|
* index to this value so that we fail on it.
|
|
*/
|
|
if (i + 1 >= parser->args_len) {
|
|
parser->idx = i;
|
|
return 1;
|
|
}
|
|
|
|
value = parser->args[i + 1];
|
|
offset = 1;
|
|
} else {
|
|
value = NULL;
|
|
offset = 0;
|
|
}
|
|
|
|
/* Caller error if args[0] is an option. */
|
|
if (i == 0)
|
|
return 0;
|
|
|
|
/* Shift args up one (or two) and insert the option */
|
|
for (j = i; j > insert_idx; j--)
|
|
parser->args[j + offset] = parser->args[j - 1];
|
|
|
|
parser->args[insert_idx] = option;
|
|
|
|
if (value)
|
|
parser->args[insert_idx + 1] = value;
|
|
|
|
insert_idx += (1 + offset);
|
|
i += offset;
|
|
|
|
changed = 1;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
cli_opt_status_t cli_opt_parser_next(cli_opt *opt, cli_opt_parser *parser)
|
|
{
|
|
assert(opt && parser);
|
|
|
|
memset(opt, 0x0, sizeof(cli_opt));
|
|
|
|
if (parser->idx >= parser->args_len) {
|
|
opt->args_len = parser->in_args;
|
|
return CLI_OPT_STATUS_DONE;
|
|
}
|
|
|
|
/* Handle options in long form, those beginning with "--" */
|
|
if (strncmp(parser->args[parser->idx], "--", 2) == 0 &&
|
|
!parser->in_short &&
|
|
!parser->in_literal)
|
|
return parse_long(opt, parser);
|
|
|
|
/* Handle options in short form, those beginning with "-" */
|
|
else if (parser->in_short ||
|
|
(strncmp(parser->args[parser->idx], "-", 1) == 0 &&
|
|
!parser->in_literal))
|
|
return parse_short(opt, parser);
|
|
|
|
/*
|
|
* We've reached the first "bare" argument. In POSIX mode, all
|
|
* remaining items on the command line are arguments. In GNU
|
|
* mode, there may be long or short options after this. Sort any
|
|
* options up to this position then re-parse the current position.
|
|
*/
|
|
if (parser->needs_sort && sort_gnu_style(parser))
|
|
return cli_opt_parser_next(opt, parser);
|
|
|
|
return parse_arg(opt, parser);
|
|
}
|
|
|
|
GIT_INLINE(int) spec_included(const cli_opt_spec **specs, const cli_opt_spec *spec)
|
|
{
|
|
const cli_opt_spec **i;
|
|
|
|
for (i = specs; *i; ++i) {
|
|
if (spec == *i)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static cli_opt_status_t validate_required(
|
|
cli_opt *opt,
|
|
const cli_opt_spec specs[],
|
|
const cli_opt_spec **given_specs)
|
|
{
|
|
const cli_opt_spec *spec, *required;
|
|
int given;
|
|
|
|
/*
|
|
* Iterate over the possible specs to identify requirements and
|
|
* ensure that those have been given on the command-line.
|
|
* Note that we can have required *choices*, where one in a
|
|
* list of choices must be specified.
|
|
*/
|
|
for (spec = specs, required = NULL, given = 0; spec->type; ++spec) {
|
|
if (!required && (spec->usage & CLI_OPT_USAGE_REQUIRED)) {
|
|
required = spec;
|
|
given = 0;
|
|
} else if (!required) {
|
|
continue;
|
|
}
|
|
|
|
if (!given)
|
|
given = spec_included(given_specs, spec);
|
|
|
|
/*
|
|
* Validate the requirement unless we're in a required
|
|
* choice. In that case, keep the required state and
|
|
* validate at the end of the choice list.
|
|
*/
|
|
if (!spec_is_choice(spec)) {
|
|
if (!given) {
|
|
opt->spec = required;
|
|
opt->status = CLI_OPT_STATUS_MISSING_ARGUMENT;
|
|
break;
|
|
}
|
|
|
|
required = NULL;
|
|
given = 0;
|
|
}
|
|
}
|
|
|
|
return opt->status;
|
|
}
|
|
|
|
cli_opt_status_t cli_opt_parse(
|
|
cli_opt *opt,
|
|
const cli_opt_spec specs[],
|
|
char **args,
|
|
size_t args_len,
|
|
unsigned int flags)
|
|
{
|
|
cli_opt_parser parser;
|
|
const cli_opt_spec **given_specs;
|
|
size_t given_idx = 0;
|
|
|
|
cli_opt_parser_init(&parser, specs, args, args_len, flags);
|
|
|
|
given_specs = alloca(sizeof(const cli_opt_spec *) * (args_len + 1));
|
|
|
|
while (cli_opt_parser_next(opt, &parser)) {
|
|
if (opt->status != CLI_OPT_STATUS_OK &&
|
|
opt->status != CLI_OPT_STATUS_DONE)
|
|
return opt->status;
|
|
|
|
if ((opt->spec->usage & CLI_OPT_USAGE_STOP_PARSING))
|
|
return (opt->status = CLI_OPT_STATUS_DONE);
|
|
|
|
given_specs[given_idx++] = opt->spec;
|
|
}
|
|
|
|
given_specs[given_idx] = NULL;
|
|
|
|
return validate_required(opt, specs, given_specs);
|
|
}
|
|
|
|
int cli_opt_foreach(
|
|
const cli_opt_spec specs[],
|
|
char **args,
|
|
size_t args_len,
|
|
unsigned int flags,
|
|
int (*callback)(cli_opt *, void *),
|
|
void *callback_data)
|
|
{
|
|
cli_opt_parser parser;
|
|
cli_opt opt;
|
|
int ret;
|
|
|
|
cli_opt_parser_init(&parser, specs, args, args_len, flags);
|
|
|
|
while (cli_opt_parser_next(&opt, &parser)) {
|
|
if ((ret = callback(&opt, callback_data)) != 0)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int spec_name_fprint(FILE *file, const cli_opt_spec *spec)
|
|
{
|
|
int error;
|
|
|
|
if (spec->type == CLI_OPT_TYPE_ARG)
|
|
error = fprintf(file, "%s", spec->value_name);
|
|
else if (spec->type == CLI_OPT_TYPE_ARGS)
|
|
error = fprintf(file, "%s", spec->value_name);
|
|
else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
|
|
error = fprintf(file, "-%c", spec->alias);
|
|
else
|
|
error = fprintf(file, "--%s", spec->name);
|
|
|
|
return error;
|
|
}
|
|
|
|
int cli_opt_status_fprint(
|
|
FILE *file,
|
|
const char *command,
|
|
const cli_opt *opt)
|
|
{
|
|
const cli_opt_spec *choice;
|
|
int error;
|
|
|
|
if (command && (error = fprintf(file, "%s: ", command)) < 0)
|
|
return error;
|
|
|
|
switch (opt->status) {
|
|
case CLI_OPT_STATUS_DONE:
|
|
error = fprintf(file, "finished processing arguments (no error)\n");
|
|
break;
|
|
case CLI_OPT_STATUS_OK:
|
|
error = fprintf(file, "no error\n");
|
|
break;
|
|
case CLI_OPT_STATUS_UNKNOWN_OPTION:
|
|
error = fprintf(file, "unknown option: %s\n", opt->arg);
|
|
break;
|
|
case CLI_OPT_STATUS_MISSING_VALUE:
|
|
if ((error = fprintf(file, "argument '")) < 0 ||
|
|
(error = spec_name_fprint(file, opt->spec)) < 0 ||
|
|
(error = fprintf(file, "' requires a value.\n")) < 0)
|
|
break;
|
|
break;
|
|
case CLI_OPT_STATUS_MISSING_ARGUMENT:
|
|
if (spec_is_choice(opt->spec)) {
|
|
int is_choice = 1;
|
|
|
|
if (spec_is_choice((opt->spec)+1))
|
|
error = fprintf(file, "one of");
|
|
else
|
|
error = fprintf(file, "either");
|
|
|
|
if (error < 0)
|
|
break;
|
|
|
|
for (choice = opt->spec; is_choice; ++choice) {
|
|
is_choice = spec_is_choice(choice);
|
|
|
|
if (!is_choice)
|
|
error = fprintf(file, " or");
|
|
else if (choice != opt->spec)
|
|
error = fprintf(file, ",");
|
|
|
|
if ((error < 0) ||
|
|
(error = fprintf(file, " '")) < 0 ||
|
|
(error = spec_name_fprint(file, choice)) < 0 ||
|
|
(error = fprintf(file, "'")) < 0)
|
|
break;
|
|
|
|
if (!spec_is_choice(choice))
|
|
break;
|
|
}
|
|
|
|
if ((error < 0) ||
|
|
(error = fprintf(file, " is required.\n")) < 0)
|
|
break;
|
|
} else {
|
|
if ((error = fprintf(file, "argument '")) < 0 ||
|
|
(error = spec_name_fprint(file, opt->spec)) < 0 ||
|
|
(error = fprintf(file, "' is required.\n")) < 0)
|
|
break;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
error = fprintf(file, "unknown status: %d\n", opt->status);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|