Re: [PATCH v2 09/20] parse-options: add support for parsing subcommands

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Aug 19 2022, SZEDER Gábor wrote:

> +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;

Nit: Maybe do the cast by assigning to an intermediate variable? Would
make this less dense, and since we already have {}-braces...

> +			return PARSE_OPT_SUBCOMMAND;
> +		}
> +
> +	return PARSE_OPT_UNKNOWN;
> +}
> +
>  static void check_typos(const char *arg, const struct option *options)
>  {
>  	if (strlen(arg) < 3)
> @@ -442,6 +457,7 @@ static void check_typos(const char *arg, const struct option *options)
>  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++) {
> @@ -489,6 +505,14 @@ static void parse_options_check(const struct option *opts)
>  			       "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");

Better split into two IMO:

	if (!opts->value)
		optbug(opts, "OPTION_SUBCOMMAND needs a value");
	if (!opts->subcommand_fn)
		optbug(opts, "OPTION_SUBCOMMAND needs a subcommand_fn");

Then if we have extra checks we don't need to keep adding to a dense ||
and amend an existing string.

> +static int has_subcommands(const struct option *options)
> +{
> +	for (; options->type != OPTION_END; options++)
> +		if (options->type == OPTION_SUBCOMMAND)
> +			return 1;
> +	return 0;
> +}

I wondered if parse_options_check() couldn't take a "int
*has_subcommands", since we already loop through the options to check it
as part of the checks there (but we'd need to re-arrange them).

Not for optimization, just wondering if it would make the code flow
clearer, maybe not.

> +	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");

MM, very long lines. Maybe something like:

	BUG("%s flag cannot be combined with %s, unless %s is also provided", ...);

> +		}
> +	}
>  	if ((flags & PARSE_OPT_KEEP_UNKNOWN_OPT) &&
>  	    (flags & PARSE_OPT_STOP_AT_NON_OPTION) &&
>  	    !(flags & PARSE_OPT_ONE_SHOT))
> @@ -589,6 +634,7 @@ static int show_gitcomp(const struct option *opts, int show_all)
>  	int nr_noopts = 0;
>  
>  	for (; opts->type != OPTION_END; opts++) {
> +		const char *prefix = "--";

Ah, here we have a prefix variable, but it's more of an "infix"... :)

> +					return PARSE_OPT_DONE;
> +				error(_("unknown subcommand: `%s'"), arg);
> +				usage_with_options(usagestr, options);

ditto earlier patch comment about usage_msg_opt() (also applies below)

> +			case PARSE_OPT_NON_OPTION:
> +				/* Impossible. */

We could just skip this comment as...
> +				BUG("parse_subcommand() cannot return these");

...this makes it clear.

>  	case PARSE_OPT_DONE:
> +		if (ctx.has_subcommands &&
> +		    !(flags & PARSE_OPT_SUBCOMMAND_OPTIONAL)) {
> +			error(_("need a subcommand"));
> +			usage_with_options(usagestr, options);

ditto.

> +typedef int parse_opt_subcommand_fn(int argc, const char **argv,
> +				    const char *prefix);

We usually define function typedefs like:

	typedef int (*parse_opt_subcommand_fn)(...);

But I see that...

>  	enum parse_opt_type type;
> @@ -145,6 +155,7 @@ struct option {
>  	intptr_t defval;
>  	parse_opt_ll_cb *ll_callback;
>  	intptr_t extra;
> +	parse_opt_subcommand_fn *subcommand_fn;

You're doing this consistently with parse_opt_ll_cb etc., fair enough.

> +#define OPT_SUBCOMMAND_F(l, v, fn, f) { \
> +	.type = OPTION_SUBCOMMAND, \
> +	.long_name = (l), \
> +	.value = (v), \
> +	.flags = (f), \

Nice to see this form

> +	.subcommand_fn = (fn) }

Almost all other such decls end with:

		.foo = bar, \
	}

I.e. we put the closing "}" at the start of the line.

> +#define OPT_SUBCOMMAND(l, v, fn)    OPT_SUBCOMMAND_F((l), (v), (fn), 0)

I think this would be more readable as:

	#define OPT_SUBCOMMAND(l, v, fn) \
		OPT_SUBCOMMAND_F((l), (v), (fn), 0)

Which both aligns nicely, and isn't using a \t in the middle of the line
(which in any case we'll end up aligning with nothing else, as the names
are all different lengths, if other things continue using this pattern.
>  	enum parse_opt_flags flags;
> +	unsigned has_subcommands;

Maybe make it: "unsigned has_subcommands:1"?

> +	printf("fn: subcmd_one\n");

s/printf/puts/g here

> +	print_args(argc, argv);
> +	return 0;
> +}
> +
> +static int subcmd_two(int argc, const char **argv, const char *prefix)
> +{
> +	printf("fn: subcmd_two\n");

ditto.





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux