0001
0002 #include "cache.h"
0003 #include "config.h"
0004 #include <poll.h>
0005 #include <stdio.h>
0006 #include <stdlib.h>
0007 #include <subcmd/help.h>
0008 #include "../builtin.h"
0009 #include "levenshtein.h"
0010 #include <linux/zalloc.h>
0011
0012 static int autocorrect;
0013
0014 static int perf_unknown_cmd_config(const char *var, const char *value,
0015 void *cb __maybe_unused)
0016 {
0017 if (!strcmp(var, "help.autocorrect"))
0018 return perf_config_int(&autocorrect, var,value);
0019
0020 return 0;
0021 }
0022
0023 static int levenshtein_compare(const void *p1, const void *p2)
0024 {
0025 const struct cmdname *const *c1 = p1, *const *c2 = p2;
0026 const char *s1 = (*c1)->name, *s2 = (*c2)->name;
0027 int l1 = (*c1)->len;
0028 int l2 = (*c2)->len;
0029 return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
0030 }
0031
0032 static int add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
0033 {
0034 unsigned int i, nr = cmds->cnt + old->cnt;
0035 void *tmp;
0036
0037 if (nr > cmds->alloc) {
0038
0039 if (alloc_nr(cmds->alloc) < nr)
0040 cmds->alloc = nr;
0041 else
0042 cmds->alloc = alloc_nr(cmds->alloc);
0043 tmp = realloc(cmds->names, cmds->alloc * sizeof(*cmds->names));
0044 if (!tmp)
0045 return -1;
0046 cmds->names = tmp;
0047 }
0048 for (i = 0; i < old->cnt; i++)
0049 cmds->names[cmds->cnt++] = old->names[i];
0050 zfree(&old->names);
0051 old->cnt = 0;
0052 return 0;
0053 }
0054
0055 const char *help_unknown_cmd(const char *cmd)
0056 {
0057 unsigned int i, n = 0, best_similarity = 0;
0058 struct cmdnames main_cmds, other_cmds;
0059
0060 memset(&main_cmds, 0, sizeof(main_cmds));
0061 memset(&other_cmds, 0, sizeof(main_cmds));
0062
0063 perf_config(perf_unknown_cmd_config, NULL);
0064
0065 load_command_list("perf-", &main_cmds, &other_cmds);
0066
0067 if (add_cmd_list(&main_cmds, &other_cmds) < 0) {
0068 fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n");
0069 goto end;
0070 }
0071 qsort(main_cmds.names, main_cmds.cnt,
0072 sizeof(main_cmds.names), cmdname_compare);
0073 uniq(&main_cmds);
0074
0075 if (main_cmds.cnt) {
0076
0077 for (i = 0; i < main_cmds.cnt; ++i)
0078 main_cmds.names[i]->len =
0079 levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
0080
0081 qsort(main_cmds.names, main_cmds.cnt,
0082 sizeof(*main_cmds.names), levenshtein_compare);
0083
0084 best_similarity = main_cmds.names[0]->len;
0085 n = 1;
0086 while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
0087 ++n;
0088 }
0089
0090 if (autocorrect && n == 1) {
0091 const char *assumed = main_cmds.names[0]->name;
0092
0093 main_cmds.names[0] = NULL;
0094 clean_cmdnames(&main_cmds);
0095 fprintf(stderr, "WARNING: You called a perf program named '%s', "
0096 "which does not exist.\n"
0097 "Continuing under the assumption that you meant '%s'\n",
0098 cmd, assumed);
0099 if (autocorrect > 0) {
0100 fprintf(stderr, "in %0.1f seconds automatically...\n",
0101 (float)autocorrect/10.0);
0102 poll(NULL, 0, autocorrect * 100);
0103 }
0104 return assumed;
0105 }
0106
0107 fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
0108
0109 if (main_cmds.cnt && best_similarity < 6) {
0110 fprintf(stderr, "\nDid you mean %s?\n",
0111 n < 2 ? "this": "one of these");
0112
0113 for (i = 0; i < n; i++)
0114 fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
0115 }
0116 end:
0117 exit(1);
0118 }