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:
NRK
2022-08-16 10:43:50 +02:00
parent d9f20a4122
commit 216f312578
4 changed files with 500 additions and 53 deletions

View File

@ -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++;