diff options
Diffstat (limited to 'parse-options.c')
| -rw-r--r-- | parse-options.c | 337 | 
1 files changed, 269 insertions, 68 deletions
| diff --git a/parse-options.c b/parse-options.c index 33bfba0ed4..5933468c19 100644 --- a/parse-options.c +++ b/parse-options.c @@ -68,14 +68,71 @@ static char *fix_filename(const char *prefix, const char *file)  		return prefix_filename_except_for_dash(prefix, file);  } +static int do_get_int_value(const void *value, size_t precision, intmax_t *ret) +{ +	switch (precision) { +	case sizeof(int8_t): +		*ret = *(int8_t *)value; +		return 0; +	case sizeof(int16_t): +		*ret = *(int16_t *)value; +		return 0; +	case sizeof(int32_t): +		*ret = *(int32_t *)value; +		return 0; +	case sizeof(int64_t): +		*ret = *(int64_t *)value; +		return 0; +	default: +		return -1; +	} +} + +static intmax_t get_int_value(const struct option *opt, enum opt_parsed flags) +{ +	intmax_t ret; +	if (do_get_int_value(opt->value, opt->precision, &ret)) +		BUG("invalid precision for option %s", optname(opt, flags)); +	return ret; +} + +static enum parse_opt_result set_int_value(const struct option *opt, +					   enum opt_parsed flags, +					   intmax_t value) +{ +	switch (opt->precision) { +	case sizeof(int8_t): +		*(int8_t *)opt->value = value; +		return 0; +	case sizeof(int16_t): +		*(int16_t *)opt->value = value; +		return 0; +	case sizeof(int32_t): +		*(int32_t *)opt->value = value; +		return 0; +	case sizeof(int64_t): +		*(int64_t *)opt->value = value; +		return 0; +	default: +		BUG("invalid precision for option %s", optname(opt, flags)); +	} +} + +static int signed_int_fits(intmax_t value, size_t precision) +{ +	size_t bits = precision * CHAR_BIT; +	intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); +	intmax_t lower_bound = -upper_bound - 1; +	return lower_bound <= value && value <= upper_bound; +} +  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 char *arg;  	const int unset = flags & OPT_UNSET; -	int err;  	if (unset && p->opt)  		return error(_("%s takes no value"), optname(opt, flags)); @@ -89,35 +146,55 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,  		return opt->ll_callback(p, opt, NULL, unset);  	case OPTION_BIT: +	{ +		intmax_t value = get_int_value(opt, flags);  		if (unset) -			*(int *)opt->value &= ~opt->defval; +			value &= ~opt->defval;  		else -			*(int *)opt->value |= opt->defval; -		return 0; +			value |= opt->defval; +		return set_int_value(opt, flags, value); +	}  	case OPTION_NEGBIT: +	{ +		intmax_t value = get_int_value(opt, flags);  		if (unset) -			*(int *)opt->value |= opt->defval; +			value |= opt->defval;  		else -			*(int *)opt->value &= ~opt->defval; -		return 0; +			value &= ~opt->defval; +		return set_int_value(opt, flags, value); +	}  	case OPTION_BITOP: +	{ +		intmax_t value = get_int_value(opt, flags);  		if (unset)  			BUG("BITOP can't have unset form"); -		*(int *)opt->value &= ~opt->extra; -		*(int *)opt->value |= opt->defval; -		return 0; +		value &= ~opt->extra; +		value |= opt->defval; +		return set_int_value(opt, flags, value); +	}  	case OPTION_COUNTUP: -		if (*(int *)opt->value < 0) -			*(int *)opt->value = 0; -		*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; -		return 0; +	{ +		size_t bits = CHAR_BIT * opt->precision; +		intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); +		intmax_t value = get_int_value(opt, flags); + +		if (value < 0) +			value = 0; +		if (unset) +			value = 0; +		else if (value < upper_bound) +			value++; +		else +			return error(_("value for %s exceeds %"PRIdMAX), +				     optname(opt, flags), upper_bound); +		return set_int_value(opt, flags, value); +	}  	case OPTION_SET_INT: -		*(int *)opt->value = unset ? 0 : opt->defval; -		return 0; +		return set_int_value(opt, flags, unset ? 0 : opt->defval);  	case OPTION_STRING:  		if (unset) @@ -131,21 +208,31 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,  	case OPTION_FILENAME:  	{  		const char *value; - -		FREE_AND_NULL(*(char **)opt->value); - -		err = 0; +		int is_optional;  		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); +			value = (char *)opt->defval; +		else { +			int err = get_arg(p, opt, flags, &value); +			if (err) +				return err; +		} +		if (!value) +			return 0; -		if (!err) -			*(char **)opt->value = fix_filename(p->prefix, value); -		return err; +		is_optional = skip_prefix(value, ":(optional)", &value); +		if (!value) +			is_optional = 0; +		value = fix_filename(p->prefix, value); +		if (is_optional && is_empty_or_missing_file(value)) { +			free((char *)value); +		} else { +			FREE_AND_NULL(*(char **)opt->value); +			*(const char **)opt->value = value; +		} +		return 0;  	}  	case OPTION_CALLBACK:  	{ @@ -172,41 +259,77 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,  			return (*opt->ll_callback)(p, opt, p_arg, p_unset);  	}  	case OPTION_INTEGER: +	{ +		intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - CHAR_BIT * opt->precision); +		intmax_t lower_bound = -upper_bound - 1; +		intmax_t value; +  		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)) +			value = 0; +		} else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { +			value = opt->defval; +		} else if (get_arg(p, opt, flags, &arg)) {  			return -1; -		if (!*arg) +		} else 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"), +		} else if (!git_parse_signed(arg, &value, upper_bound)) { +			if (errno == ERANGE) +				return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), +					     arg, optname(opt, flags), lower_bound, upper_bound); + +			return error(_("%s expects an integer value with an optional k/m/g suffix"),  				     optname(opt, flags)); -		return 0; +		} + +		if (value < lower_bound) +			return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), +				     arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound); + +		return set_int_value(opt, flags, value); +	} +	case OPTION_UNSIGNED: +	{ +		uintmax_t upper_bound = UINTMAX_MAX >> (bitsizeof(uintmax_t) - CHAR_BIT * opt->precision); +		uintmax_t value; -	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)) +			value = 0; +		} else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { +			value = opt->defval; +		} else if (get_arg(p, opt, flags, &arg)) {  			return -1; -		if (!git_parse_ulong(arg, opt->value)) +		} else if (!*arg) { +			return error(_("%s expects a numerical value"), +				     optname(opt, flags)); +		} else if (!git_parse_unsigned(arg, &value, upper_bound)) { +			if (errno == ERANGE) +				return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), +					     arg, optname(opt, flags), (uintmax_t) 0, upper_bound); +  			return error(_("%s expects a non-negative integer value"  				       " with an optional k/m/g suffix"),  				     optname(opt, flags)); -		return 0; +		} + +		switch (opt->precision) { +		case 1: +			*(uint8_t *)opt->value = value; +			return 0; +		case 2: +			*(uint16_t *)opt->value = value; +			return 0; +		case 4: +			*(uint32_t *)opt->value = value; +			return 0; +		case 8: +			*(uint64_t *)opt->value = value; +			return 0; +		default: +			BUG("invalid precision for option %s", +			    optname(opt, flags)); +		} +	}  	default:  		BUG("opt->type %d should not happen", opt->type); @@ -214,7 +337,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,  }  struct parse_opt_cmdmode_list { -	int value, *value_ptr; +	intmax_t value; +	void *value_ptr; +	size_t precision;  	const struct option *opt;  	const char *arg;  	enum opt_parsed flags; @@ -228,7 +353,7 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,  	for (; opts->type != OPTION_END; opts++) {  		struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list; -		int *value_ptr = opts->value; +		void *value_ptr = opts->value;  		if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)  			continue; @@ -240,10 +365,13 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,  		CALLOC_ARRAY(elem, 1);  		elem->value_ptr = value_ptr; -		elem->value = *value_ptr; +		elem->precision = opts->precision; +		if (do_get_int_value(value_ptr, opts->precision, &elem->value)) +			optbug(opts, "has invalid precision");  		elem->next = ctx->cmdmode_list;  		ctx->cmdmode_list = elem;  	} +	BUG_if_bug("invalid 'struct option'");  }  static char *optnamearg(const struct option *opt, const char *arg, @@ -265,7 +393,13 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,  	char *opt_name, *other_opt_name;  	for (; elem; elem = elem->next) { -		if (*elem->value_ptr == elem->value) +		intmax_t new_value; + +		if (do_get_int_value(elem->value_ptr, elem->precision, +				     &new_value)) +			BUG("impossible: invalid precision"); + +		if (new_value == elem->value)  			continue;  		if (elem->opt && @@ -275,7 +409,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,  		elem->opt = opt;  		elem->arg = arg;  		elem->flags = flags; -		elem->value = *elem->value_ptr; +		elem->value = new_value;  	}  	if (result || !elem) @@ -534,10 +668,14 @@ static void parse_options_check(const struct option *opts)  		    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_SET_INT:  		case OPTION_BIT:  		case OPTION_NEGBIT: -		case OPTION_SET_INT: +		case OPTION_BITOP: +		case OPTION_COUNTUP: +			if (!signed_int_fits(opts->defval, opts->precision)) +				optbug(opts, "has invalid defval"); +			/* fallthru */  		case OPTION_NUMBER:  			if ((opts->flags & PARSE_OPT_OPTARG) ||  			    !(opts->flags & PARSE_OPT_NOARG)) @@ -656,7 +794,7 @@ static void show_negated_gitcomp(const struct option *opts, int show_all,  		case OPTION_STRING:  		case OPTION_FILENAME:  		case OPTION_INTEGER: -		case OPTION_MAGNITUDE: +		case OPTION_UNSIGNED:  		case OPTION_CALLBACK:  		case OPTION_BIT:  		case OPTION_NEGBIT: @@ -708,7 +846,7 @@ static int show_gitcomp(const struct option *opts, int show_all)  		case OPTION_STRING:  		case OPTION_FILENAME:  		case OPTION_INTEGER: -		case OPTION_MAGNITUDE: +		case OPTION_UNSIGNED:  		case OPTION_CALLBACK:  			if (opts->flags & PARSE_OPT_NOARG)  				break; @@ -824,10 +962,16 @@ static void free_preprocessed_options(struct option *options)  	free(options);  } +#define USAGE_NORMAL 0 +#define USAGE_FULL 1 +#define USAGE_TO_STDOUT 0 +#define USAGE_TO_STDERR 1 +  static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *,  							 const char * const *,  							 const struct option *, -							 int, int); +							 int full_usage, +							 int usage_to_stderr);  enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,  					 const struct option *options, @@ -959,7 +1103,8 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,  		}  		if (internal_help && !strcmp(arg + 2, "help-all")) -			return usage_with_options_internal(ctx, usagestr, options, 1, 0); +			return usage_with_options_internal(ctx, usagestr, options, +							   USAGE_FULL, USAGE_TO_STDOUT);  		if (internal_help && !strcmp(arg + 2, "help"))  			goto show_usage;  		switch (parse_long_opt(ctx, arg + 2, options)) { @@ -1000,7 +1145,8 @@ unknown:  	return PARSE_OPT_DONE;   show_usage: -	return usage_with_options_internal(ctx, usagestr, options, 0, 0); +	return usage_with_options_internal(ctx, usagestr, options, +					   USAGE_NORMAL, USAGE_TO_STDOUT);  }  int parse_options_end(struct parse_opt_ctx_t *ctx) @@ -1076,11 +1222,48 @@ static int usage_argh(const struct option *opts, FILE *outfile)  		!opts->argh || !!strpbrk(opts->argh, "()<>[]|");  	if (opts->flags & PARSE_OPT_OPTARG)  		if (opts->long_name) -			s = literal ? "[=%s]" : "[=<%s>]"; +			/* +			 * TRANSLATORS: The "<%s>" part of this string +			 * stands for an optional value given to a command +			 * line option in the long form, and "<>" is there +			 * as a convention to signal that it is a +			 * placeholder (i.e. the user should substitute it +			 * with the real value).  If your language uses a +			 * different convention, you can change "<%s>" part +			 * to match yours, e.g. it might use "|%s|" instead, +			 * or if the alphabet is different enough it may use +			 * "%s" without any placeholder signal.  Most +			 * translations leave this message as is. +			 */ +			s = literal ? "[=%s]" : _("[=<%s>]");  		else -			s = literal ? "[%s]" : "[<%s>]"; +			/* +			 * TRANSLATORS: The "<%s>" part of this string +			 * stands for an optional value given to a command +			 * line option in the short form, and "<>" is there +			 * as a convention to signal that it is a +			 * placeholder (i.e. the user should substitute it +			 * with the real value).  If your language uses a +			 * different convention, you can change "<%s>" part +			 * to match yours, e.g. it might use "|%s|" instead, +			 * or if the alphabet is different enough it may use +			 * "%s" without any placeholder signal.  Most +			 * translations leave this message as is. +			 */ +			s = literal ? "[%s]" : _("[<%s>]");  	else -		s = literal ? " %s" : " <%s>"; +		/* +		 * TRANSLATORS: The "<%s>" part of this string stands for a +		 * value given to a command line option, and "<>" is there +		 * as a convention to signal that it is a placeholder +		 * (i.e. the user should substitute it with the real value). +		 * If your language uses a different convention, you can +		 * change "<%s>" part to match yours, e.g. it might use +		 * "|%s|" instead, or if the alphabet is different enough it +		 * may use "%s" without any placeholder signal.  Most +		 * translations leave this message as is. +		 */ +		s = literal ? " %s" : _(" <%s>");  	return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));  } @@ -1172,7 +1355,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t  		if (!saw_empty_line && !*str)  			saw_empty_line = 1; -		string_list_split(&list, str, '\n', -1); +		string_list_split(&list, str, "\n", -1);  		for (j = 0; j < list.nr; j++) {  			const char *line = list.items[j].string; @@ -1278,10 +1461,28 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t  void NORETURN usage_with_options(const char * const *usagestr,  			const struct option *opts)  { -	usage_with_options_internal(NULL, usagestr, opts, 0, 1); +	usage_with_options_internal(NULL, usagestr, opts, +				    USAGE_NORMAL, USAGE_TO_STDERR);  	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) { +		if (!strcmp(av[1], "-h")) { +			usage_with_options_internal(NULL, usagestr, opts, +						    USAGE_NORMAL, USAGE_TO_STDOUT); +			exit(129); +		} else if (!strcmp(av[1], "--help-all")) { +			usage_with_options_internal(NULL, usagestr, opts, +						    USAGE_FULL, USAGE_TO_STDOUT); +			exit(129); +		} +	} +} +  void NORETURN usage_msg_opt(const char *msg,  		   const char * const *usagestr,  		   const struct option *options) | 
