@@ -8,6 +8,8 @@ AM_CPPFLAGS = \
-include $(top_builddir)/config.h \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DPREFIX=\""$(prefix)"\" \
+ -DNDCTL_MAN_PATH=\""$(mandir)"\" \
-I${top_srcdir}/lib/ndctl \
-I${top_srcdir}/lib \
-I${top_srcdir}/ \
@@ -65,6 +67,7 @@ ndctl_SOURCES = ndctl.c \
builtin-xable-namespace.c \
builtin-xable-region.c \
builtin-test.c \
+ builtin-help.c \
builtin-zero-labels.c \
util/parse-options.c \
util/parse-options.h \
new file mode 100644
@@ -0,0 +1,163 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <builtin.h>
+#include <util/strbuf.h>
+#include <util/parse-options.h>
+
+#define pr_err(x, ...) fprintf(stderr, x, ##__VA_ARGS__)
+#define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */
+
+static void exec_man_konqueror(const char *path, const char *page)
+{
+ const char *display = getenv("DISPLAY");
+
+ if (display && *display) {
+ struct strbuf man_page = STRBUF_INIT;
+ const char *filename = "kfmclient";
+ char sbuf[STRERR_BUFSIZE];
+
+ /* It's simpler to launch konqueror using kfmclient. */
+ if (path) {
+ const char *file = strrchr(path, '/');
+ if (file && !strcmp(file + 1, "konqueror")) {
+ char *new = strdup(path);
+ char *dest = strrchr(new, '/');
+
+ /* strlen("konqueror") == strlen("kfmclient") */
+ strcpy(dest + 1, "kfmclient");
+ path = new;
+ }
+ if (file)
+ filename = file;
+ } else
+ path = "kfmclient";
+ strbuf_addf(&man_page, "man:%s(1)", page);
+ execlp(path, filename, "newTab", man_page.buf, NULL);
+ warning("failed to exec '%s': %s", path,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
+ }
+}
+
+static void exec_man_man(const char *path, const char *page)
+{
+ char sbuf[STRERR_BUFSIZE];
+
+ if (!path)
+ path = "man";
+ execlp(path, "man", page, NULL);
+ warning("failed to exec '%s': %s", path,
+ strerror_r(errno, sbuf, sizeof(sbuf)));
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+ size_t pre_len = strlen(prefix);
+ size_t cmd_len = strlen(cmd);
+ char *p = malloc(pre_len + cmd_len + 1);
+ memcpy(p, prefix, pre_len);
+ strcpy(p + pre_len, cmd);
+ return p;
+}
+
+static const char *cmd_to_page(const char *ndctl_cmd)
+{
+ if (!ndctl_cmd)
+ return "ndctl";
+ else if (!prefixcmp(ndctl_cmd, "ndctl"))
+ return ndctl_cmd;
+ else
+ return prepend("ndctl-", ndctl_cmd);
+}
+
+static int is_absolute_path(const char *path)
+{
+ return path[0] == '/';
+}
+
+static const char *system_path(const char *path)
+{
+ static const char *prefix = PREFIX;
+ struct strbuf d = STRBUF_INIT;
+
+ if (is_absolute_path(path))
+ return path;
+
+ strbuf_addf(&d, "%s/%s", prefix, path);
+ path = strbuf_detach(&d, NULL);
+ return path;
+}
+
+static void setup_man_path(void)
+{
+ struct strbuf new_path = STRBUF_INIT;
+ const char *old_path = getenv("MANPATH");
+
+ /* We should always put ':' after our path. If there is no
+ * old_path, the ':' at the end will let 'man' to try
+ * system-wide paths after ours to find the manual page. If
+ * there is old_path, we need ':' as delimiter. */
+ strbuf_addstr(&new_path, system_path(NDCTL_MAN_PATH));
+ strbuf_addch(&new_path, ':');
+ if (old_path)
+ strbuf_addstr(&new_path, old_path);
+
+ setenv("MANPATH", new_path.buf, 1);
+
+ strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+ if (!strcasecmp(name, "man"))
+ exec_man_man(NULL, page);
+ else if (!strcasecmp(name, "konqueror"))
+ exec_man_konqueror(NULL, page);
+ else
+ warning("'%s': unknown man viewer.", name);
+}
+
+static int show_man_page(const char *ndctl_cmd)
+{
+ const char *page = cmd_to_page(ndctl_cmd);
+ const char *fallback = getenv("NDCTL_MAN_VIEWER");
+
+ setup_man_path();
+ if (fallback)
+ exec_viewer(fallback, page);
+ exec_viewer("man", page);
+
+ pr_err("no man viewer handled the request");
+ return -1;
+}
+
+int cmd_help(int argc, const char **argv)
+{
+ const char * const builtin_help_subcommands[] = {
+ "enable-region", "disable-region", "zero-labels",
+ "enable-namespace", "disable-namespace", NULL };
+ struct option builtin_help_options[] = {
+ OPT_END(),
+ };
+ const char *builtin_help_usage[] = {
+ "ndctl help [command]",
+ NULL
+ };
+
+ argc = parse_options_subcommand(argc, argv, builtin_help_options,
+ builtin_help_subcommands, builtin_help_usage, 0);
+
+ if (!argv[0]) {
+ printf("\n usage: %s\n\n", ndctl_usage_string);
+ printf("\n %s\n\n", ndctl_more_info_string);
+ return 0;
+ }
+
+ return show_man_page(argv[0]);
+}
new file mode 100644
@@ -0,0 +1,26 @@
+#ifndef _NDCTL_BUILTIN_H_
+#define _NDCTL_BUILTIN_H_
+extern const char ndctl_usage_string[];
+extern const char ndctl_more_info_string[];
+
+struct cmd_struct {
+ const char *cmd;
+ int (*fn)(int, const char **);
+};
+
+int cmd_create_nfit(int argc, const char **argv);
+int cmd_enable_namespace(int argc, const char **argv);
+int cmd_disable_namespace(int argc, const char **argv);
+int cmd_enable_region(int argc, const char **argv);
+int cmd_disable_region(int argc, const char **argv);
+int cmd_zero_labels(int argc, const char **argv);
+int cmd_help(int argc, const char **argv);
+#ifdef ENABLE_TEST
+int cmd_test(int argc, const char **argv);
+#endif
+#ifdef ENABLE_DESTRUCTIVE
+int cmd_bat(int argc, const char **argv);
+#endif
+
+#endif /* _NDCTL_BUILTIN_H_ */
+
@@ -5,46 +5,22 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <builtin.h>
#include <ccan/array_size/array_size.h>
#include <util/strbuf.h>
#include <util/util.h>
-static const char *ndctl_usage_string;
-
-static const char ndctl_more_info_string[] =
+const char ndctl_usage_string[] = "ndctl [--version] [--help] COMMAND [ARGS]";
+const char ndctl_more_info_string[] =
"See 'ndctl help COMMAND' for more information on a specific command.";
-struct cmd_struct {
- const char *cmd;
- int (*fn)(int, const char **);
-};
-
static int cmd_version(int argc, const char **argv)
{
printf("%s\n", VERSION);
return 0;
}
-static int cmd_help(int argc, const char **argv)
-{
- printf("\n%s\n\n", ndctl_usage_string);
- return 0;
-}
-
-int cmd_create_nfit(int argc, const char **argv);
-int cmd_enable_namespace(int argc, const char **argv);
-int cmd_disable_namespace(int argc, const char **argv);
-int cmd_enable_region(int argc, const char **argv);
-int cmd_disable_region(int argc, const char **argv);
-int cmd_zero_labels(int argc, const char **argv);
-#ifdef ENABLE_TEST
-int cmd_test(int argc, const char **argv);
-#endif
-#ifdef ENABLE_DESTRUCTIVE
-int cmd_bat(int argc, const char **argv);
-#endif
-
static struct cmd_struct commands[] = {
{ "version", cmd_version },
{ "create-nfit", cmd_create_nfit },
@@ -53,6 +29,7 @@ static struct cmd_struct commands[] = {
{ "enable-region", cmd_enable_region },
{ "disable-region", cmd_disable_region },
{ "zero-labels", cmd_zero_labels },
+ { "help", cmd_help },
#ifdef ENABLE_TEST
{ "test", cmd_test },
#endif
@@ -61,29 +38,6 @@ static struct cmd_struct commands[] = {
#endif
};
-/* place holder until help system is implemented */
-static char *init_usage_string(void)
-{
- char *def = "ndctl [--version] [--help] COMMAND [ARGS]";
- unsigned int len = strlen(def) + 1, i, p;
- char *u;
-
- for (i = 0; i < ARRAY_SIZE(commands); i++)
- len += strlen(commands[i].cmd) + 2;
- u = calloc(1, len);
- if (!u)
- return def;
-
- p = sprintf(u, "%s", "ndctl [--version] [--help] ");
- for (i = 0; i < ARRAY_SIZE(commands); i++) {
- p += sprintf(&u[p], "%s", commands[i].cmd);
- if ((i + 1) < ARRAY_SIZE(commands))
- p += sprintf(&u[p], "|");
- }
- p = sprintf(&u[p], "%s", " [ARGS]");
- return u;
-}
-
static int handle_options(const char ***argv, int *argc)
{
int handled = 0;
@@ -93,11 +47,22 @@ static int handle_options(const char ***argv, int *argc)
if (cmd[0] != '-')
break;
- if (!strcmp(cmd, "--help"))
- exit(cmd_help(*argc, *argv));
+ if (!strcmp(cmd, "--version") || !strcmp(cmd, "--help"))
+ break;
- if (!strcmp(cmd, "--version"))
+ /*
+ * Shortcut for '-h' and '-v' options to invoke help
+ * and version command.
+ */
+ if (!strcmp(cmd, "-h")) {
+ (*argv)[0] = "--help";
break;
+ }
+
+ if (!strcmp(cmd, "-v")) {
+ (*argv)[0] = "--version";
+ break;
+ }
if (!strcmp(cmd, "--list-cmds")) {
unsigned int i;
@@ -160,6 +125,12 @@ static void handle_internal_command(int argc, const char **argv)
const char *cmd = argv[0];
unsigned int i;
+ /* Turn "ndctl cmd --help" into "ndctl help cmd" */
+ if (argc > 1 && !strcmp(argv[1], "--help")) {
+ argv[1] = argv[0];
+ argv[0] = cmd = "help";
+ }
+
for (i = 0; i < ARRAY_SIZE(commands); i++) {
struct cmd_struct *p = commands+i;
if (strcmp(p->cmd, cmd))
@@ -170,8 +141,6 @@ static void handle_internal_command(int argc, const char **argv)
int main(int argc, const char **argv)
{
- ndctl_usage_string = init_usage_string();
-
/* Look for flags.. */
argv++;
argc--;
@@ -183,7 +152,7 @@ int main(int argc, const char **argv)
} else {
/* The user didn't specify a command; give them help */
printf("\n usage: %s\n\n", ndctl_usage_string);
- /* printf("\n %s\n\n", ndctl_more_info_string); TODO */
+ printf("\n %s\n\n", ndctl_more_info_string);
goto out;
}
handle_internal_command(argc, argv);
Route "ndctl <command> --help" to "man ndctl-<command>" Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Makefile.am | 3 + builtin-help.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 26 +++++++++ ndctl.c | 81 +++++++++------------------- 4 files changed, 217 insertions(+), 56 deletions(-) create mode 100644 builtin-help.c create mode 100644 builtin.h