585 lines
16 KiB
C
585 lines
16 KiB
C
|
/**
|
||
|
* XOpt - command line parsing library
|
||
|
*
|
||
|
* Copyright (c) 2015-2019 Josh Junon
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
* THE SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#ifndef XOPT_NOSTANDARD
|
||
|
# define HAVE_STDARG_H 1
|
||
|
# define HAVE_STDLIB_H 1
|
||
|
# define HAVE_ASPRINTF_H 1
|
||
|
# define vasprintf rpl_vasprintf
|
||
|
# ifndef _GNU_SOURCE
|
||
|
# define _GNU_SOURCE
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <setjmp.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "./xopt.h"
|
||
|
#include "./snprintf.c"
|
||
|
|
||
|
#define EXTRAS_INIT 10
|
||
|
#define ERRBUF_SIZE 1024 * 4
|
||
|
|
||
|
static char errbuf[ERRBUF_SIZE];
|
||
|
|
||
|
struct xoptContext {
|
||
|
const xoptOption *options;
|
||
|
long flags;
|
||
|
const char *name;
|
||
|
bool doubledash;
|
||
|
size_t options_count;
|
||
|
bool *required;
|
||
|
jmp_buf *jmp;
|
||
|
};
|
||
|
|
||
|
static void _xopt_set_err(xoptContext *ctx, const char **err, const char *const fmt, ...);
|
||
|
static int _xopt_parse_arg(xoptContext *ctx, int argc, const char **argv,
|
||
|
int *argi, void *data, const char **err);
|
||
|
static void _xopt_assert_increment(xoptContext *ctx, const char ***extras, int extrasCount,
|
||
|
size_t *extrasCapac, const char **err);
|
||
|
static int _xopt_get_size(const char *arg);
|
||
|
static int _xopt_get_arg(const xoptContext *ctx, const char *arg, size_t len,
|
||
|
int size, const xoptOption **option, size_t *option_index);
|
||
|
static void _xopt_set(xoptContext *ctx, void *data, const xoptOption *option, const char *val,
|
||
|
bool longArg, const char **err);
|
||
|
static void _xopt_default_callback(const char *value, void *data,
|
||
|
const xoptOption *option, bool longArg, const char **err);
|
||
|
|
||
|
xoptContext* xopt_context(const char *name, const xoptOption *options, long flags,
|
||
|
const char **err) {
|
||
|
xoptContext* ctx;
|
||
|
*err = 0;
|
||
|
|
||
|
/* malloc context and check */
|
||
|
ctx = malloc(sizeof(xoptContext));
|
||
|
if (!ctx) {
|
||
|
ctx = 0;
|
||
|
_xopt_set_err(NULL, err, "could not allocate context");
|
||
|
} else {
|
||
|
const xoptOption *cur;
|
||
|
|
||
|
ctx->options = options;
|
||
|
ctx->flags = flags;
|
||
|
ctx->name = name;
|
||
|
ctx->doubledash = false;
|
||
|
ctx->required = NULL;
|
||
|
ctx->jmp = NULL;
|
||
|
|
||
|
ctx->options_count = 0;
|
||
|
cur = options;
|
||
|
for (; cur->longArg || cur->shortArg; cur++) ++ctx->options_count;
|
||
|
}
|
||
|
|
||
|
return ctx;
|
||
|
}
|
||
|
|
||
|
static int _xopt_parse_impl(xoptContext *ctx, int argc, const char **argv, void *data,
|
||
|
const char ***inextras, const char **err, int *extrasCount, size_t *extrasCapac,
|
||
|
const char ***extras, int *argi) {
|
||
|
int parseResult;
|
||
|
size_t i;
|
||
|
|
||
|
*err = 0;
|
||
|
*argi = 0;
|
||
|
*extrasCount = 0;
|
||
|
*extrasCapac = EXTRAS_INIT;
|
||
|
*extras = malloc(sizeof(**extras) * EXTRAS_INIT);
|
||
|
|
||
|
jmp_buf jmp;
|
||
|
ctx->jmp = &jmp;
|
||
|
if (setjmp(jmp)) {
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
/* check if extras malloc'd okay */
|
||
|
if (!*extras) {
|
||
|
_xopt_set_err(ctx, err, "could not allocate extras array");
|
||
|
}
|
||
|
|
||
|
/* increment argument counter if we aren't
|
||
|
instructed to check argv[0] */
|
||
|
if (!(ctx->flags & XOPT_CTX_KEEPFIRST)) {
|
||
|
++(*argi);
|
||
|
}
|
||
|
|
||
|
/* set up required parameters list */
|
||
|
ctx->required = malloc(sizeof(*ctx->required) * ctx->options_count);
|
||
|
for (i = 0; i < ctx->options_count; i++) {
|
||
|
ctx->required[i] = (ctx->options[i].options & XOPT_REQUIRED) > 0;
|
||
|
}
|
||
|
|
||
|
/* iterate over passed command line arguments */
|
||
|
for (; *argi < argc; (*argi)++) {
|
||
|
/* parse, breaking if there was a failure
|
||
|
parseResult is 0 if option, 1 if extra, or 2 if double-dash was encountered */
|
||
|
parseResult = _xopt_parse_arg(ctx, argc, argv, argi, data, err);
|
||
|
|
||
|
/* is the argument an extra? */
|
||
|
switch (parseResult) {
|
||
|
case 0: /* option */
|
||
|
/* make sure we're super-posix'd if specified to be
|
||
|
(check that no extras have been specified when an option is parsed,
|
||
|
enforcing options to be specific before [extra] arguments */
|
||
|
if ((ctx->flags & XOPT_CTX_POSIXMEHARDER) && *extrasCount) {
|
||
|
_xopt_set_err(ctx, err, "options cannot be specified after arguments: %s", argv[*argi]);
|
||
|
goto end;
|
||
|
}
|
||
|
break;
|
||
|
case 1: /* extra */
|
||
|
/* make sure we have enough room, or realloc if we don't -
|
||
|
check that it succeeded */
|
||
|
_xopt_assert_increment(ctx, extras, *extrasCount, extrasCapac, err);
|
||
|
|
||
|
/* add extra to list */
|
||
|
(*extras)[(*extrasCount)++] = argv[*argi];
|
||
|
break;
|
||
|
case 2: /* "--" was encountered */
|
||
|
/* nothing to do here - "--" was already handled for us */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
end:
|
||
|
if (!*err) {
|
||
|
for (i = 0; i < ctx->options_count; i++) {
|
||
|
if (ctx->required[i]) {
|
||
|
const xoptOption *opt = &ctx->options[i];
|
||
|
if (opt->longArg) {
|
||
|
_xopt_set_err(ctx, err, "missing required option: --%s", opt->longArg);
|
||
|
} else {
|
||
|
_xopt_set_err(ctx, err, "missing required option: -%c", opt->shortArg);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(ctx->required);
|
||
|
|
||
|
if (!*err) {
|
||
|
/* append null terminator to extras */
|
||
|
_xopt_assert_increment(ctx, extras, *extrasCount, extrasCapac, err);
|
||
|
if (!*err) {
|
||
|
(*extras)[*extrasCount] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (*err) {
|
||
|
free(*extras);
|
||
|
*inextras = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
*inextras = *extras;
|
||
|
return *extrasCount;
|
||
|
}
|
||
|
|
||
|
int xopt_parse(xoptContext *ctx, int argc, const char **argv, void *data,
|
||
|
const char ***inextras, const char **err) {
|
||
|
/* avoid longjmp clobbering */
|
||
|
int extrasCount;
|
||
|
size_t extrasCapac;
|
||
|
const char **extras;
|
||
|
int argi;
|
||
|
return _xopt_parse_impl(ctx, argc, argv, data, inextras, err, &extrasCount, &extrasCapac, &extras, &argi);
|
||
|
}
|
||
|
|
||
|
void xopt_autohelp(xoptContext *ctx, FILE *stream, const xoptAutohelpOptions *options,
|
||
|
const char **err) {
|
||
|
const xoptOption *o;
|
||
|
size_t i, width = 0, twidth;
|
||
|
const char *nl = "";
|
||
|
size_t spacer = options ? options->spacer : 2;
|
||
|
|
||
|
*err = 0;
|
||
|
|
||
|
/* make sure that if we ever write a call to _set_err() in the future here,
|
||
|
that we won't accidentally cause segfaults - we have an assertion in place
|
||
|
for ctx->jmp != NULL, so we make sure we'd trigger that assertion */
|
||
|
ctx->jmp = NULL;
|
||
|
|
||
|
if (options && options->usage) {
|
||
|
fprintf(stream, "%susage: %s %s\n", nl, ctx->name, options->usage);
|
||
|
nl = "\n";
|
||
|
}
|
||
|
|
||
|
if (options && options->prefix) {
|
||
|
fprintf(stream, "%s%s\n\n", nl, options->prefix);
|
||
|
nl = "\n";
|
||
|
}
|
||
|
|
||
|
/* find max width */
|
||
|
for (i = 0; ctx->options[i].longArg || ctx->options[i].shortArg; i++) {
|
||
|
o = &ctx->options[i];
|
||
|
twidth = 0;
|
||
|
if (o->longArg) {
|
||
|
twidth += 2 + strlen(o->longArg);
|
||
|
if (o->argDescrip) {
|
||
|
twidth += 1 + strlen(o->argDescrip);
|
||
|
}
|
||
|
}
|
||
|
if (ctx->options[i].shortArg) {
|
||
|
twidth += 2;
|
||
|
}
|
||
|
if (ctx->options[i].shortArg && ctx->options[i].longArg) {
|
||
|
twidth += 2; /* `, ` */
|
||
|
}
|
||
|
|
||
|
width = width > twidth ? width : twidth;
|
||
|
}
|
||
|
|
||
|
/* print */
|
||
|
for (i = 0; ctx->options[i].longArg || ctx->options[i].shortArg; i++) {
|
||
|
o = &ctx->options[i];
|
||
|
twidth = 0;
|
||
|
if (o->shortArg) {
|
||
|
fprintf(stream, "-%c", o->shortArg);
|
||
|
twidth += 2;
|
||
|
}
|
||
|
|
||
|
if (o->shortArg && o->longArg) {
|
||
|
fprintf(stream, ", ");
|
||
|
twidth += 2;
|
||
|
}
|
||
|
|
||
|
if (o->longArg) {
|
||
|
fprintf(stream, "--%s", o->longArg);
|
||
|
twidth += 2 + strlen(o->longArg);
|
||
|
if (o->argDescrip) {
|
||
|
fprintf(stream, "=%s", o->argDescrip);
|
||
|
twidth += 1 + strlen(o->argDescrip);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (o->descrip) {
|
||
|
for (; twidth < (width + spacer); twidth++) {
|
||
|
fprintf(stream, " ");
|
||
|
}
|
||
|
|
||
|
if (o->options & XOPT_REQUIRED) {
|
||
|
fprintf(stream, "(Required) %s\n", o->descrip);
|
||
|
} else {
|
||
|
fprintf(stream, "%s\n", o->descrip);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (options && options->suffix) {
|
||
|
fprintf(stream, "%s%s\n", nl, options->suffix);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void _xopt_set_err(xoptContext *ctx, const char **err, const char *const fmt, ...) {
|
||
|
va_list list;
|
||
|
va_start(list, fmt);
|
||
|
rpl_vsnprintf(&errbuf[0], ERRBUF_SIZE, fmt, list);
|
||
|
va_end(list);
|
||
|
*err = &errbuf[0];
|
||
|
|
||
|
if (ctx != NULL) {
|
||
|
assert(ctx->jmp != NULL);
|
||
|
longjmp(*ctx->jmp, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int _xopt_parse_arg(xoptContext *ctx, int argc, const char **argv,
|
||
|
int *argi, void *data, const char **err) {
|
||
|
int size;
|
||
|
size_t length;
|
||
|
bool isExtra = false;
|
||
|
const xoptOption *option = NULL;
|
||
|
size_t option_index = 0;
|
||
|
const char* arg = argv[*argi];
|
||
|
|
||
|
/* are we in doubledash mode? */
|
||
|
if (ctx->doubledash) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* get argument 'size' (long/short/extra) */
|
||
|
size = _xopt_get_size(arg);
|
||
|
|
||
|
/* adjust to parse from beginning of actual content */
|
||
|
arg += size;
|
||
|
length = strlen(arg);
|
||
|
|
||
|
if (size == 1 && length == 0) {
|
||
|
/* it's just a singular dash - treat it as an extra arg */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (size == 2 && length == 0) {
|
||
|
/* double-dash - everything after this is an extra */
|
||
|
ctx->doubledash = 1;
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
switch (size) {
|
||
|
int argRequirement;
|
||
|
char *valStart;
|
||
|
case 1: /* short */
|
||
|
/* parse all */
|
||
|
while (length--) {
|
||
|
/* get argument or error if not found and strict mode enabled. */
|
||
|
argRequirement = _xopt_get_arg(ctx, arg++, 1, size, &option, &option_index);
|
||
|
if (!option) {
|
||
|
if (ctx->flags & XOPT_CTX_STRICT) {
|
||
|
_xopt_set_err(ctx, err, "invalid option: -%c", arg[-1]);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (argRequirement > 0 && length > 0 && !(ctx->flags & XOPT_CTX_SLOPPYSHORTS)) {
|
||
|
_xopt_set_err(ctx, err, "short option parameters must be separated, not condensed: %s", argv[*argi]);
|
||
|
}
|
||
|
|
||
|
switch (argRequirement) {
|
||
|
case 0: /* flag; doesn't take an argument */
|
||
|
if (length > 0 && (ctx->flags & XOPT_CTX_NOCONDENSE)) {
|
||
|
_xopt_set_err(ctx, err, "short options cannot be combined: %s", argv[*argi]);
|
||
|
}
|
||
|
|
||
|
_xopt_set(ctx, data, option, 0, false, err);
|
||
|
break;
|
||
|
case 1: /* argument is optional */
|
||
|
/* is there another argument, and is it a non-option? */
|
||
|
if (*argi + 1 < argc && _xopt_get_size(argv[*argi + 1]) == 0) {
|
||
|
_xopt_set(ctx, data, option, argv[++*argi], false, err);
|
||
|
} else {
|
||
|
_xopt_set(ctx, data, option, 0, false, err);
|
||
|
}
|
||
|
break;
|
||
|
case 2: /* requires an argument */
|
||
|
/* is it the last in a set of condensed options? */
|
||
|
if (length == 0) {
|
||
|
/* is there another argument? */
|
||
|
if (*argi + 1 < argc) {
|
||
|
/* is the next argument actually an option?
|
||
|
this indicates no value was passed */
|
||
|
if (_xopt_get_size(argv[*argi + 1])) {
|
||
|
_xopt_set_err(ctx, err, "missing option value: -%c",
|
||
|
option->shortArg);
|
||
|
} else {
|
||
|
_xopt_set(ctx, data, option, argv[++*argi], false, err);
|
||
|
}
|
||
|
} else {
|
||
|
_xopt_set_err(ctx, err, "missing option value: -%c",
|
||
|
option->shortArg);
|
||
|
}
|
||
|
} else {
|
||
|
_xopt_set(ctx, data, option, arg, false, err);
|
||
|
length = 0;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 2: /* long */
|
||
|
/* find first equals sign */
|
||
|
valStart = strchr(arg, '=');
|
||
|
|
||
|
/* is there a value? */
|
||
|
if (valStart) {
|
||
|
/* we also increase valStart here in order to lop off
|
||
|
the equals sign */
|
||
|
length = valStart++ - arg;
|
||
|
|
||
|
/* but not really, if it's null */
|
||
|
if (!*valStart) {
|
||
|
valStart = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* get the option */
|
||
|
argRequirement = _xopt_get_arg(ctx, arg, length, size, &option, &option_index);
|
||
|
if (!option) {
|
||
|
_xopt_set_err(ctx, err, "invalid option: --%.*s", length, arg);
|
||
|
} else {
|
||
|
switch (argRequirement) {
|
||
|
case 0: /* flag; doesn't take an argument */
|
||
|
if (valStart) {
|
||
|
_xopt_set_err(ctx, err, "option doesn't take a value: --%s", arg);
|
||
|
}
|
||
|
|
||
|
_xopt_set(ctx, data, option, valStart, true, err);
|
||
|
break;
|
||
|
case 2: /* requires an argument */
|
||
|
if (!valStart) {
|
||
|
_xopt_set_err(ctx, err, "missing option value: --%s", arg);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
_xopt_set(ctx, data, option, valStart, true, err);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
case 0: /* extra */
|
||
|
isExtra = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (option) {
|
||
|
/* indicate that we've seen this option and thus is no longer required */
|
||
|
ctx->required[option_index] = false;
|
||
|
}
|
||
|
|
||
|
return isExtra ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
static void _xopt_assert_increment(xoptContext *ctx, const char ***extras, int extrasCount,
|
||
|
size_t *extrasCapac, const char **err) {
|
||
|
/* have we hit the list size limit? */
|
||
|
if ((size_t) extrasCount == *extrasCapac) {
|
||
|
/* increase capcity, realloc, and check for success */
|
||
|
*extrasCapac += EXTRAS_INIT;
|
||
|
*extras = realloc(*extras, sizeof(**extras) * *extrasCapac);
|
||
|
if (!*extras) {
|
||
|
_xopt_set_err(ctx, err, "could not realloc arguments array");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int _xopt_get_size(const char *arg) {
|
||
|
int size;
|
||
|
for (size = 0; size < 2; size++) {
|
||
|
if (arg[size] != '-') {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static int _xopt_get_arg(const xoptContext *ctx, const char *arg, size_t len,
|
||
|
int size, const xoptOption **option, size_t *option_index) {
|
||
|
size_t i;
|
||
|
|
||
|
*option = 0;
|
||
|
|
||
|
/* find the argument */
|
||
|
for (i = 0; i < ctx->options_count; i++) {
|
||
|
const xoptOption *opt = &ctx->options[i];
|
||
|
|
||
|
if ((size == 1 && opt->shortArg == arg[0])
|
||
|
|| (opt->longArg && strlen(opt->longArg) == len && !strncmp(opt->longArg, arg, len))) {
|
||
|
*option_index = i;
|
||
|
*option = opt;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* determine the optionality of a value */
|
||
|
if (!*option || (*option)->options & XOPT_TYPE_BOOL) {
|
||
|
return 0;
|
||
|
} else if ((*option)->options & XOPT_PARAM_OPTIONAL) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void _xopt_set(xoptContext *ctx, void *data, const xoptOption *option, const char *val,
|
||
|
bool longArg, const char **err) {
|
||
|
/* determine callback */
|
||
|
xoptCallback callback = option->callback ? option->callback : &_xopt_default_callback;
|
||
|
|
||
|
/* dispatch callback */
|
||
|
callback(val, data, option, longArg, err);
|
||
|
|
||
|
/* we check err here instead of relying upon longjmp()
|
||
|
since we can't call _set_err() with a context */
|
||
|
if (*err) {
|
||
|
assert(ctx->jmp != NULL);
|
||
|
longjmp(*ctx->jmp, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void _xopt_default_callback(const char *value, void *data,
|
||
|
const xoptOption *option, bool longArg, const char **err) {
|
||
|
void *target;
|
||
|
char *parsePtr = 0;
|
||
|
|
||
|
/* is a value specified? */
|
||
|
if ((!value || !strlen(value)) && !(option->options & XOPT_TYPE_BOOL)) {
|
||
|
/* we reach this point when they specified an optional, non-boolean
|
||
|
option but didn't specify a custom handler (therefore, it's not
|
||
|
optional).
|
||
|
|
||
|
to fix, just remove the optional flag or specify a callback to handle
|
||
|
it yourself.
|
||
|
*/
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* get location */
|
||
|
target = ((char*) data) + option->offset;
|
||
|
|
||
|
/* switch on the type */
|
||
|
switch (option->options & 0x3F) {
|
||
|
case XOPT_TYPE_BOOL:
|
||
|
/* booleans are special in that they won't have an argument passed
|
||
|
into this callback */
|
||
|
*((_Bool*) target) = true;
|
||
|
break;
|
||
|
case XOPT_TYPE_STRING:
|
||
|
/* lifetime here works out fine; argv can usually be assumed static-like
|
||
|
in nature */
|
||
|
*((const char**) target) = value;
|
||
|
break;
|
||
|
case XOPT_TYPE_INT:
|
||
|
*((int*) target) = (int) strtol(value, &parsePtr, 0);
|
||
|
break;
|
||
|
case XOPT_TYPE_LONG:
|
||
|
*((long*) target) = strtol(value, &parsePtr, 0);
|
||
|
break;
|
||
|
case XOPT_TYPE_FLOAT:
|
||
|
*((float*) target) = (float) strtod(value, &parsePtr);
|
||
|
break;
|
||
|
case XOPT_TYPE_DOUBLE:
|
||
|
*((double*) target) = strtod(value, &parsePtr);
|
||
|
break;
|
||
|
default: /* something wonky, or the implementation specifies two types */
|
||
|
fprintf(stderr, "warning: XOpt argument type invalid: %ld\n",
|
||
|
option->options & 0x2F);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* check that our parsing functions worked */
|
||
|
if (parsePtr && *parsePtr) {
|
||
|
if (longArg) {
|
||
|
_xopt_set_err(NULL, err, "value isn't a valid number: --%s=%s",
|
||
|
(void*) option->longArg, value);
|
||
|
} else {
|
||
|
_xopt_set_err(NULL, err, "value isn't a valid number: -%c %s",
|
||
|
option->shortArg, value);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|