linux/tools/perf/util/help-unknown-cmd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "cache.h"
   3#include "config.h"
   4#include <poll.h>
   5#include <stdio.h>
   6#include <subcmd/help.h>
   7#include "../builtin.h"
   8#include "levenshtein.h"
   9
  10static int autocorrect;
  11
  12static int perf_unknown_cmd_config(const char *var, const char *value,
  13                                   void *cb __maybe_unused)
  14{
  15        if (!strcmp(var, "help.autocorrect"))
  16                return perf_config_int(&autocorrect, var,value);
  17
  18        return 0;
  19}
  20
  21static int levenshtein_compare(const void *p1, const void *p2)
  22{
  23        const struct cmdname *const *c1 = p1, *const *c2 = p2;
  24        const char *s1 = (*c1)->name, *s2 = (*c2)->name;
  25        int l1 = (*c1)->len;
  26        int l2 = (*c2)->len;
  27        return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
  28}
  29
  30static int add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
  31{
  32        unsigned int i, nr = cmds->cnt + old->cnt;
  33        void *tmp;
  34
  35        if (nr > cmds->alloc) {
  36                /* Choose bigger one to alloc */
  37                if (alloc_nr(cmds->alloc) < nr)
  38                        cmds->alloc = nr;
  39                else
  40                        cmds->alloc = alloc_nr(cmds->alloc);
  41                tmp = realloc(cmds->names, cmds->alloc * sizeof(*cmds->names));
  42                if (!tmp)
  43                        return -1;
  44                cmds->names = tmp;
  45        }
  46        for (i = 0; i < old->cnt; i++)
  47                cmds->names[cmds->cnt++] = old->names[i];
  48        zfree(&old->names);
  49        old->cnt = 0;
  50        return 0;
  51}
  52
  53const char *help_unknown_cmd(const char *cmd)
  54{
  55        unsigned int i, n = 0, best_similarity = 0;
  56        struct cmdnames main_cmds, other_cmds;
  57
  58        memset(&main_cmds, 0, sizeof(main_cmds));
  59        memset(&other_cmds, 0, sizeof(main_cmds));
  60
  61        perf_config(perf_unknown_cmd_config, NULL);
  62
  63        load_command_list("perf-", &main_cmds, &other_cmds);
  64
  65        if (add_cmd_list(&main_cmds, &other_cmds) < 0) {
  66                fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n");
  67                goto end;
  68        }
  69        qsort(main_cmds.names, main_cmds.cnt,
  70              sizeof(main_cmds.names), cmdname_compare);
  71        uniq(&main_cmds);
  72
  73        if (main_cmds.cnt) {
  74                /* This reuses cmdname->len for similarity index */
  75                for (i = 0; i < main_cmds.cnt; ++i)
  76                        main_cmds.names[i]->len =
  77                                levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
  78
  79                qsort(main_cmds.names, main_cmds.cnt,
  80                      sizeof(*main_cmds.names), levenshtein_compare);
  81
  82                best_similarity = main_cmds.names[0]->len;
  83                n = 1;
  84                while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
  85                        ++n;
  86        }
  87
  88        if (autocorrect && n == 1) {
  89                const char *assumed = main_cmds.names[0]->name;
  90
  91                main_cmds.names[0] = NULL;
  92                clean_cmdnames(&main_cmds);
  93                fprintf(stderr, "WARNING: You called a perf program named '%s', "
  94                        "which does not exist.\n"
  95                        "Continuing under the assumption that you meant '%s'\n",
  96                        cmd, assumed);
  97                if (autocorrect > 0) {
  98                        fprintf(stderr, "in %0.1f seconds automatically...\n",
  99                                (float)autocorrect/10.0);
 100                        poll(NULL, 0, autocorrect * 100);
 101                }
 102                return assumed;
 103        }
 104
 105        fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
 106
 107        if (main_cmds.cnt && best_similarity < 6) {
 108                fprintf(stderr, "\nDid you mean %s?\n",
 109                        n < 2 ? "this": "one of these");
 110
 111                for (i = 0; i < n; i++)
 112                        fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
 113        }
 114end:
 115        exit(1);
 116}
 117