Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/tools/perf/builtin-help.c
10820 views
1
/*
2
* builtin-help.c
3
*
4
* Builtin help command
5
*/
6
#include "perf.h"
7
#include "util/cache.h"
8
#include "builtin.h"
9
#include "util/exec_cmd.h"
10
#include "common-cmds.h"
11
#include "util/parse-options.h"
12
#include "util/run-command.h"
13
#include "util/help.h"
14
15
static struct man_viewer_list {
16
struct man_viewer_list *next;
17
char name[FLEX_ARRAY];
18
} *man_viewer_list;
19
20
static struct man_viewer_info_list {
21
struct man_viewer_info_list *next;
22
const char *info;
23
char name[FLEX_ARRAY];
24
} *man_viewer_info_list;
25
26
enum help_format {
27
HELP_FORMAT_MAN,
28
HELP_FORMAT_INFO,
29
HELP_FORMAT_WEB,
30
};
31
32
static bool show_all = false;
33
static enum help_format help_format = HELP_FORMAT_MAN;
34
static struct option builtin_help_options[] = {
35
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
36
OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
37
OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
38
HELP_FORMAT_WEB),
39
OPT_SET_UINT('i', "info", &help_format, "show info page",
40
HELP_FORMAT_INFO),
41
OPT_END(),
42
};
43
44
static const char * const builtin_help_usage[] = {
45
"perf help [--all] [--man|--web|--info] [command]",
46
NULL
47
};
48
49
static enum help_format parse_help_format(const char *format)
50
{
51
if (!strcmp(format, "man"))
52
return HELP_FORMAT_MAN;
53
if (!strcmp(format, "info"))
54
return HELP_FORMAT_INFO;
55
if (!strcmp(format, "web") || !strcmp(format, "html"))
56
return HELP_FORMAT_WEB;
57
die("unrecognized help format '%s'", format);
58
}
59
60
static const char *get_man_viewer_info(const char *name)
61
{
62
struct man_viewer_info_list *viewer;
63
64
for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) {
65
if (!strcasecmp(name, viewer->name))
66
return viewer->info;
67
}
68
return NULL;
69
}
70
71
static int check_emacsclient_version(void)
72
{
73
struct strbuf buffer = STRBUF_INIT;
74
struct child_process ec_process;
75
const char *argv_ec[] = { "emacsclient", "--version", NULL };
76
int version;
77
78
/* emacsclient prints its version number on stderr */
79
memset(&ec_process, 0, sizeof(ec_process));
80
ec_process.argv = argv_ec;
81
ec_process.err = -1;
82
ec_process.stdout_to_stderr = 1;
83
if (start_command(&ec_process)) {
84
fprintf(stderr, "Failed to start emacsclient.\n");
85
return -1;
86
}
87
strbuf_read(&buffer, ec_process.err, 20);
88
close(ec_process.err);
89
90
/*
91
* Don't bother checking return value, because "emacsclient --version"
92
* seems to always exits with code 1.
93
*/
94
finish_command(&ec_process);
95
96
if (prefixcmp(buffer.buf, "emacsclient")) {
97
fprintf(stderr, "Failed to parse emacsclient version.\n");
98
strbuf_release(&buffer);
99
return -1;
100
}
101
102
strbuf_remove(&buffer, 0, strlen("emacsclient"));
103
version = atoi(buffer.buf);
104
105
if (version < 22) {
106
fprintf(stderr,
107
"emacsclient version '%d' too old (< 22).\n",
108
version);
109
strbuf_release(&buffer);
110
return -1;
111
}
112
113
strbuf_release(&buffer);
114
return 0;
115
}
116
117
static void exec_woman_emacs(const char *path, const char *page)
118
{
119
if (!check_emacsclient_version()) {
120
/* This works only with emacsclient version >= 22. */
121
struct strbuf man_page = STRBUF_INIT;
122
123
if (!path)
124
path = "emacsclient";
125
strbuf_addf(&man_page, "(woman \"%s\")", page);
126
execlp(path, "emacsclient", "-e", man_page.buf, NULL);
127
warning("failed to exec '%s': %s", path, strerror(errno));
128
}
129
}
130
131
static void exec_man_konqueror(const char *path, const char *page)
132
{
133
const char *display = getenv("DISPLAY");
134
if (display && *display) {
135
struct strbuf man_page = STRBUF_INIT;
136
const char *filename = "kfmclient";
137
138
/* It's simpler to launch konqueror using kfmclient. */
139
if (path) {
140
const char *file = strrchr(path, '/');
141
if (file && !strcmp(file + 1, "konqueror")) {
142
char *new = strdup(path);
143
char *dest = strrchr(new, '/');
144
145
/* strlen("konqueror") == strlen("kfmclient") */
146
strcpy(dest + 1, "kfmclient");
147
path = new;
148
}
149
if (file)
150
filename = file;
151
} else
152
path = "kfmclient";
153
strbuf_addf(&man_page, "man:%s(1)", page);
154
execlp(path, filename, "newTab", man_page.buf, NULL);
155
warning("failed to exec '%s': %s", path, strerror(errno));
156
}
157
}
158
159
static void exec_man_man(const char *path, const char *page)
160
{
161
if (!path)
162
path = "man";
163
execlp(path, "man", page, NULL);
164
warning("failed to exec '%s': %s", path, strerror(errno));
165
}
166
167
static void exec_man_cmd(const char *cmd, const char *page)
168
{
169
struct strbuf shell_cmd = STRBUF_INIT;
170
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
171
execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
172
warning("failed to exec '%s': %s", cmd, strerror(errno));
173
}
174
175
static void add_man_viewer(const char *name)
176
{
177
struct man_viewer_list **p = &man_viewer_list;
178
size_t len = strlen(name);
179
180
while (*p)
181
p = &((*p)->next);
182
*p = zalloc(sizeof(**p) + len + 1);
183
strncpy((*p)->name, name, len);
184
}
185
186
static int supported_man_viewer(const char *name, size_t len)
187
{
188
return (!strncasecmp("man", name, len) ||
189
!strncasecmp("woman", name, len) ||
190
!strncasecmp("konqueror", name, len));
191
}
192
193
static void do_add_man_viewer_info(const char *name,
194
size_t len,
195
const char *value)
196
{
197
struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
198
199
strncpy(new->name, name, len);
200
new->info = strdup(value);
201
new->next = man_viewer_info_list;
202
man_viewer_info_list = new;
203
}
204
205
static int add_man_viewer_path(const char *name,
206
size_t len,
207
const char *value)
208
{
209
if (supported_man_viewer(name, len))
210
do_add_man_viewer_info(name, len, value);
211
else
212
warning("'%s': path for unsupported man viewer.\n"
213
"Please consider using 'man.<tool>.cmd' instead.",
214
name);
215
216
return 0;
217
}
218
219
static int add_man_viewer_cmd(const char *name,
220
size_t len,
221
const char *value)
222
{
223
if (supported_man_viewer(name, len))
224
warning("'%s': cmd for supported man viewer.\n"
225
"Please consider using 'man.<tool>.path' instead.",
226
name);
227
else
228
do_add_man_viewer_info(name, len, value);
229
230
return 0;
231
}
232
233
static int add_man_viewer_info(const char *var, const char *value)
234
{
235
const char *name = var + 4;
236
const char *subkey = strrchr(name, '.');
237
238
if (!subkey)
239
return error("Config with no key for man viewer: %s", name);
240
241
if (!strcmp(subkey, ".path")) {
242
if (!value)
243
return config_error_nonbool(var);
244
return add_man_viewer_path(name, subkey - name, value);
245
}
246
if (!strcmp(subkey, ".cmd")) {
247
if (!value)
248
return config_error_nonbool(var);
249
return add_man_viewer_cmd(name, subkey - name, value);
250
}
251
252
warning("'%s': unsupported man viewer sub key.", subkey);
253
return 0;
254
}
255
256
static int perf_help_config(const char *var, const char *value, void *cb)
257
{
258
if (!strcmp(var, "help.format")) {
259
if (!value)
260
return config_error_nonbool(var);
261
help_format = parse_help_format(value);
262
return 0;
263
}
264
if (!strcmp(var, "man.viewer")) {
265
if (!value)
266
return config_error_nonbool(var);
267
add_man_viewer(value);
268
return 0;
269
}
270
if (!prefixcmp(var, "man."))
271
return add_man_viewer_info(var, value);
272
273
return perf_default_config(var, value, cb);
274
}
275
276
static struct cmdnames main_cmds, other_cmds;
277
278
void list_common_cmds_help(void)
279
{
280
unsigned int i, longest = 0;
281
282
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
283
if (longest < strlen(common_cmds[i].name))
284
longest = strlen(common_cmds[i].name);
285
}
286
287
puts(" The most commonly used perf commands are:");
288
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
289
printf(" %-*s ", longest, common_cmds[i].name);
290
puts(common_cmds[i].help);
291
}
292
}
293
294
static int is_perf_command(const char *s)
295
{
296
return is_in_cmdlist(&main_cmds, s) ||
297
is_in_cmdlist(&other_cmds, s);
298
}
299
300
static const char *prepend(const char *prefix, const char *cmd)
301
{
302
size_t pre_len = strlen(prefix);
303
size_t cmd_len = strlen(cmd);
304
char *p = malloc(pre_len + cmd_len + 1);
305
memcpy(p, prefix, pre_len);
306
strcpy(p + pre_len, cmd);
307
return p;
308
}
309
310
static const char *cmd_to_page(const char *perf_cmd)
311
{
312
if (!perf_cmd)
313
return "perf";
314
else if (!prefixcmp(perf_cmd, "perf"))
315
return perf_cmd;
316
else
317
return prepend("perf-", perf_cmd);
318
}
319
320
static void setup_man_path(void)
321
{
322
struct strbuf new_path = STRBUF_INIT;
323
const char *old_path = getenv("MANPATH");
324
325
/* We should always put ':' after our path. If there is no
326
* old_path, the ':' at the end will let 'man' to try
327
* system-wide paths after ours to find the manual page. If
328
* there is old_path, we need ':' as delimiter. */
329
strbuf_addstr(&new_path, system_path(PERF_MAN_PATH));
330
strbuf_addch(&new_path, ':');
331
if (old_path)
332
strbuf_addstr(&new_path, old_path);
333
334
setenv("MANPATH", new_path.buf, 1);
335
336
strbuf_release(&new_path);
337
}
338
339
static void exec_viewer(const char *name, const char *page)
340
{
341
const char *info = get_man_viewer_info(name);
342
343
if (!strcasecmp(name, "man"))
344
exec_man_man(info, page);
345
else if (!strcasecmp(name, "woman"))
346
exec_woman_emacs(info, page);
347
else if (!strcasecmp(name, "konqueror"))
348
exec_man_konqueror(info, page);
349
else if (info)
350
exec_man_cmd(info, page);
351
else
352
warning("'%s': unknown man viewer.", name);
353
}
354
355
static void show_man_page(const char *perf_cmd)
356
{
357
struct man_viewer_list *viewer;
358
const char *page = cmd_to_page(perf_cmd);
359
const char *fallback = getenv("PERF_MAN_VIEWER");
360
361
setup_man_path();
362
for (viewer = man_viewer_list; viewer; viewer = viewer->next)
363
exec_viewer(viewer->name, page); /* will return when unable */
364
365
if (fallback)
366
exec_viewer(fallback, page);
367
exec_viewer("man", page);
368
die("no man viewer handled the request");
369
}
370
371
static void show_info_page(const char *perf_cmd)
372
{
373
const char *page = cmd_to_page(perf_cmd);
374
setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
375
execlp("info", "info", "perfman", page, NULL);
376
}
377
378
static void get_html_page_path(struct strbuf *page_path, const char *page)
379
{
380
struct stat st;
381
const char *html_path = system_path(PERF_HTML_PATH);
382
383
/* Check that we have a perf documentation directory. */
384
if (stat(mkpath("%s/perf.html", html_path), &st)
385
|| !S_ISREG(st.st_mode))
386
die("'%s': not a documentation directory.", html_path);
387
388
strbuf_init(page_path, 0);
389
strbuf_addf(page_path, "%s/%s.html", html_path, page);
390
}
391
392
/*
393
* If open_html is not defined in a platform-specific way (see for
394
* example compat/mingw.h), we use the script web--browse to display
395
* HTML.
396
*/
397
#ifndef open_html
398
static void open_html(const char *path)
399
{
400
execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
401
}
402
#endif
403
404
static void show_html_page(const char *perf_cmd)
405
{
406
const char *page = cmd_to_page(perf_cmd);
407
struct strbuf page_path; /* it leaks but we exec bellow */
408
409
get_html_page_path(&page_path, page);
410
411
open_html(page_path.buf);
412
}
413
414
int cmd_help(int argc, const char **argv, const char *prefix __used)
415
{
416
const char *alias;
417
418
load_command_list("perf-", &main_cmds, &other_cmds);
419
420
perf_config(perf_help_config, NULL);
421
422
argc = parse_options(argc, argv, builtin_help_options,
423
builtin_help_usage, 0);
424
425
if (show_all) {
426
printf("\n usage: %s\n\n", perf_usage_string);
427
list_commands("perf commands", &main_cmds, &other_cmds);
428
printf(" %s\n\n", perf_more_info_string);
429
return 0;
430
}
431
432
if (!argv[0]) {
433
printf("\n usage: %s\n\n", perf_usage_string);
434
list_common_cmds_help();
435
printf("\n %s\n\n", perf_more_info_string);
436
return 0;
437
}
438
439
alias = alias_lookup(argv[0]);
440
if (alias && !is_perf_command(argv[0])) {
441
printf("`perf %s' is aliased to `%s'\n", argv[0], alias);
442
return 0;
443
}
444
445
switch (help_format) {
446
case HELP_FORMAT_MAN:
447
show_man_page(argv[0]);
448
break;
449
case HELP_FORMAT_INFO:
450
show_info_page(argv[0]);
451
break;
452
case HELP_FORMAT_WEB:
453
show_html_page(argv[0]);
454
default:
455
break;
456
}
457
458
return 0;
459
}
460
461