root/tools/perf/util/help-unknown-cmd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. perf_unknown_cmd_config
  2. levenshtein_compare
  3. add_cmd_list
  4. help_unknown_cmd

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

/* [<][>][^][v][top][bottom][index][help] */