Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
help.c
Go to the documentation of this file.
1 #include "cache.h"
2 #include "../builtin.h"
3 #include "exec_cmd.h"
4 #include "levenshtein.h"
5 #include "help.h"
6 #include <termios.h>
7 
8 void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
9 {
10  struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
11 
12  ent->len = len;
13  memcpy(ent->name, name, len);
14  ent->name[len] = 0;
15 
16  ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
17  cmds->names[cmds->cnt++] = ent;
18 }
19 
20 static void clean_cmdnames(struct cmdnames *cmds)
21 {
22  unsigned int i;
23 
24  for (i = 0; i < cmds->cnt; ++i)
25  free(cmds->names[i]);
26  free(cmds->names);
27  cmds->cnt = 0;
28  cmds->alloc = 0;
29 }
30 
31 static int cmdname_compare(const void *a_, const void *b_)
32 {
33  struct cmdname *a = *(struct cmdname **)a_;
34  struct cmdname *b = *(struct cmdname **)b_;
35  return strcmp(a->name, b->name);
36 }
37 
38 static void uniq(struct cmdnames *cmds)
39 {
40  unsigned int i, j;
41 
42  if (!cmds->cnt)
43  return;
44 
45  for (i = j = 1; i < cmds->cnt; i++)
46  if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
47  cmds->names[j++] = cmds->names[i];
48 
49  cmds->cnt = j;
50 }
51 
52 void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
53 {
54  size_t ci, cj, ei;
55  int cmp;
56 
57  ci = cj = ei = 0;
58  while (ci < cmds->cnt && ei < excludes->cnt) {
59  cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
60  if (cmp < 0)
61  cmds->names[cj++] = cmds->names[ci++];
62  else if (cmp == 0)
63  ci++, ei++;
64  else if (cmp > 0)
65  ei++;
66  }
67 
68  while (ci < cmds->cnt)
69  cmds->names[cj++] = cmds->names[ci++];
70 
71  cmds->cnt = cj;
72 }
73 
74 static void pretty_print_string_list(struct cmdnames *cmds, int longest)
75 {
76  int cols = 1, rows;
77  int space = longest + 1; /* min 1 SP between words */
78  struct winsize win;
79  int max_cols;
80  int i, j;
81 
83  max_cols = win.ws_col - 1; /* don't print *on* the edge */
84 
85  if (space < max_cols)
86  cols = max_cols / space;
87  rows = (cmds->cnt + cols - 1) / cols;
88 
89  for (i = 0; i < rows; i++) {
90  printf(" ");
91 
92  for (j = 0; j < cols; j++) {
93  unsigned int n = j * rows + i;
94  unsigned int size = space;
95 
96  if (n >= cmds->cnt)
97  break;
98  if (j == cols-1 || n + rows >= cmds->cnt)
99  size = 1;
100  printf("%-*s", size, cmds->names[n]->name);
101  }
102  putchar('\n');
103  }
104 }
105 
106 static int is_executable(const char *name)
107 {
108  struct stat st;
109 
110  if (stat(name, &st) || /* stat, not lstat */
111  !S_ISREG(st.st_mode))
112  return 0;
113 
114  return st.st_mode & S_IXUSR;
115 }
116 
117 static void list_commands_in_dir(struct cmdnames *cmds,
118  const char *path,
119  const char *prefix)
120 {
121  int prefix_len;
122  DIR *dir = opendir(path);
123  struct dirent *de;
124  struct strbuf buf = STRBUF_INIT;
125  int len;
126 
127  if (!dir)
128  return;
129  if (!prefix)
130  prefix = "perf-";
131  prefix_len = strlen(prefix);
132 
133  strbuf_addf(&buf, "%s/", path);
134  len = buf.len;
135 
136  while ((de = readdir(dir)) != NULL) {
137  int entlen;
138 
139  if (prefixcmp(de->d_name, prefix))
140  continue;
141 
142  strbuf_setlen(&buf, len);
143  strbuf_addstr(&buf, de->d_name);
144  if (!is_executable(buf.buf))
145  continue;
146 
147  entlen = strlen(de->d_name) - prefix_len;
148  if (has_extension(de->d_name, ".exe"))
149  entlen -= 4;
150 
151  add_cmdname(cmds, de->d_name + prefix_len, entlen);
152  }
153  closedir(dir);
154  strbuf_release(&buf);
155 }
156 
157 void load_command_list(const char *prefix,
158  struct cmdnames *main_cmds,
159  struct cmdnames *other_cmds)
160 {
161  const char *env_path = getenv("PATH");
162  const char *exec_path = perf_exec_path();
163 
164  if (exec_path) {
165  list_commands_in_dir(main_cmds, exec_path, prefix);
166  qsort(main_cmds->names, main_cmds->cnt,
167  sizeof(*main_cmds->names), cmdname_compare);
168  uniq(main_cmds);
169  }
170 
171  if (env_path) {
172  char *paths, *path, *colon;
173  path = paths = strdup(env_path);
174  while (1) {
175  if ((colon = strchr(path, PATH_SEP)))
176  *colon = 0;
177  if (!exec_path || strcmp(path, exec_path))
178  list_commands_in_dir(other_cmds, path, prefix);
179 
180  if (!colon)
181  break;
182  path = colon + 1;
183  }
184  free(paths);
185 
186  qsort(other_cmds->names, other_cmds->cnt,
187  sizeof(*other_cmds->names), cmdname_compare);
188  uniq(other_cmds);
189  }
190  exclude_cmds(other_cmds, main_cmds);
191 }
192 
193 void list_commands(const char *title, struct cmdnames *main_cmds,
194  struct cmdnames *other_cmds)
195 {
196  unsigned int i, longest = 0;
197 
198  for (i = 0; i < main_cmds->cnt; i++)
199  if (longest < main_cmds->names[i]->len)
200  longest = main_cmds->names[i]->len;
201  for (i = 0; i < other_cmds->cnt; i++)
202  if (longest < other_cmds->names[i]->len)
203  longest = other_cmds->names[i]->len;
204 
205  if (main_cmds->cnt) {
206  const char *exec_path = perf_exec_path();
207  printf("available %s in '%s'\n", title, exec_path);
208  printf("----------------");
209  mput_char('-', strlen(title) + strlen(exec_path));
210  putchar('\n');
211  pretty_print_string_list(main_cmds, longest);
212  putchar('\n');
213  }
214 
215  if (other_cmds->cnt) {
216  printf("%s available from elsewhere on your $PATH\n", title);
217  printf("---------------------------------------");
218  mput_char('-', strlen(title));
219  putchar('\n');
220  pretty_print_string_list(other_cmds, longest);
221  putchar('\n');
222  }
223 }
224 
225 int is_in_cmdlist(struct cmdnames *c, const char *s)
226 {
227  unsigned int i;
228 
229  for (i = 0; i < c->cnt; i++)
230  if (!strcmp(s, c->names[i]->name))
231  return 1;
232  return 0;
233 }
234 
235 static int autocorrect;
236 static struct cmdnames aliases;
237 
238 static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
239 {
240  if (!strcmp(var, "help.autocorrect"))
241  autocorrect = perf_config_int(var,value);
242  /* Also use aliases for command lookup */
243  if (!prefixcmp(var, "alias."))
244  add_cmdname(&aliases, var + 6, strlen(var + 6));
245 
246  return perf_default_config(var, value, cb);
247 }
248 
249 static int levenshtein_compare(const void *p1, const void *p2)
250 {
251  const struct cmdname *const *c1 = p1, *const *c2 = p2;
252  const char *s1 = (*c1)->name, *s2 = (*c2)->name;
253  int l1 = (*c1)->len;
254  int l2 = (*c2)->len;
255  return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
256 }
257 
258 static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
259 {
260  unsigned int i;
261 
262  ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
263 
264  for (i = 0; i < old->cnt; i++)
265  cmds->names[cmds->cnt++] = old->names[i];
266  free(old->names);
267  old->cnt = 0;
268  old->names = NULL;
269 }
270 
271 const char *help_unknown_cmd(const char *cmd)
272 {
273  unsigned int i, n = 0, best_similarity = 0;
274  struct cmdnames main_cmds, other_cmds;
275 
276  memset(&main_cmds, 0, sizeof(main_cmds));
277  memset(&other_cmds, 0, sizeof(main_cmds));
278  memset(&aliases, 0, sizeof(aliases));
279 
280  perf_config(perf_unknown_cmd_config, NULL);
281 
282  load_command_list("perf-", &main_cmds, &other_cmds);
283 
284  add_cmd_list(&main_cmds, &aliases);
285  add_cmd_list(&main_cmds, &other_cmds);
286  qsort(main_cmds.names, main_cmds.cnt,
287  sizeof(main_cmds.names), cmdname_compare);
288  uniq(&main_cmds);
289 
290  if (main_cmds.cnt) {
291  /* This reuses cmdname->len for similarity index */
292  for (i = 0; i < main_cmds.cnt; ++i)
293  main_cmds.names[i]->len =
294  levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
295 
296  qsort(main_cmds.names, main_cmds.cnt,
297  sizeof(*main_cmds.names), levenshtein_compare);
298 
299  best_similarity = main_cmds.names[0]->len;
300  n = 1;
301  while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
302  ++n;
303  }
304 
305  if (autocorrect && n == 1) {
306  const char *assumed = main_cmds.names[0]->name;
307 
308  main_cmds.names[0] = NULL;
309  clean_cmdnames(&main_cmds);
310  fprintf(stderr, "WARNING: You called a perf program named '%s', "
311  "which does not exist.\n"
312  "Continuing under the assumption that you meant '%s'\n",
313  cmd, assumed);
314  if (autocorrect > 0) {
315  fprintf(stderr, "in %0.1f seconds automatically...\n",
316  (float)autocorrect/10.0);
317  poll(NULL, 0, autocorrect * 100);
318  }
319  return assumed;
320  }
321 
322  fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
323 
324  if (main_cmds.cnt && best_similarity < 6) {
325  fprintf(stderr, "\nDid you mean %s?\n",
326  n < 2 ? "this": "one of these");
327 
328  for (i = 0; i < n; i++)
329  fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
330  }
331 
332  exit(1);
333 }
334 
335 int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused,
336  const char *prefix __maybe_unused)
337 {
338  printf("perf version %s\n", perf_version_string);
339  return 0;
340 }