X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/03a93dbb1eb5bcd84333f58ae577b9cdab62936e..49f2b43f1ad2b29dea90f8478120a0fafcb587e0:/tig.c diff --git a/tig.c b/tig.c index aa62a8f..90e9e5f 100644 --- a/tig.c +++ b/tig.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,7 @@ enum request { REQ_VIEW_DIFF, REQ_VIEW_LOG, REQ_VIEW_HELP, + REQ_VIEW_PAGER, REQ_ENTER, REQ_QUIT, @@ -179,9 +181,7 @@ static int opt_line_number = FALSE; static int opt_num_interval = NUMBER_INTERVAL; static enum request opt_request = REQ_VIEW_MAIN; static char opt_cmd[SIZEOF_CMD] = ""; - -char ref_head[SIZEOF_REF] = "HEAD"; -char ref_commit[SIZEOF_REF] = "HEAD"; +static FILE *opt_pipe = NULL; /* Returns the index of log or diff command or -1 to exit. */ static int @@ -203,7 +203,7 @@ parse_options(int argc, char *argv[]) !strcmp(opt, "diff")) { opt_request = opt[0] == 'l' ? REQ_VIEW_LOG : REQ_VIEW_DIFF; - return i; + break; } /** @@ -264,8 +264,10 @@ parse_options(int argc, char *argv[]) * * $ tig -- --pretty=raw tag-1.0..HEAD **/ - if (!strcmp(opt, "--")) - return i + 1; + if (!strcmp(opt, "--")) { + i++; + break; + } /* Make stuff like: * @@ -274,11 +276,40 @@ parse_options(int argc, char *argv[]) * work. */ if (opt[0] && opt[0] != '-') - return i; + break; die("unknown command '%s'", opt); } + if (!isatty(STDIN_FILENO)) { + /* XXX: When pager mode has been requested, silently override + * view startup options. */ + opt_request = REQ_VIEW_PAGER; + opt_pipe = stdin; + + } else if (i < argc) { + size_t buf_size; + + /* XXX: This is vulnerable to the user overriding options + * required for the main view parser. */ + if (opt_request == REQ_VIEW_MAIN) + string_copy(opt_cmd, "git log --stat --pretty=raw"); + else + string_copy(opt_cmd, "git"); + buf_size = strlen(opt_cmd); + + while (buf_size < sizeof(opt_cmd) && i < argc) { + opt_cmd[buf_size++] = ' '; + buf_size = sq_quote(opt_cmd, buf_size, argv[i++]); + } + + if (buf_size >= sizeof(opt_cmd)) + die("command too long"); + + opt_cmd[buf_size] = 0; + + } + return i; } @@ -424,9 +455,10 @@ LINE(DIFF_RENAME, "rename ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(DIFF_SIM, "similarity ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(DIFF_DISSIM, "dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ /* Pretty print commit header */ \ -LINE(AUTHOR, "Author: ", COLOR_CYAN, COLOR_DEFAULT, 0), \ -LINE(MERGE, "Merge: ", COLOR_BLUE, COLOR_DEFAULT, 0), \ -LINE(DATE, "Date: ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(PP_AUTHOR, "Author: ", COLOR_CYAN, COLOR_DEFAULT, 0), \ +LINE(PP_MERGE, "Merge: ", COLOR_BLUE, COLOR_DEFAULT, 0), \ +LINE(PP_DATE, "Date: ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(PP_COMMIT, "Commit: ", COLOR_GREEN, COLOR_DEFAULT, 0), \ /* Raw commit header */ \ LINE(COMMIT, "commit ", COLOR_GREEN, COLOR_DEFAULT, 0), \ LINE(PARENT, "parent ", COLOR_BLUE, COLOR_DEFAULT, 0), \ @@ -519,7 +551,7 @@ init_colors(void) struct view { const char *name; /* View name */ - const char *defcmd; /* Default command line */ + const char *cmdfmt; /* Default command line format */ char *id; /* Points to either of ref_{head,commit} */ size_t objsize; /* Size of objects in the line index */ @@ -530,9 +562,8 @@ struct view { } *ops; char cmd[SIZEOF_CMD]; /* Command buffer */ - char ref[SIZEOF_REF]; /* Hovered Commit reference */ - /* The view reference that describes the content of this view. */ - char vref[SIZEOF_REF]; + char ref[SIZEOF_REF]; /* Hovered commit reference */ + char vid[SIZEOF_REF]; /* View ID. Set to id member when updating. */ WINDOW *win; WINDOW *title; @@ -567,11 +598,15 @@ static struct view_ops main_ops; #define HELP_CMD \ "man tig 2> /dev/null" +char ref_head[SIZEOF_REF] = "HEAD"; +char ref_commit[SIZEOF_REF] = "HEAD"; + static struct view views[] = { - { "main", MAIN_CMD, ref_head, sizeof(struct commit), &main_ops }, - { "diff", DIFF_CMD, ref_commit, sizeof(char), &pager_ops }, - { "log", LOG_CMD, ref_head, sizeof(char), &pager_ops }, - { "help", HELP_CMD, ref_head, sizeof(char), &pager_ops }, + { "main", MAIN_CMD, ref_head, sizeof(struct commit), &main_ops }, + { "diff", DIFF_CMD, ref_commit, sizeof(char), &pager_ops }, + { "log", LOG_CMD, ref_head, sizeof(char), &pager_ops }, + { "help", HELP_CMD, ref_head, sizeof(char), &pager_ops }, + { "pager", "cat", ref_head, sizeof(char), &pager_ops }, }; #define VIEW(req) (&views[(req) - REQ_OFFSET - 1]) @@ -669,7 +704,12 @@ update_view_title(struct view *view) wmove(view->title, 0, 0); /* [main] ref: 334b506... - commit 6 of 4383 (0%) */ - wprintw(view->title, "[%s] ref: %s", view->name, view->ref); + + if (*view->ref) + wprintw(view->title, "[%s] ref: %s", view->name, view->ref); + else + wprintw(view->title, "[%s]", view->name); + if (view->lines) { char *type = view == VIEW(REQ_VIEW_MAIN) ? "commit" : "line"; @@ -811,7 +851,7 @@ move_view(struct view *view, enum request request) report("Already on first line"); return; - } else if (steps >= 0 && view->lineno + 1 == view->lines) { + } else if (steps >= 0 && view->lineno + 1 >= view->lines) { report("Already on last line"); return; } @@ -870,12 +910,19 @@ begin_update(struct view *view) string_copy(view->cmd, opt_cmd); opt_cmd[0] = 0; } else { - if (snprintf(view->cmd, sizeof(view->cmd), view->defcmd, + if (snprintf(view->cmd, sizeof(view->cmd), view->cmdfmt, id, id, id) >= sizeof(view->cmd)) return FALSE; } - view->pipe = popen(view->cmd, "r"); + /* Special case for the pager view. */ + if (opt_pipe) { + view->pipe = opt_pipe; + opt_pipe = NULL; + } else { + view->pipe = popen(view->cmd, "r"); + } + if (!view->pipe) return FALSE; @@ -884,6 +931,7 @@ begin_update(struct view *view) view->offset = 0; view->lines = 0; view->lineno = 0; + string_copy(view->vid, id); if (view->line) { int i; @@ -991,10 +1039,19 @@ end: return FALSE; } +enum open_flags { + OPEN_DEFAULT = 0, /* Use default view switching. */ + OPEN_SPLIT = 1, /* Split current view. */ + OPEN_BACKGROUNDED = 2, /* Backgrounded. */ + OPEN_RELOAD = 4, /* Reload view even if it is the current. */ +}; + static void -switch_view(struct view *prev, enum request request, - bool backgrounded, bool split) +open_view(struct view *prev, enum request request, enum open_flags flags) { + bool backgrounded = !!(flags & OPEN_BACKGROUNDED); + bool split = !!(flags & OPEN_SPLIT); + bool reload = !!(flags & OPEN_RELOAD); struct view *view = VIEW(request); struct view *displayed; int nviews; @@ -1012,12 +1069,12 @@ switch_view(struct view *prev, enum request request, } } - if (view == prev && nviews == 1) { + if (view == prev && nviews == 1 && !reload) { report("Already in %s view", view->name); return; } - if (strcmp(view->vref, view->id) && + if (strcmp(view->vid, view->id) && !begin_update(view)) { report("Failed to load %s view", view->name); return; @@ -1096,10 +1153,15 @@ view_driver(struct view *view, enum request request) case REQ_VIEW_DIFF: case REQ_VIEW_LOG: case REQ_VIEW_HELP: - switch_view(view, request, FALSE, FALSE); + case REQ_VIEW_PAGER: + open_view(view, request, OPEN_DEFAULT); break; case REQ_ENTER: + if (!view->lines) { + report("Nothing to enter"); + break; + } return view->ops->enter(view); case REQ_VIEW_NEXT: @@ -1124,7 +1186,7 @@ view_driver(struct view *view, enum request request) break; case REQ_PROMPT: - switch_view(view, opt_request, FALSE, FALSE); + open_view(view, opt_request, OPEN_RELOAD); break; case REQ_STOP_LOADING: @@ -1179,11 +1241,17 @@ pager_draw(struct view *view, unsigned int lineno) type = get_line_type(line); if (view->offset + lineno == view->lineno) { - if (type == LINE_COMMIT) { + switch (type) { + case LINE_COMMIT: string_copy(view->ref, line + 7); string_copy(ref_commit, view->ref); + break; + case LINE_PP_COMMIT: + string_copy(view->ref, line + 8); + string_copy(ref_commit, view->ref); + default: + break; } - type = LINE_CURSOR; } @@ -1257,7 +1325,7 @@ pager_enter(struct view *view) char *line = view->line[view->lineno]; if (get_line_type(line) == LINE_COMMIT) { - switch_view(view, REQ_VIEW_DIFF, FALSE, FALSE); + open_view(view, REQ_VIEW_DIFF, OPEN_DEFAULT); } return TRUE; @@ -1288,6 +1356,7 @@ main_draw(struct view *view, unsigned int lineno) if (view->offset + lineno == view->lineno) { string_copy(view->ref, commit->id); + string_copy(ref_commit, view->ref); type = LINE_CURSOR; } else { type = LINE_MAIN_COMMIT; @@ -1410,7 +1479,7 @@ main_read(struct view *view, char *line) static bool main_enter(struct view *view) { - switch_view(view, REQ_VIEW_DIFF, TRUE, TRUE); + open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT | OPEN_BACKGROUNDED); return TRUE; } @@ -1469,7 +1538,16 @@ init_display(void) { int x, y; - initscr(); /* Initialize the curses library */ + /* Initialize the curses library */ + if (isatty(STDIN_FILENO)) { + initscr(); + } else { + /* Leave stdin and stdout alone when acting as a pager. */ + FILE *io = fopen("/dev/tty", "r+"); + + newterm(NULL, io, io); + } + nonl(); /* Tell curses not to do NL->CR/NL on output */ cbreak(); /* Take input chars one at a time, no wait for \n */ noecho(); /* Don't echo input */ @@ -1531,28 +1609,6 @@ main(int argc, char *argv[]) if (git_arg < 0) return 0; - if (git_arg < argc) { - size_t buf_size; - - /* XXX: This is vulnerable to the user overriding options - * required for the main view parser. */ - if (opt_request == REQ_VIEW_MAIN) - string_copy(opt_cmd, "git log --stat --pretty=raw"); - else - string_copy(opt_cmd, "git"); - buf_size = strlen(opt_cmd); - - while (buf_size < sizeof(opt_cmd) && git_arg < argc) { - opt_cmd[buf_size++] = ' '; - buf_size = sq_quote(opt_cmd, buf_size, argv[git_arg++]); - } - - if (buf_size >= sizeof(opt_cmd)) - die("command too long"); - - opt_cmd[buf_size] = 0; - } - request = opt_request; init_display(); @@ -1570,13 +1626,21 @@ main(int argc, char *argv[]) request = get_request(key); if (request == REQ_PROMPT) { + report(":"); + /* Temporarily switch to line-oriented and echoed + * input. */ nocbreak(); echo(); - report(":"); - if (wgetnstr(status_win, opt_cmd, sizeof(opt_cmd)) == OK) - die("%s", opt_cmd); - cbreak(); /* Take input chars one at a time, no wait for \n */ - noecho(); /* Don't echo input */ + + if (wgetnstr(status_win, opt_cmd + 4, sizeof(opt_cmd) - 4) == OK) { + memcpy(opt_cmd, "git ", 4); + opt_request = REQ_VIEW_PAGER; + } else { + request = ERR; + } + + noecho(); + cbreak(); } }