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