diff options
| author | Lars Hjemli <hjemli@gmail.com> | 2006-12-09 15:18:17 +0100 | 
|---|---|---|
| committer | Lars Hjemli <hjemli@gmail.com> | 2006-12-09 15:18:17 +0100 | 
| commit | 0d169ada2ba81210ab1191a5f2212662e90db77e (patch) | |
| tree | 402b54583db269323ebb28e5fbf2075c4c1b3d85 /cgit.c | |
| download | cgit-0d169ada2ba81210ab1191a5f2212662e90db77e.tar.gz cgit-0d169ada2ba81210ab1191a5f2212662e90db77e.tar.bz2 cgit-0d169ada2ba81210ab1191a5f2212662e90db77e.zip | |
Import cgit prototype from git tree
This enables basic cgit functionality, using libgit.a and xdiff/lib.a from
git + a custom "git.h" + openssl for sha1 routines.
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (limited to '')
| -rw-r--r-- | cgit.c | 410 | ||||
| -rw-r--r-- | cgit.css | 63 | 
2 files changed, 473 insertions, 0 deletions
| @@ -0,0 +1,410 @@ +#include "cgit.h" + +static const char cgit_doctype[] = +"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" +"  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; + +static const char cgit_error[] = +"<div class='error'>%s</div>"; + +static const char cgit_lib_error[] = +"<div class='error'>%s: %s</div>"; + + +char *cgit_root         = "/var/git"; +char *cgit_root_title   = "Git repository browser"; +char *cgit_css          = "/cgit.css"; +char *cgit_logo         = "/git-logo.png"; +char *cgit_logo_link    = "http://www.kernel.org/pub/software/scm/git/docs/"; +char *cgit_virtual_root = NULL; + +char *cgit_repo_name    = NULL; +char *cgit_repo_desc    = NULL; +char *cgit_repo_owner   = NULL; + +char *cgit_query_repo   = NULL; +char *cgit_query_page   = NULL; +char *cgit_query_head   = NULL; + +int cgit_parse_query(char *txt, configfn fn) +{ +	char *t = txt, *value = NULL, c; + +	if (!txt) +		return 0; + +	while((c=*t) != '\0') { +		if (c=='=') { +			*t = '\0'; +			value = t+1; +		} else if (c=='&') { +			*t = '\0'; +			(*fn)(txt, value); +			txt = t+1; +			value = NULL; +		} +		t++; +	} +	if (t!=txt) +		(*fn)(txt, value); +	return 0; +} + +void cgit_global_config_cb(const char *name, const char *value) +{ +	if (!strcmp(name, "root")) +		cgit_root = xstrdup(value); +	else if (!strcmp(name, "root-title")) +		cgit_root_title = xstrdup(value); +	else if (!strcmp(name, "css")) +		cgit_css = xstrdup(value); +	else if (!strcmp(name, "logo")) +		cgit_logo = xstrdup(value); +	else if (!strcmp(name, "logo-link")) +		cgit_logo_link = xstrdup(value); +	else if (!strcmp(name, "virtual-root")) +		cgit_virtual_root = xstrdup(value); +} + +void cgit_repo_config_cb(const char *name, const char *value) +{ +	if (!strcmp(name, "name")) +		cgit_repo_name = xstrdup(value); +	else if (!strcmp(name, "desc")) +		cgit_repo_desc = xstrdup(value); +	else if (!strcmp(name, "owner")) +		cgit_repo_owner = xstrdup(value); +} + +void cgit_querystring_cb(const char *name, const char *value) +{ +	if (!strcmp(name,"r")) +		cgit_query_repo = xstrdup(value); +	else if (!strcmp(name, "p")) +		cgit_query_page = xstrdup(value); +	else if (!strcmp(name, "h")) +		cgit_query_head = xstrdup(value); +} + +char *cgit_repourl(const char *reponame) +{ +	if (cgit_virtual_root) { +		return fmt("%s/%s/", cgit_virtual_root, reponame); +	} else { +		return fmt("?r=%s", reponame); +	} +} + +char *cgit_pageurl(const char *reponame, const char *pagename,  +		   const char *query) +{ +	if (cgit_virtual_root) { +		return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame,  +			   pagename, query); +	} else { +		return fmt("?r=%s&p=%s&%s", reponame, pagename, query); +	} +} + +static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1, +				int flags, void *cb_data) +{ +	struct commit *commit; +	char buf[256], *url; + +	commit = lookup_commit(sha1); +	if (commit && !parse_commit(commit)){ +		html("<tr><td>"); +		url = cgit_pageurl(cgit_query_repo, "log",  +				   fmt("h=%s", refname)); +		html_link_open(url, NULL, NULL); +		strncpy(buf, refname, sizeof(buf)); +		html_txt(buf); +		html_link_close(); +		html("</td><td>"); +		pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, buf, +				    sizeof(buf), 0, NULL, NULL, 0); +		html_txt(buf); +		html("</td></tr>\n"); +	} else { +		html("<tr><td>"); +		html_txt(buf); +		html("</td><td>"); +		htmlf("*** bad ref %s", sha1_to_hex(sha1)); +		html("</td></tr>\n"); +	} +	return 0; +} + +static void cgit_print_docstart(char *title) +{ +	html("Content-Type: text/html; charset=utf-8\n"); +	html("\n"); +	html(cgit_doctype); +	html("<html>\n"); +	html("<head>\n"); +	html("<title>"); +	html_txt(title); +	html("</title>\n"); +	html("<link rel='stylesheet' type='text/css' href='"); +	html_attr(cgit_css); +	html("'/>\n"); +	html("</head>\n"); +	html("<body>\n"); +} + +static void cgit_print_docend() +{ +	html("</body>\n</html>\n"); +} + +static void cgit_print_pageheader(char *title) +{ +	html("<div id='header'>"); +	htmlf("<a href='%s'>", cgit_logo_link); +	htmlf("<img id='logo' src='%s'/>\n", cgit_logo); +	htmlf("</a>"); +	html_txt(title); +	html("</div>"); +} + +static void cgit_print_repolist() +{ +	DIR *d; +	struct dirent *de; +	struct stat st; +	char *name; + +	cgit_print_docstart(cgit_root_title); +	cgit_print_pageheader(cgit_root_title); + +	if (!(d = opendir("."))) { +		htmlf(cgit_lib_error, "Unable to scan repository directory", +		      strerror(errno)); +		cgit_print_docend(); +		return; +	} + +	html("<h2>Repositories</h2>\n"); +	html("<table class='list'>"); +	html("<tr><th>Name</th><th>Description</th><th>Owner</th></tr>\n"); +	while ((de = readdir(d)) != NULL) { +		if (de->d_name[0] == '.') +			continue; +		if (stat(de->d_name, &st) < 0) +			continue; +		if (!S_ISDIR(st.st_mode)) +			continue; + +		cgit_repo_name = cgit_repo_desc = cgit_repo_owner = NULL; +		name = fmt("%s/.git/info/cgit", de->d_name); +		if (cgit_read_config(name, cgit_repo_config_cb)) +			continue; + +		html("<tr><td>"); +		html_link_open(cgit_repourl(de->d_name), NULL, NULL); +		html_txt(cgit_repo_name); +		html_link_close(); +		html("</td><td>"); +		html_txt(cgit_repo_desc); +		html("</td><td>"); +		html_txt(cgit_repo_owner); +		html("</td></tr>\n"); +	} +	closedir(d); +	html("</table>"); +	cgit_print_docend(); +} + +static void cgit_print_branches() +{ +	html("<table class='list'>"); +	html("<tr><th>Branch name</th><th>Head commit</th></tr>\n"); +	for_each_branch_ref(cgit_print_branch_cb, NULL); +	html("</table>"); +} + +static int get_one_line(char *txt) +{ +	char *t; + +	for(t=txt; *t != '\n' && t != '\0'; t++) +		; +	*t = '\0'; +	return t-txt-1; +} + +static void cgit_print_commit_shortlog(struct commit *commit) +{ +	char *h, *t, *p;  +	char *tree = NULL, *author = NULL, *subject = NULL; +	int len; +	time_t sec; +	struct tm *time; +	char buf[32]; + +	h = t = commit->buffer; +	 +	if (strncmp(h, "tree ", 5)) +		die("Bad commit format: %s",  +		    sha1_to_hex(commit->object.sha1)); +	 +	len = get_one_line(h); +	tree = h+5; +	h += len + 2; + +	while (!strncmp(h, "parent ", 7)) +		h += get_one_line(h) + 2; +	 +	if (!strncmp(h, "author ", 7)) { +		author = h+7; +		h += get_one_line(h) + 2; +		t = author; +		while(t!=h && *t!='<')  +			t++; +		*t='\0'; +		p = t; +		while(--t!=author && *t==' ') +			*t='\0'; +		while(++p!=h && *p!='>') +			; +		while(++p!=h && !isdigit(*p)) +			; + +		t = p; +		while(++p && isdigit(*p)) +			; +		*p = '\0'; +		sec = atoi(t); +		time = gmtime(&sec); +	} + +	while((len = get_one_line(h)) > 0) +		h += len+2; + +	h++; +	len = get_one_line(h); + +	subject = h; + +	html("<tr><td>"); +	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time); +	html_txt(buf); +	html("</td><td>"); +	char *qry = fmt("h=%s", sha1_to_hex(commit->object.sha1)); +	char *url = cgit_pageurl(cgit_query_repo, "view", qry); +	html_link_open(url, NULL, NULL); +	html_txt(subject); +	html_link_close(); +	html("</td><td>"); +	html_txt(author); +	html("</td></tr>\n"); +} + +static void cgit_print_log(const char *tip, int ofs, int cnt) +{ +	struct rev_info rev; +	struct commit *commit; +	const char *argv[2] = {NULL, tip}; +	int n = 0; +	 +	init_revisions(&rev, NULL); +	rev.abbrev = DEFAULT_ABBREV; +	rev.commit_format = CMIT_FMT_DEFAULT; +	rev.verbose_header = 1; +	rev.show_root_diff = 0; +	setup_revisions(2, argv, &rev, NULL); +	prepare_revision_walk(&rev); + +	html("<h2>Log</h2>"); +	html("<table class='list'>"); +	html("<tr><th>Date</th><th>Message</th><th>Author</th></tr>\n"); +	while ((commit = get_revision(&rev)) != NULL && n++ < 100) { +		cgit_print_commit_shortlog(commit); +		free(commit->buffer); +		commit->buffer = NULL; +		free_commit_list(commit->parents); +		commit->parents = NULL; +	} +	html("</table>\n"); +} + +static void cgit_print_repo_summary() +{ +	html("<h2>"); +	html_txt("Repo summary page"); +	html("</h2>"); +	cgit_print_branches(); +} + +static void cgit_print_object(char *hex) +{ +	unsigned char sha1[20]; +	//struct object *object; +	char type[20]; +	unsigned char *buf; +	unsigned long size; + +	if (get_sha1_hex(hex, sha1)){ +		htmlf(cgit_error, "Bad hex value"); +	        return; +	} + +	if (sha1_object_info(sha1, type, NULL)){ +		htmlf(cgit_error, "Bad object name"); +		return; +	} + +	buf = read_sha1_file(sha1, type, &size); +	if (!buf) { +		htmlf(cgit_error, "Error reading object"); +		return; +	} + +	buf[size] = '\0'; +	html("<h2>Object view</h2>"); +	htmlf("sha1=%s<br/>type=%s<br/>size=%i<br/>", hex, type, size); +	html("<pre>"); +	html_txt(buf); +	html("</pre>"); +} + +static void cgit_print_repo_page() +{ +	if (chdir(cgit_query_repo) ||  +	    cgit_read_config(".git/info/cgit", cgit_repo_config_cb)) { +		char *title = fmt("%s - %s", cgit_root_title, "Bad request"); +		cgit_print_docstart(title); +		cgit_print_pageheader(title); +		htmlf(cgit_lib_error, "Unable to scan repository", +		      strerror(errno)); +		cgit_print_docend(); +		return; +	} +	 +	char *title = fmt("%s - %s", cgit_repo_name, cgit_repo_desc); +	cgit_print_docstart(title); +	cgit_print_pageheader(title); +	if (!cgit_query_page) +		cgit_print_repo_summary(); +	else if (!strcmp(cgit_query_page, "log")) { +		cgit_print_log(cgit_query_head, 0, 100); +	} else if (!strcmp(cgit_query_page, "view")) { +		cgit_print_object(cgit_query_head); +	} +	cgit_print_docend(); +} + +int main(int argc, const char **argv) +{ +	if (cgit_read_config("/etc/cgitrc", cgit_global_config_cb)) +		die("Error reading config: %d %s", errno, strerror(errno)); + +	chdir(cgit_root); +	cgit_parse_query(getenv("QUERY_STRING"), cgit_querystring_cb); +	if (cgit_query_repo) +		cgit_print_repo_page(); +	else +		cgit_print_repolist(); +	return 0; +} diff --git a/cgit.css b/cgit.css new file mode 100644 index 0000000..3ed0c22 --- /dev/null +++ b/cgit.css @@ -0,0 +1,63 @@ +body { +	font-family: arial; +	font-size: normal; +	background: white; +	padding: 0em; +	margin: 0.5em; +} + + +h2 { +	font-size: normal; +	font-weight: bold; +	margin-bottom: 0.1em; +} + + +table.list { +	border: solid 1px black; +	border-collapse: collapse; +	border: solid 1px #aaa; +} + +table.list th { +	text-align: left; +	font-weight: bold; +	background: #ddd; +	border-bottom: solid 1px #aaa; +	padding: 0.1em 0.5em 0.1em; +	vertical-align: baseline; +} +table.list td { +	border: none; +	padding: 0.1em 0.5em; +	background: white; +} + +img { +	border: none; +} + + +div#header { +	background-color: #ddd; +	padding: 0.25em 0.25em 0.25em 0.5em; +	font-size: 150%; +	font-weight: bold; +	border: solid 1px #aaa; +	vertical-align: middle; +} + +div#header img#logo { +	float: right; +} + +div#content { +	margin: 0.5em 0.5em; +} + +div.error { +	color: red; +	font-weight: bold; +	margin: 1em 2em; +}
\ No newline at end of file | 
