[RFC] convert shortlog to use parse_options

3 messages, 1 author, 2016-06-15 · open the first message on its own page

[RFC] convert shortlog to use parse_options

From: Shawn Bohrer <hidden>
Date: 2016-06-15 22:44:47

I guess I should have searched the list _before_ creating these patches
since I just now stumbled upon some of the questions about how this
should be done for example:

http://kerneltrap.org/mailarchive/git/2008/3/1/1035344
From my testing this seems to work fine, but I may have missed a use
case.  I actually created these patches because I was annoyed that:

git shortlog --author=bohrer -s HEAD

didn't work, and this also fixes that issue.

--
Shawn

[PATCH 1/2] parse_options: Add flag to prevent errors for further processing

From: Shawn Bohrer <hidden>
Date: 2016-06-15 22:44:47

This adds the PARSE_OPT_NO_ERROR_ON_UNKNOWN flag which prevents
parse_options() from erroring out when it finds an unknown option,
and leaves the original command and unknown options in argv.

This option is useful if the option parsing needs to be done in
multiple stages for example if the remaining options will be passed
to additional git commands.

Signed-off-by: Shawn Bohrer <redacted>
---
 parse-options.c |   25 ++++++++++++++++++++-----
 parse-options.h |    5 +++--
 2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index 8071711..2635e18 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -131,7 +131,8 @@ static int get_value(struct optparse_t *p,
 	}
 }
 
-static int parse_short_opt(struct optparse_t *p, const struct option *options)
+static int parse_short_opt(struct optparse_t *p, const struct option *options,
+			   int flags)
 {
 	for (; options->type != OPTION_END; options++) {
 		if (options->short_name == *p->opt) {
@@ -139,11 +140,16 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options)
 			return get_value(p, options, OPT_SHORT);
 		}
 	}
+
+	if (flags & PARSE_OPT_NO_ERROR_ON_UNKNOWN) {
+		p->out[p->cpidx++] = p->argv[0];
+		return 0;
+	}
 	return error("unknown switch `%c'", *p->opt);
 }
 
 static int parse_long_opt(struct optparse_t *p, const char *arg,
-                          const struct option *options)
+                          const struct option *options, int flags)
 {
 	const char *arg_end = strchr(arg, '=');
 	const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
@@ -224,6 +230,11 @@ is_abbreviated:
 			abbrev_option->long_name);
 	if (abbrev_option)
 		return get_value(p, abbrev_option, abbrev_flags);
+
+	if (flags & PARSE_OPT_NO_ERROR_ON_UNKNOWN) {
+		p->out[p->cpidx++] = p->argv[0];
+		return 0;
+	}
 	return error("unknown option `%s'", arg);
 }
 
@@ -254,6 +265,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
                   const char * const usagestr[], int flags)
 {
 	struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
+	if (flags & PARSE_OPT_NO_ERROR_ON_UNKNOWN)
+		args.out =  argv + 1;
 
 	for (; args.argc; args.argc--, args.argv++) {
 		const char *arg = args.argv[0];
@@ -269,14 +282,14 @@ int parse_options(int argc, const char **argv, const struct option *options,
 			args.opt = arg + 1;
 			if (*args.opt == 'h')
 				usage_with_options(usagestr, options);
-			if (parse_short_opt(&args, options) < 0)
+			if (parse_short_opt(&args, options, flags) < 0)
 				usage_with_options(usagestr, options);
 			if (args.opt)
 				check_typos(arg + 1, options);
 			while (args.opt) {
 				if (*args.opt == 'h')
 					usage_with_options(usagestr, options);
-				if (parse_short_opt(&args, options) < 0)
+				if (parse_short_opt(&args, options, flags) < 0)
 					usage_with_options(usagestr, options);
 			}
 			continue;
@@ -294,11 +307,13 @@ 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, flags))
 			usage_with_options(usagestr, options);
 	}
 
 	memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
