Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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         /* Choose bigger one to alloc */
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         /* This reuses cmdname->len for similarity index */
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 }