Merge branch 'jn/parse-options-extra'
authorJunio C Hamano <gitster@pobox.com>
Mon, 13 Dec 2010 05:49:53 +0000 (21:49 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 13 Dec 2010 05:49:53 +0000 (21:49 -0800)
* jn/parse-options-extra:
update-index: migrate to parse-options API
setup: save prefix (original cwd relative to toplevel) in startup_info
parse-options: make resuming easier after PARSE_OPT_STOP_AT_NON_OPTION
parse-options: allow git commands to invent new option types
parse-options: never suppress arghelp if LITERAL_ARGHELP is set
parse-options: do not infer PARSE_OPT_NOARG from option type
parse-options: sanity check PARSE_OPT_NOARG flag
parse-options: move NODASH sanity checks to parse_options_check
parse-options: clearer reporting of API misuse
parse-options: Don't call parse_options_check() so much

builtin/blame.c
builtin/shortlog.c
builtin/update-index.c
cache.h
parse-options.c
parse-options.h
setup.c
index cb25ec9ce882ac2b975420bb25735fbd156dc4ab..aa30ec52692558b0d8ed3dc3b6d671185682026e 100644 (file)
@@ -2325,8 +2325,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        dashdash_pos = 0;
 
-       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-                           PARSE_OPT_KEEP_ARGV0);
+       parse_options_start(&ctx, argc, argv, prefix, options,
+                           PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
        for (;;) {
                switch (parse_options_step(&ctx, options, blame_opt_usage)) {
                case PARSE_OPT_HELP:
index 2135b0dde11faa0c501682ed074e0ba96bf28b42..1a21e4b0538565f7a64488ed7b3a5c317e559699 100644 (file)
@@ -268,8 +268,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
        shortlog_init(&log);
        init_revisions(&rev, prefix);
-       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-                           PARSE_OPT_KEEP_ARGV0);
+       parse_options_start(&ctx, argc, argv, prefix, options,
+                           PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
 
        for (;;) {
                switch (parse_options_step(&ctx, options, shortlog_usage)) {
index 200c7efed18ebba7088f2b56d942084be280a578..56baf27fb7eb18d77e8793d089f2a719d4876689 100644 (file)
@@ -10,6 +10,7 @@
 #include "builtin.h"
 #include "refs.h"
 #include "resolve-undo.h"
+#include "parse-options.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -397,8 +398,10 @@ static void read_index_info(int line_termination)
        strbuf_release(&uq);
 }
 
-static const char update_index_usage[] =
-"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] [<file>...]";
+static const char * const update_index_usage[] = {
+       "git update-index [options] [--] [<file>...]",
+       NULL
+};
 
 static unsigned char head_sha1[20];
 static unsigned char merge_head_sha1[20];
@@ -578,19 +581,214 @@ static int do_reupdate(int ac, const char **av,
        return 0;
 }
 
+struct refresh_params {
+       unsigned int flags;
+       int *has_errors;
+};
+
+static int refresh(struct refresh_params *o, unsigned int flag)
+{
+       setup_work_tree();
+       *o->has_errors |= refresh_cache(o->flags | flag);
+       return 0;
+}
+
+static int refresh_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       return refresh(opt->value, 0);
+}
+
+static int really_refresh_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       return refresh(opt->value, REFRESH_REALLY);
+}
+
+static int chmod_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       char *flip = opt->value;
+       if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
+               return error("option 'chmod' expects \"+x\" or \"-x\"");
+       *flip = arg[0];
+       return 0;
+}
+
+static int resolve_undo_clear_callback(const struct option *opt,
+                               const char *arg, int unset)
+{
+       resolve_undo_clear();
+       return 0;
+}
+
+static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset)
+{
+       unsigned char sha1[20];
+       unsigned int mode;
+
+       if (ctx->argc <= 3)
+               return error("option 'cacheinfo' expects three arguments");
+       if (strtoul_ui(*++ctx->argv, 8, &mode) ||
+           get_sha1_hex(*++ctx->argv, sha1) ||
+           add_cacheinfo(mode, sha1, *++ctx->argv, 0))
+               die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
+       ctx->argc -= 3;
+       return 0;
+}
+
+static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+                             const struct option *opt, int unset)
+{
+       int *line_termination = opt->value;
+
+       if (ctx->argc != 1)
+               return error("option '%s' must be the last argument", opt->long_name);
+       allow_add = allow_replace = allow_remove = 1;
+       read_index_info(*line_termination);
+       return 0;
+}
+
+static int stdin_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset)
+{
+       int *read_from_stdin = opt->value;
+
+       if (ctx->argc != 1)
+               return error("option '%s' must be the last argument", opt->long_name);
+       *read_from_stdin = 1;
+       return 0;
+}
+
+static int unresolve_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int flags)
+{
+       int *has_errors = opt->value;
+       const char *prefix = startup_info->prefix;
+
+       /* consume remaining arguments. */
+       *has_errors = do_unresolve(ctx->argc, ctx->argv,
+                               prefix, prefix ? strlen(prefix) : 0);
+       if (*has_errors)
+               active_cache_changed = 0;
+
+       ctx->argv += ctx->argc - 1;
+       ctx->argc = 1;
+       return 0;
+}
+
+static int reupdate_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int flags)
+{
+       int *has_errors = opt->value;
+       const char *prefix = startup_info->prefix;
+
+       /* consume remaining arguments. */
+       setup_work_tree();
+       *has_errors = do_reupdate(ctx->argc, ctx->argv,
+                               prefix, prefix ? strlen(prefix) : 0);
+       if (*has_errors)
+               active_cache_changed = 0;
+
+       ctx->argv += ctx->argc - 1;
+       ctx->argc = 1;
+       return 0;
+}
+
 int cmd_update_index(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd, entries, has_errors = 0, line_termination = '\n';
-       int allow_options = 1;
+       int newfd, entries, has_errors = 0, line_termination = '\n';
        int read_from_stdin = 0;
        int prefix_length = prefix ? strlen(prefix) : 0;
        char set_executable_bit = 0;
-       unsigned int refresh_flags = 0;
+       struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        struct lock_file *lock_file;
+       struct parse_opt_ctx_t ctx;
+       int parseopt_state = PARSE_OPT_UNKNOWN;
+       struct option options[] = {
+               OPT_BIT('q', NULL, &refresh_args.flags,
+                       "continue refresh even when index needs update",
+                       REFRESH_QUIET),
+               OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
+                       "refresh: ignore submodules",
+                       REFRESH_IGNORE_SUBMODULES),
+               OPT_SET_INT(0, "add", &allow_add,
+                       "do not ignore new files", 1),
+               OPT_SET_INT(0, "replace", &allow_replace,
+                       "let files replace directories and vice-versa", 1),
+               OPT_SET_INT(0, "remove", &allow_remove,
+                       "notice files missing from worktree", 1),
+               OPT_BIT(0, "unmerged", &refresh_args.flags,
+                       "refresh even if index contains unmerged entries",
+                       REFRESH_UNMERGED),
+               {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
+                       "refresh stat information",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       refresh_callback},
+               {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
+                       "like --refresh, but ignore assume-unchanged setting",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       really_refresh_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
+                       "<mode> <object> <path>",
+                       "add the specified entry to the index",
+                       PARSE_OPT_NOARG |       /* disallow --cacheinfo=<mode> form */
+                       PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+                       (parse_opt_cb *) cacheinfo_callback},
+               {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x",
+                       "override the executable bit of the listed files",
+                       PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+                       chmod_callback},
+               {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
+                       "mark files as \"not changing\"",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
+                       "clear assumed-unchanged bit",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
+                       "mark files as \"index-only\"",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
+                       "clear skip-worktree bit",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               OPT_SET_INT(0, "info-only", &info_only,
+                       "add to index only; do not add content to object database", 1),
+               OPT_SET_INT(0, "force-remove", &force_remove,
+                       "remove named paths even if present in worktree", 1),
+               OPT_SET_INT('z', NULL, &line_termination,
+                       "with --stdin: input lines are terminated by null bytes", '\0'),
+               {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
+                       "read list of paths to be updated from standard input",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) stdin_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL,
+                       "add entries from standard input to the index",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) stdin_cacheinfo_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
+                       "repopulate stages #2 and #3 for the listed paths",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) unresolve_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
+                       "only update entries that differ from HEAD",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) reupdate_callback},
+               OPT_BIT(0, "ignore-missing", &refresh_args.flags,
+                       "ignore files missing from worktree",
+                       REFRESH_IGNORE_MISSING),
+               OPT_SET_INT(0, "verbose", &verbose,
+                       "report actions to standard output", 1),
+               {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
+                       "(for porcelains) forget saved unresolved conflicts",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       resolve_undo_clear_callback},
+               OPT_END()
+       };
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
-               usage(update_index_usage);
+               usage(update_index_usage[0]);
 
        git_config(git_default_config, NULL);
 
