Merge branch 'pb/gitweb'
[gitweb.git] / parse-options.c
index 04adf219cace134e2e51440b037b0e8a2622b3f7..fd08bb425c241a0861588ec0aedd15041095a95f 100644 (file)
@@ -1,26 +1,10 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
+#include "cache.h"
 
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
-static inline const char *get_arg(struct parse_opt_ctx_t *p)
-{
-       if (p->opt) {
-               const char *res = p->opt;
-               p->opt = NULL;
-               return res;
-       }
-       p->argc--;
-       return *++p->argv;
-}
-
-static inline const char *skip_prefix(const char *str, const char *prefix)
-{
-       size_t len = strlen(prefix);
-       return strncmp(str, prefix, len) ? NULL : str + len;
-}
-
 static int opterror(const struct option *opt, const char *reason, int flags)
 {
        if (flags & OPT_SHORT)
@@ -30,8 +14,24 @@ static int opterror(const struct option *opt, const char *reason, int flags)
        return error("option `%s' %s", opt->long_name, reason);
 }
 
+static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
+                  int flags, const char **arg)
+{
+       if (p->opt) {
+               *arg = p->opt;
+               p->opt = NULL;
+       } else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) {
+               *arg = (const char *)opt->defval;
+       } else if (p->argc > 1) {
+               p->argc--;
+               *arg = *++p->argv;
+       } else
+               return opterror(opt, "requires a value", flags);
+       return 0;
+}
+
 static int get_value(struct parse_opt_ctx_t *p,
-                     const struct option *opt, int flags)
+                    const struct option *opt, int flags)
 {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
@@ -57,7 +57,6 @@ static int get_value(struct parse_opt_ctx_t *p,
                }
        }
 
-       arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
        switch (opt->type) {
        case OPTION_BIT:
                if (unset)
@@ -79,29 +78,24 @@ static int get_value(struct parse_opt_ctx_t *p,
                return 0;
 
        case OPTION_STRING:
-               if (unset) {
+               if (unset)
                        *(const char **)opt->value = NULL;
-                       return 0;
-               }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+               else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
                        *(const char **)opt->value = (const char *)opt->defval;
-                       return 0;
-               }
-               if (!arg)
-                       return opterror(opt, "requires a value", flags);
-               *(const char **)opt->value = get_arg(p);
+               else
+                       return get_arg(p, opt, flags, (const char **)opt->value);
                return 0;
 
        case OPTION_CALLBACK:
                if (unset)
-                       return (*opt->callback)(opt, NULL, 1);
+                       return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
                if (opt->flags & PARSE_OPT_NOARG)
-                       return (*opt->callback)(opt, NULL, 0);
+                       return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
                if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
-                       return (*opt->callback)(opt, NULL, 0);
-               if (!arg)
-                       return opterror(opt, "requires a value", flags);
-               return (*opt->callback)(opt, get_arg(p), 0);
+                       return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
+               if (get_arg(p, opt, flags, &arg))
+                       return -1;
+               return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
 
        case OPTION_INTEGER:
                if (unset) {
@@ -112,9 +106,9 @@ static int get_value(struct parse_opt_ctx_t *p,
                        *(int *)opt->value = opt->defval;
                        return 0;
                }
-               if (!arg)
-                       return opterror(opt, "requires a value", flags);
-               *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);
+               if (get_arg(p, opt, flags, &arg))
+                       return -1;
+               *(int *)opt->value = strtol(arg, (char **)&s, 10);
                if (*s)
                        return opterror(opt, "expects a numerical value", flags);
                return 0;
@@ -132,7 +126,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio
                        return get_value(p, options, OPT_SHORT);
                }
        }
-       return error("unknown switch `%c'", *p->opt);
+       return -2;
 }
 
 static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
@@ -217,10 +211,10 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        abbrev_option->long_name);
        if (abbrev_option)
                return get_value(p, abbrev_option, abbrev_flags);
-       return error("unknown option `%s'", arg);
+       return -2;
 }
 
-void check_typos(const char *arg, const struct option *options)
+static void check_typos(const char *arg, const struct option *options)
 {
        if (strlen(arg) < 3)
                return;
@@ -247,6 +241,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
        ctx->argc = argc - 1;
        ctx->argv = argv + 1;
        ctx->out  = argv;
+       ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
        ctx->flags = flags;
 }
 
@@ -257,6 +252,9 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                       const struct option *options,
                       const char * const usagestr[])
 {
+       /* we must reset ->opt, unknown short option leave it dangling */
+       ctx->opt = NULL;
+
        for (; ctx->argc; ctx->argc--, ctx->argv++) {
                const char *arg = ctx->argv[0];
 
@@ -271,15 +269,30 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        ctx->opt = arg + 1;
                        if (*ctx->opt == 'h')
                                return parse_options_usage(usagestr, options);
-                       if (parse_short_opt(ctx, options) < 0)
-                               usage_with_options(usagestr, options);
+                       switch (parse_short_opt(ctx, options)) {
+                       case -1:
+                               return parse_options_usage(usagestr, options);
+                       case -2:
+                               return PARSE_OPT_UNKNOWN;
+                       }
                        if (ctx->opt)
                                check_typos(arg + 1, options);
                        while (ctx->opt) {
                                if (*ctx->opt == 'h')
                                        return parse_options_usage(usagestr, options);
-                               if (parse_short_opt(ctx, options) < 0)
-                                       usage_with_options(usagestr, options);
+                               switch (parse_short_opt(ctx, options)) {
+                               case -1:
+                                       return parse_options_usage(usagestr, options);
+                               case -2:
+                                       /* fake a short option thing to hide the fact that we may have
+                                        * started to parse aggregated stuff
+                                        *
+                                        * This is leaky, too bad.
+                                        */
+                                       ctx->argv[0] = xstrdup(ctx->opt - 1);
+                                       *(char *)ctx->argv[0] = '-';
+                                       return PARSE_OPT_UNKNOWN;
+                               }
                        }
                        continue;
                }
@@ -296,8 +309,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        return usage_with_options_internal(usagestr, options, 1);
                if (!strcmp(arg + 2, "help"))
                        return parse_options_usage(usagestr, options);
-               if (parse_long_opt(ctx, arg + 2, options))
-                       usage_with_options(usagestr, options);
+               switch (parse_long_opt(ctx, arg + 2, options)) {
+               case -1:
+                       return parse_options_usage(usagestr, options);
+               case -2:
+                       return PARSE_OPT_UNKNOWN;
+               }
        }
        return PARSE_OPT_DONE;
 }
@@ -321,7 +338,12 @@ int parse_options(int argc, const char **argv, const struct option *options,
        case PARSE_OPT_DONE:
                break;
        default: /* PARSE_OPT_UNKNOWN */
-               abort(); /* unreached yet */
+               if (ctx.argv[0][1] == '-') {
+                       error("unknown option `%s'", ctx.argv[0] + 2);
+               } else {
+                       error("unknown switch `%c'", *ctx.opt);
+               }
+               usage_with_options(usagestr, options);
        }
 
        return parse_options_end(&ctx);
@@ -461,3 +483,15 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
        *(unsigned long *)(opt->value) = approxidate(arg);
        return 0;
 }
+
+/*
+ * This should really be OPTION_FILENAME type as a part of
+ * parse_options that take prefix to do this while parsing.
+ */
+extern const char *parse_options_fix_filename(const char *prefix, const char *file)
+{
+       if (!file || !prefix || is_absolute_path(file) || !strcmp("-", file))
+               return file;
+       return prefix_filename(prefix, strlen(prefix), file);
+}
+