summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: a7bc4b1)
raw | patch | inline | side by side (parent: a7bc4b1)
author | Jonas Fonseca <fonseca@diku.dk> | |
Wed, 26 Apr 2006 23:27:53 +0000 (01:27 +0200) | ||
committer | Jonas Fonseca <fonseca@antimatter.localdomain> | |
Wed, 26 Apr 2006 23:27:53 +0000 (01:27 +0200) |
Makefile | patch | blob | history | |
cgit.c | [deleted file] | patch | blob | history |
tig.c | [new file with mode: 0644] | patch | blob |
diff --git a/Makefile b/Makefile
index efeca0d36e948b5f8bb57a95447204c66c046734..38728516b42deff51de25b9405b7c18c8056deac 100644 (file)
--- a/Makefile
+++ b/Makefile
LDFLAGS = -lcurses
-CFLAGS = -g
-PROGS = cgit
-DOCS = cgit.1.txt cgit.1 cgit.1.html
+CFLAGS = -g '-DVERSION="$(VERSION)"' -Wall
+PROGS = tig
+DOCS = tig.1.txt tig.1 tig.1.html
+VERSION = $(shell git-describe)
all: $(PROGS)
docs: $(DOCS)
clean:
rm -f $(PROGS) $(DOCS)
-cgit: cgit.c
+.PHONY: all docs install clean
-cgit.1.txt: cgit.c
- sed -n '/\*\*/,/\*\*/p' < $< | \
- sed '/\*\*/d' | \
- sed -n 's/^ \* *//p' > $@
+tig: tig.c
+
+tig.1.txt: tig.c
+ sed -n '/^\/\*\*/,/\*\*\//p' < $< | \
+ sed '/^[^*]\*\*/d' | \
+ sed 's/\*\///;s/^[^*]*\* *//' > $@
%.1.html : %.1.txt
asciidoc -b xhtml11 -d manpage -f asciidoc.conf $<
diff --git a/cgit.c b/cgit.c
--- a/cgit.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/**
- * gitzilla(1)
- * ===========
- *
- * NAME
- * ----
- * gitzilla - cursed git browser
- *
- * SYNOPSIS
- * --------
- * gitzilla
- *
- * DESCRIPTION
- * -----------
- *
- * a
- *
- **/
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <signal.h>
-
-#include <curses.h>
-
-/**
- * OPTIONS
- * -------
- *
- * None
- *
- **/
-
-/**
- * KEYS
- * ----
- *
- * q:: quit
- * s:: shell
- * j:: down
- * k:: up
- *
- **/
-
-#define MSG_HELP "(q)uit, (s)hell, (j) down, (k) up"
-
-#define KEY_ESC 27
-#define KEY_TAB 9
-
-struct view {
- WINDOW *win;
-
- char *cmd;
- void (*reader)(char *, int);
- FILE *pipe;
-
- unsigned long lines;
- unsigned long lineno;
-};
-
-static struct view main_view;
-static struct view diff_view;
-static struct view log_view;
-static struct view status_view;
-
-int do_resize = 1;
-
-static void
-put_status(char *msg, ...)
-{
- va_list args;
-
- va_start(args, msg);
- werase(status_view.win);
- wmove(status_view.win, 0, 0);
- vwprintw(status_view.win, msg, args);
- wrefresh(status_view.win);
- va_end(args);
-}
-
-static void
-resize_views(void)
-{
- int x, y;
-
- getmaxyx(stdscr, y, x);
-
- if (status_view.win)
- delwin(status_view.win);
- status_view.win = newwin(1, 0, y - 1, 0);
-
- wattrset(status_view.win, COLOR_PAIR(COLOR_GREEN));
- put_status(MSG_HELP);
-
- if (main_view.win)
- delwin(main_view.win);
- main_view.win = newwin(y - 1, 0, 0, 0);
-
- scrollok(main_view.win, TRUE);
- keypad(main_view.win, TRUE); /* enable keyboard mapping */
- put_status("%d %d", y, x);
-}
-
-/*
- * Init and quit
- */
-
-static void
-quit(int sig)
-{
- endwin();
-
- /* do your non-curses wrapup here */
-
- exit(0);
-}
-
-static void
-init_colors(void)
-{
- int bg = COLOR_BLACK;
-
- start_color();
-
- if (use_default_colors() != ERR)
- bg = -1;
-
- init_pair(COLOR_BLACK, COLOR_BLACK, bg);
- init_pair(COLOR_GREEN, COLOR_GREEN, bg);
- init_pair(COLOR_RED, COLOR_RED, bg);
- init_pair(COLOR_CYAN, COLOR_CYAN, bg);
- init_pair(COLOR_WHITE, COLOR_WHITE, bg);
- init_pair(COLOR_MAGENTA, COLOR_MAGENTA, bg);
- init_pair(COLOR_BLUE, COLOR_BLUE, bg);
- init_pair(COLOR_YELLOW, COLOR_YELLOW, bg);
-}
-
-static void
-init(void)
-{
- signal(SIGINT, quit);
-
- initscr(); /* initialize the curses library */
- 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 */
- leaveok(stdscr, TRUE);
- /* curs_set(0); */
-
- if (has_colors())
- init_colors();
-}
-
-/*
- * Pipe readers
- */
-
-#define DIFF_CMD \
- "git log --stat -n1 HEAD ; echo; " \
- "git diff --find-copies-harder -B -C HEAD^ HEAD"
-
-#define LOG_CMD \
- "git log --stat -n100"
-
-static void
-log_reader(char *line, int lineno)
-{
- static int log_reader_skip;
-
- if (!line) {
- wattrset(main_view.win, A_NORMAL);
- log_reader_skip = 0;
- return;
- }
-
- if (!strncmp("commit ", line, 7)) {
- wattrset(main_view.win, COLOR_PAIR(COLOR_GREEN));
-
- } else if (!strncmp("Author: ", line, 8)) {
- wattrset(main_view.win, COLOR_PAIR(COLOR_CYAN));
-
- } else if (!strncmp("Date: ", line, 8)) {
- wattrset(main_view.win, COLOR_PAIR(COLOR_YELLOW));
-
- } else if (!strncmp("diff --git ", line, 11)) {
- wattrset(main_view.win, COLOR_PAIR(COLOR_YELLOW));
-
- } else if (!strncmp("diff-tree ", line, 10)) {
- wattrset(main_view.win, COLOR_PAIR(COLOR_BLUE));
-
- } else if (!strncmp("index ", line, 6)) {
- wattrset(main_view.win, COLOR_PAIR(COLOR_BLUE));
-
- } else if (line[0] == '-') {
- wattrset(main_view.win, COLOR_PAIR(COLOR_RED));
-
- } else if (line[0] == '+') {
- wattrset(main_view.win, COLOR_PAIR(COLOR_GREEN));
-
- } else if (line[0] == '@') {
- wattrset(main_view.win, COLOR_PAIR(COLOR_MAGENTA));
-
- } else if (line[0] == ':') {
- main_view.lines--;
- log_reader_skip = 1;
- return;
-
- } else if (log_reader_skip) {
- main_view.lines--;
- log_reader_skip = 0;
- return;
-
- } else {
- wattrset(main_view.win, A_NORMAL);
- }
-
- mvwaddstr(main_view.win, lineno, 0, line);
-}
-
-static struct view *
-update_view(struct view *view, char *cmd)
-{
- view->cmd = cmd;
- view->pipe = popen(cmd, "r");
- view->lines = 0;
- view->lineno = 0;
- view->reader = log_reader;
-
- wclear(view->win);
- wmove(view->win, 0, 0);
-
- put_status("Loading...");
-
- return view;
-}
-
-static struct view *
-read_pipe(struct view *view, int lines)
-{
- char buffer[BUFSIZ];
- char *line;
- int x, y;
-
- while ((line = fgets(buffer, sizeof(buffer), view->pipe))) {
- int linelen;
-
- if (!--lines)
- break;
-
- linelen = strlen(line);
- if (linelen)
- line[linelen - 1] = 0;
-
- view->reader(line, view->lines++);
- }
-
- if (ferror(view->pipe)) {
- put_status("Failed to read %s", view->cmd, view->lines - 1);
-
- } else if (feof(view->pipe)) {
- put_status("%s (lines %d)", MSG_HELP, view->lines - 1);
-
- } else {
- return view;
- }
-
- view->reader(NULL, view->lines - 1);
- pclose(view->pipe);
- view->pipe = NULL;
- view->reader = NULL;
-}
-
-/*
- * Main
- */
-
-int
-main(int argc, char *argv[])
-{
- static struct view *loading_view;
-
- init();
-
- //pipe = open_pipe(LOG_CMD, log_reader);
-
- for (;;) {
- int c;
-
- if (do_resize) {
- resize_views();
- do_resize = 0;
- }
-
- if (loading_view && (loading_view = read_pipe(loading_view, 20)))
- nodelay(loading_view->win, TRUE);
-
- c = wgetch(main_view.win); /* refresh, accept single keystroke of input */
-
- if (loading_view)
- nodelay(loading_view->win, FALSE);
-
- /* No input from wgetch() with nodelay() enabled. */
- if (c == ERR) {
- doupdate();
- continue;
- }
-
- /* Process the command keystroke */
- switch (c) {
- case KEY_RESIZE:
- fprintf(stderr, "resize");
- exit;
- break;
-
- case KEY_ESC:
- case 'q':
- quit(0);
- main_view.lineno--;
- return 0;
-
- case KEY_DOWN:
- case 'j':
- {
- int x, y;
-
- getmaxyx(main_view.win, y, x);
- if (main_view.lineno + y < main_view.lines) {
- wscrl(main_view.win, 1);
- main_view.lineno++;
- put_status("line %d out of %d (%d%%)",
- main_view.lineno,
- main_view.lines,
- 100 * main_view.lineno / main_view.lines);
- } else {
- put_status("last line reached");
- }
- break;
- }
- case KEY_UP:
- case 'k':
- if (main_view.lineno > 1) {
- wscrl(main_view.win, -1);
- main_view.lineno--;
- put_status("line %d out of %d (%d%%)",
- main_view.lineno,
- main_view.lines,
- 100 * main_view.lineno / main_view.lines);
- } else {
- put_status("first line reached");
- }
- break;
-
- case 'c':
- wclear(main_view.win);
- break;
-
- case 'd':
- loading_view = update_view(&main_view, DIFF_CMD);
- break;
-
- case 'l':
- loading_view = update_view(&main_view, LOG_CMD);
- break;
-
- case 's':
- mvwaddstr(status_view.win, 0, 0, "Shelling out...");
- def_prog_mode(); /* save current tty modes */
- endwin(); /* restore original tty modes */
- system("sh"); /* run shell */
-
- werase(status_view.win);
- mvwaddstr(status_view.win, 0, 0, MSG_HELP);
- reset_prog_mode();
- break;
- }
-
- redrawwin(main_view.win);
- wrefresh(main_view.win);
- }
-
- quit(0);
-}
-
-/**
- * COPYRIGHT
- * ---------
- * Copyright (c) Jonas Fonseca, 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * SEE ALSO
- * --------
- * gitlink:cogito[7],
- * gitlink:git[7]
- **/
diff --git a/tig.c b/tig.c
--- /dev/null
+++ b/tig.c
@@ -0,0 +1,703 @@
+/**
+ * TIG(1)
+ * ======
+ *
+ * NAME
+ * ----
+ * tig - text-mode interface for git
+ *
+ * SYNOPSIS
+ * --------
+ * [verse]
+ * tig
+ * tig log [git log options]
+ * tig diff [git diff options]
+ * tig < [git log or git diff output]
+ *
+ * DESCRIPTION
+ * -----------
+ * Browse changes in a git repository.
+ *
+ * OPTIONS
+ * -------
+ *
+ * None.
+ *
+ **/
+
+#define DEBUG
+
+#ifndef DEBUG
+#define NDEBUG
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+
+#include <curses.h>
+#include <form.h>
+
+static void die(const char *err, ...);
+static void report(const char *msg, ...);
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#define KEY_ESC 27
+#define KEY_TAB 9
+
+#define REQ_OFFSET (MAX_COMMAND + 1)
+
+/* Requests for switching between the different views. */
+#define REQ_DIFF (REQ_OFFSET + 0)
+#define REQ_LOG (REQ_OFFSET + 1)
+#define REQ_MAIN (REQ_OFFSET + 2)
+
+#define REQ_QUIT (REQ_OFFSET + 11)
+#define REQ_VERSION (REQ_OFFSET + 12)
+#define REQ_STOP (REQ_OFFSET + 13)
+#define REQ_UPDATE (REQ_OFFSET + 14)
+#define REQ_REDRAW (REQ_OFFSET + 15)
+
+
+/**
+ * KEYS
+ * ----
+ *
+ * d::
+ * diff
+ * l::
+ * log
+ * q::
+ * quit
+ * r::
+ * redraw screen
+ * s::
+ * stop all background loading
+ * j::
+ * down
+ * k::
+ * up
+ * h, ?::
+ * help
+ * v::
+ * version
+ *
+ **/
+
+#define HELP "(d)iff, (l)og, (m)ain, (q)uit, (v)ersion, (h)elp"
+
+struct keymap {
+ int alias;
+ int request;
+};
+
+struct keymap keymap[] = {
+ { KEY_UP, REQ_PREV_LINE },
+ { 'k', REQ_PREV_LINE },
+ { KEY_DOWN, REQ_NEXT_LINE },
+ { 'j', REQ_NEXT_LINE },
+ { KEY_NPAGE, REQ_NEXT_PAGE },
+ { KEY_PPAGE, REQ_PREV_PAGE },
+
+ { 'd', REQ_DIFF },
+ { 'l', REQ_LOG },
+ { 'm', REQ_MAIN },
+
+ /* No input from wgetch() with nodelay() enabled. */
+ { ERR, REQ_UPDATE },
+
+ { KEY_ESC, REQ_QUIT },
+ { 'q', REQ_QUIT },
+ { 's', REQ_STOP },
+ { 'v', REQ_VERSION },
+ { 'r', REQ_REDRAW },
+};
+
+static int
+get_request(int request)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(keymap); i++)
+ if (keymap[i].alias == request)
+ return keymap[i].request;
+
+ return request;
+}
+
+
+/*
+ * Viewer
+ */
+
+struct view {
+ char *name;
+ char *cmd;
+
+ /* Rendering */
+ int (*render)(struct view *, int);
+ WINDOW *win;
+
+ /* Navigation */
+ unsigned long offset; /* Offset of the window top */
+ unsigned long lineno; /* Current line number */
+
+ /* Buffering */
+ unsigned long lines; /* Total number of lines */
+ char **line; /* Line index */
+
+ /* Loading */
+ FILE *pipe;
+};
+
+static int default_renderer(struct view *view, int lineno);
+
+#define DIFF_CMD \
+ "git log --stat -n1 %s ; echo; " \
+ "git diff --find-copies-harder -B -C %s^ %s"
+
+#define LOG_CMD \
+ "git log --stat -n100 %s"
+
+/* The status window at the bottom. Used for polling keystrokes. */
+static WINDOW *status_win;
+
+static struct view views[] = {
+ { "diff", DIFF_CMD, default_renderer },
+ { "log", LOG_CMD, default_renderer },
+ { "main", NULL },
+};
+
+static struct view *display[ARRAY_SIZE(views)];
+static unsigned int current_view;
+static unsigned int nloading;
+
+#define foreach_view(view, i) \
+ for (i = 0; i < sizeof(display) && (view = display[i]); i++)
+
+static void
+redraw_view(struct view *view)
+{
+ int lineno;
+ int lines, cols;
+
+ wclear(view->win);
+ wmove(view->win, 0, 0);
+
+ getmaxyx(view->win, lines, cols);
+
+ for (lineno = 0; lineno < lines; lineno++) {
+ view->render(view, lineno);
+ }
+
+ redrawwin(view->win);
+ wrefresh(view->win);
+}
+
+/* FIXME: Fix percentage. */
+static void
+report_position(struct view *view, int all)
+{
+ report(all ? "line %d of %d (%d%%) viewing from %d"
+ : "line %d of %d",
+ view->lineno + 1,
+ view->lines,
+ view->lines ? view->offset * 100 / view->lines : 0,
+ view->offset);
+}
+
+static void
+scroll_view(struct view *view, int request)
+{
+ int x, y, lines = 1;
+ enum { BACKWARD = -1, FORWARD = 1 } direction = FORWARD;
+
+ getmaxyx(view->win, y, x);
+
+ switch (request) {
+ case REQ_NEXT_PAGE:
+ lines = y;
+ case REQ_NEXT_LINE:
+ if (view->offset + lines > view->lines)
+ lines = view->lines - view->offset - 1;
+
+ if (lines == 0 || view->offset + y >= view->lines) {
+ report("already at last line");
+ return;
+ }
+ break;
+
+ case REQ_PREV_PAGE:
+ lines = y;
+ case REQ_PREV_LINE:
+ if (lines > view->offset)
+ lines = view->offset;
+
+ if (lines == 0) {
+ report("already at first line");
+ return;
+ }
+
+ direction = BACKWARD;
+ break;
+
+ default:
+ lines = 0;
+ }
+
+ report("off=%d lines=%d lineno=%d move=%d", view->offset, view->lines, view->lineno, lines * direction);
+
+ /* The rendering expects the new offset. */
+ view->offset += lines * direction;
+
+ /* Move current line into the view. */
+ if (view->lineno < view->offset)
+ view->lineno = view->offset;
+ if (view->lineno > view->offset + y)
+ view->lineno = view->offset + y;
+
+ assert(0 <= view->offset && view->offset < view->lines);
+ //assert(0 <= view->offset + lines && view->offset + lines < view->lines);
+ assert(view->offset <= view->lineno && view->lineno <= view->lines);
+
+ if (lines) {
+ int from = direction == FORWARD ? y - lines : 0;
+ int to = from + lines;
+
+ wscrl(view->win, lines * direction);
+
+ for (; from < to; from++) {
+ if (!view->render(view, from))
+ break;
+ }
+ }
+
+ redrawwin(view->win);
+ wrefresh(view->win);
+
+ report_position(view, lines);
+}
+
+static void
+resize_view(struct view *view)
+{
+ int lines, cols;
+
+ getmaxyx(stdscr, lines, cols);
+
+ if (view->win) {
+ mvwin(view->win, 0, 0);
+ wresize(view->win, lines - 1, cols);
+
+ } else {
+ view->win = newwin(lines - 1, 0, 0, 0);
+ if (!view->win) {
+ report("Failed to create %s view", view->name);
+ return;
+ }
+ scrollok(view->win, TRUE);
+ }
+}
+
+
+static bool
+begin_update(struct view *view)
+{
+ char buf[1024];
+
+ if (view->cmd) {
+ if (snprintf(buf, sizeof(buf), view->cmd, "HEAD", "HEAD", "HEAD") < sizeof(buf))
+ view->pipe = popen(buf, "r");
+
+ if (!view->pipe)
+ return FALSE;
+
+ if (nloading++ == 0)
+ nodelay(status_win, TRUE);
+ }
+
+ display[current_view] = view;
+
+ view->offset = 0;
+ view->lines = 0;
+ view->lineno = 0;
+
+ return TRUE;
+}
+
+static void
+end_update(struct view *view)
+{
+ wattrset(view->win, A_NORMAL);
+ pclose(view->pipe);
+ view->pipe = NULL;
+
+ if (nloading-- == 1)
+ nodelay(status_win, FALSE);
+}
+
+static int
+update_view(struct view *view)
+{
+ char buffer[BUFSIZ];
+ char *line;
+ int lines, cols;
+ char **tmp;
+ int redraw;
+
+ if (!view->pipe)
+ return TRUE;
+
+ getmaxyx(view->win, lines, cols);
+
+ redraw = !view->line;
+
+ tmp = realloc(view->line, sizeof(*view->line) * (view->lines + lines));
+ if (!tmp)
+ goto alloc_error;
+
+ view->line = tmp;
+
+ while ((line = fgets(buffer, sizeof(buffer), view->pipe))) {
+ int linelen;
+
+ if (!lines--)
+ break;
+
+ linelen = strlen(line);
+ if (linelen)
+ line[linelen - 1] = 0;
+
+ view->line[view->lines] = strdup(line);
+ if (!view->line[view->lines])
+ goto alloc_error;
+ view->lines++;
+ }
+
+ if (redraw)
+ redraw_view(view);
+
+ if (ferror(view->pipe)) {
+ report("Failed to read %s", view->cmd);
+ goto end;
+
+ } else if (feof(view->pipe)) {
+ report_position(view, 0);
+ goto end;
+ }
+
+ return TRUE;
+
+alloc_error:
+ report("Allocation failure");
+
+end:
+ end_update(view);
+ return FALSE;
+}
+
+
+static struct view *
+switch_view(struct view *prev, int request)
+{
+ struct view *view = &views[request - REQ_OFFSET];
+ struct view *displayed;
+ int i;
+
+ if (view == prev) {
+ foreach_view (displayed, i) ;
+
+ if (i == 1)
+ report("Already in %s view", view->name);
+ else
+ report("FIXME: Maximize");
+
+ return view;
+
+ } else {
+ foreach_view (displayed, i) {
+ if (view == displayed) {
+ current_view = i;
+ report("New current view");
+ return view;
+ }
+ }
+ }
+
+ if (!view->win)
+ resize_view(view);
+
+ /* Reload */
+
+ if (view->line) {
+ for (i = 0; i < view->lines; i++)
+ if (view->line[i])
+ free(view->line[i]);
+
+ free(view->line);
+ view->line = NULL;
+ }
+
+ if (prev && prev->pipe)
+ end_update(prev);
+
+ if (begin_update(view)) {
+ if (!view->cmd)
+ report("%s", HELP);
+ else
+ report("loading...");
+ }
+
+ return view;
+}
+
+
+/* Process a keystroke */
+static int
+view_driver(struct view *view, int key)
+{
+ int request = get_request(key);
+ int i;
+
+ switch (request) {
+ case REQ_NEXT_LINE:
+ case REQ_NEXT_PAGE:
+ case REQ_PREV_LINE:
+ case REQ_PREV_PAGE:
+ if (view)
+ scroll_view(view, request);
+ break;
+
+ case REQ_MAIN:
+ case REQ_LOG:
+ case REQ_DIFF:
+ view = switch_view(view, request);
+ break;
+
+ case REQ_REDRAW:
+ redraw_view(view);
+ break;
+
+ case REQ_STOP:
+ foreach_view (view, i) {
+ if (view->pipe) {
+ end_update(view);
+ scroll_view(view, 0);
+ }
+ }
+ break;
+
+ case REQ_VERSION:
+ report("version %s", VERSION);
+ return TRUE;
+
+ case REQ_UPDATE:
+ doupdate();
+ return TRUE;
+
+ case REQ_QUIT:
+ return FALSE;
+
+ default:
+ report(HELP);
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Rendering
+ */
+
+#define ATTR(line, attr) { (line), sizeof(line) - 1, (attr) }
+
+struct attr {
+ char *line;
+ int linelen;
+ int attr;
+};
+
+static struct attr attrs[] = {
+ ATTR("commit ", COLOR_PAIR(COLOR_GREEN)),
+ ATTR("Author: ", COLOR_PAIR(COLOR_CYAN)),
+ ATTR("Date: ", COLOR_PAIR(COLOR_YELLOW)),
+ ATTR("diff --git ", COLOR_PAIR(COLOR_YELLOW)),
+ ATTR("diff-tree ", COLOR_PAIR(COLOR_BLUE)),
+ ATTR("index ", COLOR_PAIR(COLOR_BLUE)),
+ ATTR("-", COLOR_PAIR(COLOR_RED)),
+ ATTR("+", COLOR_PAIR(COLOR_GREEN)),
+ ATTR("@", COLOR_PAIR(COLOR_MAGENTA)),
+};
+
+static int
+default_renderer(struct view *view, int lineno)
+{
+ char *line;
+ int linelen;
+ int attr = A_NORMAL;
+ int i;
+
+ line = view->line[view->offset + lineno];
+ if (!line) return FALSE;
+
+ linelen = strlen(line);
+
+ for (i = 0; i < ARRAY_SIZE(attrs); i++) {
+ if (linelen < attrs[i].linelen
+ || strncmp(attrs[i].line, line, attrs[i].linelen))
+ continue;
+
+ attr = attrs[i].attr;
+ break;
+ }
+
+ wattrset(view->win, attr);
+ mvwprintw(view->win, lineno, 0, "%4d: %s", view->offset + lineno, line);
+
+ return TRUE;
+}
+
+/*
+ * Main
+ */
+
+static void
+quit(int sig)
+{
+ if (status_win)
+ delwin(status_win);
+ endwin();
+
+ /* FIXME: Shutdown gracefully. */
+
+ exit(0);
+}
+
+static void die(const char *err, ...)
+{
+ va_list args;
+
+ endwin();
+
+ va_start(args, err);
+ fputs("tig: ", stderr);
+ vfprintf(stderr, err, args);
+ fputs("\n", stderr);
+ va_end(args);
+
+ exit(1);
+}
+
+static void
+report(const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+
+ werase(status_win);
+ wmove(status_win, 0, 0);
+
+ if (display[current_view])
+ wprintw(status_win, "%4s: ", display[current_view]->name);
+
+ vwprintw(status_win, msg, args);
+ wrefresh(status_win);
+
+ va_end(args);
+}
+
+static void
+init_colors(void)
+{
+ int bg = COLOR_BLACK;
+
+ start_color();
+
+ if (use_default_colors() != ERR)
+ bg = -1;
+
+ init_pair(COLOR_BLACK, COLOR_BLACK, bg);
+ init_pair(COLOR_GREEN, COLOR_GREEN, bg);
+ init_pair(COLOR_RED, COLOR_RED, bg);
+ init_pair(COLOR_CYAN, COLOR_CYAN, bg);
+ init_pair(COLOR_WHITE, COLOR_WHITE, bg);
+ init_pair(COLOR_MAGENTA, COLOR_MAGENTA, bg);
+ init_pair(COLOR_BLUE, COLOR_BLUE, bg);
+ init_pair(COLOR_YELLOW, COLOR_YELLOW, bg);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int request = REQ_MAIN;
+ int x, y;
+
+ signal(SIGINT, quit);
+
+ initscr(); /* initialize the curses library */
+ 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 */
+ leaveok(stdscr, TRUE);
+ /* curs_set(0); */
+
+ if (has_colors())
+ init_colors();
+
+ getmaxyx(stdscr, y, x);
+ status_win = newwin(1, 0, y - 1, 0);
+ if (!status_win)
+ die("Failed to create status window");
+
+ /* Enable keyboard mapping */
+ keypad(status_win, TRUE);
+ wattrset(status_win, COLOR_PAIR(COLOR_GREEN));
+
+ while (view_driver(display[current_view], request)) {
+ struct view *view;
+ int i;
+
+ foreach_view (view, i) {
+ if (view->pipe) {
+ update_view(view);
+ }
+ }
+
+ /* Refresh, accept single keystroke of input */
+ request = wgetch(status_win);
+ if (request == KEY_RESIZE) {
+ int lines, cols;
+
+ getmaxyx(stdscr, lines, cols);
+ mvwin(status_win, lines - 1, 0);
+ wresize(status_win, 1, cols - 1);
+ }
+ }
+
+ quit(0);
+
+ return 0;
+}
+
+/**
+ * COPYRIGHT
+ * ---------
+ * Copyright (c) Jonas Fonseca, 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SEE ALSO
+ * --------
+ * link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)],
+ * link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)]
+ **/