{"thread":{"id":"4108","subject":"[PATCH 3/6] ls-tree: convert to gitopt","startedAt":"2006-05-09T05:06:44Z","lastAt":"2006-05-09T21:31:41Z","messageCount":13,"participants":["Eric Wong","Timo Hirvonen","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"19768","messageId":"11471512103526-git-send-email-normalperson@yhbt.net","threadId":"4108","inReplyTo":"1147151209168-git-send-email-normalperson@yhbt.net","subject":"[PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-09T05:06:44Z","receivedAt":"2006-05-09T05:06:44Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"It was initially conceived as an addition to the git.c wrapper,\nand not affect other programs.  But it turns out existing C\nprograms can use it pretty easily, too.\n\nFeatures include:\n\n * getopt-style permuting (can easily be disabled for\n   things like update-index)\n\n * command-line compatibile with existing usage:\n   -S=pickaxe-arg-with-leading-equals is unchanged\n\n * printf-style rewriting (for front-ending shell scripts)\n\n * unbundling of short options: -uC20n20z => -u -C20 -n20 -z\n\n * automatically understands unambiguous abbreviations\n\n * optional argument handling (-C<num>, -M<num>)\n   -C <num> (with a space between them) has not changed,\n   however, <num> can be a sha1, or a path\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n .gitignore           |    1 \n Makefile             |   10 +\n git.c                |   11 +\n gitopt.c             |  603 ++++++++++++++++++++++++++++++++++++++++++++++++++\n gitopt.h             |  142 ++++++++++++\n gitopt/git_wrapper.h |   45 ++++\n gitopt/sh.h          |   45 ++++\n t/t0200-gitopt.sh    |  280 +++++++++++++++++++++++\n test-gitopt.c        |  112 +++++++++\n 9 files changed, 1244 insertions(+), 5 deletions(-)\n create mode 100644 gitopt.c\n create mode 100644 gitopt.h\n create mode 100644 gitopt/git_wrapper.h\n create mode 100644 gitopt/sh.h\n create mode 100755 t/t0200-gitopt.sh\n create mode 100644 test-gitopt.c\n\n083777c55feab0b68b859f8cb0d5bcf105db4ef5\ndiff --git a/.gitignore b/.gitignore\nindex b5959d6..b2d8b06 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -123,6 +123,7 @@ git-write-tree\n git-core-*/?*\n test-date\n test-delta\n+test-gitopt\n common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 45484fc..46f2401 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -197,7 +197,7 @@ LIB_H = \\\n \tblob.h cache.h commit.h csum-file.h delta.h \\\n \tdiff.h object.h pack.h pkt-line.h quote.h refs.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n-\ttree-walk.h log-tree.h\n+\ttree-walk.h log-tree.h gitopt.h\n \n DIFF_OBJS = \\\n \tdiff.o diff-lib.o diffcore-break.o diffcore-order.o \\\n@@ -212,6 +212,7 @@ LIB_OBJS = \\\n \tserver-info.o setup.o sha1_file.o sha1_name.o strbuf.o \\\n \ttag.o tree.o usage.o config.o environment.o ctype.o copy.o \\\n \tfetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \\\n+\tgitopt.o \\\n \t$(DIFF_OBJS)\n \n BUILTIN_OBJS = \\\n@@ -470,6 +471,8 @@ all:\n strip: $(PROGRAMS) git$X\n \t$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X\n \n+gitopt.o: gitopt.c gitopt.h gitopt/*.h\n+\n git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS)\n \t$(CC) -DGIT_VERSION='\"$(GIT_VERSION)\"' \\\n \t\t$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \\\n@@ -600,7 +603,7 @@ # with that.\n \n export NO_PYTHON\n \n-test: all\n+test: all test-gitopt$X\n \t$(MAKE) -C t/ all\n \n test-date$X: test-date.c date.o ctype.o\n@@ -609,6 +612,9 @@ test-date$X: test-date.c date.o ctype.o\n test-delta$X: test-delta.c diff-delta.o patch-delta.o\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz\n \n+test-gitopt$X: test-gitopt.c gitopt.o ctype.o usage.o\n+\t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^\n+\n check:\n \tfor i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done\n \ndiff --git a/git.c b/git.c\nindex 49ba518..785d97f 100644\n--- a/git.c\n+++ b/git.c\n@@ -11,6 +11,7 @@ #include <stdarg.h>\n #include \"git-compat-util.h\"\n #include \"exec_cmd.h\"\n \n+#include \"gitopt/git_wrapper.h\"\n #include \"builtin.h\"\n \n static void prepend_to_path(const char *dir, int len)\n@@ -72,6 +73,7 @@ int main(int argc, const char **argv, ch\n \tchar *slash = strrchr(cmd, '/');\n \tchar git_command[PATH_MAX + 1];\n \tconst char *exec_path = NULL;\n+\tstruct exec_args *ea;\n \n \t/*\n \t * Take the basename of argv[0] as the command\n@@ -99,7 +101,8 @@ int main(int argc, const char **argv, ch\n \tif (!strncmp(cmd, \"git-\", 4)) {\n \t\tcmd += 4;\n \t\targv[0] = cmd;\n-\t\thandle_internal_command(argc, argv, envp);\n+\t\tea = gitopt_parse_git(argc, argv);\n+\t\thandle_internal_command(ea->argc, ea->argv, envp);\n \t\tdie(\"cannot handle %s internally\", cmd);\n \t}\n \n@@ -144,6 +147,8 @@ int main(int argc, const char **argv, ch\n \t}\n \targv[0] = cmd;\n \n+\tea = gitopt_parse_git(argc, argv);\n+\n \t/*\n \t * We search for git commands in the following order:\n \t *  - git_exec_path()\n@@ -157,10 +162,10 @@ int main(int argc, const char **argv, ch\n \tprepend_to_path(exec_path, strlen(exec_path));\n \n \t/* See if it's an internal command */\n-\thandle_internal_command(argc, argv, envp);\n+\thandle_internal_command(ea->argc, ea->argv, envp);\n \n \t/* .. then try the external ones */\n-\texecv_git_cmd(argv);\n+\texecv_git_cmd(ea->argv);\n \n \tif (errno == ENOENT)\n \t\tcmd_usage(0, exec_path, \"'%s' is not a git-command\", cmd);\ndiff --git a/gitopt.c b/gitopt.c\nnew file mode 100644\nindex 0000000..056e163\n--- /dev/null\n+++ b/gitopt.c\n@@ -0,0 +1,603 @@\n+#include \"git-compat-util.h\"\n+#include \"gitopt.h\"\n+#include \"cache.h\"\n+\n+/* whether or not to pass-through unrecognized switches or to report an error.\n+ * This is only intended to be set to true for use in the git.c wrapper */\n+int gitopt_pass_through = 0;\n+static int gitopt_errno = 0;\n+int gitopt_dd_seen = 0;\t/* double-dash seen flag */\n+\n+void (*gitopt_non_option_cb)(struct exec_args *b, const int argc,\n+\t\t\t\tconst char **argv, int *argc_pos);\n+\n+void gitopt_default_non_option_cb(struct exec_args *b, const int argc,\n+\t\t\t\tconst char **argv, int *argc_pos)\n+{\n+\tb->argv[b->argc++] = argv[*argc_pos];\n+}\n+\n+struct exec_args *new_exec_args(const int argc)\n+{\n+\tstruct exec_args *ea = xmalloc(sizeof(*ea));\n+\tea->argc = argc;\n+\tea->argv = xcalloc((argc+1), sizeof(char*));\n+\n+\treturn ea;\n+}\n+\n+struct exec_args *nil_exec_args(struct exec_args *ea)\n+{\n+\tint i;\n+\tfor (i = 0; i < ea->argc; i++)\n+\t\tea->argv[i] = NULL;\n+\tea->argc = 0;\n+\treturn ea;\n+}\n+\n+static int combine_exec_args(struct exec_args *dest, struct exec_args *from)\n+{\n+\tint i;\n+\n+\tdest->argv = xrealloc(dest->argv,\n+\t\t\t\t(2 + dest->argc + from->argc)*sizeof(char*));\n+\n+\tfor (i = 0; i < from->argc; i++)\n+\t\tdest->argv[dest->argc++] = from->argv[i];\n+\n+\tdest->argv[dest->argc] = NULL;\n+\n+\treturn i;\n+}\n+\n+static struct exec_args *rewrite_args(const char *rewrite_fmt, const char *arg)\n+{\n+\tstruct exec_args *ea;\n+\tsize_t len = strlen(rewrite_fmt) + (arg ? strlen(arg) : 0);\n+\tchar *dest = xmalloc(len); /* don't free this */\n+\tint nr_ws = 0;\n+\tchar *a, *b;\n+\n+\tif (!arg) {\n+\t\tstrcpy(dest, rewrite_fmt);\n+\t\tif ((a = strstr(dest,\"=%s\")) || (a = strstr(dest,\" %s\")))\n+\t\t\ta[0] = a[1] = a[2] = '\\0';\n+\t\telse if ((a = strstr(dest,\"%s\")))\n+\t\t\ta[0] = a[1] = '\\0';\n+\t} else {\n+\t\tconst char *c = rewrite_fmt;\n+\n+\t\tdo { if (isspace(*c++)) nr_ws++; } while (*c);\n+\t\tsnprintf(dest, len, rewrite_fmt, arg);\n+\t}\n+\n+\tea = new_exec_args(1 + nr_ws);\n+\ta = b = dest;\n+\n+\tfor (ea->argc = 0; nr_ws && *b; b++) {\n+\t\tif (isspace(*b)) {\n+\t\t\t*b = '\\0';\n+\t\t\tif (strlen(a))\n+\t\t\t\tea->argv[ea->argc++] = a;\n+\t\t\ta = b + 1;\n+\t\t}\n+\t}\n+\tif (strlen(a))\n+\t\tea->argv[ea->argc++] = a;\n+\tea->argv[ea->argc] = NULL;\n+\n+\treturn ea;\n+}\n+\n+struct exec_args *one_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tconst char *c = argv[*argc_pos];\n+\tstruct exec_args *ea = NULL;\n+\tconst char *a = NULL;\n+\tsize_t l_len = s->l ? strlen(s->l) : 0;\n+\tint so = 0; /* short option passed flag */\n+\n+\tif (*c == '-')  {\n+\t\tif (s->s && c[1] != '-') {\n+\t\t\tif (isspace(s->s)) {\n+\t\t\t\ta = c + 1;\n+\t\t\t\tso = 1;\n+\t\t\t} else if (c[1] == s->s) {\n+\t\t\t\ta = c + 2;\n+\t\t\t\tso = 1;\n+\t\t\t}\n+\t\t} else if (s->l && c[1] == '-' && !strncmp(c+2, s->l, l_len))\n+\t\t\ta = c + 2 + l_len;\n+\t}\n+\n+\tif (!a)\n+\t\treturn NULL;\n+\n+\tswitch(a[0]) {\n+\tcase '\\0':\n+\t\tif (((*argc_pos + 1) < argc) && (a = argv[*argc_pos + 1])) {\n+\t\t\t/* optional arguments must be attached to the switch\n+\t\t\t * so that there are no abiguities */\n+\t\t\tif (s->arg_fl & ARG_IS_OPT)\n+\t\t\t\tbreak;\n+\t\t\t*argc_pos += 1;\n+\t\t\tif (s->rewrite_fmt)\n+\t\t\t\tea = rewrite_args(s->rewrite_fmt, a);\n+\t\t\telse {\n+\t\t\t\tea = new_exec_args(2);\n+\t\t\t\tea->argv[0] = c;\n+\t\t\t\tea->argv[1] = a;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\tcase '=': /* only long options get to use \"=\" to denote an arg */\n+\t\tif (!so) {\n+\t\t\tif (s->rewrite_fmt)\n+\t\t\t\tea = rewrite_args(s->rewrite_fmt, a + 1);\n+\t\t\telse {\n+\t\t\t\tea = new_exec_args(1);\n+\t\t\t\tea->argv[0] = c;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\t/* fall-through, so that -S'= assigned_val;' still works */\n+\tdefault:\n+\t\t/* -s<a> (short opts only), long opts are ambiguous\n+\t\t * w/o '=' or a space separating the opt */\n+\t\tif (!so)\n+\t\t\tbreak;\n+\t\tif (s->rewrite_fmt)\n+\t\t\tea = rewrite_args(s->rewrite_fmt, a);\n+\t\telse {\n+\t\t\tea = new_exec_args(1);\n+\t\t\tea->argv[0] = c;\n+\t\t}\n+\t}\n+\n+\treturn ea;\n+}\n+\n+static struct exec_args *optional_arg_common(struct exec_args *ea,\n+\t\t\tconst struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tif (!ea) {\n+\t\tif (s->rewrite_fmt)\n+\t\t\tea = rewrite_args(s->rewrite_fmt, NULL);\n+\t\telse {\n+\t\t\tea = new_exec_args(1);\n+\t\t\tea->argv[0] = argv[*argc_pos];\n+\t\t}\n+\t}\n+\treturn ea;\n+}\n+\n+struct exec_args *optional_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\treturn optional_arg_common( one_arg(s, argc, argv, argc_pos),\n+\t\t\t\t\t\ts, argc, argv, argc_pos);\n+}\n+\n+struct exec_args *int_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tstruct exec_args *ea = one_arg(s, argc, argv, argc_pos);\n+\n+\tif (ea) {\n+\t\tconst char *c = ea->argv[ea->argc - 1];\n+\t\tchar *endptr;\n+\t\tlong int tmp;\n+\n+\t\t/* -C<num>: */\n+\t\tif (ea->argc == 1) {\n+\t\t\twhile (*c && !isdigit(*c)) c++;\n+\t\t\tif (!c) goto err;\n+\t\t}\n+\n+\t\tendptr = (char *)c;\n+\t\ttmp = strtol(c, &endptr, 10);\n+\t\tif (endptr == c) {\n+err:\n+\t\t\tif (s->arg_fl & ARG_IS_INT)\n+\t\t\t\t(*argc_pos)--;\n+\t\t\tfree_exec_args(ea);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\treturn ea;\n+}\n+\n+struct exec_args *optional_int_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\treturn optional_arg_common( int_arg(s, argc, argv, argc_pos),\n+\t\t\t\t\t\ts, argc, argv, argc_pos);\n+}\n+\n+static struct exec_args *run_proc(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tif (s->fn)\n+\t\treturn s->fn(s, argc, argv, argc_pos);\n+\tswitch (s->arg_fl) {\n+\tcase ARG_ONE:\n+\t\treturn one_arg(s, argc, argv, argc_pos);\n+\tcase ARG_INT:\n+\t\treturn int_arg(s, argc, argv, argc_pos);\n+\tcase ARG_OPT:\n+\t\treturn optional_arg(s, argc, argv, argc_pos);\n+\tcase ARG_OPTINT:\n+\t\treturn optional_int_arg(s, argc, argv, argc_pos);\n+\tdefault:\n+\t\treturn NULL;\n+\t}\n+}\n+\n+static const char * parse_bundled(struct exec_args *dest,\n+\t\t\tconst struct opt_spec *s, const char *cur,\n+\t\t\tconst int argc, const char **argv, int *arg_pos)\n+{\n+\tstruct exec_args *ea = NULL;\n+\tconst char *orig = cur;\n+\tchar *c = xmalloc(strlen(cur) + 2); /* don't free this */\n+\tconst char *tmp_argv[] = { c };\n+\tint i = 0;\n+\n+\t*c++ = '-';\n+\t*c++ = *cur++;\n+\tif (!s || (!s->arg_fl && !s->fn)) {\n+\t\tea = new_exec_args(1);\n+\t\tif (s && s->rewrite_fmt)\n+\t\t\tea->argv[0] = s->rewrite_fmt;\n+\t\telse {\n+\t\t\tea->argv[0] = tmp_argv[0];\n+\t\t\t*c = '\\0';\n+\t\t}\n+\t} else if (s->arg_fl || s->fn) {\n+\t\tif (*cur) {\n+\t\t\t/* no space between the arg and opt switch: */\n+\t\t\tif (s->arg_fl & ARG_IS_INT) {\n+\t\t\t\t/* we know to handle stuff like:\n+\t\t\t\t * -h24w80 => -h=24 -w=80 */\n+\t\t\t\tchar *endptr = (char *)cur;\n+\t\t\t\tstrtol(cur, &endptr, 10);\n+\n+\t\t\t\twhile (cur < endptr)\n+\t\t\t\t\t*c++ = *cur++;\n+\t\t\t} else if (s->arg_fl & ARG_ONE) {\n+\t\t\t\t/* unfortunately, other args are less\n+\t\t\t\t * clear-cut */\n+\t\t\t\twhile (*cur)\n+\t\t\t\t\t*c++ = *cur++;\n+\t\t\t}\n+\t\t\t*c = '\\0';\n+\t\t\tea = run_proc(s, 1, tmp_argv, &i);\n+\t\t} else if ((*arg_pos + 1) < argc) {\n+\t\t\tint j = *arg_pos;\n+\t\t\tint x = argc - j;\n+\t\t\tconst char **argv2 = xmalloc(x * sizeof(char *));\n+\n+\t\t\t*c = '\\0';\n+\t\t\targv2[i++] = tmp_argv[0];\n+\t\t\twhile (i <= x) argv2[i++] = argv[++j];\n+\n+\t\t\ti = 0;\n+\t\t\tea = run_proc(s, x, argv2, &i);\n+\t\t\t*arg_pos += i;\n+\t\t\tfree(argv2);\n+\t\t}\n+\t}\n+\tif (ea) {\n+\t\tcombine_exec_args(dest, ea);\n+\t\tfree_exec_args(ea);\n+\t} else\n+\t\tgitopt_errno = error(\"Failed to parse bundled arguments in: %s\",\n+\t\t\t\t\t\t\t\t\torig);\n+\treturn cur;\n+}\n+\n+static void unbundle(struct exec_args *dest, const char *cur,\n+\t\tconst struct opt_spec *ost,\n+\t\tconst struct opt_spec *cur_spec,\n+\t\tconst int argc, const char **argv, int *arg_pos)\n+{\n+\tstruct exec_args *ea = new_exec_args(strlen(cur));\n+\tconst struct opt_spec *s;\n+\tconst char *c = cur;\n+\n+\tea->argc = 0;\n+\n+\twhile (*c) {\n+\t\tint i;\n+\t\tfor (i = 0; ost[i].s || ost[i].l; i++) {\n+\t\t\ts = ost + i;\n+\t\t\tif (!s->s || (!isspace(s->s) && s->s != *c))\n+\t\t\t\tcontinue;\n+\t\t\tc = parse_bundled(ea, s, c, argc, argv, arg_pos);\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (ost[i].l || ost[i].s) continue;\n+\t\t/* pass-through while unbundling and creating switches:\n+\t\t * this means that if we see -abc here, but we only\n+\t\t * had -a defined in ost (-a defined to not accept args),\n+\t\t * then we'd create switches\n+\t\t * for -b and -c here (since we already knew -a)\n+\t\t * and we're assuming -b and -c were just forgotten\n+\t\t * in the ost.  If we had gotten -bac, that would\n+\t\t * be passed through as -bac in gitopt_parse_ost()\n+\t\t * as an unknown option if -b is undefined in the ost\n+\t\t */\n+\t\tif (gitopt_pass_through)\n+\t\t\tc = parse_bundled(ea, NULL, c, argc, argv, arg_pos);\n+\t\telse {\n+\t\t\t/* continue for now, a higher-up function will work\n+\t\t\t * with the error */\n+\t\t\tgitopt_errno = error(\"Unknown option '%s' in '%s'\",\n+\t\t\t\t\t\t\tc,argv[*arg_pos]);\n+\t\t\tc++;\n+\t\t}\n+\t}\n+\tcombine_exec_args(dest, ea);\n+\tfree_exec_args(ea);\n+}\n+\n+static void push_one_opt(struct exec_args *dest, const struct opt_spec *s,\n+\t\tconst int argc, const char **argv, int *i)\n+{\n+\tstruct exec_args *ea;\n+\n+\tif (!s->arg_fl && !s->fn) {\n+\t\tdest->argv[dest->argc++] = s->rewrite_fmt ? s->rewrite_fmt\n+\t\t\t\t\t\t\t: argv[*i];\n+\t\treturn;\n+\t}\n+\n+\tif ((ea = run_proc(s,argc,argv,i))) {\n+\t\tcombine_exec_args(dest, ea);\n+\t\tfree_exec_args(ea);\n+\t} else\n+\t\tgitopt_errno = error(\"Failed to parse arguments for: %s %s\",\n+\t\t\t\t\targv[*i], argv[*i+1]?argv[*i+1]:\"\");\n+}\n+\n+/* look for a prefix abbreviation */\n+static int opt_abbrev_match(const struct opt_spec *s, const char *p)\n+{\n+\tconst char *l = s->l;\n+\n+\twhile (*p) {\n+\t\tif (*l++ != *p++) return 0;\n+\t\tif (!*p || (s->arg_fl && *p == '=')) return 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* match a short option switch */\n+static int opt_char_match(const struct opt_spec *s, const char *p)\n+{\n+\treturn ((s->s == p[0]) && ((!s->arg_fl && p[1] == '\\0')\n+\t\t\t\t||\n+\t\t\t(s->arg_fl && (p[1] == '\\0' || p[1] == '='))));\n+}\n+\n+/* tokenize on '-' and look for a prefix abbreviation match */\n+static int opt_token_match(const struct opt_spec *s, const char *p0)\n+{\n+\tconst char *l = s->l;\n+\tconst char *p;\n+\n+\twhile ((l = strchr(l,'-'))) {\n+\t\tl++;\n+\t\tp = p0;\n+\t\twhile (*p) {\n+\t\t\tif (*l++ != *p++) break;\n+\t\t\tif (!*p || (s->arg_fl && *p == '=')) return 1;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* look for unambigious abbreviated switches, if it can't be found,\n+ * assume it's a non-option and pass it to b */\n+static void fallback_long(const struct opt_spec *ost,\n+\t\t\tstruct exec_args *a, struct exec_args *b,\n+\t\t\tconst int argc, const char **argv,\n+\t\t\tconst char *cur, int *argc_pos)\n+{\n+\tconst struct opt_spec *s;\n+\tint i, found = -1;\n+\n+\t/* look for abbreviations: */\n+\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\ts = &(ost[i]);\n+\t\t/* maybe they wanted to use a short option\n+\t\t * (normally a single-dash) but typed two dashes instead.\n+\t\t * note: even if we find a short option here, we do not\n+\t\t * attempt to unbundle in this case */\n+\t\tif ((s->s && opt_char_match(s, cur)) ||\n+\t\t\t\t\t(s->l && opt_abbrev_match(s,cur))) {\n+\t\t\tif (found >= 0)\n+\t\t\t\tgoto pass_through;\n+\t\t\tfound = i;\n+\t\t}\n+\t}\n+\n+\t/* ok, try harder, based on tokenization on '-' */\n+\tif (found < 0) {\n+\t\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\t\ts = &(ost[i]);\n+\t\t\tif (s->l && opt_token_match(s,cur)) {\n+\t\t\t\tif (found >= 0)\n+\t\t\t\t\tgoto pass_through;\n+\t\t\t\tfound = i;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t/* should I add a strstr() matcher here for the desperate? */\n+\n+\t/* rewrite the abbreviated switch in it's unabbreviated form: */\n+\tif (found >= 0) {\n+\t\tchar *tmp;\n+\t\tsize_t len = 3 + strlen(cur); /* --cur=potential_args\\0 */\n+\t\tsize_t l_len;\n+\n+\t\ts = &(ost[found]);\n+\t\tl_len = s->l ? strlen(s->l) : 0; /* favor long options */\n+\t\ttmp = xcalloc(len + l_len, 1); /* don't free this */\n+\t\ttmp[0] = '-';\n+\n+\t\tif (s->l) {\n+\t\t\ttmp[1] = '-';\n+\t\t\tmemcpy(tmp + 2, s->l, l_len);\n+\t\t} else\n+\t\t\ttmp[1] = s->s;\n+\t\tif (s->arg_fl) {\n+\t\t\tconst char *c;\n+\t\t\tif ((c = strchr(cur,'='))) {\n+\t\t\t\tif (!l_len) c++; /* skip '=' for short opts */\n+\t\t\t\tstrcpy(tmp + 2 + l_len, c);\n+\t\t\t}\n+\t\t}\n+\n+\t\targv[*argc_pos] = tmp;\n+\t\tpush_one_opt(a, s, argc, argv, argc_pos);\n+\t\treturn;\n+\t}\n+\n+pass_through:\n+\tgitopt_non_option_cb(b, argc, argv, argc_pos);\n+\tif (!gitopt_pass_through)\n+\t\tgitopt_errno = error(\"Unknown option: '%s'\",argv[*argc_pos]);\n+}\n+\n+static int opt_complete_match(const struct opt_spec *s, const char *p)\n+{\n+\tif (s->arg_fl) {\n+\t\tsize_t len = strlen(s->l);\n+\n+\t\treturn (!strncmp(s->l,p,len) &&\n+\t\t\t\t(p[len] == '\\0' || (p[len] == '=')));\n+\t}\n+\treturn !strcmp(s->l,p);\n+}\n+\n+/* You should really only use this in git (wrapper) and test-gitopt: */\n+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv)\n+{\n+\tstruct exec_args *a = new_exec_args(argc); /* argv[0] and options: */\n+\tstruct exec_args *b = new_exec_args(argc); /* non-option args */\n+\n+\tgitopt_pass_through = 1;\n+\n+\tif (gitopt_parse_ost_split(a, b, ost, argc, argv) < 0)\n+\t\tdie(\"gitopt argument parsing failed\");\n+\tcombine_exec_args(a, b);\n+\tfree_exec_args(b);\n+\n+\treturn a;\n+}\n+\n+int gitopt_verify_b_args(const struct exec_args *b)\n+{\n+\tconst char **arg;\n+\n+\tfor (arg = b->argv; *arg; arg++) {\n+\t\t/* anything goes after a double dash */\n+\t\tif (!memcmp(\"--\",*arg,3))\n+\t\t\treturn 1;\n+\t\tif (*arg[0] == '-')\n+\t\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+void gitopt_parse_one_opt(struct exec_args *a, struct exec_args *b,\n+\t\t\tconst struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tconst char *c = argv[*argc_pos];\n+\tconst struct opt_spec *s;\n+\tint i;\n+\n+\tif (!gitopt_dd_seen && !memcmp(\"--\",c,3)) {\n+\t\tgitopt_dd_seen = 1;\n+\t\tif (!gitopt_pass_through)\n+\t\t\treturn;\n+\t}\n+\tif (gitopt_dd_seen) {\n+\t\tgitopt_non_option_cb(b, argc, argv, argc_pos);\n+\t\treturn;\n+\t}\n+\tif (!memcmp(\"--\",c,2)) {\t/* long options */\n+\t\tc += 2;\n+\t\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\t\ts = &(ost[i]);\n+\t\t\tif (!s->l || !opt_complete_match(s, c))\n+\t\t\t\tcontinue;\n+\t\t\tpush_one_opt(a, s, argc, argv, argc_pos);\n+\t\t\treturn;\n+\t\t}\n+\t\tif (ost[i].l || ost[i].s) return;\n+\t\t/* undefined --option: */\n+\t\tfallback_long(ost, a, b, argc, argv, c, argc_pos);\n+\t\treturn;\n+\t}\n+\tif ((c[0] == '-') && (c[1] != '-')) { /* short option */\n+\t\tc++;\n+\t\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\t\ts = &(ost[i]);\n+\t\t\tif (!s->s) continue;\n+\t\t\tif (isspace(s->s) && (*c == '\\0' || isdigit(*c))) {\n+\t\t\t\t/* special case for -<num> */\n+\t\t\t\tpush_one_opt(a, s, argc, argv, argc_pos);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t\tif (s->s != *c) continue;\n+\t\t\tif ((c[1] != '\\0') && (c[1] != '='))\n+\t\t\t\tunbundle(a, c, ost, s, argc, argv, argc_pos);\n+\t\t\telse\n+\t\t\t\tpush_one_opt(a, s, argc, argv, argc_pos);\n+\t\t\treturn;\n+\t\t}\n+\t\tif (ost[i].l || ost[i].s) return;\n+\t\t/* undefined: */\n+\t\tif (gitopt_pass_through)\n+\t\t\tgitopt_non_option_cb(b, argc, argv, argc_pos);\n+\t\telse\n+\t\t\tgitopt_errno = error(\"Unknown option: %s\",\n+\t\t\t\t\t\t\targv[*argc_pos]);\n+\t\treturn;\n+\t}\n+\tgitopt_non_option_cb(b, argc, argv, argc_pos);\n+}\n+\n+int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,\n+\t\t\tconst struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv)\n+{\n+\tint i;\n+\n+\tif (!gitopt_non_option_cb)\n+\t\tgitopt_non_option_cb = gitopt_default_non_option_cb;\n+\n+\tgitopt_dd_seen = 0;\n+\tb->argc = 0;\n+\ta->argv[0] = argv[0];\n+\ta->argc = 1;\n+\n+\tfor (i = 1; i < argc; i++)\n+\t\tgitopt_parse_one_opt(a, b, ost, argc, argv, &i);\n+\treturn gitopt_errno;\n+}\n+\n+void free_exec_args(struct exec_args *ea)\n+{\n+\tfree(ea->argv);\n+\tfree(ea);\n+}\n+\ndiff --git a/gitopt.h b/gitopt.h\nnew file mode 100644\nindex 0000000..78c9e5d\n--- /dev/null\n+++ b/gitopt.h\n@@ -0,0 +1,142 @@\n+#ifndef GITOPT_H\n+#define GITOPT_H\n+\n+/* gitopt_* functions will return this structure\n+ * the elements in this struct can then be treated just\n+ * like their counterparts from main(). */\n+struct exec_args {\n+\tconst char **argv;\n+\tint argc;\n+};\n+\n+/* @l: long option string (without the leading \"--\")\n+ *\n+ * @s: single option char, ' ' has a special meaning for accepting a\n+ * single '-' (dash), which can also accept an integer argument This is\n+ * for things like \"-5\" => \"--max-count=5\" or \"-\" => \"--stdin\"\n+ *\n+ * @rewrite_fmt: rewrite the passed argument(s) (if any) into this\n+ * (*printf) style string.  Only a single %s can be accepted and handled\n+ * by the default fn() handlers included in gitopt.\n+ *\n+ * Do not use this if you need to use more than one \"%s\", you'll need to\n+ * define and use a custom fn().  rewrite_fmt is only intended for\n+ * the common argument rewriting cases.\n+ *\n+ * If rewrite_fmt has a \"%s\", \" %s\" or \"=%s\" in it, it will be stripped\n+ * out if no arguments are passed to it (this can be the case where\n+ * fn() (see below) is defined to optional_arg).\n+ *\n+ * Any single space between non-space characters will be interpreted as\n+ * break in the option and the options will be split out into seperate\n+ * elements in argv.\n+ *\n+ * @fn: define this to the function you wish to use for handling any\n+ * arguments, or cases where rewrite_fmt is too limited.  You can define\n+ * your own functions and use it here, of course.  The gitopt_eat*\n+ * macros provide convenient ways to write functions for this member.\n+ * Set this to 0/NULL if you have no args to parse.\n+ */\n+struct opt_spec {\n+\tconst char *l;\n+\tconst char s;\n+\tconst char *rewrite_fmt;\n+\tunsigned int arg_fl;\n+\tstruct exec_args *(*fn)(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos);\n+};\n+\n+/* internal use: */\n+#define ARG_IS_INT\t0x08\n+#define ARG_IS_OPT\t0x10\n+\n+/* use these for opt_spec flags: */\n+#define ARG_NIL\t\t0x00\n+#define ARG_ONE\t\t0x01\n+#define ARG_TWO\t\t0x02\t/* not really supported yet */\n+#define ARG_THREE\t0x04\t/* not really supported yet */\n+#define ARG_TRE\t\tARG_THREE\n+#define ARG_INT\t\t(ARG_ONE | ARG_IS_INT)\n+#define ARG_OPT\t\t(ARG_ONE | ARG_IS_OPT)\n+#define ARG_OPTINT\t(ARG_ONE | ARG_IS_OPT | ARG_IS_INT)\n+\n+extern int gitopt_pass_through;\n+extern int gitopt_dd_seen;\t/* double-dash seen flag */\n+void (*gitopt_non_option_cb)(struct exec_args *b, const int argc,\n+\t\t\t\tconst char **argv, int *argc_pos);\n+void gitopt_default_non_option_cb(struct exec_args *b, const int argc,\n+\t\t\t\tconst char **argv, int *argc_pos);\n+struct exec_args *new_exec_args(const int argc);\n+void free_exec_args(struct exec_args *ea);\n+struct exec_args *nil_exec_args(struct exec_args *ea);\n+\n+/* You should really only use this in the git wrapper or tests: */\n+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv);\n+\n+/* Most C programs should use this internally.  Returns -1 on error */\n+int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,\n+\t\t\tconst struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv);\n+\n+# define gitopt_eat(func,code_block) \\\n+static struct exec_args *func(const struct opt_spec *s, \\\n+\t\t\tconst int argc, const char **argv, int *argc_pos) \\\n+{ \\\n+\tcode_block \\\n+\treturn new_exec_args(0); /* return non-NULL if no errors */ \\\n+}\n+\n+# define gitopt_eat_one_arg(func,code_block) \\\n+static struct exec_args *func(const struct opt_spec *s, \\\n+\t\t\tconst int argc, const char **argv, int *argc_pos) \\\n+{ \\\n+\tstruct exec_args *ea = one_arg(s, argc, argv, argc_pos); \\\n+\tif (!ea) return NULL; \\\n+\tcode_block \\\n+\treturn nil_exec_args(ea); /* return non-NULL if no errors */ \\\n+}\n+#define gitopt_eat_arg(func,code_block)\tgitopt_eat_one_arg(func,code_block)\n+\n+# define gitopt_eat_int(func,code_block) \\\n+static struct exec_args *func(const struct opt_spec *s, \\\n+\t\t\tconst int argc, const char **argv, int *argc_pos) \\\n+{ \\\n+\tstruct exec_args *ea = one_arg(s, argc, argv, argc_pos); \\\n+\tif (!ea) return NULL; \\\n+\tcode_block \\\n+\treturn nil_exec_args(ea); /* return non-NULL if no errors */ \\\n+}\n+\n+# define gitopt_eat_opt_int(func,code_block) \\\n+static struct exec_args *func(const struct opt_spec *s, \\\n+\t\t\tconst int argc, const char **argv, int *argc_pos) \\\n+{ \\\n+\tstruct exec_args *ea = optional_int_arg(s, argc, argv, argc_pos); \\\n+\tcode_block \\\n+\treturn nil_exec_args(ea); /* return non-NULL if no errors */ \\\n+}\n+\n+/* used for debugging */\n+static inline void dump_ea(const char *pfx, struct exec_args *ea)\n+{\n+\tconst char **arg;\n+\tint i = 0;\n+\tfor (arg = ea->argv; *arg; arg++)\n+\t\tfprintf(stderr,\"[%d] %s: %s\\n\",i++,pfx,*arg);\n+}\n+\n+void gitopt_parse_one_opt(struct exec_args *a, struct exec_args *b,\n+\t\t\tconst struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv, int *argc_pos);\n+void gitopt_set_pass_through();\t/* should only be used by git.c wrapper */\n+\n+struct exec_args *one_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos);\n+struct exec_args *int_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos);\n+struct exec_args *optional_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos);\n+struct exec_args *optional_int_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos);\n+#endif /* GITOPT_H */\ndiff --git a/gitopt/git_wrapper.h b/gitopt/git_wrapper.h\nnew file mode 100644\nindex 0000000..5f27bf4\n--- /dev/null\n+++ b/gitopt/git_wrapper.h\n@@ -0,0 +1,45 @@\n+/* opt_spec table mappings for the git.c wrapper */\n+\n+#include \"../gitopt.h\"\n+#include \"../gitopt/sh.h\"\n+\n+static const struct opt_spec ost_null[] = { { 0, 0 } };\n+\n+static const struct opt_spec_map {\n+\tconst char *cmd;\n+\tconst struct opt_spec *ost;\n+} opt_specs[] = {\n+\t{ \"checkout\",\t\tost_checkout },\n+\t{ \"commit\",\t\tost_commit },\n+\t{ \"am\",\t\t\tost_am},\n+};\n+\n+static const struct opt_spec *find_cmd_ost(const int argc, const char **argv)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opt_specs); i++) {\n+\t\tconst char *cmd = opt_specs[i].cmd;\n+\t\tif (strcmp(cmd, *argv))\n+\t\t\tcontinue;\n+\t\treturn opt_specs[i].ost;\n+\t}\n+\treturn NULL;\n+}\n+\n+static struct exec_args *gitopt_parse_git(const int argc, const char **argv)\n+{\n+\tconst struct opt_spec *ost;\n+\n+\tif (!strcmp(argv[0],\"help\") || !strcmp(argv[0],\"version\") ||\n+\t\t\t\t!(ost = find_cmd_ost(argc, argv))) {\n+\t\tstruct exec_args *ea = new_exec_args(argc);\n+\t\tint i;\n+\n+\t\tfor (i = 0; i <= argc; i++) /* argv[argc] == NULL */\n+\t\t\tea->argv[i] = argv[i];\n+\n+\t\treturn ea;\n+\t}\n+\treturn gitopt_parse_ost(ost, argc, argv);\n+}\ndiff --git a/gitopt/sh.h b/gitopt/sh.h\nnew file mode 100644\nindex 0000000..0ce6620\n--- /dev/null\n+++ b/gitopt/sh.h\n@@ -0,0 +1,45 @@\n+#ifndef GITOPT_SH_H\n+#define GITOPT_SH_H\n+\n+/* opt_spec tables for some git programs written in shell that don't\n+ * have too many options */\n+\n+static const struct opt_spec ost_am[] = {\n+\t{ \"dotest\",\t\t'd',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"interactive\",\t'i',\t0,\t\t0,\t\t0 },\n+\t{ \"binary\",\t\t'b',\t0,\t\t0,\t\t0 },\n+\t{ \"3way\",\t\t'3',\t0,\t\t0,\t\t0 },\n+\t{ \"signoff\",\t\t's',\t0,\t\t0,\t\t0 },\n+\t{ \"utf8\",\t\t'u',\t0,\t\t0,\t\t0 },\n+\t{ \"keep\",\t\t'k',\t0,\t\t0,\t\t0 },\n+\t{ \"resolved\",\t\t'r',\t0,\t\t0,\t\t0 },\n+\t{ \"skip\",\t\t0,\t0,\t\t0,\t\t0 },\n+\t{ \"whitespace\",\t\t0,\t0,\t\tARG_ONE,\t0 },\n+\t{ 0, 0 }\n+};\n+\n+static const struct opt_spec ost_checkout[] = {\n+\t{ 0,\t\t\t'f',\t0,\t\t0,\t\t0 },\n+\t{ 0,\t\t\t'm',\t0,\t\t0,\t\t0 },\n+\t{ 0,\t\t\t'b',\t\"-b %s\",\tARG_ONE,\t0 },\n+\t{ 0, 0 }\n+};\n+\n+static const struct opt_spec ost_commit[] = {\n+\t{ \"file\",\t\t'F',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"all\",\t\t'a',\t0,\t\t0,\t\t0 },\n+\t{ \"author\",\t\t0,\t0,\t\tARG_ONE,\t0 },\n+\t{ \"edit\",\t\t'e',\t0,\t\t0,\t\t0 },\n+\t{ \"include\",\t\t'i',\t0,\t\t0,\t\t0 },\n+\t{ \"only\",\t\t'o',\t0,\t\t0,\t\t0 },\n+\t{ \"message\",\t\t'm',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"no-verify\",\t\t'n',\t0,\t\t0,\t\t0 },\n+\t{ \"amend\",\t\t0,\t0,\t\t0,\t\t0 },\n+\t{ \"reedit\",\t\t'c',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"reuse-message\",\t'C',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"signoff\",\t\t's',\t0,\t\t0,\t\t0 },\n+\t{ \"verbose\",\t\t'v',\t0,\t\t0,\t\t0 },\n+\t{ 0, 0 }\n+};\n+\n+#endif /* GITOPT_SH_H */\ndiff --git a/t/t0200-gitopt.sh b/t/t0200-gitopt.sh\nnew file mode 100755\nindex 0000000..6da686c\n--- /dev/null\n+++ b/t/t0200-gitopt.sh\n@@ -0,0 +1,280 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Eric Wong\n+#\n+\n+test_description='gitopt command-line pa{ss,rs}er'\n+\n+. ./test-lib.sh\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '(null)'\n+EOF\n+test_expect_success 'single command' \\\n+\t'test-gitopt commit > output && cmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '(null)'\n+EOF\n+test_expect_success 'simple command with switches' \\\n+\t'test-gitopt commit -a -s > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '(null)'\n+EOF\n+test_expect_success 'command with bundled switches' \\\n+\t'test-gitopt commit -as > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '-mhello world'\n+04 '(null)'\n+EOF\n+test_expect_success 'bundle with args for last (no space)' \\\n+\t'test-gitopt commit -asm\"hello world\" > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '-m'\n+04 'hello world'\n+05 '(null)'\n+EOF\n+test_expect_success 'unbundle with args (space)' \\\n+\t'test-gitopt commit -asm \"hello world\" > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '-m'\n+04 'hello world'\n+05 'file1'\n+06 'file2'\n+07 '(null)'\n+EOF\n+test_expect_success 'unbundle and reorder switches and command w/args' \\\n+\t'test-gitopt commit file1 file2 -asm \"hello world\" > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '--edit'\n+04 'file2'\n+05 '--'\n+06 'file1'\n+07 '-as'\n+08 '--all'\n+09 '(null)'\n+EOF\n+test_expect_success 'reorder up to and pass-through \"--\"' \\\n+\t'test-gitopt commit -as file2 --edit -- file1 -as --all > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--find-copies-harder'\n+02 '(null)'\n+EOF\n+test_expect_success 'abbreviation finder (prefix)' \\\n+\t'test-gitopt whatchanged --find-c > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--patch-with-raw'\n+02 '(null)'\n+EOF\n+test_expect_success 'abbreviation finder (substring on \"-\" token)' \\\n+\t'test-gitopt whatchanged --raw > output &&\n+\t cmp expect output'\n+\n+# we assume unknown switches that cannot be resolved to a single known\n+# switch to just be an new argument we do not know about, so we pass\n+# it to the underlying command\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--name'\n+02 '(null)'\n+EOF\n+test_expect_success 'ambiguous abbreviation (pass-through)' \\\n+\t'test-gitopt whatchanged --name > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--diff-filter=MCT'\n+02 'file1'\n+03 '(null)'\n+EOF\n+test_expect_success 'rewrite on long argument' \\\n+\t'test-gitopt whatchanged file1 --diff-filter MCT > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-Shello'\n+02 '(null)'\n+EOF\n+test_expect_success 'rewrite on short argument (#1)' \\\n+\t'test-gitopt whatchanged -Shello > output &&\n+\t cmp expect output'\n+test_expect_success 'rewrite on short argument (#2)' \\\n+\t'test-gitopt whatchanged -S hello > output &&\n+\t cmp expect output'\n+test_expect_success 'rewrite on --short argument (#3)' \\\n+\t'test-gitopt whatchanged --S hello > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-Shello'\n+02 '(null)'\n+EOF\n+test_expect_success 'rewrite on short argument (leading \"=\" arg) (#1)' \\\n+\t'test-gitopt whatchanged --S=hello > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C20'\n+02 '-M'\n+03 '(null)'\n+EOF\n+test_expect_success 'pass optional arg (#1)' \\\n+\t'test-gitopt whatchanged -C20 -M > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C'\n+02 '--find-copies-harder'\n+03 '(null)'\n+EOF\n+test_expect_success 'detect optional arg bogus (#1)' \\\n+\t'test-gitopt whatchanged -C --find-copies-harder > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C20'\n+02 '(null)'\n+EOF\n+test_expect_success 'pass optional arg (#2)' \\\n+\t'test-gitopt whatchanged -C20 > output &&\n+\t cmp expect output'\n+# test_expect_success 'pass optional arg (#3)' \\\n+\t# 'test-gitopt whatchanged -C=20 > output &&\n+\t # cmp expect output'\n+\n+cat > expect <<EOF\n+00 'checkout'\n+01 '-b'\n+02 'newbranch'\n+03 '(null)'\n+EOF\n+test_expect_success 'rewrite short split arg (#1)' \\\n+\t'test-gitopt checkout -bnewbranch > output &&\n+\t cmp expect output'\n+# test_expect_success 'rewrite short split arg (#2)' \\\n+\t# 'test-gitopt checkout --b=newbranch > output &&\n+\t # cmp expect output'\n+test_expect_success 'rewrite short sanity check' \\\n+\t'test-gitopt checkout -b newbranch > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'log'\n+01 '--default'\n+02 'dunno'\n+03 '--all'\n+04 '(null)'\n+EOF\n+test_expect_success 'rewrite long split arg' \\\n+\t'test-gitopt log --default=dunno --all > output &&\n+\t cmp expect output'\n+test_expect_success 'rewrite long sanity check' \\\n+\t'test-gitopt log --default dunno --all > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'log'\n+01 '--max-count=56'\n+02 '(null)'\n+EOF\n+test_expect_success 'rewrite -<num> => --max-count=<num>' \\\n+\t'test-gitopt log -56 > output && cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-u'\n+02 '-l20'\n+03 '-p'\n+04 '-Spicktoken'\n+05 '(null)'\n+EOF\n+test_expect_success 'bundle options with integer args mixed in' \\\n+\t'test-gitopt whatchanged -ul20pSpicktoken > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-u'\n+02 '-C20'\n+03 '-p'\n+04 '(null)'\n+EOF\n+test_expect_success 'bundle options with optional integer args used' \\\n+\t'test-gitopt whatchanged -uC20p > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-u'\n+02 '-C'\n+03 '-p'\n+04 '(null)'\n+EOF\n+test_expect_success 'bundle options with optional integer args not used' \\\n+\t'test-gitopt whatchanged -uCp > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C'\n+02 '20'\n+03 '(null)'\n+EOF\n+test_expect_success 'optional integer arg switch must be attached' \\\n+\t'test-gitopt whatchanged -C 20 > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '--message'\n+02 'hello world'\n+03 '-s'\n+04 'file'\n+05 '(null)'\n+EOF\n+test_expect_success 'long option argument parsing when short option can work' \\\n+\t'test-gitopt commit --message \"hello world\" -s file > output &&\n+\tcmp expect output'\n+\n+test_done\ndiff --git a/test-gitopt.c b/test-gitopt.c\nnew file mode 100644\nindex 0000000..2692e2b\n--- /dev/null\n+++ b/test-gitopt.c\n@@ -0,0 +1,112 @@\n+\n+#include \"git-compat-util.h\"\n+#include \"gitopt.h\"\n+#include \"gitopt/sh.h\"\n+\n+#define _rev \\\n+{ \"max-count\",\t\t'n',\t\"--max-count=%s\",\tARG_INT,\t0 }, \\\n+{ 0,\t\t\t' ',\t\"--max-count=%s\",\tARG_INT,\t0 }, \\\n+{ \"max-age\",\t\t0,\t\"--max-age=%s\",\t\tARG_INT,\t0 }, \\\n+{ \"min-age\",\t\t0,\t\"--min-age=%s\",\t\tARG_INT,\t0 }, \\\n+{ \"since\",\t\t0,\t\"--since=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"after\",\t\t0,\t\"--after=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"before\",\t\t0,\t\"--before=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"until\",\t\t0,\t\"--until=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"all\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"not\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"default\",\t\t0,\t\"--default %s\",\t\tARG_ONE,\t0 }, \\\n+{ \"topo-order\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"date-order\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"parents\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"dense\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"sparse\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"remove-empty\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"no-merges\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"boundary\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"objects\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"objects-edge\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"unpacked\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'r',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t't',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'm',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'c',\t0,\t\t\t0,\t\t0 }, \\\n+{ \"cc\",\t\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'v',\t0,\t\t\t0,\t\t0 }, \\\n+{ \"pretty\",\t\t0,\t\"--pretty=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"root\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"no-commit-id\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"always\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"no-abbrev\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"abbrev\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"abbrev-commit\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"full-diff\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+\n+#define _diff \\\n+{ 0,\t\t\t'u',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'p',\t0,\t\t\t0,\t\t0 }, \\\n+{ \"patch-with-raw\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"stat\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"patch-with-stat\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'z',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'l',\t\"-l%s\",\t\t\tARG_INT,\t0 }, \\\n+{ \"full-index\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"name-only\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"name-status\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'R',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'S',\t\"-S%s\",\t\t\tARG_ONE,\t0 }, \\\n+{ 0,\t\t\t's',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'O',\t\"-O%s\",\t\t\tARG_ONE,\t0 }, \\\n+{ \"diff-filter\",\t0,\t\"--diff-filter=%s\",\tARG_ONE,\t0 }, \\\n+{ \"pickaxe-all\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"pickaxe-regex\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'B',\t\"-B%s\",\t\t\tARG_OPTINT,\t0 }, \\\n+{ 0,\t\t\t'M',\t\"-M%s\",\t\t\tARG_OPTINT,\t0 }, \\\n+{ 0,\t\t\t'C',\t\"-C%s\",\t\t\tARG_OPTINT,\t0 }, \\\n+{ \"find-copies-harder\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"abbrev\",\t\t0,\t\"--abbrev=%s\",\t\tARG_OPT,\t0 }, \\\n+\n+#define end {0,0}\n+\n+static const struct opt_spec ost_log[] = { _rev end };\n+static const struct opt_spec ost_rev_list[] = { _rev end };\n+static const struct opt_spec ost_whatchanged[] = { _diff _rev end };\n+static const struct opt_spec ost_show[] = { _diff _rev end };\n+\n+static const struct opt_spec_map {\n+\tconst char *cmd;\n+\tconst struct opt_spec *ost;\n+} opt_specs[] = {\n+\t{ \"checkout\",\t\tost_checkout },\n+\t{ \"commit\",\t\tost_commit },\n+\t{ \"log\",\t\tost_log },\n+\t{ \"rev-list\",\t\tost_rev_list },\n+\t{ \"show\",\t\tost_show },\n+\t{ \"whatchanged\",\tost_whatchanged },\n+};\n+\n+\n+int main(int argc, const char **argv, char **envp)\n+{\n+\tint i;\n+\tstruct exec_args *ea;\n+\tconst struct opt_spec *ost = NULL;\n+\n+\tif (!argv[1])\n+\t\tusage(\"test-gitopt [<options>] <command> [<options>]\");\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opt_specs); i++) {\n+\t\tif (!strcmp(argv[1], opt_specs[i].cmd)) {\n+\t\t\tost = opt_specs[i].ost;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (!ost)\n+\t\tusage(\"test-gitopt [<options>] <command> [<options>]\");\n+\n+\tea = gitopt_parse_ost(ost, argc - 1, argv + 1);\n+\n+\tfor (i = 0; i <= ea->argc; i++)\n+\t\tprintf(\"%02d '%s'\\n\", i, ea->argv[i] ? ea->argv[i] : \"(null)\");\n+\n+\treturn 0;\n+}\n-- \n1.3.2.g0a3ae\n"},{"id":"19766","messageId":"11471512101532-git-send-email-normalperson@yhbt.net","threadId":"4108","inReplyTo":"11471512103526-git-send-email-normalperson@yhbt.net","subject":"[PATCH 2/6] update-index: convert to using gitopt","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-09T05:06:45Z","receivedAt":"2006-05-09T05:06:45Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is an example how how to override gitopt_non_option_cb, as\nwell as disabling permuting of options to the front of the\ncommand list.  This way, commands like:\n\n\tgit-update-index --add new_file --remove old_file\n\nwill continue to work.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n update-index.c |  261 ++++++++++++++++++++++++++++----------------------------\n 1 files changed, 130 insertions(+), 131 deletions(-)\n\n50f33d232367e8d7a46bd014f5a5da4d0ad370b3\ndiff --git a/update-index.c b/update-index.c\nindex 00cde70..6205f85 100644\n--- a/update-index.c\n+++ b/update-index.c\n@@ -7,6 +7,7 @@ #include \"cache.h\"\n #include \"strbuf.h\"\n #include \"quote.h\"\n #include \"tree-walk.h\"\n+#include \"gitopt.h\"\n \n /*\n  * Default to not allowing changes to the list of files. The\n@@ -25,6 +26,14 @@ static int info_only;\n static int force_remove;\n static int verbose;\n static int mark_valid_only = 0;\n+static int prefix_length = 0;\n+static int line_termination = '\\n';\n+static const char *prefix = NULL;\n+static char set_executable_bit = 0;\n+static int read_from_stdin = 0;\n+static int newfd = 0;\n+static int has_errors = 0;\n+\n #define MARK_VALID 1\n #define UNMARK_VALID 2\n \n@@ -189,10 +198,9 @@ static struct cache_entry *refresh_entry\n \treturn updated;\n }\n \n-static int refresh_cache(int really)\n+static void refresh_cache(int really)\n {\n \tint i;\n-\tint has_errors = 0;\n \n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce, *new;\n@@ -205,7 +213,7 @@ static int refresh_cache(int really)\n \t\t\tif (allow_unmerged)\n \t\t\t\tcontinue;\n \t\t\tprintf(\"%s: needs merge\\n\", ce->name);\n-\t\t\thas_errors = 1;\n+\t\t\thas_errors |= 1;\n \t\t\tcontinue;\n \t\t}\n \n@@ -225,7 +233,7 @@ static int refresh_cache(int really)\n \t\t\tif (quiet)\n \t\t\t\tcontinue;\n \t\t\tprintf(\"%s: needs update\\n\", ce->name);\n-\t\t\thas_errors = 1;\n+\t\t\thas_errors |= 1;\n \t\t\tcontinue;\n \t\t}\n \t\tactive_cache_changed = 1;\n@@ -234,7 +242,7 @@ static int refresh_cache(int really)\n \t\t * from mmap(). */\n \t\tactive_cache[i] = new;\n \t}\n-\treturn has_errors;\n+\treturn;\n }\n \n /*\n@@ -600,15 +608,123 @@ static int do_unresolve(int ac, const ch\n \treturn err;\n }\n \n+static void write_cache_if_changed ()\n+{\n+\tif (active_cache_changed) {\n+\t\tif (write_cache(newfd, active_cache, active_nr) ||\n+\t\t    commit_index_file(&cache_file))\n+\t\t\tdie(\"Unable to write new cachefile\");\n+\t}\n+}\n+\n+gitopt_eat(opt_q, quiet = 1;)\n+gitopt_eat(opt_add, allow_add = 1;)\n+gitopt_eat(opt_replace, allow_replace = 1;)\n+gitopt_eat(opt_remove, allow_remove = 1;)\n+gitopt_eat(opt_unmerged, allow_unmerged = 1;)\n+gitopt_eat(opt_refresh, refresh_cache(0);)\n+gitopt_eat(opt_really_refresh, refresh_cache(1);)\n+\n+static struct exec_args *opt_cacheinfo(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tstruct exec_args *ea = new_exec_args(4);\n+\tunsigned char sha1[20];\n+\tunsigned int mode;\n+\n+\tea->argc = 1;\n+\tea->argv[0] = argv[*argc_pos];\n+\twhile (*argc_pos < (argc-1) && ea->argc < 4)\n+\t\tea->argv[ea->argc++] = argv[++(*argc_pos)];\n+\tif (ea->argc != 4)\n+\t\tdie(\"git-update-index: --cacheinfo <mode> <sha1> <path>\");\n+\n+\tif ((sscanf(ea->argv[1], \"%o\", &mode) != 1) ||\n+\t\t\t\tget_sha1_hex(ea->argv[2], sha1) ||\n+\t\t\t\tadd_cacheinfo(mode, sha1, ea->argv[3], 0))\n+\t\tdie(\"git-update-index: --cacheinfo cannot add %s\",ea->argv[3]);\n+\n+\treturn nil_exec_args(ea);\n+}\n+\n+gitopt_eat_one_arg(opt_chmod,\n+\t\tif (*argc_pos + 1 >= argc)\n+\t\t\tdie(\"git-update-index: --chmod=%s <path>\",ea->argv[0]);\n+\t\tset_executable_bit = ea->argv[0][0];)\n+\n+gitopt_eat(opt_assume_unchanged, mark_valid_only = MARK_VALID;)\n+gitopt_eat(opt_no_assume_unchanged, mark_valid_only = UNMARK_VALID;)\n+gitopt_eat(opt_info_only, info_only = 1;)\n+gitopt_eat(opt_force_remove, force_remove = 1;)\n+gitopt_eat(opt_z, line_termination = 0;)\n+gitopt_eat(opt_stdin,\n+\t\tif (*argc_pos != argc - 1)\n+\t\t\tdie(\"--stdin must be at the end\");\n+\t\tread_from_stdin = 1;)\n+\n+gitopt_eat(opt_index_info,\n+\t\tif (*argc_pos != argc - 1)\n+\t\t\tdie(\"--index-info must be at the end\");\n+\t\tallow_add = allow_replace = allow_remove = 1;\n+\t\tread_index_info(line_termination);)\n+\n+gitopt_eat(opt_unresolve,\n+\t\thas_errors = do_unresolve(argc - *argc_pos, argv + *argc_pos,\n+\t\t\t\t\tprefix, prefix_length);\n+\t\tif (has_errors)\n+\t\t\tactive_cache_changed = 0;\n+\t\twrite_cache_if_changed();\n+\t\texit(has_errors ? 1 : 0);)\n+\n+gitopt_eat(opt_ignore_missing, not_new = 1;)\n+gitopt_eat(opt_verbose, verbose = 1;)\n+gitopt_eat(opt_h, usage(update_index_usage);)\n+\n+static const struct opt_spec update_index_ost[] = {\n+\t{ 0,\t\t\t'q',\t0,\t0,\topt_q },\n+\t{ \"add\",\t\t0,\t0,\t0,\topt_add },\n+\t{ \"replace\",\t\t0,\t0,\t0,\topt_replace },\n+\t{ \"remove\",\t\t0,\t0,\t0,\topt_remove },\n+\t{ \"unmerged\",\t\t0,\t0,\t0,\topt_unmerged },\n+\t{ \"refresh\",\t\t0,\t0,\t0,\topt_refresh },\n+\t{ \"really-refresh\",\t0,\t0,\t0,\topt_really_refresh },\n+\t{ \"cacheinfo\",\t\t0,\t0,\tARG_TRE, opt_cacheinfo},\n+\t{ \"chmod\",\t\t0,\t\"%s\",\tARG_ONE, opt_chmod },\n+\t{ \"assume-unchanged\",\t0,\t0,\t0,\topt_assume_unchanged },\n+\t{ \"no-assume-unchanged\",0,\t0,\t0,\topt_no_assume_unchanged},\n+\t{ \"info-only\",\t\t0,\t0,\t0,\topt_info_only },\n+\t{ \"force-remove\",\t0,\t0,\t0,\topt_force_remove },\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ \"stdin\",\t\t0,\t0,\t0,\topt_stdin },\n+\t{ \"index-info\",\t\t0,\t0,\t0,\topt_index_info },\n+\t{ \"unresolve\",\t\t0,\t0,\t0,\topt_unresolve },\n+\t{ \"ignore-missing\",\t0,\t0,\t0,\topt_ignore_missing },\n+\t{ \"verbose\",\t\t0,\t0,\t0,\topt_verbose },\n+\t{ \"help\",\t\t'h',\t0,\t0,\topt_h },\n+\t{ 0, 0 }\n+};\n+\n+/* call this for every non-option (and everything after \"--\") we have */\n+static void update_index_non_option_cb(struct exec_args *b, const int argc,\n+\t\t\t\t\tconst char **argv, int *argc_pos)\n+{\n+\tconst char *arg = argv[*argc_pos];\n+\tupdate_one(arg, prefix, prefix_length);\n+\tif (set_executable_bit)\n+\t\tchmod_path(set_executable_bit, arg);\n+}\n+\n int main(int argc, const char **argv)\n {\n-\tint i, newfd, entries, has_errors = 0, line_termination = '\\n';\n-\tint allow_options = 1;\n-\tint read_from_stdin = 0;\n-\tconst char *prefix = setup_git_directory();\n-\tint prefix_length = prefix ? strlen(prefix) : 0;\n-\tchar set_executable_bit = 0;\n+\tint entries;\n+\tstruct exec_args *a;\n+\n+\tprefix = setup_git_directory();\n+\tif (prefix)\n+\t\tprefix_length = strlen(prefix);\n \n+\ta = new_exec_args(argc);\t/* argv[0] and options: */\n+\tgitopt_non_option_cb = update_index_non_option_cb;\n \tgit_config(git_default_config);\n \n \tnewfd = hold_index_file_for_update(&cache_file, get_index_file());\n@@ -619,120 +735,9 @@ int main(int argc, const char **argv)\n \tif (entries < 0)\n \t\tdie(\"cache corrupted\");\n \n-\tfor (i = 1 ; i < argc; i++) {\n-\t\tconst char *path = argv[i];\n+\tif (gitopt_parse_ost_split(a, a, update_index_ost, argc, argv) < 0)\n+\t\tusage(update_index_usage);\n \n-\t\tif (allow_options && *path == '-') {\n-\t\t\tif (!strcmp(path, \"--\")) {\n-\t\t\t\tallow_options = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"-q\")) {\n-\t\t\t\tquiet = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--add\")) {\n-\t\t\t\tallow_add = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--replace\")) {\n-\t\t\t\tallow_replace = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--remove\")) {\n-\t\t\t\tallow_remove = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--unmerged\")) {\n-\t\t\t\tallow_unmerged = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--refresh\")) {\n-\t\t\t\thas_errors |= refresh_cache(0);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--really-refresh\")) {\n-\t\t\t\thas_errors |= refresh_cache(1);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--cacheinfo\")) {\n-\t\t\t\tunsigned char sha1[20];\n-\t\t\t\tunsigned int mode;\n-\n-\t\t\t\tif (i+3 >= argc)\n-\t\t\t\t\tdie(\"git-update-index: --cacheinfo <mode> <sha1> <path>\");\n-\n-\t\t\t\tif ((sscanf(argv[i+1], \"%o\", &mode) != 1) ||\n-\t\t\t\t    get_sha1_hex(argv[i+2], sha1) ||\n-\t\t\t\t    add_cacheinfo(mode, sha1, argv[i+3], 0))\n-\t\t\t\t\tdie(\"git-update-index: --cacheinfo\"\n-\t\t\t\t\t    \" cannot add %s\", argv[i+3]);\n-\t\t\t\ti += 3;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--chmod=-x\") ||\n-\t\t\t    !strcmp(path, \"--chmod=+x\")) {\n-\t\t\t\tif (argc <= i+1)\n-\t\t\t\t\tdie(\"git-update-index: %s <path>\", path);\n-\t\t\t\tset_executable_bit = path[8];\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = MARK_VALID;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--no-assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = UNMARK_VALID;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--info-only\")) {\n-\t\t\t\tinfo_only = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--force-remove\")) {\n-\t\t\t\tforce_remove = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"-z\")) {\n-\t\t\t\tline_termination = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--stdin\")) {\n-\t\t\t\tif (i != argc - 1)\n-\t\t\t\t\tdie(\"--stdin must be at the end\");\n-\t\t\t\tread_from_stdin = 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--index-info\")) {\n-\t\t\t\tif (i != argc - 1)\n-\t\t\t\t\tdie(\"--index-info must be at the end\");\n-\t\t\t\tallow_add = allow_replace = allow_remove = 1;\n-\t\t\t\tread_index_info(line_termination);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--unresolve\")) {\n-\t\t\t\thas_errors = do_unresolve(argc - i, argv + i,\n-\t\t\t\t\t\t\t  prefix, prefix_length);\n-\t\t\t\tif (has_errors)\n-\t\t\t\t\tactive_cache_changed = 0;\n-\t\t\t\tgoto finish;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--ignore-missing\")) {\n-\t\t\t\tnot_new = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--verbose\")) {\n-\t\t\t\tverbose = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"-h\") || !strcmp(path, \"--help\"))\n-\t\t\t\tusage(update_index_usage);\n-\t\t\tdie(\"unknown option %s\", path);\n-\t\t}\n-\t\tupdate_one(path, prefix, prefix_length);\n-\t\tif (set_executable_bit)\n-\t\t\tchmod_path(set_executable_bit, path);\n-\t}\n \tif (read_from_stdin) {\n \t\tstruct strbuf buf;\n \t\tstrbuf_init(&buf);\n@@ -757,12 +762,6 @@ int main(int argc, const char **argv)\n \t\t}\n \t}\n \n- finish:\n-\tif (active_cache_changed) {\n-\t\tif (write_cache(newfd, active_cache, active_nr) ||\n-\t\t    commit_index_file(&cache_file))\n-\t\t\tdie(\"Unable to write new cachefile\");\n-\t}\n-\n+\twrite_cache_if_changed();\n \treturn has_errors ? 1 : 0;\n }\n-- \n1.3.2.g0a3ae\n"},{"id":"19765","messageId":"1147151211399-git-send-email-normalperson@yhbt.net","threadId":"4108","inReplyTo":"11471512101532-git-send-email-normalperson@yhbt.net","subject":"[PATCH 3/6] ls-tree: convert to gitopt","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-09T05:06:46Z","receivedAt":"2006-05-09T05:06:46Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"A pretty simple and straight forward conversion, imho.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n gitopt/abbrev.h |   24 +++++++++++++++++\n ls-tree.c       |   79 +++++++++++++++++++++++--------------------------------\n 2 files changed, 57 insertions(+), 46 deletions(-)\n create mode 100644 gitopt/abbrev.h\n\n06d5dc3649e7407f7ff8df0d42a55a906b39cb39\ndiff --git a/gitopt/abbrev.h b/gitopt/abbrev.h\nnew file mode 100644\nindex 0000000..c3b2353\n--- /dev/null\n+++ b/gitopt/abbrev.h\n@@ -0,0 +1,24 @@\n+#include \"../cache.h\"\n+\n+/* we specify rewrite_fmt here to make opt_abbrev() simpler: */\n+#define abbrev_ost_row \\\n+\t{ \"abbrev\",\t\t0,\t\"a %s\",\tARG_OPTINT,\topt_abbrev }\n+\n+#define gitopt_opt_abbrev(dest) \\\n+static struct exec_args *opt_abbrev(const struct opt_spec *s, \\\n+\t\t\tconst int argc, const char **argv, int *argc_pos) \\\n+{ \\\n+\tstruct exec_args *ea = optional_int_arg(s, argc, argv, argc_pos); \\\n+\tif (!ea) return NULL; \\\n+\tif (ea->argc == 1) \\\n+\t\tdest = DEFAULT_ABBREV; \\\n+\telse { \\\n+\t\tdest = strtoul(ea->argv[1], NULL, 10); \\\n+\t\tif (dest && dest < MINIMUM_ABBREV) \\\n+\t\t\tdest = MINIMUM_ABBREV; \\\n+\t\telse if (dest > 40) \\\n+\t\t\tdest = 40; \\\n+\t} \\\n+\treturn nil_exec_args(ea); \\\n+}\n+\ndiff --git a/ls-tree.c b/ls-tree.c\nindex f2b3bc1..4fb6343 100644\n--- a/ls-tree.c\n+++ b/ls-tree.c\n@@ -7,6 +7,8 @@ #include \"cache.h\"\n #include \"blob.h\"\n #include \"tree.h\"\n #include \"quote.h\"\n+#include \"gitopt.h\"\n+#include \"gitopt/abbrev.h\"\n \n static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n@@ -84,68 +86,53 @@ static int show_tree(unsigned char *sha1\n \treturn retval;\n }\n \n+gitopt_eat(opt_d, ls_options |= LS_TREE_ONLY;)\n+gitopt_eat(opt_t, ls_options |= LS_SHOW_TREES;)\n+gitopt_eat(opt_z, line_termination = 0;)\n+gitopt_eat(opt_r, ls_options |= LS_RECURSIVE;)\n+gitopt_eat(opt_name_only_status, ls_options |= LS_NAME_ONLY;)\n+gitopt_eat(opt_full_name, chomp_prefix = 0;)\n+gitopt_opt_abbrev(abbrev)\n+\n+static const struct opt_spec ls_tree_ost[] = {\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ 0,\t\t\t'r',\t0,\t0,\topt_r },\n+\t{ 0,\t\t\t'd',\t0,\t0,\topt_d },\n+\t{ 0,\t\t\t't',\t0,\t0,\topt_t },\n+\t{ \"name-only\",\t\t0,\t0,\t0,\topt_name_only_status },\n+\t{ \"name-status\",\t0,\t0,\t0,\topt_name_only_status },\n+\t{ \"full-name\",\t\t0,\t0,\t0,\topt_full_name },\n+\tabbrev_ost_row,\n+\t{ 0, 0 }\n+};\n+\n int main(int argc, const char **argv)\n {\n \tunsigned char sha1[20];\n \tstruct tree *tree;\n+\tstruct exec_args *a = new_exec_args(argc); /* argv[0] and options: */\n+\tstruct exec_args *b = new_exec_args(argc); /* non-option args */\n \n \tprefix = setup_git_directory();\n \tgit_config(git_default_config);\n \tif (prefix && *prefix)\n \t\tchomp_prefix = strlen(prefix);\n-\twhile (1 < argc && argv[1][0] == '-') {\n-\t\tswitch (argv[1][1]) {\n-\t\tcase 'z':\n-\t\t\tline_termination = 0;\n-\t\t\tbreak;\n-\t\tcase 'r':\n-\t\t\tls_options |= LS_RECURSIVE;\n-\t\t\tbreak;\n-\t\tcase 'd':\n-\t\t\tls_options |= LS_TREE_ONLY;\n-\t\t\tbreak;\n-\t\tcase 't':\n-\t\t\tls_options |= LS_SHOW_TREES;\n-\t\t\tbreak;\n-\t\tcase '-':\n-\t\t\tif (!strcmp(argv[1]+2, \"name-only\") ||\n-\t\t\t    !strcmp(argv[1]+2, \"name-status\")) {\n-\t\t\t\tls_options |= LS_NAME_ONLY;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(argv[1]+2, \"full-name\")) {\n-\t\t\t\tchomp_prefix = 0;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strncmp(argv[1]+2, \"abbrev=\",7)) {\n-\t\t\t\tabbrev = strtoul(argv[1]+9, NULL, 10);\n-\t\t\t\tif (abbrev && abbrev < MINIMUM_ABBREV)\n-\t\t\t\t\tabbrev = MINIMUM_ABBREV;\n-\t\t\t\telse if (abbrev > 40)\n-\t\t\t\t\tabbrev = 40;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(argv[1]+2, \"abbrev\")) {\n-\t\t\t\tabbrev = DEFAULT_ABBREV;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\t/* otherwise fallthru */\n-\t\tdefault:\n-\t\t\tusage(ls_tree_usage);\n-\t\t}\n-\t\targc--; argv++;\n-\t}\n+\n+\tif (gitopt_parse_ost_split(a, b, ls_tree_ost, argc, argv) < 0)\n+\t\tusage(ls_tree_usage);\n+\tfree_exec_args(a);\n+\n \t/* -d -r should imply -t, but -d by itself should not have to. */\n \tif ( (LS_TREE_ONLY|LS_RECURSIVE) ==\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\n \t\tls_options |= LS_SHOW_TREES;\n \n-\tif (argc < 2)\n+\tif (b->argc < 1)\n \t\tusage(ls_tree_usage);\n-\tif (get_sha1(argv[1], sha1))\n-\t\tdie(\"Not a valid object name %s\", argv[1]);\n+\tif (get_sha1(b->argv[0], sha1))\n+\t\tdie(\"Not a valid object name %s\", b->argv[0]);\n \n-\tpathspec = get_pathspec(prefix, argv + 2);\n+\tpathspec = get_pathspec(prefix, b->argv + 1);\n \ttree = parse_tree_indirect(sha1);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n-- \n1.3.2.g0a3ae\n"},{"id":"19767","messageId":"11471512121152-git-send-email-normalperson@yhbt.net","threadId":"4108","inReplyTo":"1147151211399-git-send-email-normalperson@yhbt.net","subject":"[PATCH 4/6] ls-files: convert to using gitopt","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-09T05:06:47Z","receivedAt":"2006-05-09T05:06:47Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Another simple and straight forward conversion, imho.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n ls-files.c |  187 ++++++++++++++++++++++--------------------------------------\n 1 files changed, 69 insertions(+), 118 deletions(-)\n\n6a65d6f1e59638185c846920d8c6f0dbd82f1bf7\ndiff --git a/ls-files.c b/ls-files.c\nindex 4a4af1c..62f9e10 100644\n--- a/ls-files.c\n+++ b/ls-files.c\n@@ -10,6 +10,8 @@ #include <fnmatch.h>\n \n #include \"cache.h\"\n #include \"quote.h\"\n+#include \"gitopt.h\"\n+#include \"gitopt/abbrev.h\"\n \n static int abbrev = 0;\n static int show_deleted = 0;\n@@ -648,133 +650,82 @@ static const char ls_files_usage[] =\n \t\"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] \"\n \t\"[--] [<file>]*\";\n \n+static void tag_pfx()\n+{\n+\ttag_cached = \"H \";\n+\ttag_unmerged = \"M \";\n+\ttag_removed = \"R \";\n+\ttag_modified = \"C \";\n+\ttag_other = \"? \";\n+\ttag_killed = \"K \";\n+}\n+\n+static int exc_given = 0;\n+\n+gitopt_eat(opt_z, line_terminator = 0;)\n+gitopt_eat(opt_t, tag_pfx();)\n+gitopt_eat(opt_v, tag_pfx(); show_valid_bit = 1;)\n+gitopt_eat(opt_c, show_cached = 1;)\n+gitopt_eat(opt_d, show_deleted = 1;)\n+gitopt_eat(opt_m, show_modified = 1;)\n+gitopt_eat(opt_o, show_others = 1;)\n+gitopt_eat(opt_i, show_ignored = 1;)\n+gitopt_eat(opt_s, show_stage = 1;)\n+gitopt_eat(opt_k, show_killed = 1;)\n+gitopt_eat(opt_directory, show_other_directories = 1;)\n+gitopt_eat(opt_no_empty_directory, hide_empty_directories = 1;)\n+gitopt_eat(opt_u, show_stage = 1; show_unmerged = 1;)\n+gitopt_eat_one_arg(opt_x,\n+\t\texc_given = 1;\n+\t\tadd_exclude(ea->argv[0], \"\", 0, &exclude_list[EXC_CMDL]);)\n+gitopt_eat_one_arg(opt_X,\n+\t\texc_given = 1;\n+\t\tadd_excludes_from_file(ea->argv[0]);)\n+gitopt_eat_one_arg(opt_exclude_per_dir,\n+\t\texc_given = 1;\n+\t\texclude_per_dir = ea->argv[0];)\n+gitopt_eat(opt_full_name, prefix_offset = 0;)\n+gitopt_eat(opt_error_unmatch, error_unmatch = 1;)\n+gitopt_opt_abbrev(abbrev)\n+\n+static const struct opt_spec ls_files_ost[] = {\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ 0,\t\t\t'v',\t0,\t0,\topt_v },\n+\t{ 0,\t\t\t't',\t0,\t0,\topt_t },\n+\t{ \"cached\",\t\t'c',\t0,\t0,\topt_c },\n+\t{ \"deleted\",\t\t'd',\t0,\t0,\topt_d },\n+\t{ \"modified\",\t\t'm',\t0,\t0,\topt_m },\n+\t{ \"others\",\t\t'o',\t0,\t0,\topt_o },\n+\t{ \"ignored\",\t\t'i',\t0,\t0,\topt_i },\n+\t{ \"stage\",\t\t's',\t0,\t0,\topt_s },\n+\t{ \"killed\",\t\t'k',\t0,\t0,\topt_k },\n+\t{ \"directory\",\t\t0,\t0,\t0,\topt_directory },\n+\t{ \"no-empty-directory\",\t0,\t0,\t0,\topt_no_empty_directory},\n+\t{ \"unmerged\",\t\t'u',\t0,\t0,\topt_u },\n+\t{ \"exclude\",\t\t'x',\t\"%s\",\tARG_ONE,\topt_x },\n+\t{ \"exclude-from\",\t'X',\t\"%s\",\tARG_ONE,\topt_X },\n+\t{ \"exclude-per-directory\",0,\t\"%s\",\tARG_ONE, opt_exclude_per_dir },\n+\t{ \"full-name\",\t\t0,\t0,\t0,\topt_full_name },\n+\t{ \"error-unmatch\",\t0,\t0,\t0,\topt_error_unmatch },\n+\tabbrev_ost_row,\n+\t{ 0, 0 }\n+};\n+\n int main(int argc, const char **argv)\n {\n-\tint i;\n-\tint exc_given = 0;\n+\tstruct exec_args *a = new_exec_args(argc); /* argv[0] and options: */\n+\tstruct exec_args *b = new_exec_args(argc); /* non-option args */\n \n \tprefix = setup_git_directory();\n \tif (prefix)\n \t\tprefix_offset = strlen(prefix);\n \tgit_config(git_default_config);\n \n-\tfor (i = 1; i < argc; i++) {\n-\t\tconst char *arg = argv[i];\n-\n-\t\tif (!strcmp(arg, \"--\")) {\n-\t\t\ti++;\n-\t\t\tbreak;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-z\")) {\n-\t\t\tline_terminator = 0;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-t\") || !strcmp(arg, \"-v\")) {\n-\t\t\ttag_cached = \"H \";\n-\t\t\ttag_unmerged = \"M \";\n-\t\t\ttag_removed = \"R \";\n-\t\t\ttag_modified = \"C \";\n-\t\t\ttag_other = \"? \";\n-\t\t\ttag_killed = \"K \";\n-\t\t\tif (arg[1] == 'v')\n-\t\t\t\tshow_valid_bit = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-c\") || !strcmp(arg, \"--cached\")) {\n-\t\t\tshow_cached = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-d\") || !strcmp(arg, \"--deleted\")) {\n-\t\t\tshow_deleted = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-m\") || !strcmp(arg, \"--modified\")) {\n-\t\t\tshow_modified = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-o\") || !strcmp(arg, \"--others\")) {\n-\t\t\tshow_others = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-i\") || !strcmp(arg, \"--ignored\")) {\n-\t\t\tshow_ignored = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-s\") || !strcmp(arg, \"--stage\")) {\n-\t\t\tshow_stage = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-k\") || !strcmp(arg, \"--killed\")) {\n-\t\t\tshow_killed = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--directory\")) {\n-\t\t\tshow_other_directories = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--no-empty-directory\")) {\n-\t\t\thide_empty_directories = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-u\") || !strcmp(arg, \"--unmerged\")) {\n-\t\t\t/* There's no point in showing unmerged unless\n-\t\t\t * you also show the stage information.\n-\t\t\t */\n-\t\t\tshow_stage = 1;\n-\t\t\tshow_unmerged = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-x\") && i+1 < argc) {\n-\t\t\texc_given = 1;\n-\t\t\tadd_exclude(argv[++i], \"\", 0, &exclude_list[EXC_CMDL]);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--exclude=\", 10)) {\n-\t\t\texc_given = 1;\n-\t\t\tadd_exclude(arg+10, \"\", 0, &exclude_list[EXC_CMDL]);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-X\") && i+1 < argc) {\n-\t\t\texc_given = 1;\n-\t\t\tadd_excludes_from_file(argv[++i]);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--exclude-from=\", 15)) {\n-\t\t\texc_given = 1;\n-\t\t\tadd_excludes_from_file(arg+15);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--exclude-per-directory=\", 24)) {\n-\t\t\texc_given = 1;\n-\t\t\texclude_per_dir = arg + 24;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--full-name\")) {\n-\t\t\tprefix_offset = 0;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--error-unmatch\")) {\n-\t\t\terror_unmatch = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--abbrev=\", 9)) {\n-\t\t\tabbrev = strtoul(arg+9, NULL, 10);\n-\t\t\tif (abbrev && abbrev < MINIMUM_ABBREV)\n-\t\t\t\tabbrev = MINIMUM_ABBREV;\n-\t\t\telse if (abbrev > 40)\n-\t\t\t\tabbrev = 40;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--abbrev\")) {\n-\t\t\tabbrev = DEFAULT_ABBREV;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (*arg == '-')\n-\t\t\tusage(ls_files_usage);\n-\t\tbreak;\n-\t}\n+\tif (gitopt_parse_ost_split(a, b, ls_files_ost, argc, argv) < 0)\n+\t\tusage(ls_files_usage);\n+\tfree_exec_args(a);\n \n-\tpathspec = get_pathspec(prefix, argv + i);\n+\tpathspec = get_pathspec(prefix,b->argv);\n \n \t/* Verify that the pathspec matches the prefix */\n \tif (pathspec)\n-- \n1.3.2.g0a3ae\n"},{"id":"19776","messageId":"20060509120809.4d9494b9.tihirvon@gmail.com","threadId":"4108","inReplyTo":"11471512103526-git-send-email-normalperson@yhbt.net","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Timo Hirvonen","fromEmail":"tihirvon@gmail.com","sentAt":"2006-05-09T09:08:09Z","receivedAt":"2006-05-09T09:08:09Z","isPatch":true,"sender":{"key":"tihirvon@gmail.com","avatar":null},"body":"Eric Wong <normalperson@yhbt.net> wrote:\n\n>  * unbundling of short options: -uC20n20z => -u -C20 -n20 -z\n\nDoes anyone ever use this?  I think this makes sense only for flags that\ndon't have parameters but that would create an ugly special case. Is it\ntoo hard to type \"-u -C=20 -n=20 -z\"?\n\n>  * optional argument handling (-C<num>, -M<num>)\n>    -C <num> (with a space between them) has not changed,\n>    however, <num> can be a sha1, or a path\n\nIMO optional arguments are usually bad idea.\n\n    -C 2 (is \"2\" argument?)\n    -C2  (-C=2 or -C -2?)\n\nBetter to make it obvious there's an argument\n\n    -C=2\n\nor not support optional arguments at all and \"-C 2\" becomes unambiguous.\n\n-- \nhttp://onion.dynserv.net/~timo/\n"},{"id":"19782","messageId":"7v8xpb73sq.fsf@assigned-by-dhcp.cox.net","threadId":"4108","inReplyTo":"20060509120809.4d9494b9.tihirvon@gmail.com","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-09T12:58:45Z","receivedAt":"2006-05-09T12:58:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Timo Hirvonen <tihirvon@gmail.com> writes:\n\n> Eric Wong <normalperson@yhbt.net> wrote:\n>\n>>  * unbundling of short options: -uC20n20z => -u -C20 -n20 -z\n>\n> Does anyone ever use this?  I think this makes sense only for flags that\n> don't have parameters but that would create an ugly special case. Is it\n> too hard to type \"-u -C=20 -n=20 -z\"?\n\nI can already hear in my head that people would start talking\nabout \"git understands insane abbeviations of options\".  It\nmight be unambiguous, but that does not change it is a bit on\nthe insane side.  People would probably expect -nuz can be split\ninto -n -u -z, and the current handcrafted mess (although it is\nmore obvious and easy to work with when reading and maintaining\nthe existing code) is not abbreviation friendly, which we would\nwant to do something about.  But I think squashing options with\nparameters together is going a bit too far.\n\n> --with-r => --patch-with-raw works great\n\nI personally think this also is too much.\n"},{"id":"19788","messageId":"20060509191803.GA3676@localdomain","threadId":"4108","inReplyTo":"20060509120809.4d9494b9.tihirvon@gmail.com","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-09T19:18:03Z","receivedAt":"2006-05-09T19:18:03Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Timo Hirvonen <tihirvon@gmail.com> wrote:\n> Eric Wong <normalperson@yhbt.net> wrote:\n> \n> >  * unbundling of short options: -uC20n20z => -u -C20 -n20 -z\n> \n> Does anyone ever use this?  I think this makes sense only for flags that\n> don't have parameters but that would create an ugly special case. Is it\n> too hard to type \"-u -C=20 -n=20 -z\"?\n\nIt is more for me.  Many programs that I use already accept bundled\nswitches, and '=' and '-' are relatively far away and requires me\nto stretch my hand uncomfortably (I have very small hands, and have\nlimited mobility in several fingers).\n\n> >  * optional argument handling (-C<num>, -M<num>)\n> >    -C <num> (with a space between them) has not changed,\n> >    however, <num> can be a sha1, or a path\n> \n> IMO optional arguments are usually bad idea.\n> \n>     -C 2 (is \"2\" argument?)\n>     -C2  (-C=2 or -C -2?)\n> \n> Better to make it obvious there's an argument\n> \n>     -C=2\n> \n> or not support optional arguments at all and \"-C 2\" becomes unambiguous.\n\ngit has always supported optional argument handling like this.\n\nI'm striving for backwards compatibility with existing usage.  That\nmeans as a diff option, -C alone works, as does -C20.  I've made -C 20\n_not_ work because it breaks existing usage (where 20 could be a\nfilename, or a tree-ish).  -C=20 would mean something\nelse, since I wanted to make pickaxe work exactly as it did before:\n-S=var would search for '=var', not 'var'.\n\n-- \nEric Wong\n"},{"id":"19790","messageId":"20060509193932.GB3676@localdomain","threadId":"4108","inReplyTo":"7v8xpb73sq.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-09T19:39:32Z","receivedAt":"2006-05-09T19:39:32Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Timo Hirvonen <tihirvon@gmail.com> writes:\n> \n> > Eric Wong <normalperson@yhbt.net> wrote:\n> >\n> >>  * unbundling of short options: -uC20n20z => -u -C20 -n20 -z\n> >\n> > Does anyone ever use this?  I think this makes sense only for flags that\n> > don't have parameters but that would create an ugly special case. Is it\n> > too hard to type \"-u -C=20 -n=20 -z\"?\n> \n> I can already hear in my head that people would start talking\n> about \"git understands insane abbeviations of options\".  It\n> might be unambiguous, but that does not change it is a bit on\n> the insane side.  People would probably expect -nuz can be split\n> into -n -u -z, and the current handcrafted mess (although it is\n> more obvious and easy to work with when reading and maintaining\n> the existing code) is not abbreviation friendly, which we would\n> want to do something about.  But I think squashing options with\n> parameters together is going a bit too far.\n\nI think numeric parameters are unambiguous when bundled.\nI'm used to things like `diff -ru10p` working, *shrug*\n\nNon-numeric parameters can only be used if the option is at the end of the\nbundled string:\n\ngit commit -sam'this is my commit message'  would work\n\ngit commit -m'say hello' would also work\n\nbut git commit -mas'this is my commit message' would not work as intended\n(where user wanted -a -s, too)\n\n> > --with-r => --patch-with-raw works great\n> \n> I personally think this also is too much.\n\nThere are (currently) two types of abbreviations, one is the prefix one used\ncommonly in shell scripts:  -e|--e|--ed|--edi|--edit.  I think this should\nalways be supported as most of our shell scripts already do.\n\nThe other one tokenizes on '-' first and looks for a prefix match after\neach '-'.  I'd like to make that at least optional:\n\ndiff --git a/gitopt.c b/gitopt.c\nindex 056e163..9ca6025 100644\n--- a/gitopt.c\n+++ b/gitopt.c\ngitopt.c\n@@ -427,7 +427,7 @@ static void fallback_long(const struct o\n        }\n \n        /* ok, try harder, based on tokenization on '-' */\n-       if (found < 0) {\n+       if (found < 0 && getenv(\"GIT_ABBREV_HARDER\")) {\n                for (i = 0; ost[i].l || ost[i].s; i++) {\n                        s = &(ost[i]);\n                        if (s->l && opt_token_match(s,cur)) {\n\n-- \nEric Wong\n"},{"id":"19792","messageId":"20060509231031.b62576da.tihirvon@gmail.com","threadId":"4108","inReplyTo":"20060509191803.GA3676@localdomain","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Timo Hirvonen","fromEmail":"tihirvon@gmail.com","sentAt":"2006-05-09T20:10:31Z","receivedAt":"2006-05-09T20:10:31Z","isPatch":true,"sender":{"key":"tihirvon@gmail.com","avatar":null},"body":"Eric Wong <normalperson@yhbt.net> wrote:\n\n> I'm striving for backwards compatibility with existing usage.  That\n> means as a diff option, -C alone works, as does -C20.  I've made -C 20\n> _not_ work because it breaks existing usage (where 20 could be a\n> filename, or a tree-ish).  -C=20 would mean something\n> else,\n\nI think optional arguments are still confusing.  We could support both\n-C (no args) and -C=20 syntax.\n\n> since I wanted to make pickaxe work exactly as it did before:\n> -S=var would search for '=var', not 'var'.\n\nSome other options use -x=y syntax so this would be confusing. Pickaxe's\n-Stext syntax is a bit strange.  I think -S text or -S=text would be\nmore logical.\n\n-- \nhttp://onion.dynserv.net/~timo/\n"},{"id":"19795","messageId":"7vlktb2ayy.fsf@assigned-by-dhcp.cox.net","threadId":"4108","inReplyTo":"20060509231031.b62576da.tihirvon@gmail.com","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-09T20:35:01Z","receivedAt":"2006-05-09T20:35:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Timo Hirvonen <tihirvon@gmail.com> writes:\n\n> I think optional arguments are still confusing.  We could support both\n> -C (no args) and -C=20 syntax.\n\nActually, optional numeric arguments are the norm not exception.\nThink of \"diff -u\" vs \"diff -u20\" for example.  Also I think it\nis conventional not to use = for single-letter single-dash\noptions, so -C (no args -- use the default number of the\nimplementation whatever it is) and -C20 (the same behaviour in\nprinciple as -C, but use my number instead of the default) are\nsane, while -C=20 _is_ odd.\n\nHaving said that, I think abbreviating -u20 -n -r to -u20nr\ngoing too far (-nru20 would be palatable perhaps), even if the\nimplementation allows such, unless you are entering \"obfusucated\ncommand line parameters\" contest.\n"},{"id":"19796","messageId":"20060510000826.1a708c03.tihirvon@gmail.com","threadId":"4108","inReplyTo":"7vlktb2ayy.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Timo Hirvonen","fromEmail":"tihirvon@gmail.com","sentAt":"2006-05-09T21:08:26Z","receivedAt":"2006-05-09T21:08:26Z","isPatch":true,"sender":{"key":"tihirvon@gmail.com","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n\n> Timo Hirvonen <tihirvon@gmail.com> writes:\n> \n> > I think optional arguments are still confusing.  We could support both\n> > -C (no args) and -C=20 syntax.\n> \n> Actually, optional numeric arguments are the norm not exception.\n> Think of \"diff -u\" vs \"diff -u20\" for example.  Also I think it\n> is conventional not to use = for single-letter single-dash\n> options, so -C (no args -- use the default number of the\n> implementation whatever it is) and -C20 (the same behaviour in\n> principle as -C, but use my number instead of the default) are\n> sane, while -C=20 _is_ odd.\n\nOK, if we don't support bundling flags at all then -x=y and -xy would do\nthe same thing and pickaxe's -Stext would work too.  But we could not\nmake option flag parsing global then (-S=value -> search \"=value\" or\n\"value\"?).\n\nMaybe we should just change -Stext to -S=text or -S text.\n\nBetter to support only -x=y or -x y, not both.\n\n> Having said that, I think abbreviating -u20 -n -r to -u20nr\n> going too far\n\nYes\n\n> (-nru20 would be palatable perhaps),\n\nNo :)\n\n-- \nhttp://onion.dynserv.net/~timo/\n"},{"id":"19797","messageId":"7v7j4u3nv8.fsf@assigned-by-dhcp.cox.net","threadId":"4108","inReplyTo":"20060510000826.1a708c03.tihirvon@gmail.com","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-09T21:11:07Z","receivedAt":"2006-05-09T21:11:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Timo Hirvonen <tihirvon@gmail.com> writes:\n\n> Better to support only -x=y or -x y, not both.\n\nDidn't I just say -x=y where x is a single letter _is_ odd?\nIt is either -xy or -x y, not -x=y.\n"},{"id":"19799","messageId":"20060510003141.0d0820ed.tihirvon@gmail.com","threadId":"4108","inReplyTo":"7v7j4u3nv8.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/6] gitopt: a new command-line option parser for git","fromName":"Timo Hirvonen","fromEmail":"tihirvon@gmail.com","sentAt":"2006-05-09T21:31:41Z","receivedAt":"2006-05-09T21:31:41Z","isPatch":true,"sender":{"key":"tihirvon@gmail.com","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n\n> Timo Hirvonen <tihirvon@gmail.com> writes:\n> \n> > Better to support only -x=y or -x y, not both.\n> \n> Didn't I just say -x=y where x is a single letter _is_ odd?\n> It is either -xy or -x y, not -x=y.\n\nOh, I thought parameters would use same syntax for short and long\noptions.  For optional args -C2 would make sense but -C 2 would be\nambiguous (\"-C -- 2\" or \"-C2\"?).  Maybe I'm just too drunk to\nunderstand.\n\n-- \nhttp://onion.dynserv.net/~timo/\n"}]}