aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorTaylor Holberton <tay10r@protonmail.com>2020-06-03 18:00:11 -0400
committerGitHub <noreply@github.com>2020-06-03 18:00:11 -0400
commit522a27aa163335a01fd709687d36165729a3f083 (patch)
tree03549f2da9901bb8c40cbee466805c1cdafdc6cd /utils
parent88a791f68b35ca0e4c7979d24cc9954422168dcd (diff)
parentd86996deeef8aa6d6c15d2d4e2ce636b866324f9 (diff)
Merge pull request #161 from E5ten/optparse
Add public domain header-only option-parsing library optparse
Diffstat (limited to 'utils')
-rw-r--r--utils/optparse.h403
-rw-r--r--utils/tinycap.c72
-rw-r--r--utils/tinymix.c53
-rw-r--r--utils/tinypcminfo.c46
-rw-r--r--utils/tinyplay.c159
5 files changed, 565 insertions, 168 deletions
diff --git a/utils/optparse.h b/utils/optparse.h
new file mode 100644
index 0000000..3a577a7
--- /dev/null
+++ b/utils/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 = 1;
+ 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) {
+ 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 */
diff --git a/utils/tinycap.c b/utils/tinycap.c
index 9f8dacd..7d4b8a4 100644
--- a/utils/tinycap.c
+++ b/utils/tinycap.c
@@ -34,6 +34,9 @@
#include <string.h>
#include <limits.h>
+#define OPTPARSE_IMPLEMENTATION
+#include "optparse.h"
+
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
@@ -86,7 +89,8 @@ int main(int argc, char **argv)
unsigned int period_count = 4;
unsigned int capture_time = UINT_MAX;
enum pcm_format format;
- int no_header = 0;
+ int no_header = 0, c;
+ struct optparse opts;
if (argc < 2) {
fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-c channels] "
@@ -108,43 +112,37 @@ int main(int argc, char **argv)
}
/* parse command line arguments */
- argv += 2;
- while (*argv) {
- if (strcmp(*argv, "-d") == 0) {
- argv++;
- if (*argv)
- device = atoi(*argv);
- } else if (strcmp(*argv, "-c") == 0) {
- argv++;
- if (*argv)
- channels = atoi(*argv);
- } else if (strcmp(*argv, "-r") == 0) {
- argv++;
- if (*argv)
- rate = atoi(*argv);
- } else if (strcmp(*argv, "-b") == 0) {
- argv++;
- if (*argv)
- bits = atoi(*argv);
- } else if (strcmp(*argv, "-D") == 0) {
- argv++;
- if (*argv)
- card = atoi(*argv);
- } else if (strcmp(*argv, "-p") == 0) {
- argv++;
- if (*argv)
- period_size = atoi(*argv);
- } else if (strcmp(*argv, "-n") == 0) {
- argv++;
- if (*argv)
- period_count = atoi(*argv);
- } else if (strcmp(*argv, "-t") == 0) {
- argv++;
- if (*argv)
- capture_time = atoi(*argv);
+ optparse_init(&opts, argv + 1);
+ while ((c = optparse(&opts, "D:d:c:r:b:p:n:t:")) != -1) {
+ switch (c) {
+ case 'd':
+ device = atoi(opts.optarg);
+ break;
+ case 'c':
+ channels = atoi(opts.optarg);
+ break;
+ case 'r':
+ rate = atoi(opts.optarg);
+ break;
+ case 'b':
+ bits = atoi(opts.optarg);
+ break;
+ case 'D':
+ card = atoi(opts.optarg);
+ break;
+ case 'p':
+ period_size = atoi(opts.optarg);
+ break;
+ case 'n':
+ period_count = atoi(opts.optarg);
+ break;
+ case 't':
+ capture_time = atoi(opts.optarg);
+ break;
+ case '?':
+ fprintf(stderr, "%s\n", opts.errmsg);
+ return EXIT_FAILURE;
}
- if (*argv)
- argv++;
}
header.riff_id = ID_RIFF;
diff --git a/utils/tinymix.c b/utils/tinymix.c
index 04205dd..1bb1db7 100644
--- a/utils/tinymix.c
+++ b/utils/tinymix.c
@@ -30,11 +30,13 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <getopt.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
+#define OPTPARSE_IMPLEMENTATION
+#include "optparse.h"
+
static void tinymix_list_controls(struct mixer *mixer, int print_all);
static void tinymix_detail_control(struct mixer *mixer, const char *control);
@@ -66,29 +68,22 @@ void version(void)
int main(int argc, char **argv)
{
struct mixer *mixer;
- int card = 0;
+ int card = 0, c;
char *cmd;
-
- while (1) {
- static struct option long_options[] = {
- { "version", no_argument, NULL, 'v' },
- { "help", no_argument, NULL, 'h' },
- { 0, 0, 0, 0 }
- };
-
- /* getopt_long stores the option index here. */
- int option_index = 0;
- int c = 0;
-
- c = getopt_long (argc, argv, "c:D:hv", long_options, &option_index);
-
- /* Detect the end of the options. */
- if (c == -1)
- break;
-
+ struct optparse opts;
+ static struct optparse_long long_options[] = {
+ { "card", 'D', OPTPARSE_REQUIRED },
+ { "version", 'v', OPTPARSE_NONE },
+ { "help", 'h', OPTPARSE_NONE },
+ { 0, 0, 0 }
+ };
+
+ optparse_init(&opts, argv);
+ /* Detect the end of the options. */
+ while ((c = optparse_long(&opts, long_options, NULL)) != -1) {
switch (c) {
case 'D':
- card = atoi(optarg);
+ card = atoi(opts.optarg);
break;
case 'h':
usage();
@@ -96,6 +91,9 @@ int main(int argc, char **argv)
case 'v':
version();
return EXIT_SUCCESS;
+ case '?':
+ fprintf(stderr, "%s\n", opts.errmsg);
+ return EXIT_FAILURE;
}
}
@@ -105,31 +103,32 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- cmd = argv[optind];
+ cmd = argv[opts.optind];
if (cmd == NULL) {
fprintf(stderr, "no command specified (see --help)\n");
mixer_close(mixer);
return EXIT_FAILURE;
} else if (strcmp(cmd, "get") == 0) {
- if ((optind + 1) >= argc) {
+ if ((opts.optind + 1) >= argc) {
fprintf(stderr, "no control specified\n");
mixer_close(mixer);
return EXIT_FAILURE;
}
- tinymix_detail_control(mixer, argv[optind + 1]);
+ tinymix_detail_control(mixer, argv[opts.optind + 1]);
printf("\n");
} else if (strcmp(cmd, "set") == 0) {
- if ((optind + 1) >= argc) {
+ if ((opts.optind + 1) >= argc) {
fprintf(stderr, "no control specified\n");
mixer_close(mixer);
return EXIT_FAILURE;
}
- if ((optind + 2) >= argc) {
+ if ((opts.optind + 2) >= argc) {
fprintf(stderr, "no value(s) specified\n");
mixer_close(mixer);
return EXIT_FAILURE;
}
- tinymix_set_value(mixer, argv[optind + 1], &argv[optind + 2], argc - optind - 2);
+ tinymix_set_value(mixer, argv[opts.optind + 1], &argv[opts.optind + 2],
+ argc - opts.optind - 2);
} else if (strcmp(cmd, "controls") == 0) {
tinymix_list_controls(mixer, 0);
} else if (strcmp(cmd, "contents") == 0) {
diff --git a/utils/tinypcminfo.c b/utils/tinypcminfo.c
index 0dd381d..3116b7c 100644
--- a/utils/tinypcminfo.c
+++ b/utils/tinypcminfo.c
@@ -31,6 +31,9 @@
#include <stdlib.h>
#include <string.h>
+#define OPTPARSE_IMPLEMENTATION
+#include "optparse.h"
+
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#endif
@@ -102,27 +105,32 @@ int main(int argc, char **argv)
unsigned int device = 0;
unsigned int card = 0;
int i;
-
- if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) {
- fprintf(stderr, "Usage: %s -D card -d device\n", argv[0]);
- return 1;
- }
-
+ struct optparse opts;
+ struct optparse_long long_options[] = {
+ { "help", 'h', OPTPARSE_NONE },
+ { "card", 'D', OPTPARSE_REQUIRED },
+ { "device", 'd', OPTPARSE_REQUIRED },
+ { 0, 0, 0 }
+ };
+
+ (void)argc; /* silence -Wunused-parameter */
/* parse command line arguments */
- argv += 1;
- while (*argv) {
- if (strcmp(*argv, "-D") == 0) {
- argv++;
- if (*argv)
- card = atoi(*argv);
- }
- if (strcmp(*argv, "-d") == 0) {
- argv++;
- if (*argv)
- device = atoi(*argv);
+ optparse_init(&opts, argv);
+ while ((i = optparse_long(&opts, long_options, NULL)) != -1) {
+ switch (i) {
+ case 'D':
+ card = atoi(opts.optarg);
+ break;
+ case 'd':
+ device = atoi(opts.optarg);
+ break;
+ case 'h':
+ fprintf(stderr, "Usage: %s -D card -d device\n", argv[0]);
+ return 0;
+ case '?':
+ fprintf(stderr, "%s\n", opts.errmsg);
+ return EXIT_FAILURE;
}
- if (*argv)
- argv++;
}
printf("Info for card %u, device %u:\n", card, device);
diff --git a/utils/tinyplay.c b/utils/tinyplay.c
index 9bedbab..20dd7e8 100644
--- a/utils/tinyplay.c
+++ b/utils/tinyplay.c
@@ -33,6 +33,9 @@
#include <string.h>
#include <signal.h>
+#define OPTPARSE_IMPLEMENTATION
+#include "optparse.h"
+
struct cmd {
const char *filename;
const char *filetype;
@@ -61,88 +64,6 @@ void cmd_init(struct cmd *cmd)
cmd->bits = 16;
}
-int cmd_parse_arg(struct cmd *cmd, int argc, const char **argv)
-{
- if (argc < 1) {
- return 0;
- }
-
- if ((strcmp(argv[0], "-M") == 0) || (strcmp(argv[0], "--mmap") == 0)) {
- cmd->flags |= PCM_MMAP;
- return 1;
- }
-
- if (argv[0][0] != '-' || (strcmp(argv[0],"-") == 0)) {
- cmd->filename = argv[0];
- return 1;
- }
-
- if (argc < 2) {
- fprintf(stderr, "option '%s' is missing argument\n", argv[0]);
- return -1;
- }
-
- if ((strcmp(argv[0], "-D") == 0) || (strcmp(argv[0], "--card") == 0)) {
- if (sscanf(argv[1], "%u", &cmd->card) != 1) {
- fprintf(stderr, "failed parsing card number '%s'\n", argv[1]);
- return -1;
- }
- } else if ((strcmp(argv[0], "-d") == 0) || (strcmp(argv[0], "--device") == 0)) {
- if (sscanf(argv[1], "%u", &cmd->device) != 1) {
- fprintf(stderr, "failed parsing device number '%s'\n", argv[1]);
- return -1;
- }
- } else if ((strcmp(argv[0], "-p") == 0) || (strcmp(argv[0], "--period-size") == 0)) {
- if (sscanf(argv[1], "%u", &cmd->config.period_size) != 1) {
- fprintf(stderr, "failed parsing period size '%s'\n", argv[1]);
- return -1;
- }
- } else if ((strcmp(argv[0], "-n") == 0) || (strcmp(argv[0], "--period-count") == 0)) {
- if (sscanf(argv[1], "%u", &cmd->config.period_count) != 1) {
- fprintf(stderr, "failed parsing period count '%s'\n", argv[1]);
- return -1;
- }
- } else if ((strcmp(argv[0], "-c") == 0) || (strcmp(argv[0], "--channels") == 0)) {
- if (sscanf(argv[1], "%u", &cmd->config.channels) != 1) {
- fprintf(stderr, "failed parsing channel count '%s'\n", argv[1]);
- return -1;
- }
- } else if ((strcmp(argv[0], "-r") == 0) || (strcmp(argv[0], "--rate") == 0)) {
- if (sscanf(argv[1], "%u", &cmd->config.rate) != 1) {
- fprintf(stderr, "failed parsing rate '%s'\n", argv[1]);
- return -1;
- }
- } else if ((strcmp(argv[0], "-i") == 0) || (strcmp(argv[0], "--file-type") == 0)) {
- cmd->filetype = argv[1];
- } else {
- fprintf(stderr, "unknown option '%s'\n", argv[0]);
- return -1;
- }
- return 2;
-}
-
-int cmd_parse_args(struct cmd *cmd, int argc, const char **argv)
-{
- int i = 0;
- while (i < argc) {
- int j = cmd_parse_arg(cmd, argc - i, &argv[i]);
- if (j < 0){
- break;
- }
- i += j;
- }
-
- if ((cmd->filename != NULL)
- && (cmd->filetype == NULL)) {
- cmd->filetype = strrchr(cmd->filename, '.');
- if (cmd->filetype != NULL) {
- cmd->filetype++;
- }
- }
-
- return i;
-}
-
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
@@ -313,10 +234,25 @@ void print_usage(const char *argv0)
fprintf(stderr, "-M | --mmap Use memory mapped IO to play audio\n");
}
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
{
+ int c;
struct cmd cmd;
struct ctx ctx;
+ struct optparse opts;
+ struct optparse_long long_options[] = {
+ { "card", 'D', OPTPARSE_REQUIRED },
+ { "device", 'd', OPTPARSE_REQUIRED },
+ { "period-size", 'p', OPTPARSE_REQUIRED },
+ { "period-count", 'n', OPTPARSE_REQUIRED },
+ { "file-type", 'i', OPTPARSE_REQUIRED },
+ { "channels", 'c', OPTPARSE_REQUIRED },
+ { "rate", 'r', OPTPARSE_REQUIRED },
+ { "bits", 'b', OPTPARSE_REQUIRED },
+ { "mmap", 'M', OPTPARSE_NONE },
+ { "help", 'h', OPTPARSE_NONE },
+ { 0, 0, 0 }
+ };
if (argc < 2) {
print_usage(argv[0]);
@@ -324,8 +260,61 @@ int main(int argc, const char **argv)
}
cmd_init(&cmd);
- if (cmd_parse_args(&cmd, argc - 1, &argv[1]) < 0) {
- return EXIT_FAILURE;
+ optparse_init(&opts, argv);
+ while ((c = optparse_long(&opts, long_options, NULL)) != -1) {
+ switch (c) {
+ case 'D':
+ if (sscanf(opts.optarg, "%u", &cmd.card) != 1) {
+ fprintf(stderr, "failed parsing card number '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'd':
+ if (sscanf(opts.optarg, "%u", &cmd.device) != 1) {
+ fprintf(stderr, "failed parsing device number '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'p':
+ if (sscanf(opts.optarg, "%u", &cmd.config.period_size) != 1) {
+ fprintf(stderr, "failed parsing period size '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'n':
+ if (sscanf(opts.optarg, "%u", &cmd.config.period_count) != 1) {
+ fprintf(stderr, "failed parsing period count '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'c':
+ if (sscanf(opts.optarg, "%u", &cmd.config.channels) != 1) {
+ fprintf(stderr, "failed parsing channel count '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'r':
+ if (sscanf(opts.optarg, "%u", &cmd.config.rate) != 1) {
+ fprintf(stderr, "failed parsing rate '%s'\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'i':
+ cmd.filetype = opts.optarg;
+ break;
+ case 'h':
+ print_usage(argv[0]);
+ return EXIT_SUCCESS;
+ case '?':
+ fprintf(stderr, "%s\n", opts.errmsg);
+ return EXIT_FAILURE;
+ }
+ }
+ cmd.filename = optparse_arg(&opts);
+
+ if (cmd.filename != NULL && cmd.filetype == NULL &&
+ (cmd.filetype = strrchr(cmd.filename, '.')) != NULL) {
+ cmd.filetype++;
}
if (ctx_init(&ctx, &cmd) < 0) {