parse-options.c 8.9 KB
Newer Older
P
Pierre Habouzit 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include "git-compat-util.h"
#include "parse-options.h"

#define OPT_SHORT 1
#define OPT_UNSET 2

struct optparse_t {
	const char **argv;
	int argc;
	const char *opt;
};

static inline const char *get_arg(struct optparse_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)
		return error("switch `%c' %s", opt->short_name, reason);
	if (flags & OPT_UNSET)
		return error("option `no-%s' %s", opt->long_name, reason);
	return error("option `%s' %s", opt->long_name, reason);
}

static int get_value(struct optparse_t *p,
                     const struct option *opt, int flags)
{
42
	const char *s, *arg;
P
Pierre Habouzit 已提交
43
	const int unset = flags & OPT_UNSET;
P
Pierre Habouzit 已提交
44

P
Pierre Habouzit 已提交
45
	if (unset && p->opt)
P
Pierre Habouzit 已提交
46
		return opterror(opt, "takes no value", flags);
P
Pierre Habouzit 已提交
47 48
	if (unset && (opt->flags & PARSE_OPT_NONEG))
		return opterror(opt, "isn't available", flags);
P
Pierre Habouzit 已提交
49

P
Pierre Habouzit 已提交
50 51 52 53 54 55 56 57 58 59
	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_SET_INT:
		case OPTION_SET_PTR:
P
Pierre Habouzit 已提交
60
			return opterror(opt, "takes no value", flags);
P
Pierre Habouzit 已提交
61 62 63 64 65 66 67 68 69 70
		default:
			break;
		}
	}

	arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL);
	switch (opt->type) {
	case OPTION_BIT:
		if (unset)
			*(int *)opt->value &= ~opt->defval;
P
Pierre Habouzit 已提交
71
		else
P
Pierre Habouzit 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84
			*(int *)opt->value |= opt->defval;
		return 0;

	case OPTION_BOOLEAN:
		*(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_SET_PTR:
		*(void **)opt->value = unset ? NULL : (void *)opt->defval;
P
Pierre Habouzit 已提交
85 86 87
		return 0;

	case OPTION_STRING:
P
Pierre Habouzit 已提交
88 89
		if (unset) {
			*(const char **)opt->value = NULL;
P
Pierre Habouzit 已提交
90 91
			return 0;
		}
92
		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
93 94 95 96
			*(const char **)opt->value = (const char *)opt->defval;
			return 0;
		}
		if (!arg)
P
Pierre Habouzit 已提交
97 98 99 100
			return opterror(opt, "requires a value", flags);
		*(const char **)opt->value = get_arg(p);
		return 0;

101
	case OPTION_CALLBACK:
P
Pierre Habouzit 已提交
102
		if (unset)
103
			return (*opt->callback)(opt, NULL, 1);
P
Pierre Habouzit 已提交
104
		if (opt->flags & PARSE_OPT_NOARG)
105
			return (*opt->callback)(opt, NULL, 0);
106
		if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
107 108 109 110 111
			return (*opt->callback)(opt, NULL, 0);
		if (!arg)
			return opterror(opt, "requires a value", flags);
		return (*opt->callback)(opt, get_arg(p), 0);

P
Pierre Habouzit 已提交
112
	case OPTION_INTEGER:
P
Pierre Habouzit 已提交
113
		if (unset) {
P
Pierre Habouzit 已提交
114 115 116
			*(int *)opt->value = 0;
			return 0;
		}
117
		if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
118 119 120 121
			*(int *)opt->value = opt->defval;
			return 0;
		}
		if (!arg)
P
Pierre Habouzit 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
			return opterror(opt, "requires a value", flags);
		*(int *)opt->value = strtol(get_arg(p), (char **)&s, 10);
		if (*s)
			return opterror(opt, "expects a numerical value", flags);
		return 0;

	default:
		die("should not happen, someone must be hit on the forehead");
	}
}

