From: Kristian Høgsberg Date: Mon, 01 Oct 2007 16:26:15 GMT Subject: Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c. Message-ID: <1191255975.25093.26.camel@hinata.boston.redhat.com> In-Reply-To: <20070930131133.GA11209@diku.dk> On Sun, 2007-09-30 at 15:11 +0200, Jonas Fonseca wrote: > Hello Kristian, > > I have some comments on your patch. Some of the "improvement" might have > to wait until after your builtin-commit changes hits git.git. However, > if we could agree on some of the general changes, I could start porting > other of the main porcelain commands to use the option parser without > depending on the state of the remaining builtin-commit series. Hi Jonas, That's sounds like a good plan. In fact, in you want to update the patch with your changes (they all sound good) and start porting over some of the other builtins feel free. I don't have much time follow up on these comments right now, but I will get to it eventually - unless you beat me to it of course ;) I will update builtin-commit.c to work with whatever changes you introduce once I get around to updating that patch. > Kristian Høgsberg wrote Thu, Sep 27, 2007: > > Signed-off-by: Kristian Høgsberg > > --- > > Makefile | 2 +- > > parse-options.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > parse-options.h | 29 +++++++++++++++++++++ > > 3 files changed, 104 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..2fb30cd > > --- /dev/null > > +++ b/parse-options.c > > @@ -0,0 +1,74 @@ > > +#include "git-compat-util.h" > > +#include "parse-options.h" > > + > > +int parse_options(const char ***argv, > > + struct option *options, int count, > > + const char *usage_string) > > +{ > > + const char *value, *eq; > > + int i; > > + > > + if (**argv == NULL) > > + return 0; > > + if ((**argv)[0] != '-') > > + return 0; > > + if (!strcmp(**argv, "--")) > > + return 0; > > I don't know if this is a bug, but you do not remove "--" from argv, > which is later (in the patch that adds builtin-commit.c) passed to > add_files_to_cache and then get_pathspec where it is not removed or > detected either. That's an oversight, good catch. > > + > > + value = NULL; > > + for (i = 0; i < count; i++) { > > + if ((**argv)[1] == '-') { > > + if (!prefixcmp(options[i].long_name, **argv + 2)) { > > + if (options[i].type != OPTION_BOOLEAN) > > + value = *++(*argv); > > + goto match; > > + } > > + > > + eq = strchr(**argv + 2, '='); > > + if (eq && options[i].type != OPTION_BOOLEAN && > > + !strncmp(**argv + 2, > > + options[i].long_name, eq - **argv - 2)) { > > + value = eq + 1; > > + goto match; > > + } > > + } > > + > > + if ((**argv)[1] == options[i].short_name) { > > + if ((**argv)[2] == '\0') { > > + if (options[i].type != OPTION_BOOLEAN) > > + value = *++(*argv); > > + goto match; > > + } > > + > > + if (options[i].type != OPTION_BOOLEAN) { > > + value = **argv + 2; > > + goto match; > > + } > > + } > > + } > > + > > + usage(usage_string); > > + > > + match: > > I think the goto can be avoided by simply breaking out of the above loop > when an option has been found and add ... > > > + switch (options[i].type) { > case OPTION_LAST > usage(usage_string); > break; > > > + case OPTION_BOOLEAN: > > + *(int *)options[i].value = 1; > > + break; Yeah, that looks nicer. I think the goto structure is a leftover from when there was more logic between the loop and the switch. It's good to get some fresh eyes on this code. Junio didn't like the OPTION_LAST terminator, so I changed the interface to take a count. We can do if (i == count) usage(); else switch (options[i].type) { ... } of course. > I've been looking at builtin-blame.c which IMO has some of the most > obscure option parsing and maybe this can be changed to increment in > order to support stuff like changing the meaning by passing the same arg > multiple times (e.g. "-C -C -C") better. That would be fine, yes. > Blame option parsing also sports (enum) flags being masked together, > this can of course be rewritten to a boolean option followed by > masking when parse_options is done (to keep it sane). Yup. > > + case OPTION_STRING: > > + if (value == NULL) > > + die("option %s requires a value.", (*argv)[-1]); > > Maybe change this ... > > > + *(const char **)options[i].value = value; > > + break; > > + case OPTION_INTEGER: > > + if (value == NULL) > > + die("option %s requires a value.", (*argv)[-1]); > > ... and this to: > > if (!value) { > error("option %s requires a value.", (*argv)[-1]); > usage(usage_string); > } Sure, that's friendlier. > > + *(int *)options[i].value = atoi(value); > > + break; > > + default: > > + assert(0); > > + } > > + > > + (*argv)++; > > + > > + return 1; > > +} > > diff --git a/parse-options.h b/parse-options.h > > new file mode 100644 > > index 0000000..39399c3 > > --- /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, > > + OPTION_LAST, > > +}; > > + > > +struct option { > > + enum option_type type; > > + const char *long_name; > > + char short_name; > > + void *value; > > +}; > > Space vs tab indentation. > > One of the last things I miss from Cogito is the nice abbreviated help > messages that was available via '-h'. I don't know if it would be > acceptable (at least for the main porcelain commands) to put this > functionality into the option parser by adding a "description" member to > struct option and have parse_options print a nice: > > > >