git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [proposal] make parse-options nicer wrt optional arguments (supersedes all my recent posts on the matter)
@ 2007-12-17 18:23 Pierre Habouzit
  2007-12-17 18:23 ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit
  0 siblings, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git

Here is a series that aims at fixing the various issues with
parse-options that were raised recently.

* preliminary patch:
  [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset`

* teach git parse-options to allow callbacks to ignore arguments that
  don't seem to be theirs, refactors:
  [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use.
  [PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well.
  [PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments.

* Document this (my previous proposal + Junio's squashed):
  [PATCH 5/7] parse-options: Add a gitcli(5) man page.

* Implement my `{}` proposal, a sed -e s/{}/_/ will replace {} with _
  as a wildcard. Contains documentation for this placeholder.
  [PATCH 6/7] parse-options: have a `use default value` wildcard.

* Somehow unrelated patch, but still parse-option related (resend):
  [PATCH 7/7] git-tag: fix -l switch handling regression.



This has been pushed as my ph/parseopt branch on
git://git.madism.org/git.git.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset`
  2007-12-17 18:23 [proposal] make parse-options nicer wrt optional arguments (supersedes all my recent posts on the matter) Pierre Habouzit
@ 2007-12-17 18:23 ` Pierre Habouzit
  2007-12-17 18:23   ` [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use Pierre Habouzit
  2007-12-17 18:54   ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit
  0 siblings, 2 replies; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 builtin-branch.c       |    2 +-
 builtin-commit.c       |    4 ++--
 builtin-fast-export.c  |    4 ++--
 builtin-for-each-ref.c |    2 +-
 builtin-tag.c          |    2 +-
 parse-options.c        |   37 ++++++++++++++++---------------------
 parse-options.h        |    7 ++++++-
 7 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 089cae5..677eee5 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -531,7 +531,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 		die("Branch is renamed, but update of config-file failed");
 }
 
-static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
+static int opt_parse_with_commit(const struct option *opt, const char *arg, int flags)
 {
 	unsigned char sha1[20];
 	struct commit *commit;
diff --git a/builtin-commit.c b/builtin-commit.c
index 0a91013..ca18a5c 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -52,10 +52,10 @@ static int no_edit, initial_commit, in_merge;
 const char *only_include_assumed;
 struct strbuf message;
 
-static int opt_parse_m(const struct option *opt, const char *arg, int unset)
+static int opt_parse_m(const struct option *opt, const char *arg, int flags)
 {
 	struct strbuf *buf = opt->value;
-	if (unset)
+	if (flags & PARSE_OPT_UNSET)
 		strbuf_setlen(buf, 0);
 	else {
 		strbuf_addstr(buf, arg);
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index ef27eee..9f914b9 100755
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -26,9 +26,9 @@ static int progress;
 static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
-				     const char *arg, int unset)
+				     const char *arg, int flags)
 {
-	if (unset || !strcmp(arg, "abort"))
+	if (flags & PARSE_OPT_UNSET || !strcmp(arg, "abort"))
 		signed_tag_mode = ABORT;
 	else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
 		signed_tag_mode = VERBATIM;
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index f36a43c..3eecfe9 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -802,7 +802,7 @@ static struct ref_sort *default_sort(void)
 	return sort;
 }
 
-int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+int opt_parse_sort(const struct option *opt, const char *arg, int flags)
 {
 	struct ref_sort **sort_tail = opt->value;
 	struct ref_sort *s;
diff --git a/builtin-tag.c b/builtin-tag.c
index 274901a..fd44b2e 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -348,7 +348,7 @@ struct msg_arg {
 	struct strbuf buf;
 };
 
-static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
+static int parse_msg_arg(const struct option *opt, const char *arg, int flags)
 {
 	struct msg_arg *msg = opt->value;
 
diff --git a/parse-options.c b/parse-options.c
index e12b428..8f70e5d 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1,9 +1,6 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
 
-#define OPT_SHORT 1
-#define OPT_UNSET 2
-
 struct optparse_t {
 	const char **argv;
 	int argc;
@@ -29,9 +26,9 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
 
 static int opterror(const struct option *opt, const char *reason, int flags)
 {
-	if (flags & OPT_SHORT)
+	if (flags & PARSE_OPT_SHORT)
 		return error("switch `%c' %s", opt->short_name, reason);
-	if (flags & OPT_UNSET)
+	if (flags & PARSE_OPT_UNSET)
 		return error("option `no-%s' %s", opt->long_name, reason);
 	return error("option `%s' %s", opt->long_name, reason);
 }
@@ -40,14 +37,14 @@ static int get_value(struct optparse_t *p,
                      const struct option *opt, int flags)
 {
 	const char *s, *arg;
-	const int unset = flags & OPT_UNSET;
+	const int unset = flags & PARSE_OPT_UNSET;
 
 	if (unset && p->opt)
 		return opterror(opt, "takes no value", flags);
 	if (unset && (opt->flags & PARSE_OPT_NONEG))
 		return opterror(opt, "isn't available", flags);
 
-	if (!(flags & OPT_SHORT) && p->opt) {
+	if (!(flags & PARSE_OPT_SHORT) && p->opt) {
 		switch (opt->type) {
 		case OPTION_CALLBACK:
 			if (!(opt->flags & PARSE_OPT_NOARG))
@@ -99,15 +96,13 @@ static int get_value(struct optparse_t *p,
 		return 0;
 
 	case OPTION_CALLBACK:
-		if (unset)
-			return (*opt->callback)(opt, NULL, 1);
-		if (opt->flags & PARSE_OPT_NOARG)
-			return (*opt->callback)(opt, NULL, 0);
+		if (unset || (opt->flags & PARSE_OPT_NOARG))
+			return (*opt->callback)(opt, NULL, flags);
 		if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-'))
-			return (*opt->callback)(opt, NULL, 0);
+			return (*opt->callback)(opt, NULL, flags);
 		if (!arg)
 			return opterror(opt, "requires a value", flags);
-		return (*opt->callback)(opt, get_arg(p), 0);
+		return (*opt->callback)(opt, get_arg(p), flags);
 
 	case OPTION_INTEGER:
 		if (unset) {
@@ -135,7 +130,7 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options)
 	for (; options->type != OPTION_END; options++) {
 		if (options->short_name == *p->opt) {
 			p->opt = p->opt[1] ? p->opt + 1 : NULL;
-			return get_value(p, options, OPT_SHORT);
+			return get_value(p, options, PARSE_OPT_SHORT);
 		}
 	}
 	return error("unknown switch `%c'", *p->opt);
@@ -173,7 +168,7 @@ is_abbreviated:
 					ambiguous_option = abbrev_option;
 					ambiguous_flags = abbrev_flags;
 				}
-				if (!(flags & OPT_UNSET) && *arg_end)
+				if (!(flags & PARSE_OPT_UNSET) && *arg_end)
 					p->opt = arg_end + 1;
 				abbrev_option = options;
 				abbrev_flags = flags;
@@ -181,13 +176,13 @@ is_abbreviated:
 			}
 			/* negated and abbreviated very much? */
 			if (!prefixcmp("no-", arg)) {
-				flags |= OPT_UNSET;
+				flags |= PARSE_OPT_UNSET;
 				goto is_abbreviated;
 			}
 			/* negated? */
 			if (strncmp(arg, "no-", 3))
 				continue;
-			flags |= OPT_UNSET;
+			flags |= PARSE_OPT_UNSET;
 			rest = skip_prefix(arg + 3, options->long_name);
 			/* abbreviated and negated? */
 			if (!rest && !prefixcmp(options->long_name, arg + 3))
@@ -207,9 +202,9 @@ is_abbreviated:
 		return error("Ambiguous option: %s "
 			"(could be --%s%s or --%s%s)",
 			arg,
-			(ambiguous_flags & OPT_UNSET) ?  "no-" : "",
+			(ambiguous_flags & PARSE_OPT_UNSET) ?  "no-" : "",
 			ambiguous_option->long_name,
-			(abbrev_flags & OPT_UNSET) ?  "no-" : "",
+			(abbrev_flags & PARSE_OPT_UNSET) ?  "no-" : "",
 			abbrev_option->long_name);
 	if (abbrev_option)
 		return get_value(p, abbrev_option, abbrev_flags);
@@ -351,12 +346,12 @@ void usage_with_options(const char * const *usagestr,
 /*----- some often used options -----*/
 #include "cache.h"
 
-int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
+int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int flags)
 {
 	int v;
 
 	if (!arg) {
-		v = unset ? 0 : DEFAULT_ABBREV;
+		v = flags & PARSE_OPT_UNSET ? 0 : DEFAULT_ABBREV;
 	} else {
 		v = strtol(arg, (char **)&arg, 10);
 		if (*arg)
diff --git a/parse-options.h b/parse-options.h
index 102ac31..ae6b3ca 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -27,8 +27,13 @@ enum parse_opt_option_flags {
 	PARSE_OPT_HIDDEN  = 8,
 };
 
+enum parse_opt_cbflags {
+	PARSE_OPT_SHORT   = 1,
+	PARSE_OPT_UNSET   = 2,
+};
+
 struct option;
-typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
+typedef int parse_opt_cb(const struct option *, const char *arg, int flags);
 
 /*
  * `type`::
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use.
  2007-12-17 18:23 ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit
@ 2007-12-17 18:23   ` Pierre Habouzit
  2007-12-17 18:23     ` [PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well Pierre Habouzit
  2007-12-17 18:54   ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit
  1 sibling, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 parse-options.c |   22 +++++++++++++++-------
 parse-options.h |    7 +++++++
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/parse-options.c b/parse-options.c
index 8f70e5d..d716ccc 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -38,7 +38,10 @@ static int get_value(struct optparse_t *p,
 {
 	const char *s, *arg;
 	const int unset = flags & PARSE_OPT_UNSET;
+	int may_ign = 0, res;
 
+	if (!unset && !p->opt && (opt->flags & PARSE_OPT_OPTARG))
+		may_ign = PARSE_OPT_MAY_IGN;
 	if (unset && p->opt)
 		return opterror(opt, "takes no value", flags);
 	if (unset && (opt->flags & PARSE_OPT_NONEG))
@@ -86,7 +89,7 @@ static int get_value(struct optparse_t *p,
 			*(const char **)opt->value = NULL;
 			return 0;
 		}
-		if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) {
+		if (may_ign && (!arg || *arg == '-')) {
 			*(const char **)opt->value = (const char *)opt->defval;
 			return 0;
 		}
@@ -98,18 +101,23 @@ static int get_value(struct optparse_t *p,
 	case OPTION_CALLBACK:
 		if (unset || (opt->flags & PARSE_OPT_NOARG))
 			return (*opt->callback)(opt, NULL, flags);
-		if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-'))
-			return (*opt->callback)(opt, NULL, flags);
-		if (!arg)
+		if (!may_ign && !arg)
 			return opterror(opt, "requires a value", flags);
-		return (*opt->callback)(opt, get_arg(p), flags);
+		if (may_ign && arg && arg[0] == '-' && arg[1])
+			return (*opt->callback)(opt, NULL, flags);
+		res = (*opt->callback)(opt, arg, flags);
+		if (!may_ign && res == PARSE_OPT_IGNORE)
+			die("should not happen: MAY_IGN unset, but arg was IGNOREd");
+		if (res == PARSE_OPT_IGNORE)
+			get_arg(p);
+		return res;
 
 	case OPTION_INTEGER:
 		if (unset) {
 			*(int *)opt->value = 0;
 			return 0;
 		}
-		if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) {
+		if (may_ign && (!arg || !isdigit(*arg))) {
 			*(int *)opt->value = opt->defval;
 			return 0;
 		}
@@ -251,7 +259,7 @@ int parse_options(int argc, const char **argv, const struct option *options,
 			usage_with_options_internal(usagestr, options, 1);
 		if (!strcmp(arg + 2, "help"))
 			usage_with_options(usagestr, options);
-		if (parse_long_opt(&args, arg + 2, options))
+		if (parse_long_opt(&args, arg + 2, options) < 0)
 			usage_with_options(usagestr, options);
 	}
 
diff --git a/parse-options.h b/parse-options.h
index ae6b3ca..eeb40a4 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -27,9 +27,16 @@ enum parse_opt_option_flags {
 	PARSE_OPT_HIDDEN  = 8,
 };
 
+enum parse_opt_cbres {
+	PARSE_OPT_ERR     = -1,
+	PARSE_OPT_OK      =  0,
+	PARSE_OPT_IGNORE  =  1,
+};
+
 enum parse_opt_cbflags {
 	PARSE_OPT_SHORT   = 1,
 	PARSE_OPT_UNSET   = 2,
+	PARSE_OPT_MAY_IGN = 4,
 };
 
 struct option;
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well.
  2007-12-17 18:23   ` [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use Pierre Habouzit
@ 2007-12-17 18:23     ` Pierre Habouzit
  2007-12-17 18:23       ` [PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments Pierre Habouzit
  0 siblings, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 parse-options.c |  115 ++++++++++++++++++++++++++++---------------------------
 1 files changed, 59 insertions(+), 56 deletions(-)

diff --git a/parse-options.c b/parse-options.c
index d716ccc..f3f0f2a 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -7,15 +7,14 @@ struct optparse_t {
 	const char *opt;
 };
 
-static inline const char *get_arg(struct optparse_t *p)
+static inline void use_arg(struct optparse_t *p)
 {
 	if (p->opt) {
-		const char *res = p->opt;
 		p->opt = NULL;
-		return res;
+	} else {
+		p->argc--;
+		++p->argv;
 	}
-	p->argc--;
-	return *++p->argv;
 }
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
@@ -33,15 +32,62 @@ static int opterror(const struct option *opt, const char *reason, int flags)
 	return error("option `%s' %s", opt->long_name, reason);
 }
 
+static int parse_opt_string(const struct option *opt,
+                            const char *arg, int flags)
+{
+	*(const char **)opt->value = flags & PARSE_OPT_UNSET ? NULL : arg;
+	return 0;
+}
+
+static int parse_opt_integer(const struct option *opt,
+                             const char *arg, int flags)
+{
+	int v = flags & PARSE_OPT_UNSET ? 0 : opt->defval;
+	if (arg) {
+		v = strtol(arg, (char **)&arg, 10);
+		if (*arg) {
+			if (flags & PARSE_OPT_MAY_IGN) {
+				*(int *)opt->value = opt->defval;
+				return PARSE_OPT_IGNORE;
+			}
+			return opterror(opt, "expects a numerical value", 0);
+		}
+	}
+	*(int *)opt->value = v;
+	return 0;
+}
+
+static int run_callback(struct optparse_t *p, parse_opt_cb *cb,
+						const struct option *opt, int flags)
+{
+	const char *arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
+	int may_ign = 0;
+
+	if (!p->opt && (opt->flags & PARSE_OPT_OPTARG))
+		may_ign = PARSE_OPT_MAY_IGN;
+	if ((flags & PARSE_OPT_UNSET) || (opt->flags & PARSE_OPT_NOARG))
+		return (*cb)(opt, NULL, flags);
+	if (!may_ign && !arg)
+		return opterror(opt, "requires a value", flags);
+	if (may_ign && arg && arg[0] == '-' && arg[1])
+		return (*cb)(opt, NULL, flags);
+	switch ((*cb)(opt, arg, flags | may_ign)) {
+	case PARSE_OPT_OK:
+		use_arg(p);
+		return PARSE_OPT_OK;
+	case PARSE_OPT_IGNORE:
+		if (!may_ign)
+			die("should not happen: MAY_IGN unset, but arg was IGNOREd");
+		return PARSE_OPT_IGNORE;
+	default:
+		return PARSE_OPT_ERR;
+	}
+}
+
 static int get_value(struct optparse_t *p,
                      const struct option *opt, int flags)
 {
-	const char *s, *arg;
 	const int unset = flags & PARSE_OPT_UNSET;
-	int may_ign = 0, res;
-
-	if (!unset && !p->opt && (opt->flags & PARSE_OPT_OPTARG))
-		may_ign = PARSE_OPT_MAY_IGN;
 	if (unset && p->opt)
 		return opterror(opt, "takes no value", flags);
 	if (unset && (opt->flags & PARSE_OPT_NONEG))
@@ -63,7 +109,6 @@ static int get_value(struct optparse_t *p,
 		}
 	}
 
-	arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
 	switch (opt->type) {
 	case OPTION_BIT:
 		if (unset)
@@ -71,63 +116,21 @@ static int get_value(struct optparse_t *p,
 		else
 			*(int *)opt->value |= opt->defval;
 		return 0;
-
 	case OPTION_BOOLEAN:
 		*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
 		return 0;
-
 	case OPTION_SET_INT:
 		*(int *)opt->value = unset ? 0 : opt->defval;
 		return 0;
-
 	case OPTION_SET_PTR:
 		*(void **)opt->value = unset ? NULL : (void *)opt->defval;
 		return 0;
-
 	case OPTION_STRING:
-		if (unset) {
-			*(const char **)opt->value = NULL;
-			return 0;
-		}
-		if (may_ign && (!arg || *arg == '-')) {
-			*(const char **)opt->value = (const char *)opt->defval;
-			return 0;
-		}
-		if (!arg)
-			return opterror(opt, "requires a value", flags);
-		*(const char **)opt->value = get_arg(p);
-		return 0;
-
+		return run_callback(p, &parse_opt_string, opt, flags);
 	case OPTION_CALLBACK:
-		if (unset || (opt->flags & PARSE_OPT_NOARG))
-			return (*opt->callback)(opt, NULL, flags);
-		if (!may_ign && !arg)
-			return opterror(opt, "requires a value", flags);
-		if (may_ign && arg && arg[0] == '-' && arg[1])
-			return (*opt->callback)(opt, NULL, flags);
-		res = (*opt->callback)(opt, arg, flags);
-		if (!may_ign && res == PARSE_OPT_IGNORE)
-			die("should not happen: MAY_IGN unset, but arg was IGNOREd");
-		if (res == PARSE_OPT_IGNORE)
-			get_arg(p);
-		return res;
-
+		return run_callback(p, opt->callback, opt, flags);
 	case OPTION_INTEGER:
-		if (unset) {
-			*(int *)opt->value = 0;
-			return 0;
-		}
-		if (may_ign && (!arg || !isdigit(*arg))) {
-			*(int *)opt->value = opt->defval;
-			return 0;
-		}
-		if (!arg)
-			return opterror(opt, "requires a value", flags);
-		*(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);
-		if (*s)
-			return opterror(opt, "expects a numerical value", flags);
-		return 0;
-
+		return run_callback(p, &parse_opt_integer, opt, flags);
 	default:
 		die("should not happen, someone must be hit on the forehead");
 	}
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments.
  2007-12-17 18:23     ` [PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well Pierre Habouzit
@ 2007-12-17 18:23       ` Pierre Habouzit
  2007-12-17 18:23         ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Pierre Habouzit
  0 siblings, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 parse-options.c |   16 +++++++++-------
 1 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/parse-options.c b/parse-options.c
index f3f0f2a..679a963 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -359,19 +359,21 @@ void usage_with_options(const char * const *usagestr,
 
 int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int flags)
 {
-	int v;
-
-	if (!arg) {
-		v = flags & PARSE_OPT_UNSET ? 0 : DEFAULT_ABBREV;
-	} else {
+	int v = flags & PARSE_OPT_UNSET ? 0 : DEFAULT_ABBREV;
+	if (arg) {
 		v = strtol(arg, (char **)&arg, 10);
-		if (*arg)
+		if (*arg) {
+			if (flags & PARSE_OPT_MAY_IGN) {
+				*(int *)opt->value = DEFAULT_ABBREV;
+				return PARSE_OPT_IGNORE;
+			}
 			return opterror(opt, "expects a numerical value", 0);
+		}
 		if (v && v < MINIMUM_ABBREV)
 			v = MINIMUM_ABBREV;
 		else if (v > 40)
 			v = 40;
 	}
-	*(int *)(opt->value) = v;
+	*(int *)opt->value = v;
 	return 0;
 }
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/7] parse-options: Add a gitcli(5) man page.
  2007-12-17 18:23       ` [PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments Pierre Habouzit
@ 2007-12-17 18:23         ` Pierre Habouzit
  2007-12-17 18:23           ` [PATCH 6/7] parse-options: have a `use default value` wildcard Pierre Habouzit
  2007-12-18  2:00           ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Wayne Davison
  0 siblings, 2 replies; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

This page should hold every information about the git ways to parse command
lines, and best practices to be used for scripting.

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 Documentation/Makefile   |    2 +-
 Documentation/gitcli.txt |  113 ++++++++++++++++++++++++++++++++++++++++++++++
 Makefile                 |    1 +
 3 files changed, 115 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/gitcli.txt

diff --git a/Documentation/Makefile b/Documentation/Makefile
index 76df06c..c4486d3 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -2,7 +2,7 @@ MAN1_TXT= \
 	$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
 		$(wildcard git-*.txt)) \
 	gitk.txt
-MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt
+MAN5_TXT=gitattributes.txt gitignore.txt gitcli.txt gitmodules.txt
 MAN7_TXT=git.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
new file mode 100644
index 0000000..b7dcf9c
--- /dev/null
+++ b/Documentation/gitcli.txt
@@ -0,0 +1,113 @@
+gitcli(5)
+=========
+
+NAME
+----
+gitcli - git command line interface and conventions
+
+SYNOPSIS
+--------
+gitcli
+
+
+DESCRIPTION
+-----------
+
+This manual describes best practice in how to use git CLI.  Here are
+the rules that you should follow when you are scripting git:
+
+ * it's preferred to use the non dashed form of git commands, which means that
+   you should prefer `"git foo"` to `"git-foo"`.
+
+ * splitting short options to separate words (prefer `"git foo -a -b"`
+   to `"git foo -ab"`, the latter may not even work).
+
+ * when a command line option takes an argument, use the 'sticked' form.  In
+   other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short
+   options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"`
+   for long options.  An option that takes optional option-argument must be
+   written in the 'sticked' form.
+
+ * when you give a revision parameter to a command, make sure the parameter is
+   not ambiguous with a name of a file in the work tree.  E.g. do not write
+   `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work
+   if you happen to have a file called `HEAD` in the work tree.
+
+
+ENHANCED CLI
+------------
+From the git 1.5.4 series and further, many git commands (not all of them at the
+time of the writing though) come with an enhanced option parser.
+
+Here is an exhaustive list of the facilities provided by this option parser.
+
+
+Magic Options
+~~~~~~~~~~~~~
+Commands which have the enhanced option parser activated all understand a
+couple of magic command line options:
+
+-h::
+	gives a pretty printed usage of the command.
++
+---------------------------------------------
+$ git describe -h
+usage: git-describe [options] <committish>*
+
+    --contains            find the tag that comes after the commit
+    --debug               debug search strategy on stderr
+    --all                 use any ref in .git/refs
+    --tags                use any tag in .git/refs/tags
+    --abbrev [<n>]        use <n> digits to display SHA-1s
+    --candidates <n>      consider <n> most recent tags (default: 10)
+---------------------------------------------
+
+--help-all::
+	Some git commands take options that are only used for plumbing or that
+	are deprecated, and such options are hidden from the default usage. This
+	option gives the full list of options.
+
+
+Negating options
+~~~~~~~~~~~~~~~~
+Options with long option names can be negated by prefixing `"--no-"`. For
+example, `"git branch"` has the option `"--track"` which is 'on' by default. You
+can use `"--no-track"` to override that behaviour. The same goes for `"--color"`
+and `"--no-color"`.
+
+
+Aggregating short options
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Commands that support the enhanced option parser allow you to aggregate short
+options. This means that you can for example use `"git rm -rf"` or
+`"git clean -fdx"`.
+
+
+Separating argument from the option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You can write the mandatory option parameter to an option as a separate
+word on the command line.  That means that all the following uses work:
+
+----------------------------
+$ git foo --long-opt=Arg
+$ git foo --long-opt Arg
+$ git foo -oArg
+$ git foo -o Arg
+----------------------------
+
+However, this is *NOT* allowed for switches with an optionnal value, where the
+'sticked' form must be used:
+----------------------------
+$ git describe --abbrev HEAD     # correct
+$ git describe --abbrev=10 HEAD  # correct
+$ git describe --abbrev 10 HEAD  # NOT WHAT YOU MEANT
+----------------------------
+
+
+Documentation
+-------------
+Documentation by Pierre Habouzit.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 7776077..eda7b1a 100644
--- a/Makefile
+++ b/Makefile
@@ -1172,6 +1172,7 @@ check-docs::
 		documented,gitattributes | \
 		documented,gitignore | \
 		documented,gitmodules | \
+		documented,gitcli | \
 		documented,git-tools | \
 		sentinel,not,matching,is,ok ) continue ;; \
 		esac; \
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/7] parse-options: have a `use default value` wildcard.
  2007-12-17 18:23         ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Pierre Habouzit
@ 2007-12-17 18:23           ` Pierre Habouzit
  2007-12-17 18:23             ` [PATCH 7/7] git-tag: fix -l switch handling regression Pierre Habouzit
  2007-12-18  2:00           ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Wayne Davison
  1 sibling, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 Documentation/gitcli.txt |   20 +++++++++++++++-----
 parse-options.c          |   10 ++++++++--
 2 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index b7dcf9c..a304072 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -95,14 +95,24 @@ $ git foo -oArg
 $ git foo -o Arg
 ----------------------------
 
-However, this is *NOT* allowed for switches with an optionnal value, where the
-'sticked' form must be used:
+However, this may become ambiguous for switches with an optional value. The
+enhanced option parser provides a placeholder `{}` that tells to the option
+parser that it should not try to find an argument to this switch.  Though if
+you use '{}' sticked to the option, `{}` is passed as the value.
 ----------------------------
-$ git describe --abbrev HEAD     # correct
-$ git describe --abbrev=10 HEAD  # correct
-$ git describe --abbrev 10 HEAD  # NOT WHAT YOU MEANT
+# all the following uses work
+$ git describe --abbrev HEAD
+$ git describe --abbrev {} HEAD
+$ git describe --abbrev=10 HEAD
+$ git describe --abbrev 10 HEAD
+
+# doesn't work
+$ git describe --abbrev={} HEAD
 ----------------------------
 
+Note that an optional switch will never try to use the next token as an
+argument if it starts with a dash and is not `-`.
+
 
 Documentation
 -------------
diff --git a/parse-options.c b/parse-options.c
index 679a963..8734bb1 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -69,8 +69,14 @@ static int run_callback(struct optparse_t *p, parse_opt_cb *cb,
 		return (*cb)(opt, NULL, flags);
 	if (!may_ign && !arg)
 		return opterror(opt, "requires a value", flags);
-	if (may_ign && arg && arg[0] == '-' && arg[1])
-		return (*cb)(opt, NULL, flags);
+	if (may_ign && arg) {
+		if (arg[0] == '-' && arg[1])
+			return (*cb)(opt, NULL, flags);
+		if (!strcmp(arg, "{}")) {
+			use_arg(p);
+			return (*cb)(opt, NULL, flags);
+		}
+	}
 	switch ((*cb)(opt, arg, flags | may_ign)) {
 	case PARSE_OPT_OK:
 		use_arg(p);
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 7/7] git-tag: fix -l switch handling regression.
  2007-12-17 18:23           ` [PATCH 6/7] parse-options: have a `use default value` wildcard Pierre Habouzit
@ 2007-12-17 18:23             ` Pierre Habouzit
  2007-12-17 18:56               ` Pierre Habouzit
  0 siblings, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:23 UTC (permalink / raw)
  To: gitster; +Cc: git, Pierre Habouzit

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 builtin-tag.c |   12 +++++-------
 1 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/builtin-tag.c b/builtin-tag.c
index fd44b2e..c7a1563 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -16,7 +16,7 @@
 static const char * const git_tag_usage[] = {
 	"git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
 	"git-tag -d <tagname>...",
-	"git-tag [-n [<num>]] -l [<pattern>]",
+	"git-tag -l [-n [<num>]] [<pattern>]",
 	"git-tag -v <tagname>...",
 	NULL
 };
@@ -370,13 +370,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct ref_lock *lock;
 
 	int annotate = 0, sign = 0, force = 0, lines = 0,
-					delete = 0, verify = 0;
-	char *list = NULL, *msgfile = NULL, *keyid = NULL;
-	const char *no_pattern = "NO_PATTERN";
+		list = 0, delete = 0, verify = 0;
+	char *msgfile = NULL, *keyid = NULL;
 	struct msg_arg msg = { 0, STRBUF_INIT };
 	struct option options[] = {
-		{ OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names",
-			PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern },
+		OPT_INTEGER('l', NULL, &list, "list tag names"),
 		{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
 				"print n lines of each tag message",
 				PARSE_OPT_OPTARG, NULL, 1 },
@@ -408,7 +406,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		annotate = 1;
 
 	if (list)
-		return list_tags(list == no_pattern ? NULL : list, lines);
+		return list_tags(argv[0], lines);
 	if (delete)
 		return for_each_tag_name(argv, delete_tag);
 	if (verify)
-- 
1.5.4.rc0.1148.ga3ab1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset`
  2007-12-17 18:23 ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit
  2007-12-17 18:23   ` [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use Pierre Habouzit
@ 2007-12-17 18:54   ` Pierre Habouzit
  1 sibling, 0 replies; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:54 UTC (permalink / raw)
  To: gitster; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 774 bytes --]

And of course here is the MadBug #1, to be squashed:
---
 builtin-rev-parse.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 20d1789..3e8ee62 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -210,10 +210,10 @@ static int try_difference(const char *arg)
 	return 0;
 }
 
-static int parseopt_dump(const struct option *o, const char *arg, int unset)
+static int parseopt_dump(const struct option *o, const char *arg, int flags)
 {
 	struct strbuf *parsed = o->value;
-	if (unset)
+	if (flags & PARSE_OPT_UNSET)
 		strbuf_addf(parsed, " --no-%s", o->long_name);
 	else if (o->short_name)
 		strbuf_addf(parsed, " -%c", o->short_name);
-- 
1.5.4.rc0.1151.g102b0


[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/7] git-tag: fix -l switch handling regression.
  2007-12-17 18:23             ` [PATCH 7/7] git-tag: fix -l switch handling regression Pierre Habouzit
@ 2007-12-17 18:56               ` Pierre Habouzit
  2007-12-17 19:03                 ` Pierre Habouzit
  0 siblings, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 18:56 UTC (permalink / raw)
  To: gitster; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 530 bytes --]

  And I managed to resend the broken version, hurray myself.

> +		OPT_INTEGER('l', NULL, &list, "list tag names"),
                OPT_BOOLEAN



Both these last minute fixes are applied to my public git.git.

Let's now write 1000 times: I will run the test-suite before I send
patches, I will rune the test-suite before I send patches, …
-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/7] git-tag: fix -l switch handling regression.
  2007-12-17 18:56               ` Pierre Habouzit
@ 2007-12-17 19:03                 ` Pierre Habouzit
  2007-12-17 20:13                   ` Junio C Hamano
  0 siblings, 1 reply; 13+ messages in thread
From: Pierre Habouzit @ 2007-12-17 19:03 UTC (permalink / raw)
  To: gitster, git

[-- Attachment #1: Type: text/plain, Size: 918 bytes --]

On Mon, Dec 17, 2007 at 06:56:52PM +0000, Pierre Habouzit wrote:
>   And I managed to resend the broken version, hurray myself.
> 
> > +		OPT_INTEGER('l', NULL, &list, "list tag names"),
>                 OPT_BOOLEAN
> 
> 
> 
> Both these last minute fixes are applied to my public git.git.
> 
> Let's now write 1000 times: I will run the test-suite before I send
> patches, I will rune the test-suite before I send patches, …

  oh and t7004 doesn't pass anymore because of the:

  git -n xxx -l or git -n "" -l tests. If we really want to allow that
(but it _REALLY_ feels wrong to me) we have to make '-l' a callback that
groks non integers as 0. Else the test also has to be fixed, I'm not
sure what to do here.


-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/7] git-tag: fix -l switch handling regression.
  2007-12-17 19:03                 ` Pierre Habouzit
@ 2007-12-17 20:13                   ` Junio C Hamano
  0 siblings, 0 replies; 13+ messages in thread
From: Junio C Hamano @ 2007-12-17 20:13 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: git

Pierre Habouzit <madcoder@debian.org> writes:

> On Mon, Dec 17, 2007 at 06:56:52PM +0000, Pierre Habouzit wrote:
>>   And I managed to resend the broken version, hurray myself.
>> 
>> > +		OPT_INTEGER('l', NULL, &list, "list tag names"),
>>                 OPT_BOOLEAN
>> 
>> 
>> 
>> Both these last minute fixes are applied to my public git.git.
>> 
>> Let's now write 1000 times: I will run the test-suite before I send
>> patches, I will rune the test-suite before I send patches, …
>
>   oh and t7004 doesn't pass anymore because of the:
>
>   git -n xxx -l or git -n "" -l tests. If we really want to allow that
> (but it _REALLY_ feels wrong to me) we have to make '-l' a callback that
> groks non integers as 0. Else the test also has to be fixed, I'm not
> sure what to do here.

I did not understand what "git tag -n xxx" was meant to do, either.
Time to run blame and ask the responsible party?

I suspect "-n ''" there might be meant as a way to spell the "no
argument here -- use our default" instruction.  It looks slightly nicer
than that '{}' but not quite.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/7] parse-options: Add a gitcli(5) man page.
  2007-12-17 18:23         ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Pierre Habouzit
  2007-12-17 18:23           ` [PATCH 6/7] parse-options: have a `use default value` wildcard Pierre Habouzit
@ 2007-12-18  2:00           ` Wayne Davison
  1 sibling, 0 replies; 13+ messages in thread
From: Wayne Davison @ 2007-12-18  2:00 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: git

On Mon, Dec 17, 2007 at 07:23:15PM +0100, Pierre Habouzit wrote:
> + * when a command line option takes an argument, use the 'sticked' form.

A minor issue:  the word "sticked" reads very strangely to me (in this
spot and several others in your text).  I think something like joined or
attached (or even abutted) would be better, as seen in this altered text
for the spot cited above (with a few other improvements thrown in):

 * when a command line option takes an argument, it is best to use the
   'joined' form.  In other words, write `"git foo -oArg"` instead of
   `"git foo -o Arg"` for short options, and `"git foo --long-opt=Arg"`
   instead of `"git foo --long-opt Arg"` for long options.  If an option
   takes an optional option-argument, it MUST be written using the
   'joined' form when providing the option-argument.

..wayne..

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2007-12-18  2:01 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-12-17 18:23 [proposal] make parse-options nicer wrt optional arguments (supersedes all my recent posts on the matter) Pierre Habouzit
2007-12-17 18:23 ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit
2007-12-17 18:23   ` [PATCH 2/7] parse-options: allow callbacks to ignore arguments they don't need to use Pierre Habouzit
2007-12-17 18:23     ` [PATCH 3/7] parse-options: Let the integer/string cases be callbacks as well Pierre Habouzit
2007-12-17 18:23       ` [PATCH 4/7] parse-options: let OPT__ABBREV ignore arguments Pierre Habouzit
2007-12-17 18:23         ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Pierre Habouzit
2007-12-17 18:23           ` [PATCH 6/7] parse-options: have a `use default value` wildcard Pierre Habouzit
2007-12-17 18:23             ` [PATCH 7/7] git-tag: fix -l switch handling regression Pierre Habouzit
2007-12-17 18:56               ` Pierre Habouzit
2007-12-17 19:03                 ` Pierre Habouzit
2007-12-17 20:13                   ` Junio C Hamano
2007-12-18  2:00           ` [PATCH 5/7] parse-options: Add a gitcli(5) man page Wayne Davison
2007-12-17 18:54   ` [PATCH 1/7] parse-options: Make callbacks take flags instead of boolean `unset` Pierre Habouzit

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).