static int parse_short_opt(struct optparse_t *p, const struct option *options)
{
	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);
		}
	}
	return error("unknown switch `%c'", *p->opt);
}

static int parse_long_opt(struct optparse_t *p, const char *arg,
                          const struct option *options)
{
147
	const char *arg_end = strchr(arg, '=');
148 149
	const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
	int abbrev_flags = 0, ambiguous_flags = 0;
150 151 152 153

	if (!arg_end)
		arg_end = arg + strlen(arg);

P
Pierre Habouzit 已提交
154 155 156 157 158 159 160 161 162
	for (; options->type != OPTION_END; options++) {
		const char *rest;
		int flags = 0;

		if (!options->long_name)
			continue;

		rest = skip_prefix(arg, options->long_name);
		if (!rest) {
163 164 165
			/* abbreviated? */
			if (!strncmp(options->long_name, arg, arg_end - arg)) {
is_abbreviated:
166 167 168 169 170 171 172 173 174 175
				if (abbrev_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;
				}
176 177 178 179 180 181 182 183 184 185 186 187
				if (!(flags & OPT_UNSET) && *arg_end)
					p->opt = arg_end + 1;
				abbrev_option = options;
				abbrev_flags = flags;
				continue;
			}
			/* negated and abbreviated very much? */
			if (!prefixcmp("no-", arg)) {
				flags |= OPT_UNSET;
				goto is_abbreviated;
			}
			/* negated? */
P
Pierre Habouzit 已提交
188 189 190 191
			if (strncmp(arg, "no-", 3))
				continue;
			flags |= OPT_UNSET;
			rest = skip_prefix(arg + 3, options->long_name);
192 193 194
			/* abbreviated and negated? */
			if (!rest && !prefixcmp(options->long_name, arg + 3))
				goto is_abbreviated;
P
Pierre Habouzit 已提交
195 196 197 198 199 200 201 202 203 204
			if (!rest)
				continue;
		}
		if (*rest) {
			if (*rest != '=')
				continue;
			p->opt = rest + 1;
		}
		return get_value(p, options, flags);
	}
205 206 207 208 209 210 211 212 213

	if (ambiguous_option)
		return 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);
214 215
	if (abbrev_option)
		return get_value(p, abbrev_option, abbrev_flags);
P
Pierre Habouzit 已提交
216 217 218
	return error("unknown option `%s'", arg);
}

219 220 221
static NORETURN void usage_with_options_internal(const char * const *,
                                                 const struct option *, int);

P
Pierre Habouzit 已提交
222
int parse_options(int argc, const char **argv, const struct option *options,
223
                  const char * const usagestr[], int flags)
P
Pierre Habouzit 已提交
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
{
	struct optparse_t args = { argv + 1, argc - 1, NULL };
	int j = 0;

	for (; args.argc; args.argc--, args.argv++) {
		const char *arg = args.argv[0];

		if (*arg != '-' || !arg[1]) {
			argv[j++] = args.argv[0];
			continue;
		}

		if (arg[1] != '-') {
			args.opt = arg + 1;
			do {
				if (*args.opt == 'h')
240
					usage_with_options(usagestr, options);
P
Pierre Habouzit 已提交
241
				if (parse_short_opt(&args, options) < 0)
242
					usage_with_options(usagestr, options);
P
Pierre Habouzit 已提交
243 244 245 246 247 248 249 250 251 252 253 254
			} while (args.opt);
			continue;
		}

		if (!arg[2]) { /* "--" */
			if (!(flags & PARSE_OPT_KEEP_DASHDASH)) {
				args.argc--;
				args.argv++;
			}
			break;
		}

255 256
		if (!strcmp(arg + 2, "help-all"))
			usage_with_options_internal(usagestr, options, 1);
P
Pierre Habouzit 已提交
257
		if (!strcmp(arg + 2, "help"))
258
			usage_with_options(usagestr, options);
P
Pierre Habouzit 已提交
259
		if (parse_long_opt(&args, arg + 2, options))
260
			usage_with_options(usagestr, options);
P
Pierre Habouzit 已提交
261 262 263 264 265 266
	}

	memmove(argv + j, args.argv, args.argc * sizeof(*argv));
	argv[j + args.argc] = NULL;
	return j + args.argc;
}
267 268 269 270