@@ -605,151 +803,48 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        if (entries < 0)
                die("cache corrupted");
 
-       for (i = 1 ; i < argc; i++) {
-               const char *path = argv[i];
-               const char *p;
+       /*
+        * Custom copy of parse_options() because we want to handle
+        * filename arguments as they come.
+        */
+       parse_options_start(&ctx, argc, argv, prefix,
+                           options, PARSE_OPT_STOP_AT_NON_OPTION);
+       while (ctx.argc) {
+               if (parseopt_state != PARSE_OPT_DONE)
+                       parseopt_state = parse_options_step(&ctx, options,
+                                                           update_index_usage);
+               if (!ctx.argc)
+                       break;
+               switch (parseopt_state) {
+               case PARSE_OPT_HELP:
+                       exit(129);
+               case PARSE_OPT_NON_OPTION:
+               case PARSE_OPT_DONE:
+               {
+                       const char *path = ctx.argv[0];
+                       const char *p;
 
-               if (allow_options && *path == '-') {
-                       if (!strcmp(path, "--")) {
-                               allow_options = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "-q")) {
-                               refresh_flags |= REFRESH_QUIET;
-                               continue;
-                       }
-                       if (!strcmp(path, "--ignore-submodules")) {
-                               refresh_flags |= REFRESH_IGNORE_SUBMODULES;
-                               continue;
-                       }
-                       if (!strcmp(path, "--add")) {
-                               allow_add = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--replace")) {
-                               allow_replace = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--remove")) {
-                               allow_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--unmerged")) {
-                               refresh_flags |= REFRESH_UNMERGED;
-                               continue;
-                       }
-                       if (!strcmp(path, "--refresh")) {
-                               setup_work_tree();
-                               has_errors |= refresh_cache(refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--really-refresh")) {
-                               setup_work_tree();
-                               has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--cacheinfo")) {
-                               unsigned char sha1[20];
-                               unsigned int mode;
-
-                               if (i+3 >= argc)
-                                       die("git update-index: --cacheinfo <mode> <sha1> <path>");
-
-                               if (strtoul_ui(argv[i+1], 8, &mode) ||
-                                   get_sha1_hex(argv[i+2], sha1) ||
-                                   add_cacheinfo(mode, sha1, argv[i+3], 0))
-                                       die("git update-index: --cacheinfo"
-                                           " cannot add %s", argv[i+3]);
-                               i += 3;
-                               continue;
-                       }
-                       if (!strcmp(path, "--chmod=-x") ||
-                           !strcmp(path, "--chmod=+x")) {
-                               if (argc <= i+1)
-                                       die("git update-index: %s <path>", path);
-                               set_executable_bit = path[8];
-                               continue;
-                       }
-                       if (!strcmp(path, "--assume-unchanged")) {
-                               mark_valid_only = MARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-assume-unchanged")) {
-                               mark_valid_only = UNMARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-skip-worktree")) {
-                               mark_skip_worktree_only = UNMARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--skip-worktree")) {
-                               mark_skip_worktree_only = MARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--info-only")) {
-                               info_only = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--force-remove")) {
-                               force_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "-z")) {
-                               line_termination = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "--stdin")) {
-                               if (i != argc - 1)
-                                       die("--stdin must be at the end");
-                               read_from_stdin = 1;
-                               break;
-                       }
-                       if (!strcmp(path, "--index-info")) {
-                               if (i != argc - 1)
-                                       die("--index-info must be at the end");
-                               allow_add = allow_replace = allow_remove = 1;
-                               read_index_info(line_termination);
-                               break;
-                       }
-                       if (!strcmp(path, "--unresolve")) {
-                               has_errors = do_unresolve(argc - i, argv + i,
-                                                         prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--again") || !strcmp(path, "-g")) {
-                               setup_work_tree();
-                               has_errors = do_reupdate(argc - i, argv + i,
-                                                        prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--ignore-missing")) {
-                               refresh_flags |= REFRESH_IGNORE_MISSING;
-                               continue;
-                       }
-                       if (!strcmp(path, "--verbose")) {
-                               verbose = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--clear-resolve-undo")) {
-                               resolve_undo_clear();
-                               continue;
-                       }
-                       if (!strcmp(path, "-h") || !strcmp(path, "--help"))
-                               usage(update_index_usage);
-                       die("unknown option %s", path);
+                       setup_work_tree();
+                       p = prefix_path(prefix, prefix_length, path);
+                       update_one(p, NULL, 0);
+                       if (set_executable_bit)
+                               chmod_path(set_executable_bit, p);
+                       if (p < path || p > path + strlen(path))
+                               free((char *)p);
+                       ctx.argc--;
+                       ctx.argv++;
+                       break;
+               }
+               case PARSE_OPT_UNKNOWN:
+                       if (ctx.argv[0][1] == '-')
+                               error("unknown option '%s'", ctx.argv[0] + 2);
+                       else
+                               error("unknown switch '%c'", *ctx.opt);
+                       usage_with_options(update_index_usage, options);
                }
-               setup_work_tree();
-               p = prefix_path(prefix, prefix_length, path);
-               update_one(p, NULL, 0);
-               if (set_executable_bit)
-                       chmod_path(set_executable_bit, p);
-               if (p < path || p > path + strlen(path))
-                       free((char *)p);
        }
+       argc = parse_options_end(&ctx);
+
        if (read_from_stdin) {
                struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
 
@@ -773,10 +868,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        }
 
- finish:
        if (active_cache_changed) {
                if (newfd < 0) {
-                       if (refresh_flags & REFRESH_QUIET)
+                       if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
                        unable_to_lock_index_die(get_index_file(), lock_error);
                }
diff --git a/cache.h b/cache.h
index 909b1f6e9a7c45614522866aa4f20fb41e4628ec..194f784808a4ead9b61f406d2ffe60fe864831bf 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1124,6 +1124,7 @@ const char *split_cmdline_strerror(int cmdline_errno);
 /* git.c */
 struct startup_info {
        int have_repository;
+       const char *prefix;
 };
 extern struct startup_info *startup_info;
 
index 0fa79bc31d322e2aab8fce738ca6489a35a51ca4..42b51ef14514f3c3df454e4b718718a9b58f1612 100644 (file)
@@ -11,6 +11,13 @@ static int parse_options_usage(struct parse_opt_ctx_t *ctx,
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
+static int optbug(const struct option *opt, const char *reason)
+{
+       if (opt->long_name)
+               return error("BUG: option '%s' %s", opt->long_name, reason);
+       return error("BUG: switch '%c' %s", opt->short_name, reason);
+}
+
 static int opterror(const struct option *opt, const char *reason, int flags)
 {
        if (flags & OPT_SHORT)
@@ -55,25 +62,13 @@ static int get_value(struct parse_opt_ctx_t *p,
                return opterror(opt, "takes no value", flags);
        if (unset && (opt->flags & PARSE_OPT_NONEG))
                return opterror(opt, "isn't available", flags);
-
-       if (!(flags & OPT_SHORT) && p->opt) {
-               switch (opt->type) {
-               case OPTION_CALLBACK:
-                       if (!(opt->flags & PARSE_OPT_NOARG))
-                               break;
-                       /* FALLTHROUGH */
-               case OPTION_BOOLEAN:
-               case OPTION_BIT:
-               case OPTION_NEGBIT:
-               case OPTION_SET_INT:
-               case OPTION_SET_PTR:
-                       return opterror(opt, "takes no value", flags);
-               default:
-                       break;
-               }
-       }
+       if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
+               return opterror(opt, "takes no value", flags);
 
        switch (opt->type) {
+       case OPTION_LOWLEVEL_CALLBACK:
+               return (*(parse_opt_ll_cb *)opt->callback)(p, opt, unset);
+
        case OPTION_BIT:
                if (unset)
                        *(int *)opt->value &= ~opt->defval;
@@ -281,13 +276,6 @@ static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
        for (; options->type != OPTION_END; options++) {
                if (!(options->flags & PARSE_OPT_NODASH))
                        continue;
-               if ((options->flags & PARSE_OPT_OPTARG) ||
-                   !(options->flags & PARSE_OPT_NOARG))
-                       die("BUG: dashless options don't support arguments");
-               if (!(options->flags & PARSE_OPT_NONEG))
-                       die("BUG: dashless options don't support negation");
-               if (options->long_name)
-                       die("BUG: dashless options can't be long");
                if (options->short_name == arg[0] && arg[1] == '\0')
                        return get_value(p, options, OPT_SHORT);
        }
@@ -320,25 +308,37 @@ static void parse_options_check(const struct option *opts)
 
        for (; opts->type != OPTION_END; opts++) {
                if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
-                   (opts->flags & PARSE_OPT_OPTARG)) {
-                       if (opts->long_name) {
-                               error("`--%s` uses incompatible flags "
-                                     "LASTARG_DEFAULT and OPTARG", opts->long_name);
-                       } else {
-                               error("`-%c` uses incompatible flags "
-                                     "LASTARG_DEFAULT and OPTARG", opts->short_name);
-                       }
-                       err |= 1;
+                   (opts->flags & PARSE_OPT_OPTARG))
+                       err |= optbug(opts, "uses incompatible flags "
+                                       "LASTARG_DEFAULT and OPTARG");
+               if (opts->flags & PARSE_OPT_NODASH &&
+                   ((opts->flags & PARSE_OPT_OPTARG) ||
+                    !(opts->flags & PARSE_OPT_NOARG) ||
+                    !(opts->flags & PARSE_OPT_NONEG) ||
+                    opts->long_name))
+                       err |= optbug(opts, "uses feature "
+                                       "not supported for dashless options");
+               switch (opts->type) {
+               case OPTION_BOOLEAN:
+               case OPTION_BIT:
+               case OPTION_NEGBIT:
+               case OPTION_SET_INT:
+               case OPTION_SET_PTR:
+               case OPTION_NUMBER:
+                       if ((opts->flags & PARSE_OPT_OPTARG) ||
+                           !(opts->flags & PARSE_OPT_NOARG))
+                               err |= optbug(opts, "should not accept an argument");
+               default:
+                       ; /* ok. (usually accepts an argument) */
                }
        }
-
        if (err)
-               exit(129);
+               exit(128);
 }
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
                         int argc, const char **argv, const char *prefix,
-                        int flags)
+                        const struct option *options, int flags)
 {
        memset(ctx, 0, sizeof(*ctx));
        ctx->argc = argc - 1;
@@ -350,6 +350,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
        if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
            (flags & PARSE_OPT_STOP_AT_NON_OPTION))
                die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+       parse_options_check(options);
 }
 
 static int usage_with_options_internal(struct parse_opt_ctx_t *,
@@ -362,8 +363,6 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 {
        int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
 
-       parse_options_check(options);
-
        /* we must reset ->opt, unknown short option leave it dangling */
        ctx->opt = NULL;
 
@@ -374,7 +373,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        if (parse_nodash_opt(ctx, arg, options) == 0)
                                continue;
                        if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
-                               break;
+                               return PARSE_OPT_NON_OPTION;
                        ctx->out[ctx->cpidx++] = ctx->argv[0];
                        continue;
                }
@@ -452,10 +451,11 @@ int parse_options(int argc, const char **argv, const char *prefix,
 {
        struct parse_opt_ctx_t ctx;
 
-       parse_options_start(&ctx, argc, argv, prefix, flags);
+       parse_options_start(&ctx, argc, argv, prefix, options, flags);
        switch (parse_options_step(&ctx, options, usagestr)) {
        case PARSE_OPT_HELP:
                exit(129);
+       case PARSE_OPT_NON_OPTION:
        case PARSE_OPT_DONE:
                break;
        default: /* PARSE_OPT_UNKNOWN */
@@ -541,7 +541,8 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                if (opts->type == OPTION_NUMBER)
                        pos += fprintf(outfile, "-NUM");
 
-               if (!(opts->flags & PARSE_OPT_NOARG))
+               if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
+                   !(opts->flags & PARSE_OPT_NOARG))
                        pos += usage_argh(opts, outfile);
 
                if (pos <= USAGE_OPTS_WIDTH)
index ae8647d8932512a49f5a7f9bc9decee34964976b..31ec5d24763b13b3e97c79f7a3edd6e7799bcd91 100644 (file)
@@ -17,6 +17,7 @@ enum parse_opt_type {
        OPTION_STRING,
        OPTION_INTEGER,
        OPTION_CALLBACK,
+       OPTION_LOWLEVEL_CALLBACK,
        OPTION_FILENAME
 };
 
@@ -43,6 +44,10 @@ enum parse_opt_option_flags {
 struct option;
 typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
 
+struct parse_opt_ctx_t;
+typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset);
+
 /*
  * `type`::
  *   holds the type of the option, you must have an OPTION_END last in your
@@ -87,7 +92,8 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  *                             useful for users of OPTION_NEGBIT.
  *
  * `callback`::
- *   pointer to the callback to use for OPTION_CALLBACK.
+ *   pointer to the callback to use for OPTION_CALLBACK or
+ *   OPTION_LOWLEVEL_CALLBACK.
  *
  * `defval`::
  *   default value to fill (*->value) with for PARSE_OPT_OPTARG.
@@ -161,6 +167,7 @@ extern NORETURN void usage_msg_opt(const char *msg,
 enum {
        PARSE_OPT_HELP = -1,
        PARSE_OPT_DONE,
+       PARSE_OPT_NON_OPTION,
        PARSE_OPT_UNKNOWN
 };
 
@@ -180,7 +187,7 @@ struct parse_opt_ctx_t {
 
 extern void parse_options_start(struct parse_opt_ctx_t *ctx,
                                int argc, const char **argv, const char *prefix,
-                               int flags);
+                               const struct option *options, int flags);
 
 extern int parse_options_step(struct parse_opt_ctx_t *ctx,
                              const struct option *options,
diff --git a/setup.c b/setup.c
index 346ef2eb2d678b742280c0816a09b8ca174ca799..14f91e39353bcfa1ee2b08d6a5b5f53a024a280c 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -512,8 +512,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
        const char *prefix;
 
        prefix = setup_git_directory_gently_1(nongit_ok);
-       if (startup_info)
+       if (startup_info) {
                startup_info->have_repository = !nongit_ok || !*nongit_ok;
+               startup_info->prefix = prefix;
+       }
        return prefix;
 }