all repos — cgit @ 1418034e642fee67c981b31e4c3eb6e8ae14e303

a hyperfast web frontend for git written in c

cgit.c (view raw)

  1/* cgit.c: cgi for the git scm
  2 *
  3 * Copyright (C) 2006 Lars Hjemli
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include "cgit.h"
 10
 11const char cgit_version[] = CGIT_VERSION;
 12
 13int htmlfd = 0;
 14
 15char *cgit_root         = "/usr/src/git";
 16char *cgit_root_title   = "Git repository browser";
 17char *cgit_css          = "/cgit.css";
 18char *cgit_logo         = "/git-logo.png";
 19char *cgit_logo_link    = "http://www.kernel.org/pub/software/scm/git/docs/";
 20char *cgit_virtual_root = NULL;
 21
 22char *cgit_cache_root   = "/var/cache/cgit";
 23
 24int cgit_max_lock_attempts     =  5;
 25int cgit_cache_root_ttl        =  5;
 26int cgit_cache_repo_ttl        =  5;
 27int cgit_cache_dynamic_ttl     =  5;
 28int cgit_cache_static_ttl      = -1;
 29int cgit_cache_max_create_time =  5;
 30
 31char *cgit_repo_name    = NULL;
 32char *cgit_repo_desc    = NULL;
 33char *cgit_repo_owner   = NULL;
 34
 35int cgit_query_has_symref = 0;
 36int cgit_query_has_sha1   = 0;
 37
 38char *cgit_querystring  = NULL;
 39char *cgit_query_repo   = NULL;
 40char *cgit_query_page   = NULL;
 41char *cgit_query_head   = NULL;
 42char *cgit_query_sha1   = NULL;
 43
 44struct cacheitem cacheitem;
 45
 46void cgit_global_config_cb(const char *name, const char *value)
 47{
 48	if (!strcmp(name, "root"))
 49		cgit_root = xstrdup(value);
 50	else if (!strcmp(name, "root-title"))
 51		cgit_root_title = xstrdup(value);
 52	else if (!strcmp(name, "css"))
 53		cgit_css = xstrdup(value);
 54	else if (!strcmp(name, "logo"))
 55		cgit_logo = xstrdup(value);
 56	else if (!strcmp(name, "logo-link"))
 57		cgit_logo_link = xstrdup(value);
 58	else if (!strcmp(name, "virtual-root"))
 59		cgit_virtual_root = xstrdup(value);
 60}
 61
 62void cgit_repo_config_cb(const char *name, const char *value)
 63{
 64	if (!strcmp(name, "name"))
 65		cgit_repo_name = xstrdup(value);
 66	else if (!strcmp(name, "desc"))
 67		cgit_repo_desc = xstrdup(value);
 68	else if (!strcmp(name, "owner"))
 69		cgit_repo_owner = xstrdup(value);
 70}
 71
 72void cgit_querystring_cb(const char *name, const char *value)
 73{
 74	if (!strcmp(name,"r"))
 75		cgit_query_repo = xstrdup(value);
 76	else if (!strcmp(name, "p"))
 77		cgit_query_page = xstrdup(value);
 78	else if (!strcmp(name, "h")) {
 79		cgit_query_head = xstrdup(value);
 80		cgit_query_has_symref = 1;
 81	} else if (!strcmp(name, "id")) {
 82		cgit_query_sha1 = xstrdup(value);
 83		cgit_query_has_sha1 = 1;
 84	}
 85}
 86
 87static int get_one_line(char *txt)
 88{
 89	char *t;
 90
 91	for(t=txt; *t != '\n' && t != '\0'; t++)
 92		;
 93	*t = '\0';
 94	return t-txt-1;
 95}
 96
 97static void cgit_print_commit_shortlog(struct commit *commit)
 98{
 99	char *h, *t, *p; 
100	char *tree = NULL, *author = NULL, *subject = NULL;
101	int len;
102	time_t sec;
103	struct tm *time;
104	char buf[32];
105
106	h = t = commit->buffer;
107	
108	if (strncmp(h, "tree ", 5))
109		die("Bad commit format: %s", 
110		    sha1_to_hex(commit->object.sha1));
111	
112	len = get_one_line(h);
113	tree = h+5;
114	h += len + 2;
115
116	while (!strncmp(h, "parent ", 7))
117		h += get_one_line(h) + 2;
118	
119	if (!strncmp(h, "author ", 7)) {
120		author = h+7;
121		h += get_one_line(h) + 2;
122		t = author;
123		while(t!=h && *t!='<') 
124			t++;
125		*t='\0';
126		p = t;
127		while(--t!=author && *t==' ')
128			*t='\0';
129		while(++p!=h && *p!='>')
130			;
131		while(++p!=h && !isdigit(*p))
132			;
133
134		t = p;
135		while(++p && isdigit(*p))
136			;
137		*p = '\0';
138		sec = atoi(t);
139		time = gmtime(&sec);
140	}
141
142	while((len = get_one_line(h)) > 0)
143		h += len+2;
144
145	h++;
146	len = get_one_line(h);
147
148	subject = h;
149
150	html("<tr><td>");
151	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time);
152	html_txt(buf);
153	html("</td><td>");
154	char *qry = fmt("id=%s", sha1_to_hex(commit->object.sha1));
155	char *url = cgit_pageurl(cgit_query_repo, "view", qry);
156	html_link_open(url, NULL, NULL);
157	html_txt(subject);
158	html_link_close();
159	html("</td><td>");
160	html_txt(author);
161	html("</td></tr>\n");
162}
163
164static void cgit_print_log(const char *tip, int ofs, int cnt)
165{
166	struct rev_info rev;
167	struct commit *commit;
168	const char *argv[2] = {NULL, tip};
169	int n = 0;
170	
171	init_revisions(&rev, NULL);
172	rev.abbrev = DEFAULT_ABBREV;
173	rev.commit_format = CMIT_FMT_DEFAULT;
174	rev.verbose_header = 1;
175	rev.show_root_diff = 0;
176	setup_revisions(2, argv, &rev, NULL);
177	prepare_revision_walk(&rev);
178
179	html("<h2>Log</h2>");
180	html("<table class='list'>");
181	html("<tr><th>Date</th><th>Message</th><th>Author</th></tr>\n");
182	while ((commit = get_revision(&rev)) != NULL && n++ < 100) {
183		cgit_print_commit_shortlog(commit);
184		free(commit->buffer);
185		commit->buffer = NULL;
186		free_commit_list(commit->parents);
187		commit->parents = NULL;
188	}
189	html("</table>\n");
190}
191
192static void cgit_print_object(char *hex)
193{
194	unsigned char sha1[20];
195	//struct object *object;
196	char type[20];
197	unsigned char *buf;
198	unsigned long size;
199
200	if (get_sha1_hex(hex, sha1)){
201		cgit_print_error(fmt("Bad hex value: %s", hex));
202	        return;
203	}
204
205	if (sha1_object_info(sha1, type, NULL)){
206		cgit_print_error("Bad object name");
207		return;
208	}
209
210	buf = read_sha1_file(sha1, type, &size);
211	if (!buf) {
212		cgit_print_error("Error reading object");
213		return;
214	}
215
216	buf[size] = '\0';
217	html("<h2>Object view</h2>");
218	htmlf("sha1=%s<br/>type=%s<br/>size=%i<br/>", hex, type, size);
219	html("<pre>");
220	html_txt(buf);
221	html("</pre>");
222}
223
224static void cgit_print_repo_page(struct cacheitem *item)
225{
226	if (chdir(fmt("%s/%s", cgit_root, cgit_query_repo)) || 
227	    cgit_read_config("info/cgit", cgit_repo_config_cb)) {
228		char *title = fmt("%s - %s", cgit_root_title, "Bad request");
229		cgit_print_docstart(title, item);
230		cgit_print_pageheader(title);
231		cgit_print_error(fmt("Unable to scan repository: %s",
232				     strerror(errno)));
233		cgit_print_docend();
234		return;
235	}
236	setenv("GIT_DIR", fmt("%s/%s", cgit_root, cgit_query_repo), 1);
237	char *title = fmt("%s - %s", cgit_repo_name, cgit_repo_desc);
238	cgit_print_docstart(title, item);
239	cgit_print_pageheader(title);
240	if (!cgit_query_page)
241		cgit_print_repo_summary();
242	else if (!strcmp(cgit_query_page, "log")) {
243		cgit_print_log(cgit_query_head, 0, 100);
244	} else if (!strcmp(cgit_query_page, "view")) {
245		cgit_print_object(cgit_query_sha1);
246	}
247	cgit_print_docend();
248}
249
250static void cgit_fill_cache(struct cacheitem *item)
251{
252	htmlfd = item->fd;
253	item->st.st_mtime = time(NULL);
254	if (cgit_query_repo)
255		cgit_print_repo_page(item);
256	else
257		cgit_print_repolist(item);
258}
259
260static void cgit_refresh_cache(struct cacheitem *item)
261{
262	int i = 0;
263
264	cache_prepare(item);
265 top:
266	if (++i > cgit_max_lock_attempts) {
267		die("cgit_refresh_cache: unable to lock %s: %s",
268		    item->name, strerror(errno));
269	}
270	if (!cache_exist(item)) {
271		if (!cache_lock(item)) {
272			sleep(1);
273			goto top;
274		}
275		if (!cache_exist(item))
276			cgit_fill_cache(item);
277		cache_unlock(item);
278	} else if (cache_expired(item) && cache_lock(item)) {
279		if (cache_expired(item))
280			cgit_fill_cache(item);
281		cache_unlock(item);
282	}
283}
284
285static void cgit_print_cache(struct cacheitem *item)
286{
287	static char buf[4096];
288	ssize_t i;
289
290	int fd = open(item->name, O_RDONLY);
291	if (fd<0)
292		die("Unable to open cached file %s", item->name);
293
294	while((i=read(fd, buf, sizeof(buf))) > 0)
295		write(STDOUT_FILENO, buf, i);
296
297	close(fd);
298}
299
300int main(int argc, const char **argv)
301{
302	cgit_read_config("/etc/cgitrc", cgit_global_config_cb);
303	cgit_querystring = xstrdup(getenv("QUERY_STRING"));
304	cgit_parse_query(cgit_querystring, cgit_querystring_cb);
305	cgit_refresh_cache(&cacheitem);
306	cgit_print_cache(&cacheitem);
307	return 0;
308}