read_prompt: take prompt 'name' as arg
[tig] / tig.c
diff --git a/tig.c b/tig.c
index 8bae0d5..b84c561 100644 (file)
--- a/tig.c
+++ b/tig.c
 #include <unistd.h>
 #include <time.h>
 
+#include <locale.h>
+#include <langinfo.h>
+#include <iconv.h>
+
 #include <curses.h>
 
 #if __GNUC__ >= 3
@@ -50,13 +54,15 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset,
 #define ARRAY_SIZE(x)  (sizeof(x) / sizeof(x[0]))
 #define STRING_SIZE(x) (sizeof(x) - 1)
 
+#define SIZEOF_STR     1024    /* Default string size. */
 #define SIZEOF_REF     256     /* Size of symbolic or SHA1 ID. */
-#define SIZEOF_CMD     1024    /* Size of command buffer. */
 #define SIZEOF_REVGRAPH        19      /* Size of revision ancestry graphics. */
 
 /* This color name can be used to refer to the default term colors. */
 #define COLOR_DEFAULT  (-1)
 
+#define ICONV_NONE     ((iconv_t) -1)
+
 /* The format and size of the date column in the main view. */
 #define DATE_FORMAT    "%Y-%m-%d %H:%M"
 #define DATE_COLS      STRING_SIZE("2006-04-29 14:21 ")
@@ -74,13 +80,13 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset,
        "git ls-remote . 2>/dev/null"
 
 #define TIG_DIFF_CMD \
-       "git show --patch-with-stat --find-copies-harder -B -C %s"
+       "git show --root --patch-with-stat --find-copies-harder -B -C %s 2>/dev/null"
 
 #define TIG_LOG_CMD    \
-       "git log --cc --stat -n100 %s"
+       "git log --cc --stat -n100 %s 2>/dev/null"
 
 #define TIG_MAIN_CMD \
-       "git log --topo-order --pretty=raw %s"
+       "git log --topo-order --pretty=raw %s 2>/dev/null"
 
 /* XXX: Needs to be defined to the empty string. */
 #define TIG_HELP_CMD   ""
@@ -218,11 +224,11 @@ string_enum_compare(const char *str1, const char *str2, int len)
  */
 
 static size_t