+	if (flags & PARSE_OPT_NO_ERROR_ON_UNKNOWN)
+		++args.cpidx;
 	args.out[args.cpidx + args.argc] = NULL;
 	return args.cpidx + args.argc;
 }
diff --git a/parse-options.h b/parse-options.h
index 4ee443d..416ccdd 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -18,8 +18,9 @@ enum parse_opt_type {
 };
 
 enum parse_opt_flags {
-	PARSE_OPT_KEEP_DASHDASH = 1,
-	PARSE_OPT_STOP_AT_NON_OPTION = 2,
+	PARSE_OPT_KEEP_DASHDASH       = 1,
+	PARSE_OPT_STOP_AT_NON_OPTION  = 2,
+	PARSE_OPT_NO_ERROR_ON_UNKNOWN = 4
 };
 
 enum parse_opt_option_flags {
-- 
1.5.4.3

[PATCH 2/2] git shortlog: Modify to use parse_options

From: Shawn Bohrer <hidden>
Date: 2016-06-15 22:44:47

Signed-off-by: Shawn Bohrer <redacted>
---
 builtin-shortlog.c |   54 +++++++++++++++++++++++++++------------------------
 1 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index e6a2865..b1087b5 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -7,9 +7,12 @@
 #include "utf8.h"
 #include "mailmap.h"
 #include "shortlog.h"
+#include "parse-options.h"
 
-static const char shortlog_usage[] =
-"git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
+static const char *const shortlog_usage[] = {
+	"git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]",
+	NULL
+};
 
 static int compare_by_number(const void *a1, const void *a2)
 {
@@ -189,8 +192,6 @@ static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
 
 static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
 {
-	arg += 2; /* skip -w */
-
 	*wrap = parse_uint(&arg, ',');
 	if (*wrap < 0)
 		die(wrap_arg_usage);
@@ -230,35 +231,38 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	struct shortlog log;
 	struct rev_info rev;
 	int nongit;
+	const char * wrap_options = NULL;
+	struct option options[] = {
+		OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
+			    "sort by number"),
+		OPT_BOOLEAN('s', "summary", &log.summary,
+			    "only provide commit count summary"),
+		OPT_BOOLEAN('e', "email", &log.email,
+			    "show email address of author"),
+		{ OPTION_STRING, 'w', NULL, &wrap_options,
+		  "[<width>[,<indent1>[,<indent2>]]]", "linewrap the output",
+		  PARSE_OPT_OPTARG, NULL, (intptr_t)"-w" },
+		OPT_END()
+	};
 
 	prefix = setup_git_directory_gently(&nongit);
 	shortlog_init(&log);
 
-	/* since -n is a shadowed rev argument, parse our args first */
-	while (argc > 1) {
-		if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
-			log.sort_by_number = 1;
-		else if (!strcmp(argv[1], "-s") ||
-				!strcmp(argv[1], "--summary"))
-			log.summary = 1;
-		else if (!strcmp(argv[1], "-e") ||
-			 !strcmp(argv[1], "--email"))
-			log.email = 1;
-		else if (!prefixcmp(argv[1], "-w")) {
-			log.wrap_lines = 1;
-			parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
-		}
-		else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
-			usage(shortlog_usage);
-		else
-			break;
-		argv++;
-		argc--;
+	argc = parse_options(argc, argv, options, shortlog_usage,
+			     PARSE_OPT_NO_ERROR_ON_UNKNOWN);
+
+	if (wrap_options)
+	{
+		if (!prefixcmp(wrap_options, "-w"))
+			wrap_options += 2; /* skip -w */
+		log.wrap_lines = 1;
+		parse_wrap_args(wrap_options, &log.in1, &log.in2, &log.wrap);
 	}
+
 	init_revisions(&rev, prefix);
 	argc = setup_revisions(argc, argv, &rev, NULL);
 	if (argc > 1)
-		die ("unrecognized argument: %s", argv[1]);
+		usage_with_options(shortlog_usage, options);
 
 	/* assume HEAD if from a tty */
 	if (!nongit && !rev.pending.nr && isatty(0))
-- 
1.5.4.3
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help