Many commands call usage_with_options() when they are asked to give the help message, but it sends the help text to the standard error stream. When the user asked for it with "git cmd -h", the help message is the primary output from the command, hence we should send it to the standard output stream, instead. Introduce a helper function that captures the common pattern if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(usage, options); and replaces it with show_usage_with_options_if_asked(argc, argv, usage, options); to help correct code paths. Note that this helper function still exits with status 129, and t0012 insists on it. After converting all the mistaken callers of usage_with_options() to call this new helper, we may want to address it---the end user is asking us to give the help text, and we are doing exactly as asked, so there is no reason to exit with non-zero status. Suggested-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			1349 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1349 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "git-compat-util.h"
 | 
						|
#include "parse-options.h"
 | 
						|
#include "abspath.h"
 | 
						|
#include "parse.h"
 | 
						|
#include "gettext.h"
 | 
						|
#include "strbuf.h"
 | 
						|
#include "string-list.h"
 | 
						|
#include "utf8.h"
 | 
						|
 | 
						|
static int disallow_abbreviated_options;
 | 
						|
 | 
						|
enum opt_parsed {
 | 
						|
	OPT_LONG  = 0,
 | 
						|
	OPT_SHORT = 1<<0,
 | 
						|
	OPT_UNSET = 1<<1,
 | 
						|
};
 | 
						|
 | 
						|
static void optbug(const struct option *opt, const char *reason)
 | 
						|
{
 | 
						|
	if (opt->long_name && opt->short_name)
 | 
						|
		bug("switch '%c' (--%s) %s", opt->short_name,
 | 
						|
		    opt->long_name, reason);
 | 
						|
	else if (opt->long_name)
 | 
						|
		bug("option '%s' %s", opt->long_name, reason);
 | 
						|
	else
 | 
						|
		bug("switch '%c' %s", opt->short_name, reason);
 | 
						|
}
 | 
						|
 | 
						|
