From: Heng Li Date: Wed, 29 Aug 2018 10:08:28 +0000 (-1000) Subject: portable getopt_long() X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=d71d58f0044ba5949e40f55f4c9d7cf2ab19f6dd;p=klib.git portable getopt_long() --- diff --git a/ketopt.h b/ketopt.h new file mode 100644 index 0000000..1944b09 --- /dev/null +++ b/ketopt.h @@ -0,0 +1,95 @@ +#ifndef KETOPT_H +#define KETOPT_H + +#include // for strchr() and strncmp() + +#define ko_no_argument 0 +#define ko_required_argument 1 +#define ko_optional_argument 2 + +typedef struct { + int ind; // equivalent to optind + int opt; // equivalent to optopt + char *arg; // equivalent to optarg + // private variables not intended for external uses + int i, pos, n_args; +} ketopt_t; + +typedef struct { + char *name; + int has_arg; + int val; +} ko_longopt_t; + +static ketopt_t KETOPT_INIT = { 1, 0, 0, 1, 0, 0 }; + +static void ketopt_permute(char *argv[], int j, int n) // move argv[j] over n elements to the left +{ + int k; + char *p = argv[j]; + for (k = 0; k < n; ++k) + argv[j - k] = argv[j - k - 1]; + argv[j - k] = p; +} + +static int ketopt(ketopt_t *s, int argc, char *argv[], int permute, const char *ostr, const ko_longopt_t *longopts) +{ + int opt = -1, i0, j; + if (permute) { + while (s->i < argc && (argv[s->i][0] != '-' || argv[s->i][1] == '\0')) + ++s->i, ++s->n_args; + } + s->arg = 0, i0 = s->i; + if (s->i >= argc || argv[s->i][0] != '-' || argv[s->i][1] == '\0') { + s->ind = s->i - s->n_args; + return -1; + } + if (argv[s->i][0] == '-' && argv[s->i][1] == '-') { // "--" or a long option + if (argv[s->i][2] == '\0') { // a bare "--" + ketopt_permute(argv, s->i, s->n_args); + ++s->i, s->ind = s->i - s->n_args; + return -1; + } + s->opt = 0, opt = '?', s->pos = -1; + if (longopts) { // parse long options + int k, n_matches = 0, match = -1; + const ko_longopt_t *o = 0; + for (j = 2; argv[s->i][j] != '\0' && argv[s->i][j] != '='; ++j) {} // find the end of the option name + for (k = 0; longopts[k].name != 0; ++k) + if (strncmp(&argv[s->i][2], longopts[k].name, j - 2) == 0) + ++n_matches, o = &longopts[k]; + if (n_matches == 1) { + s->opt = opt = o->val; + if (argv[s->i][j] == '=') s->arg = &argv[s->i][j + 1]; + if (o->has_arg == 1 && argv[s->i][j] == '\0') { + if (s->i < argc - 1) s->arg = argv[++s->i]; + else opt = ':'; // missing option argument + } + } + } + } else { // a short option + char *p; + if (s->pos == 0) s->pos = 1; + opt = s->opt = argv[s->i][s->pos++]; + p = strchr(ostr, opt); + if (p == 0) { + opt = '?'; // unknown option + } else if (p[1] == ':') { + if (argv[s->i][s->pos] == 0) { + if (s->i < argc - 1) s->arg = argv[++s->i]; + else opt = ':'; // missing option argument + } else s->arg = &argv[s->i][s->pos]; + s->pos = -1; + } + } + if (s->pos < 0 || argv[s->i][s->pos] == 0) { + ++s->i, s->pos = 0; + if (s->n_args > 0) // permute + for (j = i0; j < s->i; ++j) + ketopt_permute(argv, j, s->n_args); + } + s->ind = s->i - s->n_args; + return opt; +} + +#endif diff --git a/test/ketopt_test.c b/test/ketopt_test.c new file mode 100644 index 0000000..d984c21 --- /dev/null +++ b/test/ketopt_test.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include "ketopt.h" + +// -x xz -y -x -- xz -x +// -xxy1 xz +// -y -x xz + +static void test_opt(int c, int opt, const char *arg) +{ + if (c == 'x') fprintf(stderr, "-x\n"); + else if (c == 'y') fprintf(stderr, "-y %s\n", arg); + else if (c == 'u') fprintf(stderr, "--foo\n"); + else if (c == 'v') fprintf(stderr, "--bar=%s\n", arg); + else if (c == 'w') fprintf(stderr, "--opt=%s\n", arg? arg : "(null)"); + else if (c == '?') fprintf(stderr, "unknown option -%c\n", opt? opt : ':'); + else if (c == ':') fprintf(stderr, "missing option argument: -%c\n", opt? opt : ':'); +} + +static void print_cmd(int argc, char *argv[], int ind) +{ + int i; + fprintf(stderr, "CMD: %s", argv[0]); + if (ind > 1) { + fputs(" [", stderr); + for (i = 1; i < ind; ++i) { + if (i != 1) fputc(' ', stderr); + fputs(argv[i], stderr); + } + fputc(']', stderr); + } + for (i = ind; i < argc; ++i) + fprintf(stderr, " %s", argv[i]); + fputc('\n', stderr); +} + +static void test_ketopt(int argc, char *argv[]) +{ + static ko_longopt_t longopts[] = { + { "foo", ko_no_argument, 'u' }, + { "bar", ko_required_argument, 'v' }, + { "opt", ko_optional_argument, 'w' }, + { NULL, 0, 0 } + }; + ketopt_t opt = KETOPT_INIT; + int i, c; + fprintf(stderr, "===> ketopt() <===\n"); + while ((c = ketopt(&opt, argc, argv, 1, "xy:", longopts)) >= 0) + test_opt(c, opt.opt, opt.arg); + print_cmd(argc, argv, opt.ind); +} + +static void test_getopt(int argc, char *argv[]) +{ + static struct option long_options[] = { + { "foo", no_argument, 0, 'u' }, + { "bar", required_argument, 0, 'v' }, + { "opt", optional_argument, 0, 'w' }, + {0, 0, 0, 0} + }; + int i, c, option_index; + fprintf(stderr, "===> getopt() <===\n"); + opterr = 0; + while ((c = getopt_long(argc, argv, ":xy:", long_options, &option_index)) >= 0) + test_opt(c, optopt, optarg); + print_cmd(argc, argv, optind); +} + +int main(int argc, char *argv[]) +{ + int i; + char **argv2; + argv2 = (char**)malloc(sizeof(char*) * argc); + for (i = 0; i < argc; ++i) argv2[i] = argv[i]; + test_ketopt(argc, argv); + test_getopt(argc, argv2); + free(argv2); + return 0; +}