From 536cab1cd0e3bbd1d1e89fc7c175ce82797108a9 Mon Sep 17 00:00:00 2001 From: Ethan Sommer Date: Wed, 3 Jun 2020 15:34:51 -0400 Subject: utils: check in optparse.h, a public domain header-only optparsing lib https://github.com/skeeto/optparse --- utils/optparse.h | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 utils/optparse.h (limited to 'utils') 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 */ -- cgit v1.2.3 From f6ab1276ea29e4a25cd6d452ee597a64d454156a Mon Sep 17 00:00:00 2001 From: Ethan Sommer Date: Wed, 3 Jun 2020 15:36:00 -0400 Subject: tinycap: replace manual option parsing with optparse --- utils/tinycap.c | 72 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) (limited to 'utils') 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 #include +#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; -- cgit v1.2.3 From 1e144025fea2b4133744bcbe315c105a978ee52f Mon Sep 17 00:00:00 2001 From: Ethan Sommer Date: Wed, 3 Jun 2020 15:36:56 -0400 Subject: tinymix: replace getopt_long with optparse --- utils/tinymix.c | 53 ++++++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 27 deletions(-) (limited to 'utils') 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 #include #include -#include #include #include #include +#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) { -- cgit v1.2.3 From 758a1124d903ab0913b4c5aa7251232f82b83f7b Mon Sep 17 00:00:00 2001 From: Ethan Sommer Date: Wed, 3 Jun 2020 15:37:17 -0400 Subject: tinypcminfo: replace manual option parsing with optparse add equivalent longopts that are available in other commands --- utils/tinypcminfo.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'utils') 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 #include +#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); -- cgit v1.2.3 From d86996deeef8aa6d6c15d2d4e2ce636b866324f9 Mon Sep 17 00:00:00 2001 From: Ethan Sommer Date: Wed, 3 Jun 2020 15:38:10 -0400 Subject: tinyplay: replace manual option parsing with optparse This also removes the requirement for the file argument to come before options. --- utils/tinyplay.c | 159 ++++++++++++++++++++++++++----------------------------- 1 file changed, 74 insertions(+), 85 deletions(-) (limited to 'utils') 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 #include +#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) { -- cgit v1.2.3