static const char *optname(const struct option *opt, enum opt_parsed flags)
 | 
						|
{
 | 
						|
	static struct strbuf sb = STRBUF_INIT;
 | 
						|
 | 
						|
	strbuf_reset(&sb);
 | 
						|
	if (flags & OPT_SHORT)
 | 
						|
		strbuf_addf(&sb, "switch `%c'", opt->short_name);
 | 
						|
	else if (flags & OPT_UNSET)
 | 
						|
		strbuf_addf(&sb, "option `no-%s'", opt->long_name);
 | 
						|
	else if (flags == OPT_LONG)
 | 
						|
		strbuf_addf(&sb, "option `%s'", opt->long_name);
 | 
						|
	else
 | 
						|
		BUG("optname() got unknown flags %d", flags);
 | 
						|
 | 
						|
	return sb.buf;
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
 | 
						|
				     const struct option *opt,
 | 
						|
				     enum opt_parsed 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 error(_("%s requires a value"), optname(opt, flags));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static char *fix_filename(const char *prefix, const char *file)
 | 
						|
{
 | 
						|
	if (!file || !*file)
 | 
						|
		return NULL;
 | 
						|
	else
 | 
						|
		return prefix_filename_except_for_dash(prefix, file);
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
 | 
						|
					  const struct option *opt,
 | 
						|
					  enum opt_parsed flags,
 | 
						|
					  const char **argp)
 | 
						|
{
 | 
						|
	const char *s, *arg;
 | 
						|
	const int unset = flags & OPT_UNSET;
 | 
						|
	int err;
 | 
						|
 | 
						|
	if (unset && p->opt)
 | 
						|
		return error(_("%s takes no value"), optname(opt, flags));
 | 
						|
	if (unset && (opt->flags & PARSE_OPT_NONEG))
 | 
						|
		return error(_("%s isn't available"), optname(opt, flags));
 | 
						|
	if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
 | 
						|
		return error(_("%s takes no value"), optname(opt, flags));
 | 
						|
 | 
						|
	switch (opt->type) {
 | 
						|
	case OPTION_LOWLEVEL_CALLBACK:
 | 
						|
		return opt->ll_callback(p, opt, NULL, unset);
 | 
						|
 | 
						|
	case OPTION_BIT:
 | 
						|
		if (unset)
 | 
						|
			*(int *)opt->value &= ~opt->defval;
 | 
						|
		else
 | 
						|
			*(int *)opt->value |= opt->defval;
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_NEGBIT:
 | 
						|
		if (unset)
 | 
						|
			*(int *)opt->value |= opt->defval;
 | 
						|
		else
 | 
						|
			*(int *)opt->value &= ~opt->defval;
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_BITOP:
 | 
						|
		if (unset)
 | 
						|
			BUG("BITOP can't have unset form");
 | 
						|
		*(int *)opt->value &= ~opt->extra;
 | 
						|
		*(int *)opt->value |= opt->defval;
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_COUNTUP:
 | 
						|
		if (*(int *)opt->value < 0)
 | 
						|
			*(int *)opt->value = 0;
 | 
						|
		*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_SET_INT:
 | 
						|
		*(int *)opt->value = unset ? 0 : opt->defval;
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_STRING:
 | 
						|
		if (unset)
 | 
						|
			*(const char **)opt->value = NULL;
 | 
						|
		else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
 | 
						|
			*(const char **)opt->value = (const char *)opt->defval;
 | 
						|
		else
 | 
						|
			return get_arg(p, opt, flags, (const char **)opt->value);
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_FILENAME:
 | 
						|
	{
 | 
						|
		const char *value;
 | 
						|
 | 
						|
		FREE_AND_NULL(*(char **)opt->value);
 | 
						|
 | 
						|
		err = 0;
 | 
						|
 | 
						|
		if (unset)
 | 
						|
			value = NULL;
 | 
						|
		else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
 | 
						|
			value = (const char *) opt->defval;
 | 
						|
		else
 | 
						|
			err = get_arg(p, opt, flags, &value);
 | 
						|
 | 
						|
		if (!err)
 | 
						|
			*(char **)opt->value = fix_filename(p->prefix, value);
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
	case OPTION_CALLBACK:
 | 
						|
	{
 | 
						|
		const char *p_arg = NULL;
 | 
						|
		int p_unset;
 | 
						|
 | 
						|
		if (unset)
 | 
						|
			p_unset = 1;
 | 
						|
		else if (opt->flags & PARSE_OPT_NOARG)
 | 
						|
			p_unset = 0;
 | 
						|
		else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
 | 
						|
			p_unset = 0;
 | 
						|
		else if (get_arg(p, opt, flags, &arg))
 | 
						|
			return -1;
 | 
						|
		else {
 | 
						|
			p_unset = 0;
 | 
						|
			p_arg = arg;
 | 
						|
		}
 | 
						|
		if (opt->flags & PARSE_OPT_CMDMODE)
 | 
						|
			*argp = p_arg;
 | 
						|
		if (opt->callback)
 | 
						|
			return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0;
 | 
						|
		else
 | 
						|
			return (*opt->ll_callback)(p, opt, p_arg, p_unset);
 | 
						|
	}
 | 
						|
	case OPTION_INTEGER:
 | 
						|
		if (unset) {
 | 
						|
			*(int *)opt->value = 0;
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
 | 
						|
			*(int *)opt->value = opt->defval;
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (get_arg(p, opt, flags, &arg))
 | 
						|
			return -1;
 | 
						|
		if (!*arg)
 | 
						|
			return error(_("%s expects a numerical value"),
 | 
						|
				     optname(opt, flags));
 | 
						|
		*(int *)opt->value = strtol(arg, (char **)&s, 10);
 | 
						|
		if (*s)
 | 
						|
			return error(_("%s expects a numerical value"),
 | 
						|
				     optname(opt, flags));
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case OPTION_MAGNITUDE:
 | 
						|
		if (unset) {
 | 
						|
			*(unsigned long *)opt->value = 0;
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
 | 
						|
			*(unsigned long *)opt->value = opt->defval;
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (get_arg(p, opt, flags, &arg))
 | 
						|
			return -1;
 | 
						|
		if (!git_parse_ulong(arg, opt->value))
 | 
						|
			return error(_("%s expects a non-negative integer value"
 | 
						|
				       " with an optional k/m/g suffix"),
 | 
						|
				     optname(opt, flags));
 | 
						|
		return 0;
 | 
						|
 | 
						|
	default:
 | 
						|
		BUG("opt->type %d should not happen", opt->type);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct parse_opt_cmdmode_list {
 | 
						|
	int value, *value_ptr;
 | 
						|
	const struct option *opt;
 | 
						|
	const char *arg;
 | 
						|
	enum opt_parsed flags;
 | 
						|
	struct parse_opt_cmdmode_list *next;
 | 
						|
};
 | 
						|
 | 
						|
static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
 | 
						|
			       const struct option *opts)
 | 
						|
{
 | 
						|
	ctx->cmdmode_list = NULL;
 | 
						|
 | 
						|
	for (; opts->type != OPTION_END; opts++) {
 | 
						|
		struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list;
 | 
						|
		int *value_ptr = opts->value;
 | 
						|
 | 
						|
		if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)
 | 
						|
			continue;
 | 
						|
 | 
						|
		while (elem && elem->value_ptr != value_ptr)
 | 
						|
			elem = elem->next;
 | 
						|
		if (elem)
 | 
						|
			continue;
 | 
						|
 | 
						|
		CALLOC_ARRAY(elem, 1);
 | 
						|
		elem->value_ptr = value_ptr;
 | 
						|
		elem->value = *value_ptr;
 | 
						|
		elem->next = ctx->cmdmode_list;
 | 
						|
		ctx->cmdmode_list = elem;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static char *optnamearg(const struct option *opt, const char *arg,
 | 
						|
			enum opt_parsed flags)
 | 
						|
{
 | 
						|
	if (flags & OPT_SHORT)
 | 
						|
		return xstrfmt("-%c%s", opt->short_name, arg ? arg : "");
 | 
						|
	return xstrfmt("--%s%s%s%s", flags & OPT_UNSET ? "no-" : "",
 | 
						|
		       opt->long_name, arg ? "=" : "", arg ? arg : "");
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
 | 
						|
				       const struct option *opt,
 | 
						|
				       enum opt_parsed flags)
 | 
						|
{
 | 
						|
	const char *arg = NULL;
 | 
						|
	enum parse_opt_result result = do_get_value(p, opt, flags, &arg);
 | 
						|
	struct parse_opt_cmdmode_list *elem = p->cmdmode_list;
 | 
						|
	char *opt_name, *other_opt_name;
 | 
						|
 | 
						|
	for (; elem; elem = elem->next) {
 | 
						|
		if (*elem->value_ptr == elem->value)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (elem->opt &&
 | 
						|
		    (elem->opt->flags | opt->flags) & PARSE_OPT_CMDMODE)
 | 
						|
			break;
 | 
						|
 | 
						|
		elem->opt = opt;
 | 
						|
		elem->arg = arg;
 | 
						|
		elem->flags = flags;
 | 
						|
		elem->value = *elem->value_ptr;
 | 
						|
	}
 | 
						|
 | 
						|
	if (result || !elem)
 | 
						|
		return result;
 | 
						|
 | 
						|
	opt_name = optnamearg(opt, arg, flags);
 | 
						|
	other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
 | 
						|
	error(_("options '%s' and '%s' cannot be used together"),
 | 
						|
	      opt_name, other_opt_name);
 | 
						|
	free(opt_name);
 | 
						|
	free(other_opt_name);
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
 | 
						|
					     const struct option *options)
 | 
						|
{
 | 
						|
	const struct option *numopt = NULL;
 | 
						|
 | 
						|
	for (; options->type != OPTION_END; options++) {
 | 
						|
		if (options->short_name == *p->opt) {
 | 
						|
			p->opt = p->opt[1] ? p->opt + 1 : NULL;
 | 
						|
			return get_value(p, options, OPT_SHORT);
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Handle the numerical option later, explicit one-digit
 | 
						|
		 * options take precedence over it.
 | 
						|
		 */
 | 
						|
		if (options->type == OPTION_NUMBER)
 | 
						|
			numopt = options;
 | 
						|
	}
 | 
						|
	if (numopt && isdigit(*p->opt)) {
 | 
						|
		size_t len = 1;
 | 
						|
		char *arg;
 | 
						|
		int rc;
 | 
						|
 | 
						|
		while (isdigit(p->opt[len]))
 | 
						|
			len++;
 | 
						|
		arg = xmemdupz(p->opt, len);
 | 
						|
		p->opt = p->opt[len] ? p->opt + len : NULL;
 | 
						|
		if (numopt->callback)
 | 
						|
			rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
 | 
						|
		else
 | 
						|
			rc = (*numopt->ll_callback)(p, numopt, arg, 0);
 | 
						|
		free(arg);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	return PARSE_OPT_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
static int has_string(const char *it, const char **array)
 | 
						|
{
 | 
						|
	while (*array)
 | 
						|
		if (!strcmp(it, *(array++)))
 | 
						|
			return 1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int is_alias(struct parse_opt_ctx_t *ctx,
 | 
						|
		    const struct option *one_opt,
 | 
						|
		    const struct option *another_opt)
 | 
						|
{
 | 
						|
	const char **group;
 | 
						|
 | 
						|
	if (!ctx->alias_groups)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (!one_opt->long_name || !another_opt->long_name)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	for (group = ctx->alias_groups; *group; group += 3) {
 | 
						|
		/* it and other are from the same family? */
 | 
						|
		if (has_string(one_opt->long_name, group) &&
 | 
						|
		    has_string(another_opt->long_name, group))
 | 
						|
			return 1;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct parsed_option {
 | 
						|
	const struct option *option;
 | 
						|
	enum opt_parsed flags;
 | 
						|
};
 | 
						|
 | 
						|
static void register_abbrev(struct parse_opt_ctx_t *p,
 | 
						|
			    const struct option *option, enum opt_parsed flags,
 | 
						|
			    struct parsed_option *abbrev,
 | 
						|
			    struct parsed_option *ambiguous)
 | 
						|
{
 | 
						|
	if (p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)
 | 
						|
		return;
 | 
						|
	if (abbrev->option &&
 | 
						|
	    !(abbrev->flags == flags && is_alias(p, abbrev->option, option))) {
 | 
						|
		/*
 | 
						|
		 * If this is abbreviated, it is
 | 
						|
		 * ambiguous. So when there is no
 | 
						|
		 * exact match later, we need to
 | 
						|
		 * error out.
 | 
						|
		 */
 | 
						|
		ambiguous->option = abbrev->option;
 | 
						|
		ambiguous->flags = abbrev->flags;
 | 
						|
	}
 | 
						|
	abbrev->option = option;
 | 
						|
	abbrev->flags = flags;
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result parse_long_opt(
 | 
						|
	struct parse_opt_ctx_t *p, const char *arg,
 | 
						|
	const struct option *options)
 | 
						|
{
 | 
						|
	const char *arg_end = strchrnul(arg, '=');
 | 
						|
	const char *arg_start = arg;
 | 
						|
	enum opt_parsed flags = OPT_LONG;
 | 
						|
	int arg_starts_with_no_no = 0;
 | 
						|
	struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG };
 | 
						|
	struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG };
 | 
						|
 | 
						|
	if (skip_prefix(arg_start, "no-", &arg_start)) {
 | 
						|
		if (skip_prefix(arg_start, "no-", &arg_start))
 | 
						|
			arg_starts_with_no_no = 1;
 | 
						|
		else
 | 
						|
			flags |= OPT_UNSET;
 | 
						|
	}
 | 
						|
 | 
						|
	for (; options->type != OPTION_END; options++) {
 | 
						|
		const char *rest, *long_name = options->long_name;
 | 
						|
		enum opt_parsed opt_flags = OPT_LONG;
 | 
						|
		int allow_unset = !(options->flags & PARSE_OPT_NONEG);
 | 
						|
 | 
						|
		if (options->type == OPTION_SUBCOMMAND)
 | 
						|
			continue;
 | 
						|
		if (!long_name)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (skip_prefix(long_name, "no-", &long_name))
 | 
						|
			opt_flags |= OPT_UNSET;
 | 
						|
		else if (arg_starts_with_no_no)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (((flags ^ opt_flags) & OPT_UNSET) && !allow_unset)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (skip_prefix(arg_start, long_name, &rest)) {
 | 
						|
			if (*rest == '=')
 | 
						|
				p->opt = rest + 1;
 | 
						|
			else if (*rest)
 | 
						|
				continue;
 | 
						|
			return get_value(p, options, flags ^ opt_flags);
 | 
						|
		}
 | 
						|
 | 
						|
		/* abbreviated? */
 | 
						|
		if (!strncmp(long_name, arg_start, arg_end - arg_start))
 | 
						|
			register_abbrev(p, options, flags ^ opt_flags,
 | 
						|
					&abbrev, &ambiguous);
 | 
						|
 | 
						|
		/* negated and abbreviated very much? */
 | 
						|
		if (allow_unset && starts_with("no-", arg))
 | 
						|
			register_abbrev(p, options, OPT_UNSET ^ opt_flags,
 | 
						|
					&abbrev, &ambiguous);
 | 
						|
	}
 | 
						|
 | 
						|
	if (disallow_abbreviated_options && (ambiguous.option || abbrev.option))
 | 
						|
		die("disallowed abbreviated or ambiguous option '%.*s'",
 | 
						|
		    (int)(arg_end - arg), arg);
 | 
						|
 | 
						|
	if (ambiguous.option) {
 | 
						|
		error(_("ambiguous option: %s "
 | 
						|
			"(could be --%s%s or --%s%s)"),
 | 
						|
			arg,
 | 
						|
			(ambiguous.flags & OPT_UNSET) ?  "no-" : "",
 | 
						|
			ambiguous.option->long_name,
 | 
						|
			(abbrev.flags & OPT_UNSET) ?  "no-" : "",
 | 
						|
			abbrev.option->long_name);
 | 
						|
		return PARSE_OPT_HELP;
 | 
						|
	}
 | 
						|
	if (abbrev.option) {
 | 
						|
		if (*arg_end)
 | 
						|
			p->opt = arg_end + 1;
 | 
						|
		return get_value(p, abbrev.option, abbrev.flags);
 | 
						|
	}
 | 
						|
	return PARSE_OPT_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result parse_nodash_opt(struct parse_opt_ctx_t *p,
 | 
						|
					      const char *arg,
 | 
						|
					      const struct option *options)
 | 
						|
{
 | 
						|
	for (; options->type != OPTION_END; options++) {
 | 
						|
		if (!(options->flags & PARSE_OPT_NODASH))
 | 
						|
			continue;
 | 
						|
		if (options->short_name == arg[0] && arg[1] == '\0')
 | 
						|
			return get_value(p, options, OPT_SHORT);
 | 
						|
	}
 | 
						|
	return PARSE_OPT_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result parse_subcommand(const char *arg,
 | 
						|
					      const struct option *options)
 | 
						|
{
 | 
						|
	for (; options->type != OPTION_END; options++)
 | 
						|
		if (options->type == OPTION_SUBCOMMAND &&
 | 
						|
		    !strcmp(options->long_name, arg)) {
 | 
						|
			*(parse_opt_subcommand_fn **)options->value = options->subcommand_fn;
 | 
						|
			return PARSE_OPT_SUBCOMMAND;
 | 
						|
		}
 | 
						|
 | 
						|
	return PARSE_OPT_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
static void check_typos(const char *arg, const struct option *options)
 | 
						|
{
 | 
						|
	if (strlen(arg) < 3)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (starts_with(arg, "no-")) {
 | 
						|
		error(_("did you mean `--%s` (with two dashes)?"), arg);
 | 
						|
		exit(129);
 | 
						|
	}
 | 
						|
 | 
						|
	for (; options->type != OPTION_END; options++) {
 | 
						|
		if (!options->long_name)
 | 
						|
			continue;
 | 
						|
		if (starts_with(options->long_name, arg)) {
 | 
						|
			error(_("did you mean `--%s` (with two dashes)?"), arg);
 | 
						|
			exit(129);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void parse_options_check(const struct option *opts)
 | 
						|
{
 | 
						|
	char short_opts[128];
 | 
						|
	void *subcommand_value = NULL;
 | 
						|
 | 
						|
	memset(short_opts, '\0', sizeof(short_opts));
 | 
						|
	for (; opts->type != OPTION_END; opts++) {
 | 
						|
		if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
 | 
						|
		    (opts->flags & PARSE_OPT_OPTARG))
 | 
						|
			optbug(opts, "uses incompatible flags "
 | 
						|
			       "LASTARG_DEFAULT and OPTARG");
 | 
						|
		if (opts->short_name) {
 | 
						|
			if (0x7F <= opts->short_name)
 | 
						|
				optbug(opts, "invalid short name");
 | 
						|
			else if (short_opts[opts->short_name]++)
 | 
						|
				optbug(opts, "short name already used");
 | 
						|
		}
 | 
						|
		if (opts->flags & PARSE_OPT_NODASH &&
 | 
						|
		    ((opts->flags & PARSE_OPT_OPTARG) ||
 | 
						|
		     !(opts->flags & PARSE_OPT_NOARG) ||
 | 
						|
		     !(opts->flags & PARSE_OPT_NONEG) ||
 | 
						|
		     opts->long_name))
 | 
						|
			optbug(opts, "uses feature "
 | 
						|
			       "not supported for dashless options");
 | 
						|
		if (opts->type == OPTION_SET_INT && !opts->defval &&
 | 
						|
		    opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
 | 
						|
			optbug(opts, "OPTION_SET_INT 0 should not be negatable");
 | 
						|
		switch (opts->type) {
 | 
						|
		case OPTION_COUNTUP:
 | 
						|
		case OPTION_BIT:
 | 
						|
		case OPTION_NEGBIT:
 | 
						|
		case OPTION_SET_INT:
 | 
						|
		case OPTION_NUMBER:
 | 
						|
			if ((opts->flags & PARSE_OPT_OPTARG) ||
 | 
						|
			    !(opts->flags & PARSE_OPT_NOARG))
 | 
						|
				optbug(opts, "should not accept an argument");
 | 
						|
			break;
 | 
						|
		case OPTION_CALLBACK:
 | 
						|
			if (!opts->callback && !opts->ll_callback)
 | 
						|
				optbug(opts, "OPTION_CALLBACK needs one callback");
 | 
						|
			else if (opts->callback && opts->ll_callback)
 | 
						|
				optbug(opts, "OPTION_CALLBACK can't have two callbacks");
 | 
						|
			break;
 | 
						|
		case OPTION_LOWLEVEL_CALLBACK:
 | 
						|
			if (!opts->ll_callback)
 | 
						|
				optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs a callback");
 | 
						|
			if (opts->callback)
 | 
						|
				optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs no high level callback");
 | 
						|
			break;
 | 
						|
		case OPTION_ALIAS:
 | 
						|
			optbug(opts, "OPT_ALIAS() should not remain at this point. "
 | 
						|
			       "Are you using parse_options_step() directly?\n"
 | 
						|
			       "That case is not supported yet.");
 | 
						|
			break;
 | 
						|
		case OPTION_SUBCOMMAND:
 | 
						|
			if (!opts->value || !opts->subcommand_fn)
 | 
						|
				optbug(opts, "OPTION_SUBCOMMAND needs a value and a subcommand function");
 | 
						|
			if (!subcommand_value)
 | 
						|
				subcommand_value = opts->value;
 | 
						|
			else if (subcommand_value != opts->value)
 | 
						|
				optbug(opts, "all OPTION_SUBCOMMANDs need the same value");
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			; /* ok. (usually accepts an argument) */
 | 
						|
		}
 | 
						|
		if (opts->argh &&
 | 
						|
		    strcspn(opts->argh, " _") != strlen(opts->argh))
 | 
						|
			optbug(opts, "multi-word argh should use dash to separate words");
 | 
						|
	}
 | 
						|
	BUG_if_bug("invalid 'struct option'");
 | 
						|
}
 | 
						|
 | 
						|
static int has_subcommands(const struct option *options)
 | 
						|
{
 | 
						|
	for (; options->type != OPTION_END; options++)
 | 
						|
		if (options->type == OPTION_SUBCOMMAND)
 | 
						|
			return 1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
 | 
						|
				  int argc, const char **argv, const char *prefix,
 | 
						|
				  const struct option *options,
 | 
						|
				  enum parse_opt_flags flags)
 | 
						|
{
 | 
						|
	ctx->argc = argc;
 | 
						|
	ctx->argv = argv;
 | 
						|
	if (!(flags & PARSE_OPT_ONE_SHOT)) {
 | 
						|
		ctx->argc--;
 | 
						|
		ctx->argv++;
 | 
						|
	}
 | 
						|
	ctx->total = ctx->argc;
 | 
						|
	ctx->out   = argv;
 | 
						|
	ctx->prefix = prefix;
 | 
						|
	ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
 | 
						|
	ctx->flags = flags;
 | 
						|
	ctx->has_subcommands = has_subcommands(options);
 | 
						|
	if (!ctx->has_subcommands && (flags & PARSE_OPT_SUBCOMMAND_OPTIONAL))
 | 
						|
		BUG("Using PARSE_OPT_SUBCOMMAND_OPTIONAL without subcommands");
 | 
						|
	if (ctx->has_subcommands) {
 | 
						|
		if (flags & PARSE_OPT_STOP_AT_NON_OPTION)
 | 
						|
			BUG("subcommands are incompatible with PARSE_OPT_STOP_AT_NON_OPTION");
 | 
						|
		if (!(flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)) {
 | 
						|
			if (flags & PARSE_OPT_KEEP_UNKNOWN_OPT)
 | 
						|
				BUG("subcommands are incompatible with PARSE_OPT_KEEP_UNKNOWN_OPT unless in combination with PARSE_OPT_SUBCOMMAND_OPTIONAL");
 | 
						|
			if (flags & PARSE_OPT_KEEP_DASHDASH)
 | 
						|
				BUG("subcommands are incompatible with PARSE_OPT_KEEP_DASHDASH unless in combination with PARSE_OPT_SUBCOMMAND_OPTIONAL");
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if ((flags & PARSE_OPT_KEEP_UNKNOWN_OPT) &&
 | 
						|
	    (flags & PARSE_OPT_STOP_AT_NON_OPTION) &&
 | 
						|
	    !(flags & PARSE_OPT_ONE_SHOT))
 | 
						|
		BUG("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
 | 
						|
	if ((flags & PARSE_OPT_ONE_SHOT) &&
 | 
						|
	    (flags & PARSE_OPT_KEEP_ARGV0))
 | 
						|
		BUG("Can't keep argv0 if you don't have it");
 | 
						|
	parse_options_check(options);
 | 
						|
	build_cmdmode_list(ctx, options);
 | 
						|
}
 | 
						|
 | 
						|
void parse_options_start(struct parse_opt_ctx_t *ctx,
 | 
						|
			 int argc, const char **argv, const char *prefix,
 | 
						|
			 const struct option *options,
 | 
						|
			 enum parse_opt_flags flags)
 | 
						|
{
 | 
						|
	memset(ctx, 0, sizeof(*ctx));
 | 
						|
	parse_options_start_1(ctx, argc, argv, prefix, options, flags);
 | 
						|
}
 | 
						|
 | 
						|
static void show_negated_gitcomp(const struct option *opts, int show_all,
 | 
						|
				 int nr_noopts)
 | 
						|
{
 | 
						|
	int printed_dashdash = 0;
 | 
						|
 | 
						|
	for (; opts->type != OPTION_END; opts++) {
 | 
						|
		int has_unset_form = 0;
 | 
						|
		const char *name;
 | 
						|
 | 
						|
		if (!opts->long_name)
 | 
						|
			continue;
 | 
						|
		if (!show_all &&
 | 
						|
			(opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE)))
 | 
						|
			continue;
 | 
						|
		if (opts->flags & PARSE_OPT_NONEG)
 | 
						|
			continue;
 | 
						|
 | 
						|
		switch (opts->type) {
 | 
						|
		case OPTION_STRING:
 | 
						|
		case OPTION_FILENAME:
 | 
						|
		case OPTION_INTEGER:
 | 
						|
		case OPTION_MAGNITUDE:
 | 
						|
		case OPTION_CALLBACK:
 | 
						|
		case OPTION_BIT:
 | 
						|
		case OPTION_NEGBIT:
 | 
						|
		case OPTION_COUNTUP:
 | 
						|
		case OPTION_SET_INT:
 | 
						|
			has_unset_form = 1;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (!has_unset_form)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (skip_prefix(opts->long_name, "no-", &name)) {
 | 
						|
			if (nr_noopts < 0)
 | 
						|
				printf(" --%s", name);
 | 
						|
		} else if (nr_noopts >= 0) {
 | 
						|
			if (nr_noopts && !printed_dashdash) {
 | 
						|
				printf(" --");
 | 
						|
				printed_dashdash = 1;
 | 
						|
			}
 | 
						|
			printf(" --no-%s", opts->long_name);
 | 
						|
			nr_noopts++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int show_gitcomp(const struct option *opts, int show_all)
 | 
						|
{
 | 
						|
	const struct option *original_opts = opts;
 | 
						|
	int nr_noopts = 0;
 | 
						|
 | 
						|
	for (; opts->type != OPTION_END; opts++) {
 | 
						|
		const char *prefix = "--";
 | 
						|
		const char *suffix = "";
 | 
						|
 | 
						|
		if (!opts->long_name)
 | 
						|
			continue;
 | 
						|
		if (!show_all &&
 | 
						|
			(opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE | PARSE_OPT_FROM_ALIAS)))
 | 
						|
			continue;
 | 
						|
 | 
						|
		switch (opts->type) {
 | 
						|
		case OPTION_SUBCOMMAND:
 | 
						|
			prefix = "";
 | 
						|
			break;
 | 
						|
		case OPTION_GROUP:
 | 
						|
			continue;
 | 
						|
		case OPTION_STRING:
 | 
						|
		case OPTION_FILENAME:
 | 
						|
		case OPTION_INTEGER:
 | 
						|
		case OPTION_MAGNITUDE:
 | 
						|
		case OPTION_CALLBACK:
 | 
						|
			if (opts->flags & PARSE_OPT_NOARG)
 | 
						|
				break;
 | 
						|
			if (opts->flags & PARSE_OPT_OPTARG)
 | 
						|
				break;
 | 
						|
			if (opts->flags & PARSE_OPT_LASTARG_DEFAULT)
 | 
						|
				break;
 | 
						|
			suffix = "=";
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (opts->flags & PARSE_OPT_COMP_ARG)
 | 
						|
			suffix = "=";
 | 
						|
		if (starts_with(opts->long_name, "no-"))
 | 
						|
			nr_noopts++;
 | 
						|
		printf("%s%s%s%s", opts == original_opts ? "" : " ",
 | 
						|
		       prefix, opts->long_name, suffix);
 | 
						|
	}
 | 
						|
	show_negated_gitcomp(original_opts, show_all, -1);
 | 
						|
	show_negated_gitcomp(original_opts, show_all, nr_noopts);
 | 
						|
	fputc('\n', stdout);
 | 
						|
	return PARSE_OPT_COMPLETE;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Scan and may produce a new option[] array, which should be used
 | 
						|
 * instead of the original 'options'.
 | 
						|
 *
 | 
						|
 * Right now this is only used to preprocess and substitute
 | 
						|
 * OPTION_ALIAS.
 | 
						|
 *
 | 
						|
 * The returned options should be freed using free_preprocessed_options.
 | 
						|
 */
 | 
						|
static struct option *preprocess_options(struct parse_opt_ctx_t *ctx,
 | 
						|
					 const struct option *options)
 | 
						|
{
 | 
						|
	struct option *newopt;
 | 
						|
	int i, nr, alias;
 | 
						|
	int nr_aliases = 0;
 | 
						|
 | 
						|
	for (nr = 0; options[nr].type != OPTION_END; nr++) {
 | 
						|
		if (options[nr].type == OPTION_ALIAS)
 | 
						|
			nr_aliases++;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!nr_aliases)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	DUP_ARRAY(newopt, options, nr + 1);
 | 
						|
 | 
						|
	/* each alias has two string pointers and NULL */
 | 
						|
	CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1));
 | 
						|
 | 
						|
	for (alias = 0, i = 0; i < nr; i++) {
 | 
						|
		int short_name;
 | 
						|
		const char *long_name;
 | 
						|
		const char *source;
 | 
						|
		struct strbuf help = STRBUF_INIT;
 | 
						|
		int j;
 | 
						|
 | 
						|
		if (newopt[i].type != OPTION_ALIAS)
 | 
						|
			continue;
 | 
						|
 | 
						|
		short_name = newopt[i].short_name;
 | 
						|
		long_name = newopt[i].long_name;
 | 
						|
		source = newopt[i].value;
 | 
						|
 | 
						|
		if (!long_name)
 | 
						|
			BUG("An alias must have long option name");
 | 
						|
		strbuf_addf(&help, _("alias of --%s"), source);
 | 
						|
 | 
						|
		for (j = 0; j < nr; j++) {
 | 
						|
			const char *name = options[j].long_name;
 | 
						|
 | 
						|
			if (!name || strcmp(name, source))
 | 
						|
				continue;
 | 
						|
 | 
						|
			if (options[j].type == OPTION_ALIAS)
 | 
						|
				BUG("No please. Nested aliases are not supported.");
 | 
						|
 | 
						|
			memcpy(newopt + i, options + j, sizeof(*newopt));
 | 
						|
			newopt[i].short_name = short_name;
 | 
						|
			newopt[i].long_name = long_name;
 | 
						|
			newopt[i].help = strbuf_detach(&help, NULL);
 | 
						|
			newopt[i].flags |= PARSE_OPT_FROM_ALIAS;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (j == nr)
 | 
						|
			BUG("could not find source option '%s' of alias '%s'",
 | 
						|
			    source, newopt[i].long_name);
 | 
						|
		ctx->alias_groups[alias * 3 + 0] = newopt[i].long_name;
 | 
						|
		ctx->alias_groups[alias * 3 + 1] = options[j].long_name;
 | 
						|
		ctx->alias_groups[alias * 3 + 2] = NULL;
 | 
						|
		alias++;
 | 
						|
	}
 | 
						|
 | 
						|
	return newopt;
 | 
						|
}
 | 
						|
 | 
						|
static void free_preprocessed_options(struct option *options)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (!options)
 | 
						|
		return;
 | 
						|
 | 
						|
	for (i = 0; options[i].type != OPTION_END; i++) {
 | 
						|
		if (options[i].flags & PARSE_OPT_FROM_ALIAS)
 | 
						|
			free((void *)options[i].help);
 | 
						|
	}
 | 
						|
	free(options);
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *,
 | 
						|
							 const char * const *,
 | 
						|
							 const struct option *,
 | 
						|
							 int, int);
 | 
						|
 | 
						|
enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
 | 
						|
					 const struct option *options,
 | 
						|
					 const char * const usagestr[])
 | 
						|
{
 | 
						|
	int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
 | 
						|
 | 
						|
	/* 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];
 | 
						|
 | 
						|
		if (ctx->flags & PARSE_OPT_ONE_SHOT &&
 | 
						|
		    ctx->argc != ctx->total)
 | 
						|
			break;
 | 
						|
 | 
						|
		if (*arg != '-' || !arg[1]) {
 | 
						|
			if (parse_nodash_opt(ctx, arg, options) == 0)
 | 
						|
				continue;
 | 
						|
			if (!ctx->has_subcommands) {
 | 
						|
				if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
 | 
						|
					return PARSE_OPT_NON_OPTION;
 | 
						|
				ctx->out[ctx->cpidx++] = ctx->argv[0];
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			switch (parse_subcommand(arg, options)) {
 | 
						|
			case PARSE_OPT_SUBCOMMAND:
 | 
						|
				return PARSE_OPT_SUBCOMMAND;
 | 
						|
			case PARSE_OPT_UNKNOWN:
 | 
						|
				if (ctx->flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)
 | 
						|
					/*
 | 
						|
					 * arg is neither a short or long
 | 
						|
					 * option nor a subcommand.  Since
 | 
						|
					 * this command has a default
 | 
						|
					 * operation mode, we have to treat
 | 
						|
					 * this arg and all remaining args
 | 
						|
					 * as args meant to that default
 | 
						|
					 * operation mode.
 | 
						|
					 * So we are done parsing.
 | 
						|
					 */
 | 
						|
					return PARSE_OPT_DONE;
 | 
						|
				error(_("unknown subcommand: `%s'"), arg);
 | 
						|
				usage_with_options(usagestr, options);
 | 
						|
			case PARSE_OPT_COMPLETE:
 | 
						|
			case PARSE_OPT_HELP:
 | 
						|
			case PARSE_OPT_ERROR:
 | 
						|
			case PARSE_OPT_DONE:
 | 
						|
			case PARSE_OPT_NON_OPTION:
 | 
						|
				/* Impossible. */
 | 
						|
				BUG("parse_subcommand() cannot return these");
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* lone -h asks for help */
 | 
						|
		if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h"))
 | 
						|
			goto show_usage;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * lone --git-completion-helper and --git-completion-helper-all
 | 
						|
		 * are asked by git-completion.bash
 | 
						|
		 */
 | 
						|
		if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper"))
 | 
						|
			return show_gitcomp(options, 0);
 | 
						|
		if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper-all"))
 | 
						|
			return show_gitcomp(options, 1);
 | 
						|
 | 
						|
		if (arg[1] != '-') {
 | 
						|
			ctx->opt = arg + 1;
 | 
						|
			switch (parse_short_opt(ctx, options)) {
 | 
						|
			case PARSE_OPT_ERROR:
 | 
						|
				return PARSE_OPT_ERROR;
 | 
						|
			case PARSE_OPT_UNKNOWN:
 | 
						|
				if (ctx->opt)
 | 
						|
					check_typos(arg + 1, options);
 | 
						|
				if (internal_help && *ctx->opt == 'h')
 | 
						|
					goto show_usage;
 | 
						|
				goto unknown;
 | 
						|
			case PARSE_OPT_NON_OPTION:
 | 
						|
			case PARSE_OPT_SUBCOMMAND:
 | 
						|
			case PARSE_OPT_HELP:
 | 
						|
			case PARSE_OPT_COMPLETE:
 | 
						|
				BUG("parse_short_opt() cannot return these");
 | 
						|
			case PARSE_OPT_DONE:
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if (ctx->opt)
 | 
						|
				check_typos(arg + 1, options);
 | 
						|
			while (ctx->opt) {
 | 
						|
				switch (parse_short_opt(ctx, options)) {
 | 
						|
				case PARSE_OPT_ERROR:
 | 
						|
					return PARSE_OPT_ERROR;
 | 
						|
				case PARSE_OPT_UNKNOWN:
 | 
						|
					if (internal_help && *ctx->opt == 'h')
 | 
						|
						goto show_usage;
 | 
						|
 | 
						|
					/* 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] = '-';
 | 
						|
					goto unknown;
 | 
						|
				case PARSE_OPT_NON_OPTION:
 | 
						|
				case PARSE_OPT_SUBCOMMAND:
 | 
						|
				case PARSE_OPT_COMPLETE:
 | 
						|
				case PARSE_OPT_HELP:
 | 
						|
					BUG("parse_short_opt() cannot return these");
 | 
						|
				case PARSE_OPT_DONE:
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!arg[2] /* "--" */) {
 | 
						|
			if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
 | 
						|
				ctx->argc--;
 | 
						|
				ctx->argv++;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		} else if (!strcmp(arg + 2, "end-of-options")) {
 | 
						|
			if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) {
 | 
						|
				ctx->argc--;
 | 
						|
				ctx->argv++;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (internal_help && !strcmp(arg + 2, "help-all"))
 | 
						|
			return usage_with_options_internal(ctx, usagestr, options, 1, 0);
 | 
						|
		if (internal_help && !strcmp(arg + 2, "help"))
 | 
						|
			goto show_usage;
 | 
						|
		switch (parse_long_opt(ctx, arg + 2, options)) {
 | 
						|
		case PARSE_OPT_ERROR:
 | 
						|
			return PARSE_OPT_ERROR;
 | 
						|
		case PARSE_OPT_UNKNOWN:
 | 
						|
			goto unknown;
 | 
						|
		case PARSE_OPT_HELP:
 | 
						|
			goto show_usage;
 | 
						|
		case PARSE_OPT_NON_OPTION:
 | 
						|
		case PARSE_OPT_SUBCOMMAND:
 | 
						|
		case PARSE_OPT_COMPLETE:
 | 
						|
			BUG("parse_long_opt() cannot return these");
 | 
						|
		case PARSE_OPT_DONE:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		continue;
 | 
						|
unknown:
 | 
						|
		if (ctx->flags & PARSE_OPT_ONE_SHOT)
 | 
						|
			break;
 | 
						|
		if (ctx->has_subcommands &&
 | 
						|
		    (ctx->flags & PARSE_OPT_SUBCOMMAND_OPTIONAL) &&
 | 
						|
		    (ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) {
 | 
						|
			/*
 | 
						|
			 * Found an unknown option given to a command with
 | 
						|
			 * subcommands that has a default operation mode:
 | 
						|
			 * we treat this option and all remaining args as
 | 
						|
			 * arguments meant to that default operation mode.
 | 
						|
			 * So we are done parsing.
 | 
						|
			 */
 | 
						|
			return PARSE_OPT_DONE;
 | 
						|
		}
 | 
						|
		if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT))
 | 
						|
			return PARSE_OPT_UNKNOWN;
 | 
						|
		ctx->out[ctx->cpidx++] = ctx->argv[0];
 | 
						|
		ctx->opt = NULL;
 | 
						|
	}
 | 
						|
	return PARSE_OPT_DONE;
 | 
						|
 | 
						|
 show_usage:
 | 
						|
	return usage_with_options_internal(ctx, usagestr, options, 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
int parse_options_end(struct parse_opt_ctx_t *ctx)
 | 
						|
{
 | 
						|
	if (ctx->flags & PARSE_OPT_ONE_SHOT)
 | 
						|
		return ctx->total - ctx->argc;
 | 
						|
 | 
						|
	MOVE_ARRAY(ctx->out + ctx->cpidx, ctx->argv, ctx->argc);
 | 
						|
	ctx->out[ctx->cpidx + ctx->argc] = NULL;
 | 
						|
	return ctx->cpidx + ctx->argc;
 | 
						|
}
 | 
						|
 | 
						|
int parse_options(int argc, const char **argv,
 | 
						|
		  const char *prefix,
 | 
						|
		  const struct option *options,
 | 
						|
		  const char * const usagestr[],
 | 
						|
		  enum parse_opt_flags flags)
 | 
						|
{
 | 
						|
	struct parse_opt_ctx_t ctx;
 | 
						|
	struct option *real_options;
 | 
						|
 | 
						|
	disallow_abbreviated_options =
 | 
						|
		git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
 | 
						|
 | 
						|
	memset(&ctx, 0, sizeof(ctx));
 | 
						|
	real_options = preprocess_options(&ctx, options);
 | 
						|
	if (real_options)
 | 
						|
		options = real_options;
 | 
						|
	parse_options_start_1(&ctx, argc, argv, prefix, options, flags);
 | 
						|
	switch (parse_options_step(&ctx, options, usagestr)) {
 | 
						|
	case PARSE_OPT_HELP:
 | 
						|
	case PARSE_OPT_ERROR:
 | 
						|
		exit(129);
 | 
						|
	case PARSE_OPT_COMPLETE:
 | 
						|
		exit(0);
 | 
						|
	case PARSE_OPT_NON_OPTION:
 | 
						|
	case PARSE_OPT_SUBCOMMAND:
 | 
						|
		break;
 | 
						|
	case PARSE_OPT_DONE:
 | 
						|
		if (ctx.has_subcommands &&
 | 
						|
		    !(flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)) {
 | 
						|
			error(_("need a subcommand"));
 | 
						|
			usage_with_options(usagestr, options);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case PARSE_OPT_UNKNOWN:
 | 
						|
		if (ctx.argv[0][1] == '-') {
 | 
						|
			error(_("unknown option `%s'"), ctx.argv[0] + 2);
 | 
						|
		} else if (isascii(*ctx.opt)) {
 | 
						|
			error(_("unknown switch `%c'"), *ctx.opt);
 | 
						|
		} else {
 | 
						|
			error(_("unknown non-ascii option in string: `%s'"),
 | 
						|
			      ctx.argv[0]);
 | 
						|
		}
 | 
						|
		usage_with_options(usagestr, options);
 | 
						|
	}
 | 
						|
 | 
						|
	precompose_argv_prefix(argc, argv, NULL);
 | 
						|
	free_preprocessed_options(real_options);
 | 
						|
	free(ctx.alias_groups);
 | 
						|
	for (struct parse_opt_cmdmode_list *elem = ctx.cmdmode_list; elem;) {
 | 
						|
		struct parse_opt_cmdmode_list *next = elem->next;
 | 
						|
		free(elem);
 | 
						|
		elem = next;
 | 
						|
	}
 | 
						|
	return parse_options_end(&ctx);
 | 
						|
}
 | 
						|
 | 
						|
static int usage_argh(const struct option *opts, FILE *outfile)
 | 
						|
{
 | 
						|
	const char *s;
 | 
						|
	int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
 | 
						|
		!opts->argh || !!strpbrk(opts->argh, "()<>[]|");
 | 
						|
	if (opts->flags & PARSE_OPT_OPTARG)
 | 
						|
		if (opts->long_name)
 | 
						|
			s = literal ? "[=%s]" : "[=<%s>]";
 | 
						|
		else
 | 
						|
			s = literal ? "[%s]" : "[<%s>]";
 | 
						|
	else
 | 
						|
		s = literal ? " %s" : " <%s>";
 | 
						|
	return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
 | 
						|
}
 | 
						|
 | 
						|
static int usage_indent(FILE *outfile)
 | 
						|
{
 | 
						|
	return fprintf(outfile, "    ");
 | 
						|
}
 | 
						|
 | 
						|
#define USAGE_OPTS_WIDTH 26
 | 
						|
 | 
						|
static void usage_padding(FILE *outfile, size_t pos)
 | 
						|
{
 | 
						|
	if (pos < USAGE_OPTS_WIDTH)
 | 
						|
		fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, "");
 | 
						|
	else
 | 
						|
		fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, "");
 | 
						|
}
 | 
						|
 | 
						|
static const struct option *find_option_by_long_name(const struct option *opts,
 | 
						|
						     const char *long_name)
 | 
						|
{
 | 
						|
	for (; opts->type != OPTION_END; opts++) {
 | 
						|
		if (opts->long_name && !strcmp(opts->long_name, long_name))
 | 
						|
			return opts;
 | 
						|
	}
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
 | 
						|
							 const char * const *usagestr,
 | 
						|
							 const struct option *opts,
 | 
						|
							 int full, int err)
 | 
						|
{
 | 
						|
	const struct option *all_opts = opts;
 | 
						|
	FILE *outfile = err ? stderr : stdout;
 | 
						|
	int need_newline;
 | 
						|
 | 
						|
	const char *usage_prefix = _("usage: %s");
 | 
						|
	/*
 | 
						|
	 * The translation could be anything, but we can count on
 | 
						|
	 * msgfmt(1)'s --check option to have asserted that "%s" is in
 | 
						|
	 * the translation. So compute the length of the "usage: "
 | 
						|
	 * part. We are assuming that the translator wasn't overly
 | 
						|
	 * clever and used e.g. "%1$s" instead of "%s", there's only
 | 
						|
	 * one "%s" in "usage_prefix" above, so there's no reason to
 | 
						|
	 * do so even with a RTL language.
 | 
						|
	 */
 | 
						|
	size_t usage_len = strlen(usage_prefix) - strlen("%s");
 | 
						|
	/*
 | 
						|
	 * TRANSLATORS: the colon here should align with the
 | 
						|
	 * one in "usage: %s" translation.
 | 
						|
	 */
 | 
						|
	const char *or_prefix = _("   or: %s");
 | 
						|
	/*
 | 
						|
	 * TRANSLATORS: You should only need to translate this format
 | 
						|
	 * string if your language is a RTL language (e.g. Arabic,
 | 
						|
	 * Hebrew etc.), not if it's a LTR language (e.g. German,
 | 
						|
	 * Russian, Chinese etc.).
 | 
						|
	 *
 | 
						|
	 * When a translated usage string has an embedded "\n" it's
 | 
						|
	 * because options have wrapped to the next line. The line
 | 
						|
	 * after the "\n" will then be padded to align with the
 | 
						|
	 * command name, such as N_("git cmd [opt]\n<8
 | 
						|
	 * spaces>[opt2]"), where the 8 spaces are the same length as
 | 
						|
	 * "git cmd ".
 | 
						|
	 *
 | 
						|
	 * This format string prints out that already-translated
 | 
						|
	 * line. The "%*s" is whitespace padding to account for the
 | 
						|
	 * padding at the start of the line that we add in this
 | 
						|
	 * function. The "%s" is a line in the (hopefully already
 | 
						|
	 * translated) N_() usage string, which contained embedded
 | 
						|
	 * newlines before we split it up.
 | 
						|
	 */
 | 
						|
	const char *usage_continued = _("%*s%s");
 | 
						|
	const char *prefix = usage_prefix;
 | 
						|
	int saw_empty_line = 0;
 | 
						|
 | 
						|
	if (!usagestr)
 | 
						|
		return PARSE_OPT_HELP;
 | 
						|
 | 
						|
	if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
 | 
						|
		fprintf(outfile, "cat <<\\EOF\n");
 | 
						|
 | 
						|
	while (*usagestr) {
 | 
						|
		const char *str = _(*usagestr++);
 | 
						|
		struct string_list list = STRING_LIST_INIT_DUP;
 | 
						|
		unsigned int j;
 | 
						|
 | 
						|
		if (!saw_empty_line && !*str)
 | 
						|
			saw_empty_line = 1;
 | 
						|
 | 
						|
		string_list_split(&list, str, '\n', -1);
 | 
						|
		for (j = 0; j < list.nr; j++) {
 | 
						|
			const char *line = list.items[j].string;
 | 
						|
 | 
						|
			if (saw_empty_line && *line)
 | 
						|
				fprintf_ln(outfile, _("    %s"), line);
 | 
						|
			else if (saw_empty_line)
 | 
						|
				fputc('\n', outfile);
 | 
						|
			else if (!j)
 | 
						|
				fprintf_ln(outfile, prefix, line);
 | 
						|
			else
 | 
						|
				fprintf_ln(outfile, usage_continued,
 | 
						|
					   (int)usage_len, "", line);
 | 
						|
		}
 | 
						|
		string_list_clear(&list, 0);
 | 
						|
 | 
						|
		prefix = or_prefix;
 | 
						|
	}
 | 
						|
 | 
						|
	need_newline = 1;
 | 
						|
 | 
						|
	for (; opts->type != OPTION_END; opts++) {
 | 
						|
		size_t pos;
 | 
						|
		const char *cp, *np;
 | 
						|
		const char *positive_name = NULL;
 | 
						|
 | 
						|
		if (opts->type == OPTION_SUBCOMMAND)
 | 
						|
			continue;
 | 
						|
		if (opts->type == OPTION_GROUP) {
 | 
						|
			fputc('\n', outfile);
 | 
						|
			need_newline = 0;
 | 
						|
			if (*opts->help)
 | 
						|
				fprintf(outfile, "%s\n", _(opts->help));
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (!full && (opts->flags & PARSE_OPT_HIDDEN))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (need_newline) {
 | 
						|
			fputc('\n', outfile);
 | 
						|
			need_newline = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		pos = usage_indent(outfile);
 | 
						|
		if (opts->short_name) {
 | 
						|
			if (opts->flags & PARSE_OPT_NODASH)
 | 
						|
				pos += fprintf(outfile, "%c", opts->short_name);
 | 
						|
			else
 | 
						|
				pos += fprintf(outfile, "-%c", opts->short_name);
 | 
						|
		}
 | 
						|
		if (opts->long_name && opts->short_name)
 | 
						|
			pos += fprintf(outfile, ", ");
 | 
						|
		if (opts->long_name) {
 | 
						|
			const char *long_name = opts->long_name;
 | 
						|
			if ((opts->flags & PARSE_OPT_NONEG) ||
 | 
						|
			    skip_prefix(long_name, "no-", &positive_name))
 | 
						|
				pos += fprintf(outfile, "--%s", long_name);
 | 
						|
			else
 | 
						|
				pos += fprintf(outfile, "--[no-]%s", long_name);
 | 
						|
		}
 | 
						|
 | 
						|
		if (opts->type == OPTION_NUMBER)
 | 
						|
			pos += utf8_fprintf(outfile, _("-NUM"));
 | 
						|
 | 
						|
		if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
 | 
						|
		    !(opts->flags & PARSE_OPT_NOARG))
 | 
						|
			pos += usage_argh(opts, outfile);
 | 
						|
 | 
						|
		if (opts->type == OPTION_ALIAS) {
 | 
						|
			usage_padding(outfile, pos);
 | 
						|
			fprintf_ln(outfile, _("alias of --%s"),
 | 
						|
				   (const char *)opts->value);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		for (cp = opts->help ? _(opts->help) : ""; *cp; cp = np) {
 | 
						|
			np = strchrnul(cp, '\n');
 | 
						|
			if (*np)
 | 
						|
				np++;
 | 
						|
			usage_padding(outfile, pos);
 | 
						|
			fwrite(cp, 1, np - cp, outfile);
 | 
						|
			pos = 0;
 | 
						|
		}
 | 
						|
		fputc('\n', outfile);
 | 
						|
 | 
						|
		if (positive_name) {
 | 
						|
			if (find_option_by_long_name(all_opts, positive_name))
 | 
						|
				continue;
 | 
						|
			pos = usage_indent(outfile);
 | 
						|
			pos += fprintf(outfile, "--%s", positive_name);
 | 
						|
			usage_padding(outfile, pos);
 | 
						|
			fprintf_ln(outfile, _("opposite of --no-%s"),
 | 
						|
				   positive_name);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	fputc('\n', outfile);
 | 
						|
 | 
						|
	if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
 | 
						|
		fputs("EOF\n", outfile);
 | 
						|
 | 
						|
	return PARSE_OPT_HELP;
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN usage_with_options(const char * const *usagestr,
 | 
						|
			const struct option *opts)
 | 
						|
{
 | 
						|
	usage_with_options_internal(NULL, usagestr, opts, 0, 1);
 | 
						|
	exit(129);
 | 
						|
}
 | 
						|
 | 
						|
void show_usage_with_options_if_asked(int ac, const char **av,
 | 
						|
				      const char * const *usagestr,
 | 
						|
				      const struct option *opts)
 | 
						|
{
 | 
						|
	if (ac == 2 && !strcmp(av[1], "-h")) {
 | 
						|
		usage_with_options_internal(NULL, usagestr, opts, 0, 0);
 | 
						|
		exit(129);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN usage_msg_opt(const char *msg,
 | 
						|
		   const char * const *usagestr,
 | 
						|
		   const struct option *options)
 | 
						|
{
 | 
						|
	die_message("%s\n", msg); /* The extra \n is intentional */
 | 
						|
	usage_with_options(usagestr, options);
 | 
						|
}
 | 
						|
 | 
						|
void NORETURN usage_msg_optf(const char * const fmt,
 | 
						|
			     const char * const *usagestr,
 | 
						|
			     const struct option *options, ...)
 | 
						|
{
 | 
						|
	struct strbuf msg = STRBUF_INIT;
 | 
						|
	va_list ap;
 | 
						|
	va_start(ap, options);
 | 
						|
	strbuf_vaddf(&msg, fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	usage_msg_opt(msg.buf, usagestr, options);
 | 
						|
}
 | 
						|
 | 
						|
void die_for_incompatible_opt4(int opt1, const char *opt1_name,
 | 
						|
			       int opt2, const char *opt2_name,
 | 
						|
			       int opt3, const char *opt3_name,
 | 
						|
			       int opt4, const char *opt4_name)
 | 
						|
{
 | 
						|
	int count = 0;
 | 
						|
	const char *options[4];
 | 
						|
 | 
						|
	if (opt1)
 | 
						|
		options[count++] = opt1_name;
 | 
						|
	if (opt2)
 | 
						|
		options[count++] = opt2_name;
 | 
						|
	if (opt3)
 | 
						|
		options[count++] = opt3_name;
 | 
						|
	if (opt4)
 | 
						|
		options[count++] = opt4_name;
 | 
						|
	switch (count) {
 | 
						|
	case 4:
 | 
						|
		die(_("options '%s', '%s', '%s', and '%s' cannot be used together"),
 | 
						|
		    opt1_name, opt2_name, opt3_name, opt4_name);
 | 
						|
		break;
 | 
						|
	case 3:
 | 
						|
		die(_("options '%s', '%s', and '%s' cannot be used together"),
 | 
						|
		    options[0], options[1], options[2]);
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		die(_("options '%s' and '%s' cannot be used together"),
 | 
						|
		    options[0], options[1]);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 |