#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP         2

271 272
void usage_with_options_internal(const char * const *usagestr,
                                 const struct option *opts, int full)
273
{
274 275 276 277 278
	fprintf(stderr, "usage: %s\n", *usagestr++);
	while (*usagestr && **usagestr)
		fprintf(stderr, "   or: %s\n", *usagestr++);
	while (*usagestr)
		fprintf(stderr, "    %s\n", *usagestr++);
279 280

	if (opts->type != OPTION_GROUP)
281
		fputc('\n', stderr);
282 283 284 285 286 287

	for (; opts->type != OPTION_END; opts++) {
		size_t pos;
		int pad;

		if (opts->type == OPTION_GROUP) {
288
			fputc('\n', stderr);
289
			if (*opts->help)
290
				fprintf(stderr, "%s\n", opts->help);
291 292
			continue;
		}
293 294
		if (!full && (opts->flags & PARSE_OPT_HIDDEN))
			continue;
295

296
		pos = fprintf(stderr, "    ");
297
		if (opts->short_name)
298
			pos += fprintf(stderr, "-%c", opts->short_name);
299
		if (opts->long_name && opts->short_name)
300
			pos += fprintf(stderr, ", ");
301
		if (opts->long_name)
302
			pos += fprintf(stderr, "--%s", opts->long_name);
303 304 305

		switch (opts->type) {
		case OPTION_INTEGER:
306 307 308 309
			if (opts->flags & PARSE_OPT_OPTARG)
				pos += fprintf(stderr, " [<n>]");
			else
				pos += fprintf(stderr, " <n>");
310
			break;
311
		case OPTION_CALLBACK:
312 313 314 315
			if (opts->flags & PARSE_OPT_NOARG)
				break;
			/* FALLTHROUGH */
		case OPTION_STRING:
316 317 318 319 320 321 322 323 324 325 326
			if (opts->argh) {
				if (opts->flags & PARSE_OPT_OPTARG)
					pos += fprintf(stderr, " [<%s>]", opts->argh);
				else
					pos += fprintf(stderr, " <%s>", opts->argh);
			} else {
				if (opts->flags & PARSE_OPT_OPTARG)
					pos += fprintf(stderr, " [...]");
				else
					pos += fprintf(stderr, " ...");
			}
327
			break;
P
Pierre Habouzit 已提交
328
		default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */
329 330 331
			break;
		}

332 333
		if (pos <= USAGE_OPTS_WIDTH)
			pad = USAGE_OPTS_WIDTH - pos;
334
		else {
335
			fputc('\n', stderr);
336 337
			pad = USAGE_OPTS_WIDTH;
		}
338
		fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
339
	}
340 341 342
	fputc('\n', stderr);

	exit(129);
343
}
344

345 346 347 348 349 350
void usage_with_options(const char * const *usagestr,
                        const struct option *opts)
{
	usage_with_options_internal(usagestr, opts, 0);
}

351 352
/*----- some often used options -----*/
#include "cache.h"
P
Pierre Habouzit 已提交
353

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
{
	int v;

	if (!arg) {
		v = unset ? 0 : DEFAULT_ABBREV;
	} else {
		v = strtol(arg, (char **)&arg, 10);
		if (*arg)
			return opterror(opt, "expects a numerical value", 0);
		if (v && v < MINIMUM_ABBREV)
			v = MINIMUM_ABBREV;
		else if (v > 40)
			v = 40;
	}
	*(int *)(opt->value) = v;
	return 0;
}