From: Pierre Habouzit <madcoder@debian•org>
To: gitster@pobox•com
To: Junio C Hamano <gitster@pobox•com>
Cc: git@vger•kernel.org, Pierre Habouzit <madcoder@debian•org>
Subject: [PATCH 4/4] Implement OPTION_SUBARRAY handling.
Date: Mon, 5 Nov 2007 13:03:24 +0100 [thread overview]
Message-ID: <1194264204-3475-5-git-send-email-madcoder@debian.org> (raw)
In-Reply-To: <1194264204-3475-4-git-send-email-madcoder@debian.org>
Basically, an OPTION_SUBARRAY is a pointer to a new `struct option` array.
This array should start with an OPTION_BASEOFFSET used to relocate ->value
pointers.
The sizeof() of the struct used for the BASEOFFSET entry is also stored so
that a subarray can also have global side effects (->values pointers that
are not in the memory range of the BASE struct are not relocated).
Arbitrary nesting of subarrays is supported, though no checks that there is
a subarray loop is performed.
Signed-off-by: Pierre Habouzit <madcoder@debian•org>
---
parse-options.c | 78 +++++++++++++++++++++++++++++++++++++++------
parse-options.h | 5 +++
t/t0040-parse-options.sh | 31 +++++++++++++++++-
test-parse-options.c | 25 ++++++++++++++-
4 files changed, 126 insertions(+), 13 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index 5cea511..001d1e5 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -4,6 +4,12 @@
#define OPT_SHORT 1
#define OPT_UNSET 2
+struct optreloc_t {
+ const char *base;
+ const char *end;
+ intptr_t offs;
+};
+
struct optparse_t {
const char **argv;
int argc;
@@ -11,6 +17,8 @@ struct optparse_t {
const struct option *abbrev_option, *conflict_option;
int abbrev_flags, conflict_flags;
+
+ struct optreloc_t reloc;
};
static inline const char *get_arg(struct optparse_t *p)
@@ -39,10 +47,32 @@ static int opterror(const struct option *opt, const char *reason, int flags)
return error("option `%s' %s", opt->long_name, reason);
}
+static void *reloc_value(struct optparse_t *p, const struct option *opt)
+{
+ char *value = opt->value;
+ if (value >= p->reloc.base && value < p->reloc.end)
+ return value + p->reloc.offs;
+ return value;
+}
+
+static const struct option *reloc_option(struct optparse_t *p,
+ const struct option *opt,
+ struct option *buf)
+{
+ char *value = opt->value;
+ if (value >= p->reloc.base && value < p->reloc.end) {
+ *buf = *opt;
+ buf->value = value + p->reloc.offs;
+ return buf;
+ }
+ return opt;
+}
+
static int get_value(struct optparse_t *p,
const struct option *opt, int flags)
{
const char *s, *arg;
+ struct option tmp;
arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
if (p->opt && (flags & OPT_UNSET))
@@ -53,26 +83,27 @@ static int get_value(struct optparse_t *p,
if (!(flags & OPT_SHORT) && p->opt)
return opterror(opt, "takes no value", flags);
if (flags & OPT_UNSET)
- *(int *)opt->value = 0;
+ *(int *)reloc_value(p, opt) = 0;
else
- (*(int *)opt->value)++;
+ (*(int *)reloc_value(p, opt))++;
return 0;
case OPTION_STRING:
if (flags & OPT_UNSET) {
- *(const char **)opt->value = (const char *)NULL;
+ *(const char **)reloc_value(p, opt) = (const char *)NULL;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) {
- *(const char **)opt->value = (const char *)opt->defval;
+ *(const char **)reloc_value(p, opt) = (const char *)opt->defval;
return 0;
}
if (!arg)
return opterror(opt, "requires a value", flags);
- *(const char **)opt->value = get_arg(p);
+ *(const char **)reloc_value(p, opt) = get_arg(p);
return 0;
case OPTION_CALLBACK:
+ opt = reloc_option(p, opt, &tmp);
if (flags & OPT_UNSET)
return (*opt->callback)(opt, NULL, 1);
if (opt->flags & PARSE_OPT_NOARG) {
@@ -88,16 +119,16 @@ static int get_value(struct optparse_t *p,
case OPTION_INTEGER:
if (flags & OPT_UNSET) {
- *(int *)opt->value = 0;
+ *(int *)reloc_value(p, opt) = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) {
- *(int *)opt->value = opt->defval;
+ *(int *)reloc_value(p, opt) = opt->defval;
return 0;
}
if (!arg)
return opterror(opt, "requires a value", flags);
- *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);
+ *(int *)reloc_value(p, opt) = strtol(get_arg(p), (char **)&s, 10);
if (*s)
return opterror(opt, "expects a numerical value", flags);
return 0;
@@ -107,9 +138,24 @@ static int get_value(struct optparse_t *p,
}
}
+static const struct option *prepare_recursion(struct optparse_t *p,
+ const struct option *opt)
+{
+ const struct option *subarray = (const struct option *)opt->defval;
+ if (subarray->type != OPTION_BASEOFFSET)
+ die("subarray does not begins with a relocation stanza");
+ p->reloc.base = subarray->value;
+ p->reloc.end = p->reloc.base + subarray->defval;
+ p->reloc.offs = (const char *)opt->value - p->reloc.base;
+ return ++subarray;
+}
+
static int parse_short_opt(struct optparse_t *p, const struct option *options,
int level)
{
+ struct optreloc_t reloc;
+ int res;
+
for (;; options++) {
switch (options->type) {
case OPTION_END:
@@ -118,7 +164,11 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options,
case OPTION_BASEOFFSET:
continue;
case OPTION_SUBARRAY:
- die("unsupported yet");
+ reloc = p->reloc;
+ res = parse_short_opt(p, prepare_recursion(p, options), level + 1);
+ p->reloc = reloc;
+ if (!res)
+ return 0;
break;
default:
if (options->short_name != *p->opt)
@@ -141,15 +191,21 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
arg_end = arg + strlen(arg);
for (; options->type != OPTION_END; options++) {
+ struct optreloc_t reloc;
const char *rest;
- int flags = 0;
+ int res, flags = 0;
switch (options->type) {
case OPTION_GROUP:
case OPTION_BASEOFFSET:
continue;
case OPTION_SUBARRAY:
- die("unsupported yet");
+ reloc = p->reloc;
+ res = parse_long_opt(p, arg, prepare_recursion(p, options),
+ level + 1);
+ p->reloc = reloc;
+ if (!res)
+ return 0;
break;
default:
break;
diff --git a/parse-options.h b/parse-options.h
index 6668924..4f5a241 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -84,6 +84,11 @@ struct option {
#define OPT_CALLBACK(s, l, v, a, h, f) \
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
+#define OPT_BASEOFFS(v) \
+ { OPTION_BASEOFFSET, 0, NULL, (v), NULL, NULL, 0, NULL, sizeof(*(v)) }
+#define OPT_SUBARRAY(v, sub) \
+ { OPTION_SUBARRAY, 0, NULL, (v), NULL, NULL, 0, NULL, (intptr_t)sub }
+
/* parse_options() will filter out the processed options and leave the
* non-option argments in argv[].
* Returns the number of arguments left in argv[].
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ee758e5..c7a754e 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -20,6 +20,10 @@ string options
--string2 <str> get another string
--st <st> get another string (pervert ordering)
+test subarray
+ --incr-reloc increment a relocated integer
+ --incr-fixed increment a fixed integer
+
EOF
test_expect_success 'test help' '
@@ -32,6 +36,8 @@ cat > expect << EOF
boolean: 2
integer: 1729
string: 123
+diverted i: 0
+fixed i: 0
EOF
test_expect_success 'short options' '
@@ -43,6 +49,8 @@ cat > expect << EOF
boolean: 2
integer: 1729
string: 321
+diverted i: 0
+fixed i: 0
EOF
test_expect_success 'long options' '
@@ -56,6 +64,8 @@ cat > expect << EOF
boolean: 1
integer: 13
string: 123
+diverted i: 0
+fixed i: 0
arg 00: a1
arg 01: b1
arg 02: --boolean
@@ -72,6 +82,8 @@ cat > expect << EOF
boolean: 0
integer: 2
string: (not set)
+diverted i: 0
+fixed i: 0
EOF
test_expect_success 'unambiguously abbreviated option' '
@@ -93,11 +105,28 @@ test_expect_failure 'ambiguously abbreviated option' '
cat > expect << EOF
boolean: 0
+integer: 0
+string: (not set)
+diverted i: 1
+fixed i: 2
+EOF
+
+test_expect_success 'subarrays and partial relocation of options' '
+ test-parse-options --incr-reloc --incr-fixed --incr-fixed > output 2> output.err &&
+ test ! -s output.err &&
+ git diff expect output
+'
+
+test_done
+cat > expect << EOF
+boolean: 0
integer: 2
string: 123
+diverted i: 0
+fixed i: 0
EOF
-test_expect_failure 'non ambiguous option (after two options it abbreviates)' '
+test_expect_failure 'non ambiguous option (after two options it abbreviates, across subarray)' '
test-parse-options --st 123 &&
test ! -s output.err &&
git diff expect output
diff --git a/test-parse-options.c b/test-parse-options.c
index 4d3e2ec..5f740da 100644
--- a/test-parse-options.c
+++ b/test-parse-options.c
@@ -1,16 +1,34 @@
#include "cache.h"
#include "parse-options.h"
+struct reloc_me_please {
+ int integer;
+};
+
static int boolean = 0;
static int integer = 0;
static char *string = NULL;
+static int reloc_integer = 0;
+static int fixed_integer = 0;
+
+static const struct option subopts[] = {
+ OPT_BASEOFFS(&reloc_integer),
+ OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
+ OPT_GROUP("test subarray"),
+ OPT_BOOLEAN(0, "incr-reloc", &reloc_integer, "increment a relocated integer"),
+ OPT_BOOLEAN(0, "incr-fixed", &fixed_integer, "increment a fixed integer"),
+ OPT_END(),
+};
+
int main(int argc, const char **argv)
{
const char *usage[] = {
"test-parse-options <options>",
NULL
};
+ int diverted_integer = 0;
+
struct option options[] = {
OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"),
OPT_INTEGER('i', "integer", &integer, "get a integer"),
@@ -18,7 +36,7 @@ int main(int argc, const char **argv)
OPT_GROUP("string options"),
OPT_STRING('s', "string", &string, "string", "get a string"),
OPT_STRING(0, "string2", &string, "str", "get another string"),
- OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
+ OPT_SUBARRAY(&diverted_integer, subopts),
OPT_END(),
};
int i;
@@ -28,9 +46,14 @@ int main(int argc, const char **argv)
printf("boolean: %d\n", boolean);
printf("integer: %d\n", integer);
printf("string: %s\n", string ? string : "(not set)");
+ printf("diverted i: %d\n", diverted_integer);
+ printf("fixed i: %d\n", fixed_integer);
for (i = 0; i < argc; i++)
printf("arg %02d: %s\n", i, argv[i]);
+ if (reloc_integer)
+ die("reloc_integer should not ever change");
+
return 0;
}
--
1.5.3.5.1531.g59008
next prev parent reply other threads:[~2007-11-05 12:03 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-11-05 12:03 proposal for an OPTION_SUBARRAY (recursive parser) Pierre Habouzit
2007-11-05 12:03 ` [PATCH 1/4] parse-options: abbreviation engine fix Pierre Habouzit
2007-11-05 12:03 ` [PATCH 2/4] Some better parse-options documentation Pierre Habouzit
2007-11-05 12:03 ` [PATCH 3/4] Add OPTION_BASEOFFSET/OPTION_SUBARRAY Pierre Habouzit
2007-11-05 12:03 ` Pierre Habouzit [this message]
2007-11-05 12:34 ` [PATCH] parse-options: abbreviation engine fix Johannes Schindelin
2007-11-05 12:38 ` Johannes Schindelin
2007-11-05 13:15 ` [PATCH 1/3] " Johannes Schindelin
2007-11-05 13:15 ` [PATCH 2/3] parseopt: introduce OPT_RECURSE to specify shared options Johannes Schindelin
2007-11-05 13:46 ` Johannes Schindelin
2007-11-05 16:15 ` Linus Torvalds
2007-11-05 16:29 ` Johannes Schindelin
2007-11-05 16:53 ` Pierre Habouzit
2007-11-05 21:48 ` Junio C Hamano
2007-11-05 22:14 ` Pierre Habouzit
2007-11-05 13:15 ` [PATCH 3/3] parseopt: do not list options with the same name twice Johannes Schindelin
2007-11-05 12:59 ` [PATCH] parse-options: abbreviation engine fix 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=1194264204-3475-5-git-send-email-madcoder@debian.org \
--to=madcoder@debian$(echo .)org \
--cc=git@vger$(echo .)kernel.org \
--cc=gitster@pobox$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox