From: Pierre Habouzit <madcoder@debian.org>
To: "Kristian Høgsberg" <krh@redhat.com>, git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>
Subject: [ALTERNATE PATCH] Add a simple option parser.
Date: Fri, 05 Oct 2007 16:25:07 +0200 [thread overview]
Message-ID: <20071005142507.GL19879@artemis.corp> (raw)
In-Reply-To: <20071005142140.GK19879@artemis.corp>
[-- Attachment #1: Type: text/plain, Size: 6456 bytes --]
The option parser takes argc, argv, an array of struct option
and a usage string. Each of the struct option elements in the array
describes a valid option, its type and a pointer to the location where the
value is written. The entry point is parse_options(), which scans through
the given argv, and matches each option there against the list of valid
options. During the scan, argv is rewritten to only contain the
non-option command line arguments and the number of these is returned.
Aggregation of single switches is allowed:
-rC0 is the same as -r -C 0 (supposing that -C wants an arg).
Boolean switches automatically support the option with the same name,
prefixed with 'no-' to disable the switch:
--no-color / --color only need to have an entry for "color".
Long options are supported either with '=' or without:
--some-option=foo is the same as --some-option foo
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
I'm sorry about the "From" I don't intend to "steal" the patch in any
sense, it's just an alternate proposal.
oh and I don't grok what OPTION_LAST is for, so I left it apart, but
it seems unused ?
Makefile | 2 +-
parse-options.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
parse-options.h | 29 ++++++++++
3 files changed, 184 insertions(+), 1 deletions(-)
create mode 100644 parse-options.c
create mode 100644 parse-options.h
diff --git a/Makefile b/Makefile
index 62bdac6..d90e959 100644
--- a/Makefile
+++ b/Makefile
@@ -310,7 +310,7 @@ LIB_OBJS = \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
- transport.o bundle.o
+ transport.o bundle.o parse-options.o
BUILTIN_OBJS = \
builtin-add.o \
diff --git a/parse-options.c b/parse-options.c
new file mode 100644
index 0000000..eb3ff40
--- /dev/null
+++ b/parse-options.c
@@ -0,0 +1,154 @@
+#include "git-compat-util.h"
+#include "parse-options.h"
+
+struct optparse_t {
+ const char **argv;
+ int argc;
+ const char *opt;
+};
+
+static inline const char *skippfx(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+static int opterror(struct option *opt, const char *reason, int shorterr)
+{
+ if (shorterr) {
+ return error("switch `%c' %s", opt->short_name, reason);
+ } else {
+ return error("option `%s' %s", opt->long_name, reason);
+ }
+}
+
+static int get_value(struct optparse_t *p, struct option *opt,
+ int boolean, int shorterr)
+{
+ switch (opt->type) {
+ const char *s;
+ int v;
+
+ case OPTION_BOOLEAN:
+ *(int *)opt->value = boolean;
+ return 0;
+
+ case OPTION_STRING:
+ if (p->opt && *p->opt) {
+ *(const char **)opt->value = p->opt;
+ p->opt = NULL;
+ } else {
+ if (p->argc < 1)
+ return opterror(opt, "requires a value", shorterr);
+ *(const char **)opt->value = *++p->argv;
+ p->argc--;
+ }
+ return 0;
+
+ case OPTION_INTEGER:
+ if (p->opt && *p->opt) {
+ v = strtol(p->opt, (char **)&s, 10);
+ p->opt = NULL;
+ } else {
+ if (p->argc < 1)
+ return opterror(opt, "requires a value", shorterr);
+ v = strtol(*++p->argv, (char **)&s, 10);
+ p->argc--;
+ }
+ if (*s)
+ return opterror(opt, "expects a numerical value", shorterr);
+ *(int *)opt->value = v;
+ return 0;
+ }
+
+ abort();
+}
+
+static int parse_short_opt(struct optparse_t *p, struct option *options, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (options[i].short_name == *p->opt) {
+ p->opt++;
+ return get_value(p, options + i, 1, 1);
+ }
+ }
+ return error("unknown switch `%c'", *p->opt);
+}
+
+static int parse_long_opt(struct optparse_t *p, const char *arg,
+ struct option *options, int count)
+{
+ int boolean = 1;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ const char *rest;
+
+ if (!options[i].long_name)
+ continue;
+
+ rest = skippfx(arg, options[i].long_name);
+ if (!rest && options[i].type == OPTION_BOOLEAN) {
+ if (!rest && skippfx(arg, "no-")) {
+ rest = skippfx(arg + 3, options[i].long_name);
+ boolean = 0;
+ }
+ if (rest && *rest == '=')
+ return opterror(options + i, "takes no value", 0);
+ }
+ if (!rest || (*rest && *rest != '='))
+ continue;
+ if (*rest) {
+ p->opt = rest;
+ }
+ return get_value(p, options + i, boolean, 0);
+ }
+ return error("unknown option `%s'", arg);
+}
+
+int parse_options(int argc, const char **argv,
+ struct option *options, int count,
+ const char *usage_string)
+{
+ struct optparse_t optp = { argv + 1, argc - 1, NULL };
+ int j = 0;
+
+ while (optp.argc) {
+ const char *arg = optp.argv[0];
+
+ if (*arg != '-' || !arg[1]) {
+ argv[j++] = *optp.argv++;
+ optp.argc--;
+ continue;
+ }
+
+ if (arg[1] != '-') {
+ optp.opt = arg + 1;
+ while (*optp.opt) {
+ if (parse_short_opt(&optp, options, count) < 0) {
+ usage(usage_string);
+ return -1;
+ }
+ }
+ optp.argc--;
+ optp.argv++;
+ continue;
+ }
+
+ if (!arg[2]) /* "--" */
+ break;
+
+ if (parse_long_opt(&optp, arg + 2, options, count)) {
+ usage(usage_string);
+ return -1;
+ }
+ optp.argc--;
+ optp.argv++;
+ }
+
+ memmove(argv + j, optp.argv, optp.argc * sizeof(argv));
+ argv[j + optp.argc] = NULL;
+ return j + optp.argc;
+}
diff --git a/parse-options.h b/parse-options.h
new file mode 100644
index 0000000..e4749d0
--- /dev/null
+++ b/parse-options.h
@@ -0,0 +1,29 @@
+#ifndef PARSE_OPTIONS_H
+#define PARSE_OPTIONS_H
+
+enum option_type {
+ OPTION_BOOLEAN,
+ OPTION_STRING,
+ OPTION_INTEGER,
+#if 0
+ OPTION_LAST,
+#endif
+};
+
+struct option {
+ enum option_type type;
+ const char *long_name;
+ char short_name;
+ void *value;
+};
+
+/* parse_options() will filter out the processed options and leave the
+ * non-option argments in argv[]. The return value is the number of
+ * arguments left in argv[].
+ */
+
+extern int parse_options(int argc, const char **argv,
+ struct option *options, int count,
+ const char *usage_string);
+
+#endif
--
1.5.3.4.1156.ga72f9d-dirty
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
next prev parent reply other threads:[~2007-10-05 14:25 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-10-03 21:45 [PATCH] Add a simple option parser Kristian Høgsberg
2007-10-03 21:45 ` [PATCH] Port builtin-add.c to use the new " Kristian Høgsberg
2007-10-03 23:11 ` [PATCH] Add a simple " Pierre Habouzit
2007-10-04 14:57 ` Kristian Høgsberg
2007-10-04 15:15 ` Pierre Habouzit
2007-10-04 16:31 ` Pierre Habouzit
2007-10-04 16:39 ` Johannes Schindelin
2007-10-05 10:08 ` Pierre Habouzit
2007-10-05 14:21 ` Pierre Habouzit
2007-10-05 14:25 ` Pierre Habouzit [this message]
2007-10-05 14:30 ` [ALTERNATE PATCH] " Mike Hommey
2007-10-05 14:45 ` Pierre Habouzit
2007-10-05 15:45 ` Medve Emilian-EMMEDVE1
2007-10-05 15:56 ` Pierre Habouzit
2007-10-05 16:10 ` Medve Emilian-EMMEDVE1
2007-10-05 16:20 ` David Kastrup
2007-10-05 16:38 ` Pierre Habouzit
2007-10-06 8:46 ` Sven Verdoolaege
2007-10-05 16:28 ` Linus Torvalds
2007-10-05 16:41 ` Medve Emilian-EMMEDVE1
2007-10-05 16:49 ` Pierre Habouzit
2007-10-05 16:51 ` Linus Torvalds
2007-10-05 14:59 ` David Kastrup
2007-10-05 15:33 ` Kristian Høgsberg
2007-10-05 15:54 ` Pierre Habouzit
2007-10-07 17:01 ` Pierre Habouzit
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20071005142507.GL19879@artemis.corp \
--to=madcoder@debian.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=krh@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.