]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - tools/perf/util/parse-options.c
perf tools: Improve ambiguous option help message
[karo-tx-linux.git] / tools / perf / util / parse-options.c
index 4a015f77e2b5bb6ff7f6ab3e8c22ec42bf1b8b67..b8d98229a8af7e907bc5b94e2070a2a7afc0d3af 100644 (file)
@@ -2,6 +2,7 @@
 #include "parse-options.h"
 #include "cache.h"
 #include "header.h"
+#include <linux/string.h>
 
 #define OPT_SHORT 1
 #define OPT_UNSET 2
@@ -37,6 +38,7 @@ static int get_value(struct parse_opt_ctx_t *p,
 {
        const char *s, *arg = NULL;
        const int unset = flags & OPT_UNSET;
+       int err;
 
        if (unset && p->opt)
                return opterror(opt, "takes no value", flags);
@@ -114,13 +116,29 @@ static int get_value(struct parse_opt_ctx_t *p,
                return 0;
 
        case OPTION_STRING:
+               err = 0;
                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;
+                       err = get_arg(p, opt, flags, (const char **)opt->value);
+
+               /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */
+               if (opt->flags & PARSE_OPT_NOEMPTY) {
+                       const char *val = *(const char **)opt->value;
+
+                       if (!val)
+                               return err;
+
+                       /* Similar to unset if we are given an empty string. */
+                       if (val[0] == '\0') {
+                               *(const char **)opt->value = NULL;
+                               return 0;
+                       }
+               }
+
+               return err;
 
        case OPTION_CALLBACK:
                if (unset)
@@ -355,7 +373,8 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
 }
 
 static int usage_with_options_internal(const char * const *,
-                                      const struct option *, int);
+                                      const struct option *, int,
+                                      struct parse_opt_ctx_t *);
 
 int parse_options_step(struct parse_opt_ctx_t *ctx,
                       const struct option *options,
@@ -379,8 +398,9 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
                if (arg[1] != '-') {
                        ctx->opt = ++arg;
-                       if (internal_help && *ctx->opt == 'h')
-                               return usage_with_options_internal(usagestr, options, 0);
+                       if (internal_help && *ctx->opt == 'h') {
+                               return usage_with_options_internal(usagestr, options, 0, ctx);
+                       }
                        switch (parse_short_opt(ctx, options)) {
                        case -1:
                                return parse_options_usage(usagestr, options, arg, 1);
@@ -395,7 +415,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                                check_typos(arg, options);
                        while (ctx->opt) {
                                if (internal_help && *ctx->opt == 'h')
-                                       return usage_with_options_internal(usagestr, options, 0);
+                                       return usage_with_options_internal(usagestr, options, 0, ctx);
                                arg = ctx->opt;
                                switch (parse_short_opt(ctx, options)) {
                                case -1:
@@ -428,9 +448,9 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 
                arg += 2;
                if (internal_help && !strcmp(arg, "help-all"))
-                       return usage_with_options_internal(usagestr, options, 1);
+                       return usage_with_options_internal(usagestr, options, 1, ctx);
                if (internal_help && !strcmp(arg, "help"))
-                       return usage_with_options_internal(usagestr, options, 0);
+                       return usage_with_options_internal(usagestr, options, 0, ctx);
                if (!strcmp(arg, "list-opts"))
                        return PARSE_OPT_LIST_OPTS;
                if (!strcmp(arg, "list-cmds"))
@@ -479,7 +499,7 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
 {
        struct parse_opt_ctx_t ctx;
 
-       perf_header__set_cmdline(argc, argv);
+       perf_env__set_cmdline(&perf_env, argc, argv);
 
        /* build usage string if it's not provided */
        if (subcommands && !usagestr[0]) {
@@ -505,13 +525,18 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
                break;
        case PARSE_OPT_LIST_OPTS:
                while (options->type != OPTION_END) {
-                       printf("--%s ", options->long_name);
+                       if (options->long_name)
+                               printf("--%s ", options->long_name);
                        options++;
                }
+               putchar('\n');
                exit(130);
        case PARSE_OPT_LIST_SUBCMDS:
-               for (int i = 0; subcommands[i]; i++)
-                       printf("%s ", subcommands[i]);
+               if (subcommands) {
+                       for (int i = 0; subcommands[i]; i++)
+                               printf("%s ", subcommands[i]);
+               }
+               putchar('\n');
                exit(130);
        default: /* PARSE_OPT_UNKNOWN */
                if (ctx.argv[0][1] == '-') {
@@ -620,13 +645,73 @@ static void print_option_help(const struct option *opts, int full)
        fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
 }
 
+static int option__cmp(const void *va, const void *vb)
+{
+       const struct option *a = va, *b = vb;
+       int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
+
+       if (sa == 0)
+               sa = 'z' + 1;
+       if (sb == 0)
+               sb = 'z' + 1;
+
+       ret = sa - sb;
+
+       if (ret == 0) {
+               const char *la = a->long_name ?: "",
+                          *lb = b->long_name ?: "";
+               ret = strcmp(la, lb);
+       }
+
+       return ret;
+}
+
+static struct option *options__order(const struct option *opts)
+{
+       int nr_opts = 0;
+       const struct option *o = opts;
+       struct option *ordered;
+
+       for (o = opts; o->type != OPTION_END; o++)
+               ++nr_opts;
+
+       ordered = memdup(opts, sizeof(*o) * (nr_opts + 1));
+       if (ordered == NULL)
+               goto out;
+
+       qsort(ordered, nr_opts, sizeof(*o), option__cmp);
+out:
+       return ordered;
+}
+
+static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
+{
+       int i;
+
+       for (i = 1; i < ctx->argc; ++i) {
+               const char *arg = ctx->argv[i];
+
+               if (arg[0] != '-')
+                       continue;
+
+               if (arg[1] == opt->short_name ||
+                   (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
+                       return true;
+       }
+
+       return false;
+}
+
 int usage_with_options_internal(const char * const *usagestr,
-                               const struct option *opts, int full)
+                               const struct option *opts, int full,
+                               struct parse_opt_ctx_t *ctx)
 {
+       struct option *ordered;
+
        if (!usagestr)
                return PARSE_OPT_HELP;
 
-       fprintf(stderr, "\n usage: %s\n", *usagestr++);
+       fprintf(stderr, "\n Usage: %s\n", *usagestr++);
        while (*usagestr && **usagestr)
                fprintf(stderr, "    or: %s\n", *usagestr++);
        while (*usagestr) {
@@ -639,11 +724,20 @@ int usage_with_options_internal(const char * const *usagestr,
        if (opts->type != OPTION_GROUP)
                fputc('\n', stderr);
 
-       for (  ; opts->type != OPTION_END; opts++)
+       ordered = options__order(opts);
+       if (ordered)
+               opts = ordered;
+
+       for (  ; opts->type != OPTION_END; opts++) {
+               if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
+                       continue;
                print_option_help(opts, full);
+       }
 
        fputc('\n', stderr);
 
+       free(ordered);
+
        return PARSE_OPT_HELP;
 }
 
@@ -651,7 +745,7 @@ void usage_with_options(const char * const *usagestr,
                        const struct option *opts)
 {
        exit_browser(false);
-       usage_with_options_internal(usagestr, opts, 0);
+       usage_with_options_internal(usagestr, opts, 0, NULL);
        exit(129);
 }
 
@@ -662,7 +756,7 @@ int parse_options_usage(const char * const *usagestr,
        if (!usagestr)
                goto opt;
 
-       fprintf(stderr, "\n usage: %s\n", *usagestr++);
+       fprintf(stderr, "\n Usage: %s\n", *usagestr++);
        while (*usagestr && **usagestr)
                fprintf(stderr, "    or: %s\n", *usagestr++);
        while (*usagestr) {
@@ -676,24 +770,23 @@ int parse_options_usage(const char * const *usagestr,
 opt:
        for (  ; opts->type != OPTION_END; opts++) {
                if (short_opt) {
-                       if (opts->short_name == *optstr)
+                       if (opts->short_name == *optstr) {
+                               print_option_help(opts, 0);
                                break;
+                       }
                        continue;
                }
 
                if (opts->long_name == NULL)
                        continue;
 
-               if (!prefixcmp(optstr, opts->long_name))
-                       break;
-               if (!prefixcmp(optstr, "no-") &&
-                   !prefixcmp(optstr + 3, opts->long_name))
-                       break;
+               if (!prefixcmp(opts->long_name, optstr))
+                       print_option_help(opts, 0);
+               if (!prefixcmp("no-", optstr) &&
+                   !prefixcmp(opts->long_name, optstr + 3))
+                       print_option_help(opts, 0);
        }
 
-       if (opts->type != OPTION_END)
-               print_option_help(opts, 0);
-
        return PARSE_OPT_HELP;
 }