aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNRK <nrk@disroot.org>2022-08-16 10:43:50 +0200
committerNRK <nrk@disroot.org>2022-08-16 10:43:50 +0200
commit216f312578be5c959527c03ccc70d27eebcc76aa (patch)
tree0dac281db909a1d27cc248c63f3c939f2632863d
parentd9f20a41224265573395381fbbdea40ac9891d84 (diff)
downloadnsxiv-216f312578be5c959527c03ccc70d27eebcc76aa.tar.zst
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>
-rw-r--r--Makefile2
-rw-r--r--etc/nsxiv.156
-rw-r--r--options.c92
-rw-r--r--optparse.h403
4 files changed, 500 insertions, 53 deletions
diff --git a/Makefile b/Makefile
index 04d2ed7..3527741 100644
--- a/Makefile
+++ b/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:
diff --git a/etc/nsxiv.1 b/etc/nsxiv.1
index f4801b0..29f036f 100644
--- a/etc/nsxiv.1
+++ b/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
diff --git a/options.c b/options.c
index 69b45b9..3b191ea 100644
--- a/options.c
+++ b/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++;
diff --git a/optparse.h b/optparse.h
new file mode 100644
index 0000000..8d6c0a9
--- /dev/null
+++ b/optparse.h
@@ -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 */