Update draft release notes for 1.5.5
[gitweb.git] / help.c
diff --git a/help.c b/help.c
index 5feb8491b96746e5b89edd4c04aaea72de933204..ecaca770d398e8068ec18e9f4c5058044001d4ea 100644 (file)
--- a/help.c
+++ b/help.c
@@ -8,6 +8,12 @@
 #include "exec_cmd.h"
 #include "common-cmds.h"
 #include "parse-options.h"
+#include "run-command.h"
+
+static struct man_viewer_list {
+       void (*exec)(const char *);
+       struct man_viewer_list *next;
+} *man_viewer_list;
 
 enum help_format {
        HELP_FORMAT_MAN,
@@ -42,6 +48,102 @@ static enum help_format parse_help_format(const char *format)
        die("unrecognized help format '%s'", format);
 }
 
+static int check_emacsclient_version(void)
+{
+       struct strbuf buffer = STRBUF_INIT;
+       struct child_process ec_process;
+       const char *argv_ec[] = { "emacsclient", "--version", NULL };
+       int version;
+
+       /* emacsclient prints its version number on stderr */
+       memset(&ec_process, 0, sizeof(ec_process));
+       ec_process.argv = argv_ec;
+       ec_process.err = -1;
+       ec_process.stdout_to_stderr = 1;
+       if (start_command(&ec_process)) {
+               fprintf(stderr, "Failed to start emacsclient.\n");
+               return -1;
+       }
+       strbuf_read(&buffer, ec_process.err, 20);
+       close(ec_process.err);
+
+       /*
+        * Don't bother checking return value, because "emacsclient --version"
+        * seems to always exits with code 1.
+        */
+       finish_command(&ec_process);
+
+       if (prefixcmp(buffer.buf, "emacsclient")) {
+               fprintf(stderr, "Failed to parse emacsclient version.\n");
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_remove(&buffer, 0, strlen("emacsclient"));
+       version = atoi(buffer.buf);
+
+       if (version < 22) {
+               fprintf(stderr,
+                       "emacsclient version '%d' too old (< 22).\n",
+                       version);
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_release(&buffer);
+       return 0;
+}
+
+static void exec_woman_emacs(const char *page)
+{
+       if (!check_emacsclient_version()) {
+               /* This works only with emacsclient version >= 22. */
+               struct strbuf man_page = STRBUF_INIT;
+               strbuf_addf(&man_page, "(woman \"%s\")", page);
+               execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL);
+       }
+}
+
+static void exec_man_konqueror(const char *page)
+{
+       const char *display = getenv("DISPLAY");
+       if (display && *display) {
+               struct strbuf man_page = STRBUF_INIT;
+               strbuf_addf(&man_page, "man:%s(1)", page);
+               execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL);
+       }
+}
+
+static void exec_man_man(const char *page)
+{
+       execlp("man", "man", page, NULL);
+}
+
+static void do_add_man_viewer(void (*exec)(const char *))
+{
+       struct man_viewer_list **p = &man_viewer_list;
+
+       while (*p)
+               p = &((*p)->next);
+       *p = xmalloc(sizeof(**p));
+       (*p)->next = NULL;
+       (*p)->exec = exec;
+}
+
+static int add_man_viewer(const char *value)
+{
+       if (!strcasecmp(value, "man"))
+               do_add_man_viewer(exec_man_man);
+       else if (!strcasecmp(value, "woman"))
+               do_add_man_viewer(exec_woman_emacs);
+       else if (!strcasecmp(value, "konqueror"))
+               do_add_man_viewer(exec_man_konqueror);
+       else
+               warning("'%s': unsupported man viewer.", value);
+
+       return 0;
+}
+
 static int git_help_config(const char *var, const char *value)
 {
        if (!strcmp(var, "help.format")) {
@@ -50,6 +152,11 @@ static int git_help_config(const char *var, const char *value)
                help_format = parse_help_format(value);
                return 0;
        }
+       if (!strcmp(var, "man.viewer")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer(value);
+       }
        return git_default_config(var, value);
 }
 
@@ -210,7 +317,7 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds,
        return longest;
 }
 
-static void list_commands(void)
+static unsigned int load_command_list(void)
 {
        unsigned int longest = 0;
        unsigned int len;
@@ -250,6 +357,14 @@ static void list_commands(void)
        uniq(&other_cmds);
        exclude_cmds(&other_cmds, &main_cmds);
 
+       return longest;
+}
+
+static void list_commands(void)
+{
+       unsigned int longest = load_command_list();
+       const char *exec_path = git_exec_path();
+
        if (main_cmds.cnt) {
                printf("available git commands in '%s'\n", exec_path);
                printf("----------------------------");
@@ -284,6 +399,22 @@ void list_common_cmds_help(void)
        }
 }
 
+static int is_in_cmdlist(struct cmdnames *c, const char *s)
+{
+       int i;
+       for (i = 0; i < c->cnt; i++)
+               if (!strcmp(s, c->names[i]->name))
+                       return 1;
+       return 0;
+}
+
+static int is_git_command(const char *s)
+{
+       load_command_list();
+       return is_in_cmdlist(&main_cmds, s) ||
+               is_in_cmdlist(&other_cmds, s);
+}
+
 static const char *cmd_to_page(const char *git_cmd)
 {
        if (!git_cmd)
@@ -323,9 +454,16 @@ static void setup_man_path(void)
 
 static void show_man_page(const char *git_cmd)
 {
+       struct man_viewer_list *viewer;
        const char *page = cmd_to_page(git_cmd);
+
        setup_man_path();
-       execlp("man", "man", page, NULL);
+       for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+       {
+               viewer->exec(page); /* will return when unable */
+       }
+       exec_man_man(page);
+       die("no man viewer handled the request");
 }
 
 static void show_info_page(const char *git_cmd)
@@ -372,6 +510,7 @@ int cmd_version(int argc, const char **argv, const char *prefix)
 int cmd_help(int argc, const char **argv, const char *prefix)
 {
        int nongit;
+       const char *alias;
 
        setup_git_directory_gently(&nongit);
        git_config(git_help_config);
@@ -391,6 +530,12 @@ int cmd_help(int argc, const char **argv, const char *prefix)
                return 0;
        }
 
+       alias = alias_lookup(argv[0]);
+       if (alias && !is_git_command(argv[0])) {
+               printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+               return 0;
+       }
+
        switch (help_format) {
        case HELP_FORMAT_MAN:
                show_man_page(argv[0]);