chiark / gitweb /
Misc cleanups
[tig] / cgit.c
1 /* Cursed git browser
2  *
3  * Copyright (c) Jonas Fonseca, 2006
4  *
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.
9  */
10
11 #include <stdarg.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <signal.h>
17
18 #include <curses.h>
19
20
21 #define MSG_HELP "(q)uit, (s)hell, (j) down, (k) up"
22
23 #define KEY_ESC 27
24 #define KEY_TAB 9
25
26 //WINDOW *titlewin;
27 WINDOW *mainwin;
28 WINDOW *statuswin;
29
30 typedef void (*pipe_reader_T)(char *, int);
31
32 FILE *pipe;
33 long  pipe_lineno;
34 pipe_reader_T pipe_reader;
35
36
37 static void
38 put_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
50 /*
51  * Init and quit
52  */
53
54 static void
55 quit(int sig)
56 {
57         endwin();
58
59         /* do your non-curses wrapup here */
60
61         exit(0);
62 }
63
64 static void
65 init_colors(void)
66 {
67         int bg = COLOR_BLACK;
68
69         start_color();
70
71         if (use_default_colors() != ERR)
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);
82 }
83
84 static void
85 init(void)
86 {
87         int x, y;
88
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 */
95         leaveok(stdscr, TRUE);
96
97         if (has_colors())
98                 init_colors();
99
100         getmaxyx(stdscr, y, x);
101
102 #if 0
103         titlewin = newwin(1, 0, y - 2, 0);
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);
111 #endif
112         statuswin = newwin(1, 0, y - 1, 0);
113
114         wattrset(statuswin, COLOR_PAIR(COLOR_GREEN));
115         put_status(MSG_HELP);
116
117         mainwin = newwin(y - 1, 0, 0, 0);
118         scrollok(mainwin, TRUE);
119         keypad(mainwin, TRUE);  /* enable keyboard mapping */
120 }
121
122 /*
123  * Pipe readers
124  */
125
126 #define DIFF_CMD        \
127         "git-rev-list HEAD^..HEAD | " \
128         "git-diff-tree --stdin --pretty -r --cc --always --stat"
129
130
131 #define LOG_CMD0 \
132         "git-rev-list $(git-rev-parse --since=1.month) HEAD | " \
133         "git-diff-tree --stdin --pretty -r --root"
134
135 #define LOG_CMD \
136         "git-rev-list HEAD | git-diff-tree --stdin --pretty -r --root"
137
138 static void
139 log_reader(char *line, int lineno)
140 {
141         static int log_reader_skip;
142
143         if (!line) {
144                 wattrset(mainwin, A_NORMAL);
145                 log_reader_skip = 0;
146                 return;
147         }
148
149         if (!strncmp("commit ", line, 7)) {
150                 wattrset(mainwin, COLOR_PAIR(COLOR_GREEN));
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] == ':') {
177                 pipe_lineno--;
178                 log_reader_skip = 1;
179                 return;
180
181         } else if (log_reader_skip) {
182                 pipe_lineno--;
183                 log_reader_skip = 0;
184                 return;
185
186         } else {
187                 wattrset(mainwin, A_NORMAL);
188         }
189
190         mvwaddstr(mainwin, lineno, 0, line);
191 }
192
193 static FILE *
194 open_pipe(char *cmd, pipe_reader_T reader)
195 {
196         pipe = popen(cmd, "r");
197         pipe_lineno = 0;
198         pipe_reader = reader;
199         wclear(mainwin);
200         wmove(mainwin, 0, 0);
201         put_status("Loading...");
202         return pipe;
203 }
204
205 static void
206 read_pipe(int lines)
207 {
208         char buffer[BUFSIZ];
209         char *line;
210         int x, y;
211
212         while ((line = fgets(buffer, sizeof(buffer), pipe))) {
213                 int linelen;
214
215                 if (!--lines)
216                         break;
217
218                 linelen = strlen(line);
219                 if (linelen)
220                         line[linelen - 1] = 0;
221
222                 pipe_reader(line, pipe_lineno++);
223         }
224
225         if (feof(pipe) || ferror(pipe)) {
226                 pipe_reader(NULL, pipe_lineno - 1);
227                 pclose(pipe);
228                 pipe = NULL;
229                 pipe_reader = NULL;
230                 put_status("%s (lines %d)", MSG_HELP, pipe_lineno - 1);
231         }
232 }
233
234 /*
235  * Main
236  */
237
238 int
239 main(int argc, char *argv[])
240 {
241         init();
242
243         //pipe = open_pipe(LOG_CMD, log_reader);
244
245         for (;;) {
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
255                 /* No input from wgetch() with nodelay() enabled. */
256                 if (c == ERR)
257                         continue;
258
259                 /* Process the command keystroke */
260                 switch (c) {
261                 case KEY_ESC:
262                 case 'q':
263                         quit(0);
264                         return 0;
265
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
276                 case 'c':
277                         wclear(mainwin);
278                         break;
279
280                 case 'd':
281                         pipe = open_pipe(DIFF_CMD, log_reader);
282                         break;
283
284                 case 'l':
285                         pipe = open_pipe(LOG_CMD, log_reader);
286                         break;
287
288                 case 's':
289                         mvwaddstr(statuswin, 0, 0, "Shelling out...");
290                         def_prog_mode();           /* save current tty modes */
291                         endwin();                  /* restore original tty modes */
292                         system("sh");              /* run shell */
293
294                         werase(statuswin);
295                         mvwaddstr(statuswin, 0, 0, MSG_HELP);
296                         reset_prog_mode();
297                         break;
298                 }
299
300                 redrawwin(mainwin);
301                 wrefresh(mainwin);
302         }
303
304         quit(0);
305 }