diff options
| -rw-r--r-- | cgit_70.mk | 3 | ||||
| -rw-r--r-- | ui_70-refs.c | 4 | ||||
| -rw-r--r-- | ui_70-shared.c | 215 | ||||
| -rw-r--r-- | ui_70-shared.h | 6 | ||||
| -rw-r--r-- | ui_70-tree.c | 328 | 
5 files changed, 479 insertions, 77 deletions
| @@ -100,7 +100,8 @@ CGIT_OBJ_NAMES += ui-stats.o  ##CGIT_OBJ_NAMES += ui-summary.o  CGIT_OBJ_NAMES += ui_70-summary.o  CGIT_OBJ_NAMES += ui-tag.o -CGIT_OBJ_NAMES += ui-tree.o +##CGIT_OBJ_NAMES += ui-tree.o +CGIT_OBJ_NAMES += ui_70-tree.o  CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES)) diff --git a/ui_70-refs.c b/ui_70-refs.c index da18f8a..6b7c900 100644 --- a/ui_70-refs.c +++ b/ui_70-refs.c @@ -71,10 +71,10 @@ static int print_branch(struct refinfo *ref)  		html("</td><td></td><td>");  		cgit_object_link(ref->object);  	} -	cgit_gopher_text("\t");	 +	cgit_gopher_tab();	  	cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL,  		      ctx.qry.showmsg, 0); -	cgit_gopher_text("\t"); +	cgit_gopher_tab();  	cgit_gopher_end_selector();  	return 0;  } diff --git a/ui_70-shared.c b/ui_70-shared.c index 30a1eaf..b5a6fa8 100644 --- a/ui_70-shared.c +++ b/ui_70-shared.c @@ -73,6 +73,28 @@ void gopher_vtxtf(const char *format, va_list ap)  	strbuf_release(&buf);  } + +void gopherf(const char *format, ...) +{ +	va_list args; +	struct strbuf buf = STRBUF_INIT; + +	va_start(args, format); +	strbuf_vaddf(&buf, format, args); +	va_end(args); +	cgit_gopher_text(buf.buf); +	strbuf_release(&buf); +} + + + +void gopher_fileperm(unsigned short mode) +{ +	gopherf("%c%c%c", (mode & 4 ? 'r' : '-'), +	      (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-')); +} + +  void cgit_gopher_textf(const char *fmt, va_list ap){  	va_list cp; @@ -320,7 +342,7 @@ static void site_url(const char *page, const char *search, const char *sort, int  	}  	if (page) { -		cgit_gopher_textf("?p=%s", page); +		gopherf("?p=%s", page);  		delim = "&";  	}  	if (search) { @@ -337,7 +359,7 @@ static void site_url(const char *page, const char *search, const char *sort, int  	}  	if (ofs) {  		cgit_gopher_text(delim); -		cgit_gopher_textf("ofs=%d", ofs); +		gopherf("ofs=%d", ofs);  	}  } @@ -477,9 +499,21 @@ void cgit_tag_link(const char *name, const char *title, const char *class,  void cgit_tree_link(const char *name, const char *title, const char *class,  		    const char *head, const char *rev, const char *path)  { +	  	reporevlink("tree", name, title, class, head, rev, path);  } + +void cgit_gopher_tree_link(const char *name, const char *title, const char *class, +		    const char *head, const char *rev, const char *path) +{ +	cgit_gopher_start_selector(GOPHER_MENU); +	cgit_gopher_text(name); +	cgit_gopher_tab(); +	cgit_tree_link(name, title, class, head, rev, path); +	cgit_gopher_end_selector(); +}	 +  void cgit_plain_link(const char *name, const char *title, const char *class,  		     const char *head, const char *rev, const char *path)  { @@ -518,7 +552,7 @@ void cgit_log_link(const char *name, const char *title, const char *class,  	if (ofs > 0) {  		cgit_gopher_text(delim);  		cgit_gopher_text("ofs="); -		cgit_gopher_textf("%d", ofs); +		gopherf("%d", ofs);  		delim = "&";  	}  	if (showmsg) { @@ -530,6 +564,21 @@ void cgit_log_link(const char *name, const char *title, const char *class,  		cgit_gopher_text(delim);  		cgit_gopher_text("follow=1");  	} +	cgit_gopher_tab(); +} + + +void cgit_gopher_log_link(const char *name, const char *title, const char *class, +		   const char *head, const char *rev, const char *path, +		   int ofs, const char *grep, const char *pattern, int showmsg, +		   int follow) +{ +	cgit_gopher_start_selector(GOPHER_MENU); +	cgit_gopher_text(name); +	cgit_gopher_tab(); +	cgit_log_link(name, title, class, head, rev, path, ofs, grep, pattern, showmsg, follow);	 +	cgit_gopher_end_selector(); +  }  void cgit_commit_link(const char *name, const char *title, const char *class, @@ -539,50 +588,64 @@ void cgit_commit_link(const char *name, const char *title, const char *class,  	delim = repolink(title, class, "commit", head, path);  	if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) { -		html(delim); -		html("id="); -		html_url_arg(rev); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("id="); +		cgit_gopher_text(rev); +		delim = "&";  	}  	if (ctx.qry.difftype) { -		html(delim); -		htmlf("dt=%d", ctx.qry.difftype); -		delim = "&"; +		cgit_gopher_text(delim); +		gopherf("dt=%d", ctx.qry.difftype); +		delim = "&";  	}  	if (ctx.qry.context > 0 && ctx.qry.context != 3) { -		html(delim); -		html("context="); -		htmlf("%d", ctx.qry.context); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("context="); +		gopherf("%d", ctx.qry.context); +		delim = "&";  	}  	if (ctx.qry.ignorews) { -		html(delim); -		html("ignorews=1"); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("ignorews=1"); +		delim = "&";  	}  	if (ctx.qry.follow) { -		html(delim); -		html("follow=1"); +		cgit_gopher_text(delim); +		cgit_gopher_text("follow=1");  	} -	html("'>");  	if (name[0] != '\0') { -		if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { -			html_ntxt(name, ctx.cfg.max_msg_len - 3); -			html("..."); -		} else -			html_txt(name); +		cgit_gopher_text(name);  	} else -		html_txt("(no commit message)"); -	html("</a>"); +		cgit_gopher_text("(no commit message)"); +	cgit_gopher_tab();  } +	 + +void cgit_gopher_commit_link(const char *name, const char *title, const char *class, +		      const char *head, const char *rev, const char *path) +{ +	cgit_gopher_start_selector(GOPHER_MENU); +	cgit_gopher_text(name); +	cgit_gopher_tab(); +	cgit_commit_link(name, title, class, head, rev, path);	 +	cgit_gopher_end_selector(); +} +  void cgit_refs_link(const char *name, const char *title, const char *class,  		    const char *head, const char *rev, const char *path)  { +	reporevlink("refs", name, title, class, head, rev, path); +} + + +void cgit_gopher_refs_link(const char *name, const char *title, const char *class, +		    const char *head, const char *rev, const char *path) +{  	cgit_gopher_start_selector(GOPHER_MENU);  	cgit_gopher_text(name);  	cgit_gopher_tab(); -	reporevlink("refs", name, title, class, head, rev, path); +	cgit_refs_link(name, title, class, head, rev, path);  	cgit_gopher_end_selector();  } @@ -601,40 +664,52 @@ void cgit_diff_link(const char *name, const char *title, const char *class,  	delim = repolink(title, class, "diff", head, path);  	if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) { -		html(delim); -		html("id="); -		html_url_arg(new_rev); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("id="); +		cgit_gopher_text(new_rev); +		delim = "&";  	}  	if (old_rev) { -		html(delim); -		html("id2="); -		html_url_arg(old_rev); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("id2="); +		cgit_gopher_text(old_rev); +		delim = "&";  	}  	if (ctx.qry.difftype) { -		html(delim); -		htmlf("dt=%d", ctx.qry.difftype); -		delim = "&"; +		cgit_gopher_text(delim); +		gopherf("dt=%d", ctx.qry.difftype); +		delim = "&";  	}  	if (ctx.qry.context > 0 && ctx.qry.context != 3) { -		html(delim); -		html("context="); -		htmlf("%d", ctx.qry.context); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("context="); +		gopherf("%d", ctx.qry.context); +		delim = "&";  	}  	if (ctx.qry.ignorews) { -		html(delim); -		html("ignorews=1"); -		delim = "&"; +		cgit_gopher_text(delim); +		cgit_gopher_text("ignorews=1"); +		delim = "&";  	}  	if (ctx.qry.follow) { -		html(delim); -		html("follow=1"); +		cgit_gopher_text(delim); +		cgit_gopher_text("follow=1");  	} -	html("'>"); -	html_txt(name); -	html("</a>"); +	cgit_gopher_tab(); +} +	 + +void cgit_gopher_diff_link(const char *name, const char *title, const char *class, +		    const char *head, const char *new_rev, const char *old_rev, +		    const char *path) +{ + +	cgit_gopher_start_selector(GOPHER_MENU); +	cgit_gopher_text(name); +	cgit_gopher_tab(); +	cgit_diff_link(name, title, class, head, new_rev, old_rev, path);	 +	cgit_gopher_end_selector(); +  }  void cgit_patch_link(const char *name, const char *title, const char *class, @@ -962,7 +1037,7 @@ void cgit_print_layout_start(void)  void cgit_print_layout_end(void)  { -	cgit_print_docend(); +	/*cgit_print_docend();*/  }  static void add_clone_urls(void (*fn)(const char *), char *txt, char *suffix) @@ -1091,20 +1166,20 @@ void cgit_print_pageheader(void)  				    NULL);  		cgit_summary_link("summary", NULL, hc("summary"),  				  ctx.qry.head); -		cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head, +		cgit_gopher_refs_link("refs", NULL, hc("refs"), ctx.qry.head,  			       ctx.qry.sha1, NULL); -		cgit_log_link("log", NULL, hc("log"), ctx.qry.head, +		cgit_gopher_log_link("log", NULL, hc("log"), ctx.qry.head,  			      NULL, ctx.qry.vpath, 0, NULL, NULL,  			      ctx.qry.showmsg, ctx.qry.follow);  		if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))  			cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,  				        ctx.qry.sha1, ctx.qry.vpath);  		else -			cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, +			cgit_gopher_tree_link("tree", NULL, hc("tree"), ctx.qry.head,  				       ctx.qry.sha1, ctx.qry.vpath); -		cgit_commit_link("commit", NULL, hc("commit"), +		cgit_gopher_commit_link("commit", NULL, hc("commit"),  				 ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath); -		cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head, +		cgit_gopher_diff_link("diff", NULL, hc("diff"), ctx.qry.head,  			       ctx.qry.sha1, ctx.qry.sha2, ctx.qry.vpath);  		if (ctx.repo->max_stats)  			cgit_stats_link("stats", NULL, hc("stats"), @@ -1114,15 +1189,7 @@ void cgit_print_pageheader(void)  			html_attr(ctx.repo->homepage);  			html("'>homepage</a>");  		} -		html("</td><td class='form'>"); -		html("<form class='right' method='get' action='"); -		if (ctx.cfg.virtual_root) { -			char *fileurl = cgit_fileurl(ctx.qry.repo, "log", -						   ctx.qry.vpath, NULL); -			html_url_path(fileurl); -			free(fileurl); -		} -	} else if (ctx.env.authenticated) { +	} else {  		char *currenturl = cgit_currenturl();  		site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0, 1);  		if (ctx.cfg.root_readme) @@ -1139,7 +1206,7 @@ void cgit_print_pageheader(void)  		html("</form>");  		free(currenturl);  	} -	if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) { +	if (ctx.repo && ctx.qry.vpath) {  		html("<div class='path'>");  		html("path: ");  		cgit_print_path_crumbs(ctx.qry.vpath); @@ -1158,16 +1225,16 @@ void cgit_print_pageheader(void)  void cgit_print_filemode(unsigned short mode)  {  	if (S_ISDIR(mode)) -		html("d"); +		cgit_gopher_text("d");  	else if (S_ISLNK(mode)) -		html("l"); +		cgit_gopher_text("l");  	else if (S_ISGITLINK(mode)) -		html("m"); +		cgit_gopher_text("m");  	else -		html("-"); -	html_fileperm(mode >> 6); -	html_fileperm(mode >> 3); -	html_fileperm(mode); +		cgit_gopher_text("-"); +	gopher_fileperm(mode >> 6); +	gopher_fileperm(mode >> 3); +	gopher_fileperm(mode);  }  void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base, diff --git a/ui_70-shared.h b/ui_70-shared.h index 7dd2d35..8abc3c6 100644 --- a/ui_70-shared.h +++ b/ui_70-shared.h @@ -24,6 +24,7 @@  #define GOPHER_SUMMARY_DATE_LEN	20  #define GOPHER_SUMMARY_AUTH_LEN 20  #define GOPHER_SUMMARY_AGE_LEN  10 +#define GOPHER_SUMMARY_MODE_LEN 12  #define GOPHER_PAD_CHAR ' ' @@ -39,6 +40,7 @@ void cgit_gopher_start_selector(char type);  void cgit_gopher_selector_descr(const char *descr);  void cgit_gopher_selector_link(const char *sel);  void cgit_gopher_text(const char *txt); +void cgit_gopher_tab();  void cgit_gopher_text_pad(const char *txt, int len);  void cgit_gopher_end_selector(); @@ -76,6 +78,10 @@ extern void cgit_log_link(const char *name, const char *title,  			  const char *class, const char *head, const char *rev,  			  const char *path, int ofs, const char *grep,  			  const char *pattern, int showmsg, int follow); +extern void cgit_gopher_log_link(const char *name, const char *title, +			  const char *class, const char *head, const char *rev, +			  const char *path, int ofs, const char *grep, +			  const char *pattern, int showmsg, int follow);  extern void cgit_commit_link(const char *name, const char *title,  			     const char *class, const char *head,  			     const char *rev, const char *path); diff --git a/ui_70-tree.c b/ui_70-tree.c new file mode 100644 index 0000000..ecef53e --- /dev/null +++ b/ui_70-tree.c @@ -0,0 +1,328 @@ +/* ui-tree.c: functions for tree output + * + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + *   (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-tree.h" +#include "html.h" +#include "ui_70-shared.h" + +struct walk_tree_context { +	char *curr_rev; +	char *match_path; +	int state; +}; + +static void print_text_buffer(const char *name, char *buf, unsigned long size) +{ +	cgit_gopher_text(buf); +} + +#define ROWLEN 32 + +static void print_binary_buffer(char *buf, unsigned long size) +{ +	unsigned long ofs, idx; +	static char ascii[ROWLEN + 1]; + +	html("<table summary='blob content' class='bin-blob'>\n"); +	html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); +	for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { +		htmlf("<tr><td class='right'>%04lx</td><td class='hex'>", ofs); +		for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) +			htmlf("%*s%02x", +			      idx == 16 ? 4 : 1, "", +			      buf[idx] & 0xff); +		html(" </td><td class='hex'>"); +		for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) +			ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; +		ascii[idx] = '\0'; +		html_txt(ascii); +		html("</td></tr>\n"); +	} +	html("</table>\n"); +} + +static void print_object(const struct object_id *oid, char *path, const char *basename, const char *rev) +{ +	enum object_type type; +	char *buf; +	unsigned long size; + +	type = oid_object_info(the_repository, oid, &size); +	if (type == OBJ_BAD) { +		cgit_gopher_error("Bad object name"); +		return; +	} + +	buf = read_object_file(oid, &type, &size); +	if (!buf) { +		cgit_gopher_error("Error reading object"); +		return; +	} + +	cgit_set_title_from_path(path); + +	/*cgit_print_layout_start();*/ + +	if (buffer_is_binary(buf, size)) +		print_binary_buffer(buf, size); +	else +		print_text_buffer(basename, buf, size); + +	free(buf); +} + +struct single_tree_ctx { +	struct strbuf *path; +	struct object_id oid; +	char *name; +	size_t count; +}; + +static int single_tree_cb(const struct object_id *oid, struct strbuf *base, +			  const char *pathname, unsigned mode, int stage, +			  void *cbdata) +{ +	struct single_tree_ctx *ctx = cbdata; + +	if (++ctx->count > 1) +		return -1; + +	if (!S_ISDIR(mode)) { +		ctx->count = 2; +		return -1; +	} + +	ctx->name = xstrdup(pathname); +	oidcpy(&ctx->oid, oid); +	strbuf_addf(ctx->path, "/%s", pathname); +	return 0; +} + +static void write_tree_link(const struct object_id *oid, char *name, +			    char *rev, struct strbuf *fullpath) +{ +	size_t initial_length = fullpath->len; +	struct tree *tree; +	struct single_tree_ctx tree_ctx = { +		.path = fullpath, +		.count = 1, +	}; +	struct pathspec paths = { +		.nr = 0 +	}; + +	oidcpy(&tree_ctx.oid, oid); + +	while (tree_ctx.count == 1) { +		cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev, +			       fullpath->buf); + +		tree = lookup_tree(&tree_ctx.oid); +		if (!tree) +			return; + +		free(tree_ctx.name); +		tree_ctx.name = NULL; +		tree_ctx.count = 0; + +		read_tree_recursive(tree, "", 0, 1, &paths, single_tree_cb, +				    &tree_ctx); + +		if (tree_ctx.count != 1) +			break; + +		html(" / "); +		name = tree_ctx.name; +	} + +	strbuf_setlen(fullpath, initial_length); +} + +static int ls_item(const struct object_id *oid, struct strbuf *base, +		const char *pathname, unsigned mode, int stage, void *cbdata) +{ +	struct walk_tree_context *walk_tree_ctx = cbdata; +	char *name; +	struct strbuf fullpath = STRBUF_INIT; +	struct strbuf class = STRBUF_INIT; +	enum object_type type; +	unsigned long size = 0; + +	name = xstrdup(pathname); +	strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", +		    ctx.qry.path ? "/" : "", name); + +	if (!S_ISGITLINK(mode)) { +		type = oid_object_info(the_repository, oid, &size); +		if (type == OBJ_BAD) { +			cgit_gopher_error("Bad object"); +			free(name); +			return 0; +		} +	} + +	cgit_gopher_start_selector(GOPHER_TXT); +	cgit_print_filemode(mode); +	cgit_gopher_text(" "); +	cgit_gopher_text_pad(name, GOPHER_SUMMARY_NAME_LEN); +	gopherf("%10ld", size); +	cgit_gopher_tab(); + +	if (S_ISGITLINK(mode)) { +		cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid)); +	} else if (S_ISDIR(mode)) { +		write_tree_link(oid, name, walk_tree_ctx->curr_rev, +				&fullpath); +	} else { +		char *ext = strrchr(name, '.'); +		strbuf_addstr(&class, "ls-blob"); +		if (ext) +			strbuf_addf(&class, " %s", ext + 1); +		cgit_tree_link(name, NULL, class.buf, ctx.qry.head, +			       walk_tree_ctx->curr_rev, fullpath.buf); +	} +/*	if (ctx.repo->max_stats) +		cgit_stats_link("stats", NULL, "button", ctx.qry.head, +				fullpath.buf); +	if (!S_ISGITLINK(mode)) +		cgit_plain_link("plain", NULL, "button", ctx.qry.head, +				walk_tree_ctx->curr_rev, fullpath.buf); +	if (!S_ISDIR(mode) && ctx.cfg.enable_blame) +		cgit_blame_link("blame", NULL, "button", ctx.qry.head, +				walk_tree_ctx->curr_rev, fullpath.buf); +*/ +	cgit_gopher_tab(); +	cgit_gopher_end_selector(); +	free(name); +	strbuf_release(&fullpath); +	strbuf_release(&class); +	return 0; +} + +static void ls_head(void) +{ +	cgit_print_layout_start(); +	cgit_gopher_start_selector(GOPHER_INFO); +	cgit_gopher_text_pad("Mode", GOPHER_SUMMARY_MODE_LEN); +	cgit_gopher_text_pad("Name", GOPHER_SUMMARY_NAME_LEN); +	cgit_gopher_text_pad("Size", GOPHER_SUMMARY_AGE_LEN); +	cgit_gopher_tab(); +	cgit_gopher_selector_link("Err"); +	cgit_gopher_end_selector(); +} + +static void ls_tail(void) +{ +/*	html("</table>\n"); +	cgit_print_layout_end(); +*/ +} + +static void ls_tree(const struct object_id *oid, char *path, struct walk_tree_context *walk_tree_ctx) +{ +	struct tree *tree; +	struct pathspec paths = { +		.nr = 0 +	}; + +	tree = parse_tree_indirect(oid); +	if (!tree) { +		cgit_gopher_error("Not a tree object"); +		return; +	} + +	ls_head(); +	read_tree_recursive(tree, "", 0, 1, &paths, ls_item, walk_tree_ctx); +	ls_tail(); +} + + +static int walk_tree(const struct object_id *oid, struct strbuf *base, +		const char *pathname, unsigned mode, int stage, void *cbdata) +{ +	struct walk_tree_context *walk_tree_ctx = cbdata; + +	if (walk_tree_ctx->state == 0) { +		struct strbuf buffer = STRBUF_INIT; + +		strbuf_addbuf(&buffer, base); +		strbuf_addstr(&buffer, pathname); +		if (strcmp(walk_tree_ctx->match_path, buffer.buf)) +			return READ_TREE_RECURSIVE; + +		if (S_ISDIR(mode)) { +			walk_tree_ctx->state = 1; +			cgit_set_title_from_path(buffer.buf); +			strbuf_release(&buffer); +			ls_head(); +			return READ_TREE_RECURSIVE; +		} else { +			walk_tree_ctx->state = 2; +			print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev); +			strbuf_release(&buffer); +			return 0; +		} +	} +	ls_item(oid, base, pathname, mode, stage, walk_tree_ctx); +	return 0; +} + +/* + * Show a tree or a blob + *   rev:  the commit pointing at the root tree object + *   path: path to tree or blob + */ +void cgit_print_tree(const char *rev, char *path) +{ +	struct object_id oid; +	struct commit *commit; +	struct pathspec_item path_items = { +		.match = path, +		.len = path ? strlen(path) : 0 +	}; +	struct pathspec paths = { +		.nr = path ? 1 : 0, +		.items = &path_items +	}; +	struct walk_tree_context walk_tree_ctx = { +		.match_path = path, +		.state = 0 +	}; + +	if (!rev) +		rev = ctx.qry.head; + +	if (get_oid(rev, &oid)) { +		cgit_gopher_error("Invalid revision name"); +		return; +	} +	commit = lookup_commit_reference(&oid); +	if (!commit || parse_commit(commit)) { +		cgit_gopher_error("Invalid commit reference"); +		return; +	} + +	walk_tree_ctx.curr_rev = xstrdup(rev); + +	if (path == NULL) { +		ls_tree(&commit->maybe_tree->object.oid, NULL, &walk_tree_ctx); +		goto cleanup; +	} + +	read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); +	if (walk_tree_ctx.state == 1) +		ls_tail(); +	else if (walk_tree_ctx.state == 2) +		; +	else +		cgit_print_error_page(404, "Not found", "Path not found"); + +cleanup: +	free(walk_tree_ctx.curr_rev); +} | 
