Misc cleanups
[tig] / cgit.c
CommitLineData
a74fa190 1/* Cursed git browser
800a900c 2 *
a74fa190 3 * Copyright (c) Jonas Fonseca, 2006
800a900c 4 *
a74fa190
JF
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
800a900c
JF
9 */
10
e3e7b9fc 11#include <stdarg.h>
800a900c
JF
12#include <stdlib.h>
13#include <stdio.h>
e3e7b9fc 14#include <string.h>
800a900c
JF
15#include <ctype.h>
16#include <signal.h>
17
18#include <curses.h>
19
e3e7b9fc 20
a74fa190
JF
21#define MSG_HELP "(q)uit, (s)hell, (j) down, (k) up"
22
05f1685b
JF
23#define KEY_ESC 27
24#define KEY_TAB 9
25
e3e7b9fc 26//WINDOW *titlewin;
05f1685b
JF
27WINDOW *mainwin;
28WINDOW *statuswin;
29
a74fa190 30typedef void (*pipe_reader_T)(char *, int);
05f1685b
JF
31
32FILE *pipe;
33long pipe_lineno;
a74fa190 34pipe_reader_T pipe_reader;
05f1685b 35
e3e7b9fc
JF
36
37static void
38put_status(char *msg, ...)
39{
40 va_list args;
41
42 va_start(args, msg);
43 werase(statuswin);
44 wmove(statuswin, 0, 0);
45 vwprintw(statuswin, msg, args);
46 wrefresh(statuswin);
47 va_end(args);
48}
49
800a900c
JF
50/*
51 * Init and quit
52 */
53
05f1685b
JF
54static void
55quit(int sig)
800a900c
JF
56{
57 endwin();
58
59 /* do your non-curses wrapup here */
60
61 exit(0);
62}
63
64static void
65init_colors(void)
66{
05f1685b
JF
67 int bg = COLOR_BLACK;
68
800a900c
JF
69 start_color();
70
e3e7b9fc 71 if (use_default_colors() != ERR)
05f1685b
JF
72 bg = -1;
73
74 init_pair(COLOR_BLACK, COLOR_BLACK, bg);
75 init_pair(COLOR_GREEN, COLOR_GREEN, bg);
76 init_pair(COLOR_RED, COLOR_RED, bg);
77 init_pair(COLOR_CYAN, COLOR_CYAN, bg);
78 init_pair(COLOR_WHITE, COLOR_WHITE, bg);
79 init_pair(COLOR_MAGENTA, COLOR_MAGENTA, bg);
80 init_pair(COLOR_BLUE, COLOR_BLUE, bg);
81 init_pair(COLOR_YELLOW, COLOR_YELLOW, bg);
800a900c
JF
82}
83
84static void
85init(void)
86{
05f1685b 87 int x, y;
800a900c 88
05f1685b
JF
89 signal(SIGINT, quit);
90
91 initscr(); /* initialize the curses library */
92 nonl(); /* tell curses not to do NL->CR/NL on output */
93 cbreak(); /* take input chars one at a time, no wait for \n */
94 noecho(); /* don't echo input */
e3e7b9fc 95 leaveok(stdscr, TRUE);
800a900c
JF
96
97 if (has_colors())
98 init_colors();
05f1685b
JF
99
100 getmaxyx(stdscr, y, x);
101
e3e7b9fc
JF
102#if 0
103 titlewin = newwin(1, 0, y - 2, 0);
05f1685b
JF
104
105 wattrset(titlewin, COLOR_PAIR(COLOR_GREEN));
106 waddch(titlewin, ACS_VLINE);
107 wprintw(titlewin, "%s", "cg-view");
108 waddch(titlewin, ACS_LTEE);
109 whline(titlewin, ACS_HLINE, x);
110 wrefresh(titlewin);
e3e7b9fc 111#endif
05f1685b
JF
112 statuswin = newwin(1, 0, y - 1, 0);
113
114 wattrset(statuswin, COLOR_PAIR(COLOR_GREEN));
a74fa190 115 put_status(MSG_HELP);
05f1685b 116
e3e7b9fc 117 mainwin = newwin(y - 1, 0, 0, 0);
05f1685b
JF
118 scrollok(mainwin, TRUE);
119 keypad(mainwin, TRUE); /* enable keyboard mapping */
120}
121
122/*
a74fa190 123 * Pipe readers
05f1685b
JF
124 */
125
126#define DIFF_CMD \
e3e7b9fc 127 "git-rev-list HEAD^..HEAD | " \
a74fa190 128 "git-diff-tree --stdin --pretty -r --cc --always --stat"
05f1685b
JF
129
130
a74fa190 131#define LOG_CMD0 \
05f1685b 132 "git-rev-list $(git-rev-parse --since=1.month) HEAD | " \
67392cc4 133 "git-diff-tree --stdin --pretty -r --root"
05f1685b 134
a74fa190
JF
135#define LOG_CMD \
136 "git-rev-list HEAD | git-diff-tree --stdin --pretty -r --root"
137
05f1685b 138static void
a74fa190 139log_reader(char *line, int lineno)
05f1685b 140{
a74fa190 141 static int log_reader_skip;
05f1685b
JF
142
143 if (!line) {
144 wattrset(mainwin, A_NORMAL);
a74fa190 145 log_reader_skip = 0;
05f1685b
JF
146 return;
147 }
148
149 if (!strncmp("commit ", line, 7)) {
a74fa190 150 wattrset(mainwin, COLOR_PAIR(COLOR_GREEN));
05f1685b
JF
151
152 } else if (!strncmp("Author: ", line, 8)) {
153 wattrset(mainwin, COLOR_PAIR(COLOR_CYAN));
154
155 } else if (!strncmp("Date: ", line, 6)) {
156 wattrset(mainwin, COLOR_PAIR(COLOR_YELLOW));
157
158 } else if (!strncmp("diff --git ", line, 11)) {
159 wattrset(mainwin, COLOR_PAIR(COLOR_YELLOW));
160
161 } else if (!strncmp("diff-tree ", line, 10)) {
162 wattrset(mainwin, COLOR_PAIR(COLOR_BLUE));
163
164 } else if (!strncmp("index ", line, 6)) {
165 wattrset(mainwin, COLOR_PAIR(COLOR_BLUE));
166
167 } else if (line[0] == '-') {
168 wattrset(mainwin, COLOR_PAIR(COLOR_RED));
169
170 } else if (line[0] == '+') {
171 wattrset(mainwin, COLOR_PAIR(COLOR_GREEN));
172
173 } else if (line[0] == '@') {
174 wattrset(mainwin, COLOR_PAIR(COLOR_MAGENTA));
175
176 } else if (line[0] == ':') {
67392cc4 177 pipe_lineno--;
a74fa190 178 log_reader_skip = 1;
05f1685b
JF
179 return;
180
a74fa190 181 } else if (log_reader_skip) {
67392cc4 182 pipe_lineno--;
a74fa190 183 log_reader_skip = 0;
05f1685b
JF
184 return;
185
186 } else {
187 wattrset(mainwin, A_NORMAL);
188 }
189
190 mvwaddstr(mainwin, lineno, 0, line);
191}
192
193static FILE *
a74fa190 194open_pipe(char *cmd, pipe_reader_T reader)
05f1685b
JF
195{
196 pipe = popen(cmd, "r");
e3e7b9fc 197 pipe_lineno = 0;
a74fa190 198 pipe_reader = reader;
e3e7b9fc
JF
199 wclear(mainwin);
200 wmove(mainwin, 0, 0);
201 put_status("Loading...");
05f1685b
JF
202 return pipe;
203}
204
205static void
206read_pipe(int lines)
207{
208 char buffer[BUFSIZ];
209 char *line;
e3e7b9fc 210 int x, y;
05f1685b
JF
211
212 while ((line = fgets(buffer, sizeof(buffer), pipe))) {
e3e7b9fc
JF
213 int linelen;
214
05f1685b
JF
215 if (!--lines)
216 break;
e3e7b9fc
JF
217
218 linelen = strlen(line);
219 if (linelen)
220 line[linelen - 1] = 0;
221
a74fa190 222 pipe_reader(line, pipe_lineno++);
05f1685b
JF
223 }
224
225 if (feof(pipe) || ferror(pipe)) {
a74fa190 226 pipe_reader(NULL, pipe_lineno - 1);
05f1685b
JF
227 pclose(pipe);
228 pipe = NULL;
a74fa190
JF
229 pipe_reader = NULL;
230 put_status("%s (lines %d)", MSG_HELP, pipe_lineno - 1);
05f1685b 231 }
800a900c
JF
232}
233
234/*
235 * Main
236 */
237
238int
239main(int argc, char *argv[])
240{
800a900c
JF
241 init();
242
a74fa190 243 //pipe = open_pipe(LOG_CMD, log_reader);
800a900c
JF
244
245 for (;;) {
05f1685b
JF
246 int c;
247
248 if (pipe) read_pipe(20);
249 if (pipe) nodelay(mainwin, TRUE);
250
251 c = wgetch(mainwin); /* refresh, accept single keystroke of input */
252
253 if (pipe) nodelay(mainwin, FALSE);
254
e3e7b9fc 255 /* No input from wgetch() with nodelay() enabled. */
05f1685b
JF
256 if (c == ERR)
257 continue;
800a900c
JF
258
259 /* Process the command keystroke */
260 switch (c) {
05f1685b 261 case KEY_ESC:
800a900c
JF
262 case 'q':
263 quit(0);
264 return 0;
265
05f1685b
JF
266 case KEY_DOWN:
267 case 'j':
268 wscrl(mainwin, 1);
269 break;
270
271 case KEY_UP:
272 case 'k':
273 wscrl(mainwin, -1);
274 break;
275
e3e7b9fc
JF
276 case 'c':
277 wclear(mainwin);
278 break;
279
05f1685b 280 case 'd':
a74fa190 281 pipe = open_pipe(DIFF_CMD, log_reader);
05f1685b
JF
282 break;
283
284 case 'l':
a74fa190 285 pipe = open_pipe(LOG_CMD, log_reader);
05f1685b
JF
286 break;
287
800a900c 288 case 's':
e3e7b9fc 289 mvwaddstr(statuswin, 0, 0, "Shelling out...");
800a900c
JF
290 def_prog_mode(); /* save current tty modes */
291 endwin(); /* restore original tty modes */
292 system("sh"); /* run shell */
05f1685b 293
e3e7b9fc 294 werase(statuswin);
a74fa190 295 mvwaddstr(statuswin, 0, 0, MSG_HELP);
800a900c 296 reset_prog_mode();
800a900c 297 break;
800a900c
JF
298 }
299
05f1685b
JF
300 redrawwin(mainwin);
301 wrefresh(mainwin);
800a900c
JF
302 }
303
304 quit(0);
305}