18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * builtin-help.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Builtin help command 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include "util/cache.h" 88c2ecf20Sopenharmony_ci#include "util/config.h" 98c2ecf20Sopenharmony_ci#include "util/strbuf.h" 108c2ecf20Sopenharmony_ci#include "builtin.h" 118c2ecf20Sopenharmony_ci#include <subcmd/exec-cmd.h> 128c2ecf20Sopenharmony_ci#include "common-cmds.h" 138c2ecf20Sopenharmony_ci#include <subcmd/parse-options.h> 148c2ecf20Sopenharmony_ci#include <subcmd/run-command.h> 158c2ecf20Sopenharmony_ci#include <subcmd/help.h> 168c2ecf20Sopenharmony_ci#include "util/debug.h" 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 208c2ecf20Sopenharmony_ci#include <errno.h> 218c2ecf20Sopenharmony_ci#include <stdio.h> 228c2ecf20Sopenharmony_ci#include <stdlib.h> 238c2ecf20Sopenharmony_ci#include <string.h> 248c2ecf20Sopenharmony_ci#include <sys/types.h> 258c2ecf20Sopenharmony_ci#include <sys/stat.h> 268c2ecf20Sopenharmony_ci#include <unistd.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct man_viewer_list { 298c2ecf20Sopenharmony_ci struct man_viewer_list *next; 308c2ecf20Sopenharmony_ci char name[0]; 318c2ecf20Sopenharmony_ci} *man_viewer_list; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct man_viewer_info_list { 348c2ecf20Sopenharmony_ci struct man_viewer_info_list *next; 358c2ecf20Sopenharmony_ci const char *info; 368c2ecf20Sopenharmony_ci char name[0]; 378c2ecf20Sopenharmony_ci} *man_viewer_info_list; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cienum help_format { 408c2ecf20Sopenharmony_ci HELP_FORMAT_NONE, 418c2ecf20Sopenharmony_ci HELP_FORMAT_MAN, 428c2ecf20Sopenharmony_ci HELP_FORMAT_INFO, 438c2ecf20Sopenharmony_ci HELP_FORMAT_WEB, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic enum help_format parse_help_format(const char *format) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci if (!strcmp(format, "man")) 498c2ecf20Sopenharmony_ci return HELP_FORMAT_MAN; 508c2ecf20Sopenharmony_ci if (!strcmp(format, "info")) 518c2ecf20Sopenharmony_ci return HELP_FORMAT_INFO; 528c2ecf20Sopenharmony_ci if (!strcmp(format, "web") || !strcmp(format, "html")) 538c2ecf20Sopenharmony_ci return HELP_FORMAT_WEB; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci pr_err("unrecognized help format '%s'", format); 568c2ecf20Sopenharmony_ci return HELP_FORMAT_NONE; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const char *get_man_viewer_info(const char *name) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct man_viewer_info_list *viewer; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) { 648c2ecf20Sopenharmony_ci if (!strcasecmp(name, viewer->name)) 658c2ecf20Sopenharmony_ci return viewer->info; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci return NULL; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int check_emacsclient_version(void) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct strbuf buffer = STRBUF_INIT; 738c2ecf20Sopenharmony_ci struct child_process ec_process; 748c2ecf20Sopenharmony_ci const char *argv_ec[] = { "emacsclient", "--version", NULL }; 758c2ecf20Sopenharmony_ci int version; 768c2ecf20Sopenharmony_ci int ret = -1; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* emacsclient prints its version number on stderr */ 798c2ecf20Sopenharmony_ci memset(&ec_process, 0, sizeof(ec_process)); 808c2ecf20Sopenharmony_ci ec_process.argv = argv_ec; 818c2ecf20Sopenharmony_ci ec_process.err = -1; 828c2ecf20Sopenharmony_ci ec_process.stdout_to_stderr = 1; 838c2ecf20Sopenharmony_ci if (start_command(&ec_process)) { 848c2ecf20Sopenharmony_ci fprintf(stderr, "Failed to start emacsclient.\n"); 858c2ecf20Sopenharmony_ci return -1; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci if (strbuf_read(&buffer, ec_process.err, 20) < 0) { 888c2ecf20Sopenharmony_ci fprintf(stderr, "Failed to read emacsclient version\n"); 898c2ecf20Sopenharmony_ci goto out; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci close(ec_process.err); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Don't bother checking return value, because "emacsclient --version" 958c2ecf20Sopenharmony_ci * seems to always exits with code 1. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci finish_command(&ec_process); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!strstarts(buffer.buf, "emacsclient")) { 1008c2ecf20Sopenharmony_ci fprintf(stderr, "Failed to parse emacsclient version.\n"); 1018c2ecf20Sopenharmony_ci goto out; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci version = atoi(buffer.buf + strlen("emacsclient")); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (version < 22) { 1078c2ecf20Sopenharmony_ci fprintf(stderr, 1088c2ecf20Sopenharmony_ci "emacsclient version '%d' too old (< 22).\n", 1098c2ecf20Sopenharmony_ci version); 1108c2ecf20Sopenharmony_ci } else 1118c2ecf20Sopenharmony_ci ret = 0; 1128c2ecf20Sopenharmony_ciout: 1138c2ecf20Sopenharmony_ci strbuf_release(&buffer); 1148c2ecf20Sopenharmony_ci return ret; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void exec_failed(const char *cmd) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 1208c2ecf20Sopenharmony_ci pr_warning("failed to exec '%s': %s", cmd, str_error_r(errno, sbuf, sizeof(sbuf))); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void exec_woman_emacs(const char *path, const char *page) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci if (!check_emacsclient_version()) { 1268c2ecf20Sopenharmony_ci /* This works only with emacsclient version >= 22. */ 1278c2ecf20Sopenharmony_ci char *man_page; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!path) 1308c2ecf20Sopenharmony_ci path = "emacsclient"; 1318c2ecf20Sopenharmony_ci if (asprintf(&man_page, "(woman \"%s\")", page) > 0) { 1328c2ecf20Sopenharmony_ci execlp(path, "emacsclient", "-e", man_page, NULL); 1338c2ecf20Sopenharmony_ci free(man_page); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci exec_failed(path); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void exec_man_konqueror(const char *path, const char *page) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci const char *display = getenv("DISPLAY"); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (display && *display) { 1448c2ecf20Sopenharmony_ci char *man_page; 1458c2ecf20Sopenharmony_ci const char *filename = "kfmclient"; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* It's simpler to launch konqueror using kfmclient. */ 1488c2ecf20Sopenharmony_ci if (path) { 1498c2ecf20Sopenharmony_ci const char *file = strrchr(path, '/'); 1508c2ecf20Sopenharmony_ci if (file && !strcmp(file + 1, "konqueror")) { 1518c2ecf20Sopenharmony_ci char *new = strdup(path); 1528c2ecf20Sopenharmony_ci char *dest = strrchr(new, '/'); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* strlen("konqueror") == strlen("kfmclient") */ 1558c2ecf20Sopenharmony_ci strcpy(dest + 1, "kfmclient"); 1568c2ecf20Sopenharmony_ci path = new; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci if (file) 1598c2ecf20Sopenharmony_ci filename = file; 1608c2ecf20Sopenharmony_ci } else 1618c2ecf20Sopenharmony_ci path = "kfmclient"; 1628c2ecf20Sopenharmony_ci if (asprintf(&man_page, "man:%s(1)", page) > 0) { 1638c2ecf20Sopenharmony_ci execlp(path, filename, "newTab", man_page, NULL); 1648c2ecf20Sopenharmony_ci free(man_page); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci exec_failed(path); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void exec_man_man(const char *path, const char *page) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci if (!path) 1738c2ecf20Sopenharmony_ci path = "man"; 1748c2ecf20Sopenharmony_ci execlp(path, "man", page, NULL); 1758c2ecf20Sopenharmony_ci exec_failed(path); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void exec_man_cmd(const char *cmd, const char *page) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci char *shell_cmd; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (asprintf(&shell_cmd, "%s %s", cmd, page) > 0) { 1838c2ecf20Sopenharmony_ci execl("/bin/sh", "sh", "-c", shell_cmd, NULL); 1848c2ecf20Sopenharmony_ci free(shell_cmd); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci exec_failed(cmd); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void add_man_viewer(const char *name) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct man_viewer_list **p = &man_viewer_list; 1928c2ecf20Sopenharmony_ci size_t len = strlen(name); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci while (*p) 1958c2ecf20Sopenharmony_ci p = &((*p)->next); 1968c2ecf20Sopenharmony_ci *p = zalloc(sizeof(**p) + len + 1); 1978c2ecf20Sopenharmony_ci strcpy((*p)->name, name); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int supported_man_viewer(const char *name, size_t len) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci return (!strncasecmp("man", name, len) || 2038c2ecf20Sopenharmony_ci !strncasecmp("woman", name, len) || 2048c2ecf20Sopenharmony_ci !strncasecmp("konqueror", name, len)); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void do_add_man_viewer_info(const char *name, 2088c2ecf20Sopenharmony_ci size_t len, 2098c2ecf20Sopenharmony_ci const char *value) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci strncpy(new->name, name, len); 2148c2ecf20Sopenharmony_ci new->info = strdup(value); 2158c2ecf20Sopenharmony_ci new->next = man_viewer_info_list; 2168c2ecf20Sopenharmony_ci man_viewer_info_list = new; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void unsupported_man_viewer(const char *name, const char *var) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci pr_warning("'%s': path for unsupported man viewer.\n" 2228c2ecf20Sopenharmony_ci "Please consider using 'man.<tool>.%s' instead.", name, var); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int add_man_viewer_path(const char *name, 2268c2ecf20Sopenharmony_ci size_t len, 2278c2ecf20Sopenharmony_ci const char *value) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (supported_man_viewer(name, len)) 2308c2ecf20Sopenharmony_ci do_add_man_viewer_info(name, len, value); 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci unsupported_man_viewer(name, "cmd"); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int add_man_viewer_cmd(const char *name, 2388c2ecf20Sopenharmony_ci size_t len, 2398c2ecf20Sopenharmony_ci const char *value) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci if (supported_man_viewer(name, len)) 2428c2ecf20Sopenharmony_ci unsupported_man_viewer(name, "path"); 2438c2ecf20Sopenharmony_ci else 2448c2ecf20Sopenharmony_ci do_add_man_viewer_info(name, len, value); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int add_man_viewer_info(const char *var, const char *value) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci const char *name = var + 4; 2528c2ecf20Sopenharmony_ci const char *subkey = strrchr(name, '.'); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!subkey) { 2558c2ecf20Sopenharmony_ci pr_err("Config with no key for man viewer: %s", name); 2568c2ecf20Sopenharmony_ci return -1; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!strcmp(subkey, ".path")) { 2608c2ecf20Sopenharmony_ci if (!value) 2618c2ecf20Sopenharmony_ci return config_error_nonbool(var); 2628c2ecf20Sopenharmony_ci return add_man_viewer_path(name, subkey - name, value); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci if (!strcmp(subkey, ".cmd")) { 2658c2ecf20Sopenharmony_ci if (!value) 2668c2ecf20Sopenharmony_ci return config_error_nonbool(var); 2678c2ecf20Sopenharmony_ci return add_man_viewer_cmd(name, subkey - name, value); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pr_warning("'%s': unsupported man viewer sub key.", subkey); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int perf_help_config(const char *var, const char *value, void *cb) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci enum help_format *help_formatp = cb; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!strcmp(var, "help.format")) { 2798c2ecf20Sopenharmony_ci if (!value) 2808c2ecf20Sopenharmony_ci return config_error_nonbool(var); 2818c2ecf20Sopenharmony_ci *help_formatp = parse_help_format(value); 2828c2ecf20Sopenharmony_ci if (*help_formatp == HELP_FORMAT_NONE) 2838c2ecf20Sopenharmony_ci return -1; 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci if (!strcmp(var, "man.viewer")) { 2878c2ecf20Sopenharmony_ci if (!value) 2888c2ecf20Sopenharmony_ci return config_error_nonbool(var); 2898c2ecf20Sopenharmony_ci add_man_viewer(value); 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci if (strstarts(var, "man.")) 2938c2ecf20Sopenharmony_ci return add_man_viewer_info(var, value); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct cmdnames main_cmds, other_cmds; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_civoid list_common_cmds_help(void) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci unsigned int i, longest = 0; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { 3058c2ecf20Sopenharmony_ci if (longest < strlen(common_cmds[i].name)) 3068c2ecf20Sopenharmony_ci longest = strlen(common_cmds[i].name); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci puts(" The most commonly used perf commands are:"); 3108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { 3118c2ecf20Sopenharmony_ci printf(" %-*s ", longest, common_cmds[i].name); 3128c2ecf20Sopenharmony_ci puts(common_cmds[i].help); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic const char *cmd_to_page(const char *perf_cmd) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci char *s; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!perf_cmd) 3218c2ecf20Sopenharmony_ci return "perf"; 3228c2ecf20Sopenharmony_ci else if (strstarts(perf_cmd, "perf")) 3238c2ecf20Sopenharmony_ci return perf_cmd; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return asprintf(&s, "perf-%s", perf_cmd) < 0 ? NULL : s; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void setup_man_path(void) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci char *new_path; 3318c2ecf20Sopenharmony_ci const char *old_path = getenv("MANPATH"); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* We should always put ':' after our path. If there is no 3348c2ecf20Sopenharmony_ci * old_path, the ':' at the end will let 'man' to try 3358c2ecf20Sopenharmony_ci * system-wide paths after ours to find the manual page. If 3368c2ecf20Sopenharmony_ci * there is old_path, we need ':' as delimiter. */ 3378c2ecf20Sopenharmony_ci if (asprintf(&new_path, "%s:%s", system_path(PERF_MAN_PATH), old_path ?: "") > 0) { 3388c2ecf20Sopenharmony_ci setenv("MANPATH", new_path, 1); 3398c2ecf20Sopenharmony_ci free(new_path); 3408c2ecf20Sopenharmony_ci } else { 3418c2ecf20Sopenharmony_ci pr_err("Unable to setup man path"); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void exec_viewer(const char *name, const char *page) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci const char *info = get_man_viewer_info(name); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!strcasecmp(name, "man")) 3508c2ecf20Sopenharmony_ci exec_man_man(info, page); 3518c2ecf20Sopenharmony_ci else if (!strcasecmp(name, "woman")) 3528c2ecf20Sopenharmony_ci exec_woman_emacs(info, page); 3538c2ecf20Sopenharmony_ci else if (!strcasecmp(name, "konqueror")) 3548c2ecf20Sopenharmony_ci exec_man_konqueror(info, page); 3558c2ecf20Sopenharmony_ci else if (info) 3568c2ecf20Sopenharmony_ci exec_man_cmd(info, page); 3578c2ecf20Sopenharmony_ci else 3588c2ecf20Sopenharmony_ci pr_warning("'%s': unknown man viewer.", name); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int show_man_page(const char *perf_cmd) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct man_viewer_list *viewer; 3648c2ecf20Sopenharmony_ci const char *page = cmd_to_page(perf_cmd); 3658c2ecf20Sopenharmony_ci const char *fallback = getenv("PERF_MAN_VIEWER"); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci setup_man_path(); 3688c2ecf20Sopenharmony_ci for (viewer = man_viewer_list; viewer; viewer = viewer->next) 3698c2ecf20Sopenharmony_ci exec_viewer(viewer->name, page); /* will return when unable */ 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (fallback) 3728c2ecf20Sopenharmony_ci exec_viewer(fallback, page); 3738c2ecf20Sopenharmony_ci exec_viewer("man", page); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci pr_err("no man viewer handled the request"); 3768c2ecf20Sopenharmony_ci return -1; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int show_info_page(const char *perf_cmd) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci const char *page = cmd_to_page(perf_cmd); 3828c2ecf20Sopenharmony_ci setenv("INFOPATH", system_path(PERF_INFO_PATH), 1); 3838c2ecf20Sopenharmony_ci execlp("info", "info", "perfman", page, NULL); 3848c2ecf20Sopenharmony_ci return -1; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int get_html_page_path(char **page_path, const char *page) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct stat st; 3908c2ecf20Sopenharmony_ci const char *html_path = system_path(PERF_HTML_PATH); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Check that we have a perf documentation directory. */ 3938c2ecf20Sopenharmony_ci if (stat(mkpath("%s/perf.html", html_path), &st) 3948c2ecf20Sopenharmony_ci || !S_ISREG(st.st_mode)) { 3958c2ecf20Sopenharmony_ci pr_err("'%s': not a documentation directory.", html_path); 3968c2ecf20Sopenharmony_ci return -1; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return asprintf(page_path, "%s/%s.html", html_path, page); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* 4038c2ecf20Sopenharmony_ci * If open_html is not defined in a platform-specific way (see for 4048c2ecf20Sopenharmony_ci * example compat/mingw.h), we use the script web--browse to display 4058c2ecf20Sopenharmony_ci * HTML. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci#ifndef open_html 4088c2ecf20Sopenharmony_cistatic void open_html(const char *path) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci execl_cmd("web--browse", "-c", "help.browser", path, NULL); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci#endif 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int show_html_page(const char *perf_cmd) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci const char *page = cmd_to_page(perf_cmd); 4178c2ecf20Sopenharmony_ci char *page_path; /* it leaks but we exec bellow */ 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (get_html_page_path(&page_path, page) < 0) 4208c2ecf20Sopenharmony_ci return -1; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci open_html(page_path); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciint cmd_help(int argc, const char **argv) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci bool show_all = false; 4308c2ecf20Sopenharmony_ci enum help_format help_format = HELP_FORMAT_MAN; 4318c2ecf20Sopenharmony_ci struct option builtin_help_options[] = { 4328c2ecf20Sopenharmony_ci OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), 4338c2ecf20Sopenharmony_ci OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), 4348c2ecf20Sopenharmony_ci OPT_SET_UINT('w', "web", &help_format, "show manual in web browser", 4358c2ecf20Sopenharmony_ci HELP_FORMAT_WEB), 4368c2ecf20Sopenharmony_ci OPT_SET_UINT('i', "info", &help_format, "show info page", 4378c2ecf20Sopenharmony_ci HELP_FORMAT_INFO), 4388c2ecf20Sopenharmony_ci OPT_END(), 4398c2ecf20Sopenharmony_ci }; 4408c2ecf20Sopenharmony_ci const char * const builtin_help_subcommands[] = { 4418c2ecf20Sopenharmony_ci "buildid-cache", "buildid-list", "diff", "evlist", "help", "list", 4428c2ecf20Sopenharmony_ci "record", "report", "bench", "stat", "timechart", "top", "annotate", 4438c2ecf20Sopenharmony_ci "script", "sched", "kallsyms", "kmem", "lock", "kvm", "test", "inject", "mem", "data", 4448c2ecf20Sopenharmony_ci#ifdef HAVE_LIBELF_SUPPORT 4458c2ecf20Sopenharmony_ci "probe", 4468c2ecf20Sopenharmony_ci#endif 4478c2ecf20Sopenharmony_ci#if defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT) 4488c2ecf20Sopenharmony_ci "trace", 4498c2ecf20Sopenharmony_ci#endif 4508c2ecf20Sopenharmony_ci NULL }; 4518c2ecf20Sopenharmony_ci const char *builtin_help_usage[] = { 4528c2ecf20Sopenharmony_ci "perf help [--all] [--man|--web|--info] [command]", 4538c2ecf20Sopenharmony_ci NULL 4548c2ecf20Sopenharmony_ci }; 4558c2ecf20Sopenharmony_ci int rc; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci load_command_list("perf-", &main_cmds, &other_cmds); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci rc = perf_config(perf_help_config, &help_format); 4608c2ecf20Sopenharmony_ci if (rc) 4618c2ecf20Sopenharmony_ci return rc; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci argc = parse_options_subcommand(argc, argv, builtin_help_options, 4648c2ecf20Sopenharmony_ci builtin_help_subcommands, builtin_help_usage, 0); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (show_all) { 4678c2ecf20Sopenharmony_ci printf("\n Usage: %s\n\n", perf_usage_string); 4688c2ecf20Sopenharmony_ci list_commands("perf commands", &main_cmds, &other_cmds); 4698c2ecf20Sopenharmony_ci printf(" %s\n\n", perf_more_info_string); 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (!argv[0]) { 4748c2ecf20Sopenharmony_ci printf("\n usage: %s\n\n", perf_usage_string); 4758c2ecf20Sopenharmony_ci list_common_cmds_help(); 4768c2ecf20Sopenharmony_ci printf("\n %s\n\n", perf_more_info_string); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci switch (help_format) { 4818c2ecf20Sopenharmony_ci case HELP_FORMAT_MAN: 4828c2ecf20Sopenharmony_ci rc = show_man_page(argv[0]); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case HELP_FORMAT_INFO: 4858c2ecf20Sopenharmony_ci rc = show_info_page(argv[0]); 4868c2ecf20Sopenharmony_ci break; 4878c2ecf20Sopenharmony_ci case HELP_FORMAT_WEB: 4888c2ecf20Sopenharmony_ci rc = show_html_page(argv[0]); 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci case HELP_FORMAT_NONE: 4918c2ecf20Sopenharmony_ci /* fall-through */ 4928c2ecf20Sopenharmony_ci default: 4938c2ecf20Sopenharmony_ci rc = -1; 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return rc; 4988c2ecf20Sopenharmony_ci} 499