-sq_quote(char buf[SIZEOF_CMD], size_t bufsize, const char *src)
+sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
 {
        char c;
 
-#define BUFPUT(x) do { if (bufsize < SIZEOF_CMD) buf[bufsize++] = (x); } while (0)
+#define BUFPUT(x) do { if (bufsize < SIZEOF_STR) buf[bufsize++] = (x); } while (0)
 
        BUFPUT('\'');
        while ((c = *src++)) {
@@ -360,10 +366,12 @@ static bool opt_rev_graph = TRUE;
 static int opt_num_interval    = NUMBER_INTERVAL;
 static int opt_tab_size                = TABSIZE;
 static enum request opt_request = REQ_VIEW_MAIN;
-static char opt_cmd[SIZEOF_CMD]        = "";
-static char opt_encoding[20]   = "";
-static bool opt_utf8           = TRUE;
+static char opt_cmd[SIZEOF_STR]        = "";
 static FILE *opt_pipe          = NULL;
+static char opt_encoding[20]   = "UTF-8";
+static bool opt_utf8           = TRUE;
+static char opt_codeset[20]    = "UTF-8";
+static iconv_t opt_iconv       = ICONV_NONE;
 
 enum option_type {
        OPT_NONE,
@@ -679,7 +687,7 @@ static struct keybinding default_keybindings[] = {
        { 'v',          REQ_SHOW_VERSION },
        { 'r',          REQ_SCREEN_REDRAW },
        { 'n',          REQ_TOGGLE_LINENO },
-       { 'g',          REQ_TOGGLE_REV_GRAPH},
+       { 'g',          REQ_TOGGLE_REV_GRAPH },
        { ':',          REQ_PROMPT },
 
        /* wgetch() with nodelay() enabled returns ERR when there's no input. */
@@ -1081,7 +1089,7 @@ static int
 load_options(void)
 {
        char *home = getenv("HOME");
-       char buf[1024];
+       char buf[SIZEOF_STR];
        FILE *file;
 
        config_lineno = 0;
@@ -1133,7 +1141,7 @@ struct view {
 
        enum keymap keymap;     /* What keymap does this view have */
 
-       char cmd[SIZEOF_CMD];   /* Command buffer */
+       char cmd[SIZEOF_STR];   /* Command buffer */
        char ref[SIZEOF_REF];   /* Hovered commit reference */
        char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
 
@@ -1616,7 +1624,8 @@ realloc_lines(struct view *view, size_t line_size)
 static bool
 update_view(struct view *view)
 {
-       char buffer[BUFSIZ];
+       char in_buffer[BUFSIZ];
+       char out_buffer[BUFSIZ * 2];
        char *line;
        /* The number of lines to read. If too low it will cause too much
         * redrawing (and possible flickering), if too high responsiveness
@@ -1634,12 +1643,28 @@ update_view(struct view *view)
        if (!realloc_lines(view, view->lines + lines))
                goto alloc_error;
 
-       while ((line = fgets(buffer, sizeof(buffer), view->pipe))) {
-               int linelen = strlen(line);
+       while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
+               size_t linelen = strlen(line);
 
                if (linelen)
                        line[linelen - 1] = 0;
 
+               if (opt_iconv != ICONV_NONE) {
+                       char *inbuf = line;
+                       size_t inlen = linelen;
+
+                       char *outbuf = out_buffer;
+                       size_t outlen = sizeof(out_buffer);
+
+                       size_t ret;
+
+                       ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
+                       if (ret != (size_t) -1) {
+                               line = out_buffer;
+                               linelen = strlen(out_buffer);
+                       }
+               }
+
                if (!view->ops->read(view, line))
                        goto alloc_error;
 
@@ -2042,20 +2067,52 @@ pager_draw(struct view *view, struct line *line, unsigned int lineno)
        return TRUE;
 }
 
+static bool
+add_describe_ref(char *buf, int *bufpos, char *commit_id, const char *sep)
+{
+       char refbuf[SIZEOF_STR];
+       char *ref = NULL;
+       FILE *pipe;
+
+       if (!string_format(refbuf, "git describe %s", commit_id))
+               return TRUE;
+
+       pipe = popen(refbuf, "r");
+       if (!pipe)
+               return TRUE;
+
+       if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
+               ref = chomp_string(ref);
+       pclose(pipe);
+
+       if (!ref || !*ref)
+               return TRUE;
+
+       /* This is the only fatal call, since it can "corrupt" the buffer. */
+       if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
+               return FALSE;
+
+       return TRUE;
+}
+
 static void
 add_pager_refs(struct view *view, struct line *line)
 {
-       char buf[1024];
-       char *data = line->data;
+       char buf[SIZEOF_STR];
+       char *commit_id = line->data + STRING_SIZE("commit ");
        struct ref **refs;
        int bufpos = 0, refpos = 0;
        const char *sep = "Refs: ";
+       bool is_tag = FALSE;
 
        assert(line->type == LINE_COMMIT);
 
-       refs = get_refs(data + STRING_SIZE("commit "));
-       if (!refs)
+       refs = get_refs(commit_id);
+       if (!refs) {
+               if (view == VIEW(REQ_VIEW_DIFF))
+                       goto try_add_describe_ref;
                return;
+       }
 
        do {
                struct ref *ref = refs[refpos];
@@ -2064,8 +2121,16 @@ add_pager_refs(struct view *view, struct line *line)
                if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
                        return;
                sep = ", ";
+               if (ref->tag)
+                       is_tag = TRUE;
        } while (refs[refpos++]->next);
 
+       if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
+try_add_describe_ref:
+               if (!add_describe_ref(buf, &bufpos, commit_id, sep))
+                       return;
+       }
+
        if (!realloc_lines(view, view->line_size + 1))
                return;
 
@@ -2286,10 +2351,22 @@ main_read(struct view *view, char *line)
                        break;
 
                if (end) {
+                       char *email = end + 1;
+
                        for (; end > ident && isspace(end[-1]); end--) ;
+
+                       if (end == ident && *email) {
+                               ident = email;
+                               end = strchr(ident, '>');
+                               for (; end > ident && isspace(end[-1]); end--) ;
+                       }
                        *end = 0;
                }
 
+               /* End is NULL or ident meaning there's no author. */
+               if (end <= ident)
+                       ident = "Unknown";
+
                string_copy(commit->author, ident);
 
                /* Parse epoch and timezone */
@@ -2583,6 +2660,8 @@ init_display(void)
                /* Leave stdin and stdout alone when acting as a pager. */
                FILE *io = fopen("/dev/tty", "r+");
 
+               if (!io)
+                       die("Failed to open /dev/tty");
                cursed = !!newterm(NULL, io, io);
        }
 
@@ -2608,7 +2687,7 @@ init_display(void)
 }
 
 static int
-read_prompt(void)
+read_prompt(const char *prompt)
 {
        enum { READING, STOP, CANCEL } status = READING;
        char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
@@ -2621,7 +2700,7 @@ read_prompt(void)
                foreach_view (view, i)
                        update_view(view);
 
-               report(":%.*s", pos, buf);
+               report("%s%.*s", prompt, pos, buf);
                /* Refresh, accept single keystroke of input */
                key = wgetch(status_win);
                switch (key) {
@@ -2665,7 +2744,10 @@ read_prompt(void)
        buf[pos++] = 0;
        if (!string_format(opt_cmd, "git %s", buf))
                return ERR;
-       opt_request = REQ_VIEW_PAGER;
+       if (strncmp(buf, "show", 4) && isspace(buf[4]))
+               opt_request = REQ_VIEW_DIFF;
+       else
+               opt_request = REQ_VIEW_PAGER;
 
        return OK;
 }
@@ -2877,6 +2959,10 @@ main(int argc, char *argv[])
 
        signal(SIGINT, quit);
 
+       if (setlocale(LC_ALL, "")) {
+               string_copy(opt_codeset, nl_langinfo(CODESET));
+       }
+
        if (load_options() == ERR)
                die("Failed to load user config.");
 
@@ -2888,6 +2974,12 @@ main(int argc, char *argv[])
        if (!parse_options(argc, argv))
                return 0;
 
+       if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
+               opt_iconv = iconv_open(opt_codeset, opt_encoding);
+               if (opt_iconv == (iconv_t) -1)
+                       die("Failed to initialize character set conversion");
+       }
+
        if (load_refs() == ERR)
                die("Failed to load refs.");
 
@@ -2918,7 +3010,7 @@ main(int argc, char *argv[])
                 * status_win restricted. */
                switch (request) {
                case REQ_PROMPT:
-                       if (read_prompt() == ERR)
+                       if (read_prompt(":") == ERR)
                                request = REQ_SCREEN_UPDATE;
                        break;