add support for long-opts (#332)
Uses [optparse] to add support for long-opts. optparse is posix compliant with getopt(3) and thus would be backwards compatible. It does not have any dependency (not even the c standard library!) and is C89 compatible and thus fits our current code-style. [optparse]: https://github.com/skeeto/optparse Note that we're using a couple `pragma`-s to silence some harmless warnings. This should be portable because these pragma-s don't change the behavior of the program. Furthermore, C standard mandates that unknown pragma's should be ignored by the compiler and thus would not result in build failure on compilers which do not recognize them. Closes: https://codeberg.org/nsxiv/nsxiv/issues/328 Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/332 Reviewed-by: eylles <eylles@noreply.codeberg.org>
This commit is contained in:
parent
d9f20a4122
commit
216f312578
2
Makefile
2
Makefile
|
@ -41,7 +41,7 @@ nsxiv: $(objs)
|
|||
$(CC) $(CFLAGS) $(nsxiv_cppflags) -c -o $@ $<
|
||||
|
||||
$(objs): Makefile config.mk nsxiv.h config.h commands.h
|
||||
options.o: version.h
|
||||
options.o: version.h optparse.h
|
||||
window.o: icon/data.h utf8.h
|
||||
|
||||
config.h:
|
||||
|
|
56
etc/nsxiv.1
56
etc/nsxiv.1
|
@ -34,84 +34,84 @@ Please note, that the fullscreen mode requires an EWMH/NetWM-compliant window
|
|||
manager.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI "\-A " FRAMERATE
|
||||
.BI "\-A, \-\-framerate " FRAMERATE
|
||||
Play animations with a constant frame rate set to
|
||||
.IR FRAMERATE .
|
||||
.TP
|
||||
.B \-a
|
||||
.B "\-a, \-\-animate"
|
||||
Play animations of multi-frame images.
|
||||
.TP
|
||||
.B \-b
|
||||
.B "\-b, \-\-no\-bar"
|
||||
Do not show statusbar at the bottom of the window.
|
||||
.TP
|
||||
.B \-c
|
||||
.B "\-c, \-\-clean\-cache"
|
||||
Remove all orphaned cache files from the thumbnail cache directory and exit.
|
||||
.TP
|
||||
.BI "\-e " WID
|
||||
.BI "\-e, \-\-embed " WID
|
||||
Embed nsxiv's window into window whose ID is
|
||||
.IR WID .
|
||||
.TP
|
||||
.B \-f
|
||||
.B "\-f, \-\-fullscreen"
|
||||
Start in fullscreen mode.
|
||||
.TP
|
||||
.BI "\-G " GAMMA
|
||||
.BI "\-G, \-\-gamma " GAMMA
|
||||
Set image gamma to GAMMA (\-32..32).
|
||||
.TP
|
||||
.BI "\-g " GEOMETRY
|
||||
.BI "\-g, \-\-geometry " GEOMETRY
|
||||
Set window position and size. See section GEOMETRY SPECIFICATIONS of X(7) for
|
||||
more information on GEOMETRY argument.
|
||||
.TP
|
||||
.BI "\-N " NAME
|
||||
Set the resource name of nsxiv's X window to NAME.
|
||||
.TP
|
||||
.BI "\-n " NUM
|
||||
Start at picture number NUM.
|
||||
.TP
|
||||
.B \-h
|
||||
.B "\-h, \-\-help"
|
||||
Print brief usage information to standard output and exit.
|
||||
.TP
|
||||
.B \-i
|
||||
.B "\-i, \-\-stdin"
|
||||
Read names of files to open from standard input. Also done if FILE is `-'.
|
||||
.TP
|
||||
.B \-o
|
||||
.BI "\-N, \-\-class " NAME
|
||||
Set the resource name (WM_CLASS) of nsxiv's X window to NAME.
|
||||
.TP
|
||||
.BI "\-n, \-\-start\-at " NUM
|
||||
Start at picture number NUM.
|
||||
.TP
|
||||
.B "\-o, \-\-stdout"
|
||||
Write list of all marked files to standard output when quitting. In combination
|
||||
with
|
||||
.B \-i
|
||||
.B "\-i, \-\-stdin"
|
||||
nsxiv can be used as a visual filter/pipe.
|
||||
.TP
|
||||
.B \-p
|
||||
.B "\-p, \-\-private"
|
||||
Enable private mode, in which nsxiv does not write any cache or temporary files.
|
||||
.TP
|
||||
.B \-q
|
||||
.B "\-q, \-\-quiet"
|
||||
Be quiet, and disable warnings to standard error stream.
|
||||
.TP
|
||||
.B \-r
|
||||
.B "\-r, \-\-recursive"
|
||||
Search the given directories recursively for images to view.
|
||||
.TP
|
||||
.BI "\-S " DELAY
|
||||
.BI "\-S, \-\-ss\-delay " DELAY
|
||||
Start in slideshow mode. Set the delay between images to
|
||||
.I DELAY
|
||||
seconds.
|
||||
.I DELAY
|
||||
may be a floating-point number.
|
||||
.TP
|
||||
.BI "\-s " MODE
|
||||
.BI "\-s, \-\-scale\-mode " MODE
|
||||
Set scale mode according to MODE character. Supported modes are: [d]own,
|
||||
[f]it, [F]ill, [w]idth, [h]eight.
|
||||
.TP
|
||||
.B \-t
|
||||
.B "\-t, \-\-thumbnail"
|
||||
Start in thumbnail mode.
|
||||
.TP
|
||||
.B \-v
|
||||
.B "\-v, \-\-version"
|
||||
Print version information to standard output and exit.
|
||||
.TP
|
||||
.B \-Z
|
||||
.B "\-Z, \-\-zoom\-100"
|
||||
The same as `\-z 100'.
|
||||
.TP
|
||||
.BI "\-z " ZOOM
|
||||
.BI "\-z, \-\-zoom " ZOOM
|
||||
Set zoom level to ZOOM percent.
|
||||
.TP
|
||||
.B \-0
|
||||
.B "\-0, \-\-null"
|
||||
Use NULL-separator. With this option, output of \-o and file-list sent to the
|
||||
key-handler and the input of \-i will be separated by a NULL character.
|
||||
.SH KEYBOARD COMMANDS
|
||||
|
|
92
options.c
92
options.c
|
@ -20,11 +20,20 @@
|
|||
#include "nsxiv.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define OPTPARSE_IMPLEMENTATION
|
||||
#define OPTPARSE_API static
|
||||
#pragma GCC diagnostic push /* also works on clang */
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#include "optparse.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
const opt_t *options;
|
||||
|
||||
void print_usage(void)
|
||||
|
@ -54,12 +63,41 @@ static void print_version(void)
|
|||
|
||||
void parse_options(int argc, char **argv)
|
||||
{
|
||||
static const struct optparse_long longopts[] = {
|
||||
{ "framerate", 'A', OPTPARSE_REQUIRED },
|
||||
{ "animate", 'a', OPTPARSE_NONE },
|
||||
{ "no-bar", 'b', OPTPARSE_NONE },
|
||||
{ "clean-cache", 'c', OPTPARSE_NONE },
|
||||
{ "embed", 'e', OPTPARSE_REQUIRED },
|
||||
{ "fullscreen", 'f', OPTPARSE_NONE },
|
||||
{ "gamma", 'G', OPTPARSE_REQUIRED },
|
||||
{ "geometry", 'g', OPTPARSE_REQUIRED },
|
||||
{ "help", 'h', OPTPARSE_NONE },
|
||||
{ "stdin", 'i', OPTPARSE_NONE },
|
||||
{ "class", 'N', OPTPARSE_REQUIRED },
|
||||
{ "start-at", 'n', OPTPARSE_REQUIRED },
|
||||
{ "stdout", 'o', OPTPARSE_NONE },
|
||||
{ "private", 'p', OPTPARSE_NONE },
|
||||
{ "quiet", 'q', OPTPARSE_NONE },
|
||||
{ "recursive", 'r', OPTPARSE_NONE },
|
||||
{ "ss-delay", 'S', OPTPARSE_REQUIRED },
|
||||
{ "scale-mode", 's', OPTPARSE_REQUIRED },
|
||||
{ NULL, 'T', OPTPARSE_REQUIRED },
|
||||
{ "thumbnail", 't', OPTPARSE_NONE },
|
||||
{ "version", 'v', OPTPARSE_NONE },
|
||||
{ "zoom-100", 'Z', OPTPARSE_NONE },
|
||||
{ "zoom", 'z', OPTPARSE_REQUIRED },
|
||||
{ "null", '0', OPTPARSE_NONE },
|
||||
{ 0 }, /* end */
|
||||
};
|
||||
|
||||
int n, opt;
|
||||
char *end, *s;
|
||||
const char *scalemodes = "dfFwh";
|
||||
struct optparse op;
|
||||
const char scalemodes[] = "dfFwh"; /* must be sorted according to scalemode_t */
|
||||
static opt_t _options;
|
||||
options = &_options;
|
||||
|
||||
options = &_options;
|
||||
progname = strrchr(argv[0], '/');
|
||||
progname = progname ? progname + 1 : argv[0];
|
||||
|
||||
|
@ -87,15 +125,21 @@ void parse_options(int argc, char **argv)
|
|||
_options.clean_cache = false;
|
||||
_options.private_mode = false;
|
||||
|
||||
while ((opt = getopt(argc, argv, "A:abce:fG:g:hin:N:opqrS:s:T:tvZz:0")) != -1) {
|
||||
optparse_init(&op, argv);
|
||||
while ((opt = optparse_long(&op, longopts, NULL)) != -1) {
|
||||
for (n = 0; n < (int)ARRLEN(longopts); ++n) { /* clang-tidy finds some non-sensical branch and thinks optarg == NULL is possible */
|
||||
if (opt == longopts[n].shortname && longopts[n].argtype == OPTPARSE_REQUIRED)
|
||||
assert(op.optarg != NULL);
|
||||
}
|
||||
switch (opt) {
|
||||
case '?':
|
||||
fprintf(stderr, "%s\n", op.errmsg);
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
case 'A':
|
||||
n = strtol(optarg, &end, 0);
|
||||
n = strtol(op.optarg, &end, 0);
|
||||
if (*end != '\0' || n <= 0)
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -A: %s", optarg);
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -A: %s", op.optarg);
|
||||
_options.framerate = n;
|
||||
/* fall through */
|
||||
case 'a':
|
||||
|
@ -108,22 +152,22 @@ void parse_options(int argc, char **argv)
|
|||
_options.clean_cache = true;
|
||||
break;
|
||||
case 'e':
|
||||
n = strtol(optarg, &end, 0);
|
||||
n = strtol(op.optarg, &end, 0);
|
||||
if (*end != '\0')
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -e: %s", optarg);
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -e: %s", op.optarg);
|
||||
_options.embed = n;
|
||||
break;
|
||||
case 'f':
|
||||
_options.fullscreen = true;
|
||||
break;
|
||||
case 'G':
|
||||
n = strtol(optarg, &end, 0);
|
||||
n = strtol(op.optarg, &end, 0);
|
||||
if (*end != '\0')
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -G: %s", optarg);
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -G: %s", op.optarg);
|
||||
_options.gamma = n;
|
||||
break;
|
||||
case 'g':
|
||||
_options.geometry = optarg;
|
||||
_options.geometry = op.optarg;
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
|
@ -132,13 +176,13 @@ void parse_options(int argc, char **argv)
|
|||
_options.from_stdin = true;
|
||||
break;
|
||||
case 'n':
|
||||
n = strtol(optarg, &end, 0);
|
||||
n = strtol(op.optarg, &end, 0);
|
||||
if (*end != '\0' || n <= 0)
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -n: %s", optarg);
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -n: %s", op.optarg);
|
||||
_options.startnum = n - 1;
|
||||
break;
|
||||
case 'N':
|
||||
_options.res_name = optarg;
|
||||
_options.res_name = op.optarg;
|
||||
break;
|
||||
case 'o':
|
||||
_options.to_stdout = true;
|
||||
|
@ -153,15 +197,15 @@ void parse_options(int argc, char **argv)
|
|||
_options.recursive = true;
|
||||
break;
|
||||
case 'S':
|
||||
n = strtof(optarg, &end) * 10;
|
||||
n = strtof(op.optarg, &end) * 10;
|
||||
if (*end != '\0' || n <= 0)
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -S: %s", optarg);
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -S: %s", op.optarg);
|
||||
_options.slideshow = n;
|
||||
break;
|
||||
case 's':
|
||||
s = strchr(scalemodes, optarg[0]);
|
||||
if (s == NULL || *s == '\0' || strlen(optarg) != 1)
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -s: %s", optarg);
|
||||
s = strchr(scalemodes, op.optarg[0]);
|
||||
if (s == NULL || *s == '\0' || strlen(op.optarg) != 1)
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -s: %s", op.optarg);
|
||||
_options.scalemode = s - scalemodes;
|
||||
break;
|
||||
case 'T':
|
||||
|
@ -175,14 +219,14 @@ void parse_options(int argc, char **argv)
|
|||
exit(EXIT_SUCCESS);
|
||||
case 'Z':
|
||||
_options.scalemode = SCALE_ZOOM;
|
||||
_options.zoom = 1.0;
|
||||
_options.zoom = 1.0f;
|
||||
break;
|
||||
case 'z':
|
||||
n = strtol(optarg, &end, 0);
|
||||
n = strtol(op.optarg, &end, 0);
|
||||
if (*end != '\0' || n <= 0)
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -z: %s", optarg);
|
||||
error(EXIT_FAILURE, 0, "Invalid argument for option -z: %s", op.optarg);
|
||||
_options.scalemode = SCALE_ZOOM;
|
||||
_options.zoom = (float) n / 100.0;
|
||||
_options.zoom = (float) n / 100.0f;
|
||||
break;
|
||||
case '0':
|
||||
_options.using_null = true;
|
||||
|
@ -190,8 +234,8 @@ void parse_options(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
_options.filenames = argv + optind;
|
||||
_options.filecnt = argc - optind;
|
||||
_options.filenames = argv + op.optind;
|
||||
_options.filecnt = argc - op.optind;
|
||||
|
||||
if (_options.filecnt == 1 && STREQ(_options.filenames[0], "-")) {
|
||||
_options.filenames++;
|
||||
|
|
|
@ -0,0 +1,403 @@
|
|||
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
|
||||
* Optionally define OPTPARSE_API to control the API's visibility
|
||||
* and/or linkage (static, __attribute__, __declspec).
|
||||
*
|
||||
* The POSIX getopt() option parser has three fatal flaws. These flaws
|
||||
* are solved by Optparse.
|
||||
*
|
||||
* 1) Parser state is stored entirely in global variables, some of
|
||||
* which are static and inaccessible. This means only one thread can
|
||||
* use getopt(). It also means it's not possible to recursively parse
|
||||
* nested sub-arguments while in the middle of argument parsing.
|
||||
* Optparse fixes this by storing all state on a local struct.
|
||||
*
|
||||
* 2) The POSIX standard provides no way to properly reset the parser.
|
||||
* This means for portable code that getopt() is only good for one
|
||||
* run, over one argv with one option string. It also means subcommand
|
||||
* options cannot be processed with getopt(). Most implementations
|
||||
* provide a method to reset the parser, but it's not portable.
|
||||
* Optparse provides an optparse_arg() function for stepping over
|
||||
* subcommands and continuing parsing of options with another option
|
||||
* string. The Optparse struct itself can be passed around to
|
||||
* subcommand handlers for additional subcommand option parsing. A
|
||||
* full reset can be achieved by with an additional optparse_init().
|
||||
*
|
||||
* 3) Error messages are printed to stderr. This can be disabled with
|
||||
* opterr, but the messages themselves are still inaccessible.
|
||||
* Optparse solves this by writing an error message in its errmsg
|
||||
* field. The downside to Optparse is that this error message will
|
||||
* always be in English rather than the current locale.
|
||||
*
|
||||
* Optparse should be familiar with anyone accustomed to getopt(), and
|
||||
* it could be a nearly drop-in replacement. The option string is the
|
||||
* same and the fields have the same names as the getopt() global
|
||||
* variables (optarg, optind, optopt).
|
||||
*
|
||||
* Optparse also supports GNU-style long options with optparse_long().
|
||||
* The interface is slightly different and simpler than getopt_long().
|
||||
*
|
||||
* By default, argv is permuted as it is parsed, moving non-option
|
||||
* arguments to the end. This can be disabled by setting the `permute`
|
||||
* field to 0 after initialization.
|
||||
*/
|
||||
#ifndef OPTPARSE_H
|
||||
#define OPTPARSE_H
|
||||
|
||||
#ifndef OPTPARSE_API
|
||||
# define OPTPARSE_API
|
||||
#endif
|
||||
|
||||
struct optparse {
|
||||
char **argv;
|
||||
int permute;
|
||||
int optind;
|
||||
int optopt;
|
||||
char *optarg;
|
||||
char errmsg[64];
|
||||
int subopt;
|
||||
};
|
||||
|
||||
enum optparse_argtype {
|
||||
OPTPARSE_NONE,
|
||||
OPTPARSE_REQUIRED,
|
||||
OPTPARSE_OPTIONAL
|
||||
};
|
||||
|
||||
struct optparse_long {
|
||||
const char *longname;
|
||||
int shortname;
|
||||
enum optparse_argtype argtype;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the parser state.
|
||||
*/
|
||||
OPTPARSE_API
|
||||
void optparse_init(struct optparse *options, char **argv);
|
||||
|
||||
/**
|
||||
* Read the next option in the argv array.
|
||||
* @param optstring a getopt()-formatted option string.
|
||||
* @return the next option character, -1 for done, or '?' for error
|
||||
*
|
||||
* Just like getopt(), a character followed by no colons means no
|
||||
* argument. One colon means the option has a required argument. Two
|
||||
* colons means the option takes an optional argument.
|
||||
*/
|
||||
OPTPARSE_API
|
||||
int optparse(struct optparse *options, const char *optstring);
|
||||
|
||||
/**
|
||||
* Handles GNU-style long options in addition to getopt() options.
|
||||
* This works a lot like GNU's getopt_long(). The last option in
|
||||
* longopts must be all zeros, marking the end of the array. The
|
||||
* longindex argument may be NULL.
|
||||
*/
|
||||
OPTPARSE_API
|
||||
int optparse_long(struct optparse *options,
|
||||
const struct optparse_long *longopts,
|
||||
int *longindex);
|
||||
|
||||
/**
|
||||
* Used for stepping over non-option arguments.
|
||||
* @return the next non-option argument, or NULL for no more arguments
|
||||
*
|
||||
* Argument parsing can continue with optparse() after using this
|
||||
* function. That would be used to parse the options for the
|
||||
* subcommand returned by optparse_arg(). This function allows you to
|
||||
* ignore the value of optind.
|
||||
*/
|
||||
OPTPARSE_API
|
||||
char *optparse_arg(struct optparse *options);
|
||||
|
||||
/* Implementation */
|
||||
#ifdef OPTPARSE_IMPLEMENTATION
|
||||
|
||||
#define OPTPARSE_MSG_INVALID "invalid option"
|
||||
#define OPTPARSE_MSG_MISSING "option requires an argument"
|
||||
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
|
||||
|
||||
static int
|
||||
optparse_error(struct optparse *options, const char *msg, const char *data)
|
||||
{
|
||||
unsigned p = 0;
|
||||
const char *sep = " -- '";
|
||||
while (*msg)
|
||||
options->errmsg[p++] = *msg++;
|
||||
while (*sep)
|
||||
options->errmsg[p++] = *sep++;
|
||||
while (p < sizeof(options->errmsg) - 2 && *data)
|
||||
options->errmsg[p++] = *data++;
|
||||
options->errmsg[p++] = '\'';
|
||||
options->errmsg[p++] = '\0';
|
||||
return '?';
|
||||
}
|
||||
|
||||
OPTPARSE_API
|
||||
void
|
||||
optparse_init(struct optparse *options, char **argv)
|
||||
{
|
||||
options->argv = argv;
|
||||
options->permute = 1;
|
||||
options->optind = argv[0] != 0;
|
||||
options->subopt = 0;
|
||||
options->optarg = 0;
|
||||
options->errmsg[0] = '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
optparse_is_dashdash(const char *arg)
|
||||
{
|
||||
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
optparse_is_shortopt(const char *arg)
|
||||
{
|
||||
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
optparse_is_longopt(const char *arg)
|
||||
{
|
||||
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
|
||||
}
|
||||
|
||||
static void
|
||||
optparse_permute(struct optparse *options, int index)
|
||||
{
|
||||
char *nonoption = options->argv[index];
|
||||
int i;
|
||||
for (i = index; i < options->optind - 1; i++)
|
||||
options->argv[i] = options->argv[i + 1];
|
||||
options->argv[options->optind - 1] = nonoption;
|
||||
}
|
||||
|
||||
static int
|
||||
optparse_argtype(const char *optstring, char c)
|
||||
{
|
||||
int count = OPTPARSE_NONE;
|
||||
if (c == ':')
|
||||
return -1;
|
||||
for (; *optstring && c != *optstring; optstring++);
|
||||
if (!*optstring)
|
||||
return -1;
|
||||
if (optstring[1] == ':')
|
||||
count += optstring[2] == ':' ? 2 : 1;
|
||||
return count;
|
||||
}
|
||||
|
||||
OPTPARSE_API
|
||||
int
|
||||
optparse(struct optparse *options, const char *optstring)
|
||||
{
|
||||
int type;
|
||||
char *next;
|
||||
char *option = options->argv[options->optind];
|
||||
options->errmsg[0] = '\0';
|
||||
options->optopt = 0;
|
||||
options->optarg = 0;
|
||||
if (option == 0) {
|
||||
return -1;
|
||||
} else if (optparse_is_dashdash(option)) {
|
||||
options->optind++; /* consume "--" */
|
||||
return -1;
|
||||
} else if (!optparse_is_shortopt(option)) {
|
||||
if (options->permute) {
|
||||
int index = options->optind++;
|
||||
int r = optparse(options, optstring);
|
||||
optparse_permute(options, index);
|
||||
options->optind--;
|
||||
return r;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
option += options->subopt + 1;
|
||||
options->optopt = option[0];
|
||||
type = optparse_argtype(optstring, option[0]);
|
||||
next = options->argv[options->optind + 1];
|
||||
switch (type) {
|
||||
case -1: {
|
||||
char str[2] = {0, 0};
|
||||
str[0] = option[0];
|
||||
options->optind++;
|
||||
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
|
||||
}
|
||||
case OPTPARSE_NONE:
|
||||
if (option[1]) {
|
||||
options->subopt++;
|
||||
} else {
|
||||
options->subopt = 0;
|
||||
options->optind++;
|
||||
}
|
||||
return option[0];
|
||||
case OPTPARSE_REQUIRED:
|
||||
options->subopt = 0;
|
||||
options->optind++;
|
||||
if (option[1]) {
|
||||
options->optarg = option + 1;
|
||||
} else if (next != 0) {
|
||||
options->optarg = next;
|
||||
options->optind++;
|
||||
} else {
|
||||
char str[2] = {0, 0};
|
||||
str[0] = option[0];
|
||||
options->optarg = 0;
|
||||
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
|
||||
}
|
||||
return option[0];
|
||||
case OPTPARSE_OPTIONAL:
|
||||
options->subopt = 0;
|
||||
options->optind++;
|
||||
if (option[1])
|
||||
options->optarg = option + 1;
|
||||
else
|
||||
options->optarg = 0;
|
||||
return option[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPTPARSE_API
|
||||
char *
|
||||
optparse_arg(struct optparse *options)
|
||||
{
|
||||
char *option = options->argv[options->optind];
|
||||
options->subopt = 0;
|
||||
if (option != 0)
|
||||
options->optind++;
|
||||
return option;
|
||||
}
|
||||
|
||||
static int
|
||||
optparse_longopts_end(const struct optparse_long *longopts, int i)
|
||||
{
|
||||
return !longopts[i].longname && !longopts[i].shortname;
|
||||
}
|
||||
|
||||
static void
|
||||
optparse_from_long(const struct optparse_long *longopts, char *optstring)
|
||||
{
|
||||
char *p = optstring;
|
||||
int i;
|
||||
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
|
||||
if (longopts[i].shortname && longopts[i].shortname < 127) {
|
||||
int a;
|
||||
*p++ = longopts[i].shortname;
|
||||
for (a = 0; a < (int)longopts[i].argtype; a++)
|
||||
*p++ = ':';
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/* Unlike strcmp(), handles options containing "=". */
|
||||
static int
|
||||
optparse_longopts_match(const char *longname, const char *option)
|
||||
{
|
||||
const char *a = option, *n = longname;
|
||||
if (longname == 0)
|
||||
return 0;
|
||||
for (; *a && *n && *a != '='; a++, n++)
|
||||
if (*a != *n)
|
||||
return 0;
|
||||
return *n == '\0' && (*a == '\0' || *a == '=');
|
||||
}
|
||||
|
||||
/* Return the part after "=", or NULL. */
|
||||
static char *
|
||||
optparse_longopts_arg(char *option)
|
||||
{
|
||||
for (; *option && *option != '='; option++);
|
||||
if (*option == '=')
|
||||
return option + 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
optparse_long_fallback(struct optparse *options,
|
||||
const struct optparse_long *longopts,
|
||||
int *longindex)
|
||||
{
|
||||
int result;
|
||||
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
|
||||
optparse_from_long(longopts, optstring);
|
||||
result = optparse(options, optstring);
|
||||
if (longindex != 0) {
|
||||
*longindex = -1;
|
||||
if (result != -1) {
|
||||
int i;
|
||||
for (i = 0; !optparse_longopts_end(longopts, i); i++)
|
||||
if (longopts[i].shortname == options->optopt)
|
||||
*longindex = i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OPTPARSE_API
|
||||
int
|
||||
optparse_long(struct optparse *options,
|
||||
const struct optparse_long *longopts,
|
||||
int *longindex)
|
||||
{
|
||||
int i;
|
||||
char *option = options->argv[options->optind];
|
||||
if (option == 0) {
|
||||
return -1;
|
||||
} else if (optparse_is_dashdash(option)) {
|
||||
options->optind++; /* consume "--" */
|
||||
return -1;
|
||||
} else if (optparse_is_shortopt(option)) {
|
||||
return optparse_long_fallback(options, longopts, longindex);
|
||||
} else if (!optparse_is_longopt(option)) {
|
||||
if (options->permute) {
|
||||
int index = options->optind++;
|
||||
int r = optparse_long(options, longopts, longindex);
|
||||
optparse_permute(options, index);
|
||||
options->optind--;
|
||||
return r;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse as long option. */
|
||||
options->errmsg[0] = '\0';
|
||||
options->optopt = 0;
|
||||
options->optarg = 0;
|
||||
option += 2; /* skip "--" */
|
||||
options->optind++;
|
||||
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
|
||||
const char *name = longopts[i].longname;
|
||||
if (optparse_longopts_match(name, option)) {
|
||||
char *arg;
|
||||
if (longindex)
|
||||
*longindex = i;
|
||||
options->optopt = longopts[i].shortname;
|
||||
arg = optparse_longopts_arg(option);
|
||||
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
|
||||
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
|
||||
} if (arg != 0) {
|
||||
options->optarg = arg;
|
||||
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
|
||||
options->optarg = options->argv[options->optind];
|
||||
if (options->optarg == 0)
|
||||
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
|
||||
else
|
||||
options->optind++;
|
||||
}
|
||||
return options->optopt;
|
||||
}
|
||||
}
|
||||
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
|
||||
}
|
||||
|
||||
#endif /* OPTPARSE_IMPLEMENTATION */
|
||||
#endif /* OPTPARSE_H */
|
Loading…
Reference in New Issue