summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Fleischer <cgit@cryptocrack.de>2014-01-15 21:53:15 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2014-01-17 00:44:54 +0100
commitf60ffa143cca61e9729ac71033e1a556cf422871 (patch)
treeff9122fef2779ddea8e37806cc66dc67b63df99f
parenta431326e8fab8153905fbde036dd3c9fb4cc8eaa (diff)
Switch to exclusively using global ctx
Drop the context parameter from the following functions (and all static
helpers used by them) and use the global context instead:

* cgit_print_http_headers()
* cgit_print_docstart()
* cgit_print_pageheader()

Remove context parameter from all commands

Drop the context parameter from the following functions (and all static
helpers used by them) and use the global context instead:

* cgit_get_cmd()
* All cgit command functions.
* cgit_clone_info()
* cgit_clone_objects()
* cgit_clone_head()
* cgit_print_plain()
* cgit_show_stats()

In initialization routines, use the global context variable instead of
passing a pointer around locally.

Remove callback data parameter for cache slots

This is no longer needed since the context is always read from the
global context variable.

Signed-off-by: Lukas Fleischer <cgit@cryptocrack.de>
-rw-r--r--cache.c14
-rw-r--r--cache.h5
-rw-r--r--cgit.c322
-rw-r--r--cmd.c102
-rw-r--r--cmd.h4
-rw-r--r--ui-atom.c2
-rw-r--r--ui-blob.c2
-rw-r--r--ui-clone.c48
-rw-r--r--ui-clone.h6
-rw-r--r--ui-diff.c2
-rw-r--r--ui-patch.c2
-rw-r--r--ui-plain.c14
-rw-r--r--ui-plain.h2
-rw-r--r--ui-repolist.c6
-rw-r--r--ui-shared.c303
-rw-r--r--ui-shared.h6
-rw-r--r--ui-snapshot.c8
-rw-r--r--ui-stats.c29
-rw-r--r--ui-stats.h2
19 files changed, 437 insertions, 442 deletions
diff --git a/cache.c b/cache.c
index fa83ddc..fcd461f 100644
--- a/cache.c
+++ b/cache.c
@@ -24,7 +24,6 @@ struct cache_slot {
 	int keylen;
 	int ttl;
 	cache_fill_fn fn;
-	void *cbdata;
 	int cache_fd;
 	int lock_fd;
 	const char *cache_name;
@@ -187,7 +186,7 @@ static int fill_slot(struct cache_slot *slot)
 		return errno;
 
 	/* Generate cache content */
-	slot->fn(slot->cbdata);
+	slot->fn();
 
 	/* Restore stdout */
 	if (dup2(tmp, STDOUT_FILENO) == -1)
@@ -277,7 +276,7 @@ static int process_slot(struct cache_slot *slot)
 	if ((err = lock_slot(slot)) != 0) {
 		cache_log("[cgit] Unable to lock slot %s: %s (%d)\n",
 			  slot->lock_name, strerror(err), err);
-		slot->fn(slot->cbdata);
+		slot->fn();
 		return 0;
 	}
 
@@ -286,7 +285,7 @@ static int process_slot(struct cache_slot *slot)
 			  slot->lock_name, strerror(err), err);
 		unlock_slot(slot, 0);
 		close_lock(slot);
-		slot->fn(slot->cbdata);
+		slot->fn();
 		return 0;
 	}
 	// We've got a valid cache slot in the lock file, which
@@ -310,7 +309,7 @@ static int process_slot(struct cache_slot *slot)
 
 /* Print cached content to stdout, generate the content if necessary. */
 int cache_process(int size, const char *path, const char *key, int ttl,
-		  cache_fill_fn fn, void *cbdata)
+		  cache_fill_fn fn)
 {
 	unsigned long hash;
 	int i;
@@ -321,14 +320,14 @@ int cache_process(int size, const char *path, const char *key, int ttl,
 
 	/* If the cache is disabled, just generate the content */
 	if (size <= 0) {
-		fn(cbdata);
+		fn();
 		return 0;
 	}
 
 	/* Verify input, calculate filenames */
 	if (!path) {
 		cache_log("[cgit] Cache path not specified, caching is disabled\n");
-		fn(cbdata);
+		fn();
 		return 0;
 	}
 	if (!key)
@@ -343,7 +342,6 @@ int cache_process(int size, const char *path, const char *key, int ttl,
 	strbuf_addbuf(&lockname, &filename);
 	strbuf_addstr(&lockname, ".lock");
 	slot.fn = fn;
-	slot.cbdata = cbdata;
 	slot.ttl = ttl;
 	slot.cache_name = filename.buf;
 	slot.lock_name = lockname.buf;
diff --git a/cache.h b/cache.h
index 5cfdb4f..9392836 100644
--- a/cache.h
+++ b/cache.h
@@ -6,7 +6,7 @@
 #ifndef CGIT_CACHE_H
 #define CGIT_CACHE_H
 
-typedef void (*cache_fill_fn)(void *cbdata);
+typedef void (*cache_fill_fn)(void);
 
 
 /* Print cached content to stdout, generate the content if necessary.
@@ -17,13 +17,12 @@ typedef void (*cache_fill_fn)(void *cbdata);
  *   key     the key used to lookup cache files
  *   ttl     max cache time in seconds for this key
  *   fn      content generator function for this key
- *   cbdata  user-supplied data to the content generator function
  *
  * Return value
  *   0 indicates success, everyting else is an error
  */
 extern int cache_process(int size, const char *path, const char *key, int ttl,
-			 cache_fill_fn fn, void *cbdata);
+			 cache_fill_fn fn);
 
 
 /* List info about all cache entries on stdout */
diff --git a/cgit.c b/cgit.c
index 994957f..ec8f69c 100644
--- a/cgit.c
+++ b/cgit.c
@@ -322,82 +322,82 @@ static void querystring_cb(const char *name, const char *value)
 	}
 }
 
-static void prepare_context(struct cgit_context *ctx)
+static void prepare_context(void)
 {
-	memset(ctx, 0, sizeof(*ctx));
-	ctx->cfg.agefile = "info/web/last-modified";
-	ctx->cfg.nocache = 0;
-	ctx->cfg.cache_size = 0;
-	ctx->cfg.cache_max_create_time = 5;
-	ctx->cfg.cache_root = CGIT_CACHE_ROOT;
-	ctx->cfg.cache_about_ttl = 15;
-	ctx->cfg.cache_repo_ttl = 5;
-	ctx->cfg.cache_root_ttl = 5;
-	ctx->cfg.cache_scanrc_ttl = 15;
-	ctx->cfg.cache_dynamic_ttl = 5;
-	ctx->cfg.cache_static_ttl = -1;
-	ctx->cfg.case_sensitive_sort = 1;
-	ctx->cfg.branch_sort = 0;
-	ctx->cfg.commit_sort = 0;
-	ctx->cfg.css = "/cgit.css";
-	ctx->cfg.logo = "/cgit.png";
-	ctx->cfg.favicon = "/favicon.ico";
-	ctx->cfg.local_time = 0;
-	ctx->cfg.enable_http_clone = 1;
-	ctx->cfg.enable_index_owner = 1;
-	ctx->cfg.enable_tree_linenumbers = 1;
-	ctx->cfg.enable_git_config = 0;
-	ctx->cfg.max_repo_count = 50;
-	ctx->cfg.max_commit_count = 50;
-	ctx->cfg.max_lock_attempts = 5;
-	ctx->cfg.max_msg_len = 80;
-	ctx->cfg.max_repodesc_len = 80;
-	ctx->cfg.max_blob_size = 0;
-	ctx->cfg.max_stats = 0;
-	ctx->cfg.project_list = NULL;
-	ctx->cfg.renamelimit = -1;
-	ctx->cfg.remove_suffix = 0;
-	ctx->cfg.robots = "index, nofollow";
-	ctx->cfg.root_title = "Git repository browser";
-	ctx->cfg.root_desc = "a fast webinterface for the git dscm";
-	ctx->cfg.scan_hidden_path = 0;
-	ctx->cfg.script_name = CGIT_SCRIPT_NAME;
-	ctx->cfg.section = "";
-	ctx->cfg.repository_sort = "name";
-	ctx->cfg.section_sort = 1;
-	ctx->cfg.summary_branches = 10;
-	ctx->cfg.summary_log = 10;
-	ctx->cfg.summary_tags = 10;
-	ctx->cfg.max_atom_items = 10;
-	ctx->cfg.ssdiff = 0;
-	ctx->env.cgit_config = getenv("CGIT_CONFIG");
-	ctx->env.http_host = getenv("HTTP_HOST");
-	ctx->env.https = getenv("HTTPS");
-	ctx->env.no_http = getenv("NO_HTTP");
-	ctx->env.path_info = getenv("PATH_INFO");
-	ctx->env.query_string = getenv("QUERY_STRING");
-	ctx->env.request_method = getenv("REQUEST_METHOD");
-	ctx->env.script_name = getenv("SCRIPT_NAME");
-	ctx->env.server_name = getenv("SERVER_NAME");
-	ctx->env.server_port = getenv("SERVER_PORT");
-	ctx->env.http_cookie = getenv("HTTP_COOKIE");
-	ctx->env.http_referer = getenv("HTTP_REFERER");
-	ctx->env.content_length = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0;
-	ctx->env.authenticated = 0;
-	ctx->page.mimetype = "text/html";
-	ctx->page.charset = PAGE_ENCODING;
-	ctx->page.filename = NULL;
-	ctx->page.size = 0;
-	ctx->page.modified = time(NULL);
-	ctx->page.expires = ctx->page.modified;
-	ctx->page.etag = NULL;
-	memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
-	if (ctx->env.script_name)
-		ctx->cfg.script_name = xstrdup(ctx->env.script_name);
-	if (ctx->env.query_string)
-		ctx->qry.raw = xstrdup(ctx->env.query_string);
-	if (!ctx->env.cgit_config)
-		ctx->env.cgit_config = CGIT_CONFIG;
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.cfg.agefile = "info/web/last-modified";
+	ctx.cfg.nocache = 0;
+	ctx.cfg.cache_size = 0;
+	ctx.cfg.cache_max_create_time = 5;
+	ctx.cfg.cache_root = CGIT_CACHE_ROOT;
+	ctx.cfg.cache_about_ttl = 15;
+	ctx.cfg.cache_repo_ttl = 5;
+	ctx.cfg.cache_root_ttl = 5;
+	ctx.cfg.cache_scanrc_ttl = 15;
+	ctx.cfg.cache_dynamic_ttl = 5;
+	ctx.cfg.cache_static_ttl = -1;
+	ctx.cfg.case_sensitive_sort = 1;
+	ctx.cfg.branch_sort = 0;
+	ctx.cfg.commit_sort = 0;
+	ctx.cfg.css = "/cgit.css";
+	ctx.cfg.logo = "/cgit.png";
+	ctx.cfg.favicon = "/favicon.ico";
+	ctx.cfg.local_time = 0;
+	ctx.cfg.enable_http_clone = 1;
+	ctx.cfg.enable_index_owner = 1;
+	ctx.cfg.enable_tree_linenumbers = 1;
+	ctx.cfg.enable_git_config = 0;
+	ctx.cfg.max_repo_count = 50;
+	ctx.cfg.max_commit_count = 50;
+	ctx.cfg.max_lock_attempts = 5;
+	ctx.cfg.max_msg_len = 80;
+	ctx.cfg.max_repodesc_len = 80;
+	ctx.cfg.max_blob_size = 0;
+	ctx.cfg.max_stats = 0;
+	ctx.cfg.project_list = NULL;
+	ctx.cfg.renamelimit = -1;
+	ctx.cfg.remove_suffix = 0;
+	ctx.cfg.robots = "index, nofollow";
+	ctx.cfg.root_title = "Git repository browser";
+	ctx.cfg.root_desc = "a fast webinterface for the git dscm";
+	ctx.cfg.scan_hidden_path = 0;
+	ctx.cfg.script_name = CGIT_SCRIPT_NAME;
+	ctx.cfg.section = "";
+	ctx.cfg.repository_sort = "name";
+	ctx.cfg.section_sort = 1;
+	ctx.cfg.summary_branches = 10;
+	ctx.cfg.summary_log = 10;
+	ctx.cfg.summary_tags = 10;
+	ctx.cfg.max_atom_items = 10;
+	ctx.cfg.ssdiff = 0;
+	ctx.env.cgit_config = getenv("CGIT_CONFIG");
+	ctx.env.http_host = getenv("HTTP_HOST");
+	ctx.env.https = getenv("HTTPS");
+	ctx.env.no_http = getenv("NO_HTTP");
+	ctx.env.path_info = getenv("PATH_INFO");
+	ctx.env.query_string = getenv("QUERY_STRING");
+	ctx.env.request_method = getenv("REQUEST_METHOD");
+	ctx.env.script_name = getenv("SCRIPT_NAME");
+	ctx.env.server_name = getenv("SERVER_NAME");
+	ctx.env.server_port = getenv("SERVER_PORT");
+	ctx.env.http_cookie = getenv("HTTP_COOKIE");
+	ctx.env.http_referer = getenv("HTTP_REFERER");
+	ctx.env.content_length = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0;
+	ctx.env.authenticated = 0;
+	ctx.page.mimetype = "text/html";
+	ctx.page.charset = PAGE_ENCODING;
+	ctx.page.filename = NULL;
+	ctx.page.size = 0;
+	ctx.page.modified = time(NULL);
+	ctx.page.expires = ctx.page.modified;
+	ctx.page.etag = NULL;
+	memset(&ctx.cfg.mimetypes, 0, sizeof(struct string_list));
+	if (ctx.env.script_name)
+		ctx.cfg.script_name = xstrdup(ctx.env.script_name);
+	if (ctx.env.query_string)
+		ctx.qry.raw = xstrdup(ctx.env.query_string);
+	if (!ctx.env.cgit_config)
+		ctx.env.cgit_config = CGIT_CONFIG;
 }
 
 struct refmatch {
@@ -527,14 +527,14 @@ static void choose_readme(struct cgit_repo *repo)
 		string_list_append(&repo->readme, filename)->util = ref;
 }
 
-static int prepare_repo_cmd(struct cgit_context *ctx)
+static int prepare_repo_cmd(void)
 {
 	unsigned char sha1[20];
 	int nongit = 0;
 	int rc;
 
 	/* The path to the git repository. */
-	setenv("GIT_DIR", ctx->repo->path, 1);
+	setenv("GIT_DIR", ctx.repo->path, 1);
 
 	/* Do not look in /etc/ for gitconfig and gitattributes. */
 	setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
@@ -549,69 +549,69 @@ static int prepare_repo_cmd(struct cgit_context *ctx)
 	init_display_notes(NULL);
 
 	if (nongit) {
-		const char *name = ctx->repo->name;
+		const char *name = ctx.repo->name;
 		rc = errno;
-		ctx->page.title = fmtalloc("%s - %s", ctx->cfg.root_title,
+		ctx.page.title = fmtalloc("%s - %s", ctx.cfg.root_title,
 						"config error");
-		ctx->repo = NULL;
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
+		ctx.repo = NULL;
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
 		cgit_print_error("Failed to open %s: %s", name,
 				 rc ? strerror(rc) : "Not a valid git repository");
 		cgit_print_docend();
 		return 1;
 	}
-	ctx->page.title = fmtalloc("%s - %s", ctx->repo->name, ctx->repo->desc);
+	ctx.page.title = fmtalloc("%s - %s", ctx.repo->name, ctx.repo->desc);
 
-	if (!ctx->repo->defbranch)
-		ctx->repo->defbranch = guess_defbranch();
+	if (!ctx.repo->defbranch)
+		ctx.repo->defbranch = guess_defbranch();
 
-	if (!ctx->qry.head) {
-		ctx->qry.nohead = 1;
-		ctx->qry.head = find_default_branch(ctx->repo);
+	if (!ctx.qry.head) {
+		ctx.qry.nohead = 1;
+		ctx.qry.head = find_default_branch(ctx.repo);
 	}
 
-	if (!ctx->qry.head) {
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
+	if (!ctx.qry.head) {
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
 		cgit_print_error("Repository seems to be empty");
 		cgit_print_docend();
 		return 1;
 	}
 
-	if (get_sha1(ctx->qry.head, sha1)) {
-		char *tmp = xstrdup(ctx->qry.head);
-		ctx->qry.head = ctx->repo->defbranch;
-		ctx->page.status = 404;
-		ctx->page.statusmsg = "Not found";
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
+	if (get_sha1(ctx.qry.head, sha1)) {
+		char *tmp = xstrdup(ctx.qry.head);
+		ctx.qry.head = ctx.repo->defbranch;
+		ctx.page.status = 404;
+		ctx.page.statusmsg = "Not found";
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
 		cgit_print_error("Invalid branch: %s", tmp);
 		cgit_print_docend();
 		return 1;
 	}
-	sort_string_list(&ctx->repo->submodules);
-	cgit_prepare_repo_env(ctx->repo);
-	choose_readme(ctx->repo);
+	sort_string_list(&ctx.repo->submodules);
+	cgit_prepare_repo_env(ctx.repo);
+	choose_readme(ctx.repo);
 	return 0;
 }
 
-static inline void open_auth_filter(struct cgit_context *ctx, const char *function)
+static inline void open_auth_filter(const char *function)
 {
-	cgit_open_filter(ctx->cfg.auth_filter, function,
-		ctx->env.http_cookie ? ctx->env.http_cookie : "",
-		ctx->env.request_method ? ctx->env.request_method : "",
-		ctx->env.query_string ? ctx->env.query_string : "",
-		ctx->env.http_referer ? ctx->env.http_referer : "",
-		ctx->env.path_info ? ctx->env.path_info : "",
-		ctx->env.http_host ? ctx->env.http_host : "",
-		ctx->env.https ? ctx->env.https : "",
-		ctx->qry.repo ? ctx->qry.repo : "",
-		ctx->qry.page ? ctx->qry.page : "",
-		ctx->qry.url ? ctx->qry.url : "",
+	cgit_open_filter(ctx.cfg.auth_filter, function,
+		ctx.env.http_cookie ? ctx.env.http_cookie : "",
+		ctx.env.request_method ? ctx.env.request_method : "",
+		ctx.env.query_string ? ctx.env.query_string : "",
+		ctx.env.http_referer ? ctx.env.http_referer : "",
+		ctx.env.path_info ? ctx.env.path_info : "",
+		ctx.env.http_host ? ctx.env.http_host : "",
+		ctx.env.https ? ctx.env.https : "",
+		ctx.qry.repo ? ctx.qry.repo : "",
+		ctx.qry.page ? ctx.qry.page : "",
+		ctx.qry.url ? ctx.qry.url : "",
 		cgit_loginurl());
 }
 
@@ -622,107 +622,106 @@ static inline void open_auth_filter(struct cgit_context *ctx, const char *functi
  * will complain on the mailing list, and we'll increase it as needed. */
 #define MAX_AUTHENTICATION_POST_BYTES 4096
 /* The filter is expected to spit out "Status: " and all headers. */
-static inline void authenticate_post(struct cgit_context *ctx)
+static inline void authenticate_post(void)
 {
 	char buffer[MAX_AUTHENTICATION_POST_BYTES];
 	int len;
 
-	open_auth_filter(ctx, "authenticate-post");
-	len = ctx->env.content_length;
+	open_auth_filter("authenticate-post");
+	len = ctx.env.content_length;
 	if (len > MAX_AUTHENTICATION_POST_BYTES)
 		len = MAX_AUTHENTICATION_POST_BYTES;
 	if (read(STDIN_FILENO, buffer, len) < 0)
 		die_errno("Could not read POST from stdin");
 	if (write(STDOUT_FILENO, buffer, len) < 0)
 		die_errno("Could not write POST to stdout");
-	cgit_close_filter(ctx->cfg.auth_filter);
+	cgit_close_filter(ctx.cfg.auth_filter);
 	exit(0);
 }
 
-static inline void authenticate_cookie(struct cgit_context *ctx)
+static inline void authenticate_cookie(void)
 {
 	/* If we don't have an auth_filter, consider all cookies valid, and thus return early. */
-	if (!ctx->cfg.auth_filter) {
-		ctx->env.authenticated = 1;
+	if (!ctx.cfg.auth_filter) {
+		ctx.env.authenticated = 1;
 		return;
 	}
 
 	/* If we're having something POST'd to /login, we're authenticating POST,
 	 * instead of the cookie, so call authenticate_post and bail out early.
 	 * This pattern here should match /?p=login with POST. */
-	if (ctx->env.request_method && ctx->qry.page && !ctx->repo && \
-	    !strcmp(ctx->env.request_method, "POST") && !strcmp(ctx->qry.page, "login")) {
-		authenticate_post(ctx);
+	if (ctx.env.request_method && ctx.qry.page && !ctx.repo && \
+	    !strcmp(ctx.env.request_method, "POST") && !strcmp(ctx.qry.page, "login")) {
+		authenticate_post();
 		return;
 	}
 
 	/* If we've made it this far, we're authenticating the cookie for real, so do that. */
-	open_auth_filter(ctx, "authenticate-cookie");
-	ctx->env.authenticated = cgit_close_filter(ctx->cfg.auth_filter);
+	open_auth_filter("authenticate-cookie");
+	ctx.env.authenticated = cgit_close_filter(ctx.cfg.auth_filter);
 }
 
-static void process_request(void *cbdata)
+static void process_request(void)
 {
-	struct cgit_context *ctx = cbdata;
 	struct cgit_cmd *cmd;
 
 	/* If we're not yet authenticated, no matter what page we're on,
 	 * display the authentication body from the auth_filter. This should
 	 * never be cached. */
-	if (!ctx->env.authenticated) {
-		ctx->page.title = "Authentication Required";
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
-		open_auth_filter(ctx, "body");
-		cgit_close_filter(ctx->cfg.auth_filter);
+	if (!ctx.env.authenticated) {
+		ctx.page.title = "Authentication Required";
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
+		open_auth_filter("body");
+		cgit_close_filter(ctx.cfg.auth_filter);
 		cgit_print_docend();
 		return;
 	}
 
-	cmd = cgit_get_cmd(ctx);
+	cmd = cgit_get_cmd();
 	if (!cmd) {
-		ctx->page.title = "cgit error";
-		ctx->page.status = 404;
-		ctx->page.statusmsg = "Not found";
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
+		ctx.page.title = "cgit error";
+		ctx.page.status = 404;
+		ctx.page.statusmsg = "Not found";
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
 		cgit_print_error("Invalid request");
 		cgit_print_docend();
 		return;
 	}
 
-	if (!ctx->cfg.enable_http_clone && cmd->is_clone) {
+	if (!ctx.cfg.enable_http_clone && cmd->is_clone) {
 		html_status(404, "Not found", 0);
 		return;
 	}
 
-	/* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual"
-	 * in-project path limit to be made available at ctx->qry.vpath.
-	 * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL).
+	/* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual"
+	 * in-project path limit to be made available at ctx.qry.vpath.
+	 * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL).
 	 */
-	ctx->qry.vpath = cmd->want_vpath ? ctx->qry.path : NULL;
+	ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL;
 
-	if (cmd->want_repo && !ctx->repo) {
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
+	if (cmd->want_repo && !ctx.repo) {
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
 		cgit_print_error("No repository selected");
 		cgit_print_docend();
 		return;
 	}
 
-	if (ctx->repo && prepare_repo_cmd(ctx))
+	if (ctx.repo && prepare_repo_cmd())
 		return;
 
 	if (cmd->want_layout) {
-		cgit_print_http_headers(ctx);
-		cgit_print_docstart(ctx);
-		cgit_print_pageheader(ctx);
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
 	}
 
-	cmd->fn(ctx);
+	cmd->fn();
 
 	if (cmd->want_layout)
 		cgit_print_docend();
@@ -995,7 +994,7 @@ int main(int argc, const char **argv)
 	cgit_init_filters();
 	atexit(cgit_cleanup_filters);
 
-	prepare_context(&ctx);
+	prepare_context();
 	cgit_repolist.length = 0;
 	cgit_repolist.count = 0;
 	cgit_repolist.repos = NULL;
@@ -1034,7 +1033,7 @@ int main(int argc, const char **argv)
 	/* Before we go any further, we set ctx.env.authenticated by checking to see
 	 * if the supplied cookie is valid. All cookies are valid if there is no
 	 * auth_filter. If there is an auth_filter, the filter decides. */
-	authenticate_cookie(&ctx);
+	authenticate_cookie();
 
 	ttl = calc_ttl();
 	if (ttl < 0)
@@ -1046,7 +1045,8 @@ int main(int argc, const char **argv)
 	if (ctx.cfg.nocache)
 		ctx.cfg.cache_size = 0;
 	err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
-			    ctx.qry.raw, ttl, process_request, &ctx);
+			    ctx.qry.raw, ttl, process_request);
+	cgit_cleanup_filters();
 	if (err)
 		cgit_print_error("Error processing page: %s (%d)",
 				 strerror(err), err);
diff --git a/cmd.c b/cmd.c
index 420b3b1..cbd235c 100644
--- a/cmd.c
+++ b/cmd.c
@@ -26,120 +26,120 @@
 #include "ui-tag.h"
 #include "ui-tree.h"
 
-static void HEAD_fn(struct cgit_context *ctx)
+static void HEAD_fn(void)
 {
-	cgit_clone_head(ctx);
+	cgit_clone_head();
 }
 
-static void atom_fn(struct cgit_context *ctx)
+static void atom_fn(void)
 {
-	cgit_print_atom(ctx->qry.head, ctx->qry.path, ctx->cfg.max_atom_items);
+	cgit_print_atom(ctx.qry.head, ctx.qry.path, ctx.cfg.max_atom_items);
 }
 
-static void about_fn(struct cgit_context *ctx)
+static void about_fn(void)
 {
-	if (ctx->repo)
-		cgit_print_repo_readme(ctx->qry.path);
+	if (ctx.repo)
+		cgit_print_repo_readme(ctx.qry.path);
 	else
 		cgit_print_site_readme();
 }
 
-static void blob_fn(struct cgit_context *ctx)
+static void blob_fn(void)
 {
-	cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head, 0);
+	cgit_print_blob(ctx.qry.sha1, ctx.qry.path, ctx.qry.head, 0);
 }
 
-static void commit_fn(struct cgit_context *ctx)
+static void commit_fn(void)
 {
-	cgit_print_commit(ctx->qry.sha1, ctx->qry.path);
+	cgit_print_commit(ctx.qry.sha1, ctx.qry.path);
 }
 
-static void diff_fn(struct cgit_context *ctx)
+static void diff_fn(void)
 {
-	cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path, 1, 0);
+	cgit_print_diff(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1, 0);
 }
 
-static void rawdiff_fn(struct cgit_context *ctx)
+static void rawdiff_fn(void)
 {
-	cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path, 1, 1);
+	cgit_print_diff(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1, 1);
 }
 
-static void info_fn(struct cgit_context *ctx)
+static void info_fn(void)
 {
-	cgit_clone_info(ctx);
+	cgit_clone_info();
 }
 
-static void log_fn(struct cgit_context *ctx)
+static void log_fn(void)
 {
-	cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
-		       ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1,
-		       ctx->repo->enable_commit_graph,
-		       ctx->repo->commit_sort);
+	cgit_print_log(ctx.qry.sha1, ctx.qry.ofs, ctx.cfg.max_commit_count,
+		       ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1,
+		       ctx.repo->enable_commit_graph,
+		       ctx.repo->commit_sort);
 }
 
-static void ls_cache_fn(struct cgit_context *ctx)
+static void ls_cache_fn(void)
 {
-	ctx->page.mimetype = "text/plain";
-	ctx->page.filename = "ls-cache.txt";
-	cgit_print_http_headers(ctx);
-	cache_ls(ctx->cfg.cache_root);
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = "ls-cache.txt";
+	cgit_print_http_headers();
+	cache_ls(ctx.cfg.cache_root);
 }
 
-static void objects_fn(struct cgit_context *ctx)
+static void objects_fn(void)
 {
-	cgit_clone_objects(ctx);
+	cgit_clone_objects();
 }
 
-static void repolist_fn(struct cgit_context *ctx)
+static void repolist_fn(void)
 {
 	cgit_print_repolist();
 }
 
-static void patch_fn(struct cgit_context *ctx)
+static void patch_fn(void)
 {
-	cgit_print_patch(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
+	cgit_print_patch(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path);
 }
 
-static void plain_fn(struct cgit_context *ctx)
+static void plain_fn(void)
 {
-	cgit_print_plain(ctx);
+	cgit_print_plain();
 }
 
-static void refs_fn(struct cgit_context *ctx)
+static void refs_fn(void)
 {
 	cgit_print_refs();
 }
 
-static void snapshot_fn(struct cgit_context *ctx)
+static void snapshot_fn(void)
 {
-	cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path,
-			    ctx->repo->snapshots, ctx->qry.nohead);
+	cgit_print_snapshot(ctx.qry.head, ctx.qry.sha1, ctx.qry.path,
+			    ctx.repo->snapshots, ctx.qry.nohead);
 }
 
-static void stats_fn(struct cgit_context *ctx)
+static void stats_fn(void)
 {
-	cgit_show_stats(ctx);
+	cgit_show_stats();
 }
 
-static void summary_fn(struct cgit_context *ctx)
+static void summary_fn(void)
 {
 	cgit_print_summary();
 }
 
-static void tag_fn(struct cgit_context *ctx)
+static void tag_fn(void)
 {
-	cgit_print_tag(ctx->qry.sha1);
+	cgit_print_tag(ctx.qry.sha1);
 }
 
-static void tree_fn(struct cgit_context *ctx)
+static void tree_fn(void)
 {
-	cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
+	cgit_print_tree(ctx.qry.sha1, ctx.qry.path);
 }
 
 #define def_cmd(name, want_repo, want_layout, want_vpath, is_clone) \
 	{#name, name##_fn, want_repo, want_layout, want_vpath, is_clone}
 
-struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
+struct cgit_cmd *cgit_get_cmd(void)
 {
 	static struct cgit_cmd cmds[] = {
 		def_cmd(HEAD, 1, 0, 0, 1),
@@ -165,15 +165,15 @@ struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
 	};
 	int i;
 
-	if (ctx->qry.page == NULL) {
-		if (ctx->repo)
-			ctx->qry.page = "summary";
+	if (ctx.qry.page == NULL) {
+		if (ctx.repo)
+			ctx.qry.page = "summary";
 		else
-			ctx->qry.page = "repolist";
+			ctx.qry.page = "repolist";
 	}
 
 	for (i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
-		if (!strcmp(ctx->qry.page, cmds[i].name))
+		if (!strcmp(ctx.qry.page, cmds[i].name))
 			return &cmds[i];
 	return NULL;
 }
diff --git a/cmd.h b/cmd.h
index eb5bc87..752f078 100644
--- a/cmd.h
+++ b/cmd.h
@@ -1,7 +1,7 @@
 #ifndef CMD_H
 #define CMD_H
 
-typedef void (*cgit_cmd_fn)(struct cgit_context *ctx);
+typedef void (*cgit_cmd_fn)(void);
 
 struct cgit_cmd {
 	const char *name;
@@ -12,6 +12,6 @@ struct cgit_cmd {
 		is_clone:1;
 };
 
-extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx);
+extern struct cgit_cmd *cgit_get_cmd(void);
 
 #endif /* CMD_H */
diff --git a/ui-atom.c b/ui-atom.c
index 838f220..b22d745 100644
--- a/ui-atom.c
+++ b/ui-atom.c
@@ -108,7 +108,7 @@ void cgit_print_atom(char *tip, char *path, int max_count)
 	host = cgit_hosturl();
 	ctx.page.mimetype = "text/xml";
 	ctx.page.charset = "utf-8";
-	cgit_print_http_headers(&ctx);
+	cgit_print_http_headers();
 	html("<feed xmlns='http://www.w3.org/2005/Atom'>\n");
 	html("<title>");
 	html_txt(ctx.repo->name);
diff --git a/ui-blob.c b/ui-blob.c
index 608926e..9c99519 100644
--- a/ui-blob.c
+++ b/ui-blob.c
@@ -164,6 +164,6 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl
 			ctx.page.mimetype = "text/plain";
 	}
 	ctx.page.filename = path;
-	cgit_print_http_headers(&ctx);
+	cgit_print_http_headers();
 	html_raw(buf, size);
 }
diff --git a/ui-clone.c b/ui-clone.c
index 9d0d6ad..d25553b 100644
--- a/ui-clone.c
+++ b/ui-clone.c
@@ -29,22 +29,22 @@ static int print_ref_info(const char *refname, const unsigned char *sha1,
 	return 0;
 }
 
-static void print_pack_info(struct cgit_context *ctx)
+static void print_pack_info(void)
 {
 	struct packed_git *pack;
 	int ofs;
 
-	ctx->page.mimetype = "text/plain";
-	ctx->page.filename = "objects/info/packs";
-	cgit_print_http_headers(ctx);
-	ofs = strlen(ctx->repo->path) + strlen("/objects/pack/");
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = "objects/info/packs";
+	cgit_print_http_headers();
+	ofs = strlen(ctx.repo->path) + strlen("/objects/pack/");
 	prepare_packed_git();
 	for (pack = packed_git; pack; pack = pack->next)
 		if (pack->pack_local)
 			htmlf("P %s\n", pack->pack_name + ofs);
 }
 
-static void send_file(struct cgit_context *ctx, char *path)
+static void send_file(char *path)
 {
 	struct stat st;
 
@@ -61,41 +61,41 @@ static void send_file(struct cgit_context *ctx, char *path)
 		}
 		return;
 	}
-	ctx->page.mimetype = "application/octet-stream";
-	ctx->page.filename = path;
-	if (prefixcmp(ctx->repo->path, path))
-		ctx->page.filename += strlen(ctx->repo->path) + 1;
-	cgit_print_http_headers(ctx);
+	ctx.page.mimetype = "application/octet-stream";
+	ctx.page.filename = path;
+	if (prefixcmp(ctx.repo->path, path))
+		ctx.page.filename += strlen(ctx.repo->path) + 1;
+	cgit_print_http_headers();
 	html_include(path);
 }
 
-void cgit_clone_info(struct cgit_context *ctx)
+void cgit_clone_info(void)
 {
-	if (!ctx->qry.path || strcmp(ctx->qry.path, "refs"))
+	if (!ctx.qry.path || strcmp(ctx.qry.path, "refs"))
 		return;
 
-	ctx->page.mimetype = "text/plain";
-	ctx->page.filename = "info/refs";
-	cgit_print_http_headers(ctx);
-	for_each_ref(print_ref_info, ctx);
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = "info/refs";
+	cgit_print_http_headers();
+	for_each_ref(print_ref_info, NULL);
 }
 
-void cgit_clone_objects(struct cgit_context *ctx)
+void cgit_clone_objects(void)
 {
-	if (!ctx->qry.path) {
+	if (!ctx.qry.path) {
 		html_status(400, "Bad request", 0);
 		return;
 	}
 
-	if (!strcmp(ctx->qry.path, "info/packs")) {
-		print_pack_info(ctx);
+	if (!strcmp(ctx.qry.path, "info/packs")) {
+		print_pack_info();
 		return;
 	}
 
-	send_file(ctx, git_path("objects/%s", ctx->qry.path));
+	send_file(git_path("objects/%s", ctx.qry.path));
 }
 
-void cgit_clone_head(struct cgit_context *ctx)
+void cgit_clone_head(void)
 {
-	send_file(ctx, git_path("%s", "HEAD"));
+	send_file(git_path("%s", "HEAD"));
 }
diff --git a/ui-clone.h b/ui-clone.h
index 89cd4f1..3e460a3 100644
--- a/ui-clone.h
+++ b/ui-clone.h
@@ -1,8 +1,8 @@
 #ifndef UI_CLONE_H
 #define UI_CLONE_H
 
-void cgit_clone_info(struct cgit_context *ctx);
-void cgit_clone_objects(struct cgit_context *ctx);
-void cgit_clone_head(struct cgit_context *ctx);
+void cgit_clone_info(void);
+void cgit_clone_objects(void);
+void cgit_clone_head(void);
 
 #endif /* UI_CLONE_H */
diff --git a/ui-diff.c b/ui-diff.c
index 5ccd03e..71273aa 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -407,7 +407,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
 		diff_setup_done(&diffopt);
 
 		ctx.page.mimetype = "text/plain";
-		cgit_print_http_headers(&ctx);
+		cgit_print_http_headers();
 		if (old_tree_sha1) {
 			diff_tree_sha1(old_tree_sha1, new_tree_sha1, "",
 				       &diffopt);
diff --git a/ui-patch.c b/ui-patch.c
index 3086608..6878a46 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -59,7 +59,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
 	patchname = fmt("%s.patch", rev_range);
 	ctx.page.mimetype = "text/plain";
 	ctx.page.filename = patchname;
-	cgit_print_http_headers(&ctx);
+	cgit_print_http_headers();
 
 	if (ctx.cfg.noplainemail) {
 		rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 "
diff --git a/ui-plain.c b/ui-plain.c
index 68e0387..30fff89 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -103,7 +103,7 @@ static int print_object(const unsigned char *sha1, const char *path)
 	ctx.page.filename = path;
 	ctx.page.size = size;
 	ctx.page.etag = sha1_to_hex(sha1);
-	cgit_print_http_headers(&ctx);
+	cgit_print_http_headers();
 	html_raw(buf, size);
 	/* If we allocated this, then casting away const is safe. */
 	if (freemime)
@@ -128,7 +128,7 @@ static void print_dir(const unsigned char *sha1, const char *base,
 	fullpath = buildpath(base, baselen, path);
 	slash = (fullpath[0] == '/' ? "" : "/");
 	ctx.page.etag = sha1_to_hex(sha1);
-	cgit_print_http_headers(&ctx);
+	cgit_print_http_headers();
 	htmlf("<html><head><title>%s", slash);
 	html_txt(fullpath);
 	htmlf("</title></head>\n<body>\n<h2>%s", slash);
@@ -206,14 +206,14 @@ static int basedir_len(const char *path)
 	return 0;
 }
 
-void cgit_print_plain(struct cgit_context *ctx)
+void cgit_print_plain(void)
 {
-	const char *rev = ctx->qry.sha1;
+	const char *rev = ctx.qry.sha1;
 	unsigned char sha1[20];
 	struct commit *commit;
 	struct pathspec_item path_items = {
-		.match = ctx->qry.path,
-		.len = ctx->qry.path ? strlen(ctx->qry.path) : 0
+		.match = ctx.qry.path,
+		.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
 	};
 	struct pathspec paths = {
 		.nr = 1,
@@ -224,7 +224,7 @@ void cgit_print_plain(struct cgit_context *ctx)
 	};
 
 	if (!rev)
-		rev = ctx->qry.head;
+		rev = ctx.qry.head;
 
 	if (get_sha1(rev, sha1)) {
 		html_status(404, "Not found", 0);
diff --git a/ui-plain.h b/ui-plain.h
index 4373118..5bff07b 100644
--- a/ui-plain.h
+++ b/ui-plain.h
@@ -1,6 +1,6 @@
 #ifndef UI_PLAIN_H
 #define UI_PLAIN_H
 
-extern void cgit_print_plain(struct cgit_context *ctx);
+extern void cgit_print_plain(void);
 
 #endif /* UI_PLAIN_H */
diff --git a/ui-repolist.c b/ui-repolist.c
index f9cb21a..92e80cf 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -259,9 +259,9 @@ void cgit_print_repolist()
 		++columns;
 
 	ctx.page.title = ctx.cfg.root_title;
-	cgit_print_http_headers(&ctx);
-	cgit_print_docstart(&ctx);
-	cgit_print_pageheader(&ctx);
+	cgit_print_http_headers();
+	cgit_print_docstart();
+	cgit_print_pageheader();
 
 	if (ctx.cfg.index_header)
 		html_include(ctx.cfg.index_header);
diff --git a/ui-shared.c b/ui-shared.c
index 0838e18..070971f 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -436,59 +436,58 @@ void cgit_stats_link(const char *name, const char *title, const char *class,
 	reporevlink("stats", name, title, class, head, NULL, path);
 }
 
-static void cgit_self_link(char *name, const char *title, const char *class,
-			   struct cgit_context *ctx)
-{
-	if (!strcmp(ctx->qry.page, "repolist"))
-		cgit_index_link(name, title, class, ctx->qry.search, ctx->qry.sort,
-				ctx->qry.ofs);
-	else if (!strcmp(ctx->qry.page, "summary"))
-		cgit_summary_link(name, title, class, ctx->qry.head);
-	else if (!strcmp(ctx->qry.page, "tag"))
-		cgit_tag_link(name, title, class, ctx->qry.head,
-			      ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL);
-	else if (!strcmp(ctx->qry.page, "tree"))
-		cgit_tree_link(name, title, class, ctx->qry.head,
-			       ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-			       ctx->qry.path);
-	else if (!strcmp(ctx->qry.page, "plain"))
-		cgit_plain_link(name, title, class, ctx->qry.head,
-				ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-				ctx->qry.path);
-	else if (!strcmp(ctx->qry.page, "log"))
-		cgit_log_link(name, title, class, ctx->qry.head,
-			      ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-			      ctx->qry.path, ctx->qry.ofs,
-			      ctx->qry.grep, ctx->qry.search,
-			      ctx->qry.showmsg);
-	else if (!strcmp(ctx->qry.page, "commit"))
-		cgit_commit_link(name, title, class, ctx->qry.head,
-				 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-				 ctx->qry.path, 0);
-	else if (!strcmp(ctx->qry.page, "patch"))
-		cgit_patch_link(name, title, class, ctx->qry.head,
-				ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-				ctx->qry.path);
-	else if (!strcmp(ctx->qry.page, "refs"))
-		cgit_refs_link(name, title, class, ctx->qry.head,
-			       ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-			       ctx->qry.path);
-	else if (!strcmp(ctx->qry.page, "snapshot"))
-		cgit_snapshot_link(name, title, class, ctx->qry.head,
-				   ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
-				   ctx->qry.path);
-	else if (!strcmp(ctx->qry.page, "diff"))
-		cgit_diff_link(name, title, class, ctx->qry.head,
-			       ctx->qry.sha1, ctx->qry.sha2,
-			       ctx->qry.path, 0);
-	else if (!strcmp(ctx->qry.page, "stats"))
-		cgit_stats_link(name, title, class, ctx->qry.head,
-				ctx->qry.path);
+static void cgit_self_link(char *name, const char *title, const char *class)
+{
+	if (!strcmp(ctx.qry.page, "repolist"))
+		cgit_index_link(name, title, class, ctx.qry.search, ctx.qry.sort,
+				ctx.qry.ofs);
+	else if (!strcmp(ctx.qry.page, "summary"))
+		cgit_summary_link(name, title, class, ctx.qry.head);
+	else if (!strcmp(ctx.qry.page, "tag"))
+		cgit_tag_link(name, title, class, ctx.qry.head,
+			      ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL);
+	else if (!strcmp(ctx.qry.page, "tree"))
+		cgit_tree_link(name, title, class, ctx.qry.head,
+			       ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+			       ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "plain"))
+		cgit_plain_link(name, title, class, ctx.qry.head,
+				ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+				ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "log"))
+		cgit_log_link(name, title, class, ctx.qry.head,
+			      ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+			      ctx.qry.path, ctx.qry.ofs,
+			      ctx.qry.grep, ctx.qry.search,
+			      ctx.qry.showmsg);
+	else if (!strcmp(ctx.qry.page, "commit"))
+		cgit_commit_link(name, title, class, ctx.qry.head,
+				 ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+				 ctx.qry.path, 0);
+	else if (!strcmp(ctx.qry.page, "patch"))
+		cgit_patch_link(name, title, class, ctx.qry.head,
+				ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+				ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "refs"))
+		cgit_refs_link(name, title, class, ctx.qry.head,
+			       ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+			       ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "snapshot"))
+		cgit_snapshot_link(name, title, class, ctx.qry.head,
+				   ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+				   ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "diff"))
+		cgit_diff_link(name, title, class, ctx.qry.head,
+			       ctx.qry.sha1, ctx.qry.sha2,
+			       ctx.qry.path, 0);
+	else if (!strcmp(ctx.qry.page, "stats"))
+		cgit_stats_link(name, title, class, ctx.qry.head,
+				ctx.qry.path);
 	else {
 		/* Don't known how to make link for this page */
-		repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
+		repolink(title, class, ctx.qry.page, ctx.qry.head, ctx.qry.path);
 		html("><!-- cgit_self_link() doesn't know how to make link for page '");
-		html_txt(ctx->qry.page);
+		html_txt(ctx.qry.page);
 		html("' -->");
 		html_txt(name);
 		html("</a>");
@@ -632,39 +631,39 @@ void cgit_print_age(time_t t, time_t max_relative, const char *format)
 	      secs * 1.0 / TM_YEAR);
 }
 
-void cgit_print_http_headers(struct cgit_context *ctx)
+void cgit_print_http_headers(void)
 {
-	if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1"))
+	if (ctx.env.no_http && !strcmp(ctx.env.no_http, "1"))
 		return;
 
-	if (ctx->page.status)
-		htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg);
-	if (ctx->page.mimetype && ctx->page.charset)
-		htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
-		      ctx->page.charset);
-	else if (ctx->page.mimetype)
-		htmlf("Content-Type: %s\n", ctx->page.mimetype);
-	if (ctx->page.size)
-		htmlf("Content-Length: %zd\n", ctx->page.size);
-	if (ctx->page.filename)
+	if (ctx.page.status)
+		htmlf("Status: %d %s\n", ctx.page.status, ctx.page.statusmsg);
+	if (ctx.page.mimetype && ctx.page.charset)
+		htmlf("Content-Type: %s; charset=%s\n", ctx.page.mimetype,
+		      ctx.page.charset);
+	else if (ctx.page.mimetype)
+		htmlf("Content-Type: %s\n", ctx.page.mimetype);
+	if (ctx.page.size)
+		htmlf("Content-Length: %zd\n", ctx.page.size);
+	if (ctx.page.filename)
 		htmlf("Content-Disposition: inline; filename=\"%s\"\n",
-		      ctx->page.filename);
-	if (!ctx->env.authenticated)
+		      ctx.page.filename);
+	if (!ctx.env.authenticated)
 		html("Cache-Control: no-cache, no-store\n");
-	htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
-	htmlf("Expires: %s\n", http_date(ctx->page.expires));
-	if (ctx->page.etag)
-		htmlf("ETag: \"%s\"\n", ctx->page.etag);
+	htmlf("Last-Modified: %s\n", http_date(ctx.page.modified));
+	htmlf("Expires: %s\n", http_date(ctx.page.expires));
+	if (ctx.page.etag)
+		htmlf("ETag: \"%s\"\n", ctx.page.etag);
 	html("\n");
-	if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD"))
+	if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
 		exit(0);
 }
 
-void cgit_print_docstart(struct cgit_context *ctx)
+void cgit_print_docstart(void)
 {
-	if (ctx->cfg.embedded) {
-		if (ctx->cfg.header)
-			html_include(ctx->cfg.header);
+	if (ctx.cfg.embedded) {
+		if (ctx.cfg.header)
+			html_include(ctx.cfg.header);
 		return;
 	}
 
@@ -673,37 +672,37 @@ void cgit_print_docstart(struct cgit_context *ctx)
 	html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
 	html("<head>\n");
 	html("<title>");
-	html_txt(ctx->page.title);
+	html_txt(ctx.page.title);
 	html("</title>\n");
 	htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
-	if (ctx->cfg.robots && *ctx->cfg.robots)
-		htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
+	if (ctx.cfg.robots && *ctx.cfg.robots)
+		htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots);
 	html("<link rel='stylesheet' type='text/css' href='");
-	html_attr(ctx->cfg.css);
+	html_attr(ctx.cfg.css);
 	html("'/>\n");
-	if (ctx->cfg.favicon) {
+	if (ctx.cfg.favicon) {
 		html("<link rel='shortcut icon' href='");
-		html_attr(ctx->cfg.favicon);
+		html_attr(ctx.cfg.favicon);
 		html("'/>\n");
 	}
-	if (host && ctx->repo && ctx->qry.head) {
+	if (host && ctx.repo && ctx.qry.head) {
 		struct strbuf sb = STRBUF_INIT;
-		strbuf_addf(&sb, "h=%s", ctx->qry.head);
+		strbuf_addf(&sb, "h=%s", ctx.qry.head);
 
 		html("<link rel='alternate' title='Atom feed' href='");
 		html(cgit_httpscheme());
 		html_attr(cgit_hosturl());
-		html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
+		html_attr(cgit_fileurl(ctx.repo->url, "atom", ctx.qry.vpath,
 				       sb.buf));
 		html("' type='application/atom+xml'/>\n");
 		strbuf_release(&sb);
 	}
-	if (ctx->cfg.head_include)
-		html_include(ctx->cfg.head_include);
+	if (ctx.cfg.head_include)
+		html_include(ctx.cfg.head_include);
 	html("</head>\n");
 	html("<body>\n");
-	if (ctx->cfg.header)
-		html_include(ctx->cfg.header);
+	if (ctx.cfg.header)
+		html_include(ctx.cfg.header);
 }
 
 void cgit_print_docend()
@@ -767,47 +766,47 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search,
 	}
 }
 
-static const char *hc(struct cgit_context *ctx, const char *page)
+static const char *hc(const char *page)
 {
-	return strcmp(ctx->qry.page, page) ? NULL : "active";
+	return strcmp(ctx.qry.page, page) ? NULL : "active";
 }
 
-static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path)
+static void cgit_print_path_crumbs(char *path)
 {
-	char *old_path = ctx->qry.path;
+	char *old_path = ctx.qry.path;
 	char *p = path, *q, *end = path + strlen(path);
 
-	ctx->qry.path = NULL;
-	cgit_self_link("root", NULL, NULL, ctx);
-	ctx->qry.path = p = path;
+	ctx.qry.path = NULL;
+	cgit_self_link("root", NULL, NULL);
+	ctx.qry.path = p = path;
 	while (p < end) {
 		if (!(q = strchr(p, '/')))
 			q = end;
 		*q = '\0';
 		html_txt("/");
-		cgit_self_link(p, NULL, NULL, ctx);
+		cgit_self_link(p, NULL, NULL);
 		if (q < end)
 			*q = '/';
 		p = q + 1;
 	}
-	ctx->qry.path = old_path;
+	ctx.qry.path = old_path;
 }
 
-static void print_header(struct cgit_context *ctx)
+static void print_header(void)
 {
 	char *logo = NULL, *logo_link = NULL;
 
 	html("<table id='header'>\n");
 	html("<tr>\n");
 
-	if (ctx->repo && ctx->repo->logo && *ctx->repo->logo)
-		logo = ctx->repo->logo;
+	if (ctx.repo && ctx.repo->logo && *ctx.repo->logo)
+		logo = ctx.repo->logo;
 	else
-		logo = ctx->cfg.logo;
-	if (ctx->repo && ctx->repo->logo_link && *ctx->repo->logo_link)
-		logo_link = ctx->repo->logo_link;
+		logo = ctx.cfg.logo;
+	if (ctx.repo && ctx.repo->logo_link && *ctx.repo->logo_link)
+		logo_link = ctx.repo->logo_link;
 	else
-		logo_link = ctx->cfg.logo_link;
+		logo_link = ctx.cfg.logo_link;
 	if (logo && *logo) {
 		html("<td class='logo' rowspan='2'><a href='");
 		if (logo_link && *logo_link)
@@ -820,104 +819,104 @@ static void print_header(struct cgit_context *ctx)
 	}
 
 	html("<td class='main'>");
-	if (ctx->repo) {
+	if (ctx.repo) {
 		cgit_index_link("index", NULL, NULL, NULL, NULL, 0);
 		html(" : ");
-		cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
-		if (ctx->env.authenticated) {
+		cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
+		if (ctx.env.authenticated) {
 			html("</td><td class='form'>");
 			html("<form method='get' action=''>\n");
-			cgit_add_hidden_formfields(0, 1, ctx->qry.page);
+			cgit_add_hidden_formfields(0, 1, ctx.qry.page);
 			html("<select name='h' onchange='this.form.submit();'>\n");
-			for_each_branch_ref(print_branch_option, ctx->qry.head);
+			for_each_branch_ref(print_branch_option, ctx.qry.head);
 			html("</select> ");
 			html("<input type='submit' name='' value='switch'/>");
 			html("</form>");
 		}
 	} else
-		html_txt(ctx->cfg.root_title);
+		html_txt(ctx.cfg.root_title);
 	html("</td></tr>\n");
 
 	html("<tr><td class='sub'>");
-	if (ctx->repo) {
-		html_txt(ctx->repo->desc);
+	if (ctx.repo) {
+		html_txt(ctx.repo->desc);
 		html("</td><td class='sub right'>");
-		html_txt(ctx->repo->owner);
+		html_txt(ctx.repo->owner);
 	} else {
-		if (ctx->cfg.root_desc)
-			html_txt(ctx->cfg.root_desc);
-		else if (ctx->cfg.index_info)
-			html_include(ctx->cfg.index_info);
+		if (ctx.cfg.root_desc)
+			html_txt(ctx.cfg.root_desc);
+		else if (ctx.cfg.index_info)
+			html_include(ctx.cfg.index_info);
 	}
 	html("</td></tr></table>\n");
 }
 
-void cgit_print_pageheader(struct cgit_context *ctx)
+void cgit_print_pageheader(void)
 {
 	html("<div id='cgit'>");
-	if (!ctx->env.authenticated || !ctx->cfg.noheader)
-		print_header(ctx);
+	if (!ctx.env.authenticated || !ctx.cfg.noheader)
+		print_header();
 
 	html("<table class='tabs'><tr><td>\n");
-	if (ctx->env.authenticated && ctx->repo) {
-		cgit_summary_link("summary", NULL, hc(ctx, "summary"),
-				  ctx->qry.head);
-		cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head,
-			       ctx->qry.sha1, NULL);
-		cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head,
-			      NULL, ctx->qry.vpath, 0, NULL, NULL,
-			      ctx->qry.showmsg);
-		cgit_tree_link("tree", NULL, hc(ctx, "tree"), ctx->qry.head,
-			       ctx->qry.sha1, ctx->qry.vpath);
-		cgit_commit_link("commit", NULL, hc(ctx, "commit"),
-				 ctx->qry.head, ctx->qry.sha1, ctx->qry.vpath, 0);
-		cgit_diff_link("diff", NULL, hc(ctx, "diff"), ctx->qry.head,
-			       ctx->qry.sha1, ctx->qry.sha2, ctx->qry.vpath, 0);
-		if (ctx->repo->max_stats)
-			cgit_stats_link("stats", NULL, hc(ctx, "stats"),
-					ctx->qry.head, ctx->qry.vpath);
-		if (ctx->repo->readme.nr)
+	if (ctx.env.authenticated && ctx.repo) {
+		cgit_summary_link("summary", NULL, hc("summary"),
+				  ctx.qry.head);
+		cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,
+			       ctx.qry.sha1, NULL);
+		cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
+			      NULL, ctx.qry.vpath, 0, NULL, NULL,
+			      ctx.qry.showmsg);
+		cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
+			       ctx.qry.sha1, ctx.qry.vpath);
+		cgit_commit_link("commit", NULL, hc("commit"),
+				 ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, 0);
+		cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
+			       ctx.qry.sha1, ctx.qry.sha2, ctx.qry.vpath, 0);
+		if (ctx.repo->max_stats)
+			cgit_stats_link("stats", NULL, hc("stats"),
+					ctx.qry.head, ctx.qry.vpath);
+		if (ctx.repo->readme.nr)
 			reporevlink("about", "about", NULL,
-				    hc(ctx, "about"), ctx->qry.head, NULL,
+				    hc("about"), ctx.qry.head, NULL,
 				    NULL);
 		html("</td><td class='form'>");
 		html("<form class='right' method='get' action='");
-		if (ctx->cfg.virtual_root)
-			html_url_path(cgit_fileurl(ctx->qry.repo, "log",
-						   ctx->qry.vpath, NULL));
+		if (ctx.cfg.virtual_root)
+			html_url_path(cgit_fileurl(ctx.qry.repo, "log",
+						   ctx.qry.vpath, NULL));
 		html("'>\n");
 		cgit_add_hidden_formfields(1, 0, "log");
 		html("<select name='qt'>\n");
-		html_option("grep", "log msg", ctx->qry.grep);
-		html_option("author", "author", ctx->qry.grep);
-		html_option("committer", "committer", ctx->qry.grep);
-		html_option("range", "range", ctx->qry.grep);
+		html_option("grep", "log msg", ctx.qry.grep);
+		html_option("author", "author", ctx.qry.grep);
+		html_option("committer", "committer", ctx.qry.grep);
+		html_option("range", "range", ctx.qry.grep);
 		html("</select>\n");
 		html("<input class='txt' type='text' size='10' name='q' value='");
-		html_attr(ctx->qry.search);
+		html_attr(ctx.qry.search);
 		html("'/>\n");
 		html("<input type='submit' value='search'/>\n");
 		html("</form>\n");
-	} else if (ctx->env.authenticated) {
-		site_link(NULL, "index", NULL, hc(ctx, "repolist"), NULL, NULL, 0);
-		if (ctx->cfg.root_readme)
-			site_link("about", "about", NULL, hc(ctx, "about"),
+	} else if (ctx.env.authenticated) {
+		site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0);
+		if (ctx.cfg.root_readme)
+			site_link("about", "about", NULL, hc("about"),
 				  NULL, NULL, 0);
 		html("</td><td class='form'>");
 		html("<form method='get' action='");
 		html_attr(cgit_rooturl());
 		html("'>\n");
 		html("<input type='text' name='q' size='10' value='");
-		html_attr(ctx->qry.search);
+		html_attr(ctx.qry.search);
 		html("'/>\n");
 		html("<input type='submit' value='search'/>\n");
 		html("</form>");
 	}
 	html("</td></tr></table>\n");
-	if (ctx->env.authenticated && ctx->qry.vpath) {
+	if (ctx.env.authenticated && ctx.qry.vpath) {
 		html("<div class='path'>");
 		html("path: ");
-		cgit_print_path_crumbs(ctx, ctx->qry.vpath);
+		cgit_print_path_crumbs(ctx.qry.vpath);
 		html("</div>");
 	}
 	html("<div class='content'>");
diff --git a/ui-shared.h b/ui-shared.h
index 889c28f..3e7a91b 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -59,10 +59,10 @@ __attribute__((format (printf,1,0)))
 extern void cgit_vprint_error(const char *fmt, va_list ap);
 extern void cgit_print_date(time_t secs, const char *format, int local_time);
 extern void cgit_print_age(time_t t, time_t max_relative, const char *format);
-extern void cgit_print_http_headers(struct cgit_context *ctx);
-extern void cgit_print_docstart(struct cgit_context *ctx);
+extern void cgit_print_http_headers(void);
+extern void cgit_print_docstart(void);
 extern void cgit_print_docend();
-extern void cgit_print_pageheader(struct cgit_context *ctx);
+extern void cgit_print_pageheader(void);
 extern void cgit_print_filemode(unsigned short mode);
 extern void cgit_print_snapshot_links(const char *repo, const char *head,
 				      const char *hex, int snapshots);
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 7115ec4..582dc31 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -121,7 +121,7 @@ static int make_snapshot(const struct cgit_snapshot_format *format,
 	}
 	ctx.page.mimetype = xstrdup(format->mimetype);
 	ctx.page.filename = xstrdup(filename);
-	cgit_print_http_headers(&ctx);
+	cgit_print_http_headers();
 	format->write_func(hex, prefix);
 	return 0;
 }
@@ -183,9 +183,9 @@ static void show_error(char *fmt, ...)
 	va_list ap;
 
 	ctx.page.mimetype = "text/html";
-	cgit_print_http_headers(&ctx);
-	cgit_print_docstart(&ctx);
-	cgit_print_pageheader(&ctx);
+	cgit_print_http_headers();
+	cgit_print_docstart();
+	cgit_print_pageheader();
 	va_start(ap, fmt);
 	cgit_vprint_error(fmt, ap);
 	va_end(ap);
diff --git a/ui-stats.c b/ui-stats.c
index 84b247c..bc27308 100644
--- a/ui-stats.c
+++ b/ui-stats.c
@@ -209,13 +209,12 @@ static int cmp_total_commits(const void *a1, const void *a2)
 /* Walk the commit DAG and collect number of commits per author per
  * timeperiod into a nested string_list collection.
  */
-static struct string_list collect_stats(struct cgit_context *ctx,
-					struct cgit_period *period)
+static struct string_list collect_stats(struct cgit_period *period)
 {
 	struct string_list authors;
 	struct rev_info rev;
 	struct commit *commit;
-	const char *argv[] = {NULL, ctx->qry.head, NULL, NULL, NULL, NULL};
+	const char *argv[] = {NULL, ctx.qry.head, NULL, NULL, NULL, NULL};
 	int argc = 3;
 	time_t now;
 	long i;
@@ -229,9 +228,9 @@ static struct string_list collect_stats(struct cgit_context *ctx,
 		period->dec(tm);
 	strftime(tmp, sizeof(tmp), "%Y-%m-%d", tm);
 	argv[2] = xstrdup(fmt("--since=%s", tmp));
-	if (ctx->qry.path) {
+	if (ctx.qry.path) {
 		argv[3] = "--";
-		argv[4] = ctx->qry.path;
+		argv[4] = ctx.qry.path;
 		argc += 2;
 	}
 	init_revisions(&rev, NULL);
@@ -360,30 +359,30 @@ static void print_authors(struct string_list *authors, int top,
  * for each author is another string_list which is used to calculate the
  * number of commits per time-interval.
  */
-void cgit_show_stats(struct cgit_context *ctx)
+void cgit_show_stats(void)
 {
 	struct string_list authors;
 	struct cgit_period *period;
 	int top, i;
 	const char *code = "w";
 
-	if (ctx->qry.period)
-		code = ctx->qry.period;
+	if (ctx.qry.period)
+		code = ctx.qry.period;
 
 	i = cgit_find_stats_period(code, &period);
 	if (!i) {
 		cgit_print_error("Unknown statistics type: %c", code[0]);
 		return;
 	}
-	if (i > ctx->repo->max_stats) {
+	if (i > ctx.repo->max_stats) {
 		cgit_print_error("Statistics type disabled: %s", period->name);
 		return;
 	}
-	authors = collect_stats(ctx, period);
+	authors = collect_stats(period);
 	qsort(authors.items, authors.nr, sizeof(struct string_list_item),
 		cmp_total_commits);
 
-	top = ctx->qry.ofs;
+	top = ctx.qry.ofs;
 	if (!top)
 		top = 10;
 
@@ -392,10 +391,10 @@ void cgit_show_stats(struct cgit_context *ctx)
 	html("<form method='get' action=''>");
 	cgit_add_hidden_formfields(1, 0, "stats");
 	html("<table><tr><td colspan='2'/></tr>");
-	if (ctx->repo->max_stats > 1) {
+	if (ctx.repo->max_stats > 1) {
 		html("<tr><td class='label'>Period:</td>");
 		html("<td class='ctrl'><select name='period' onchange='this.form.submit();'>");
-		for (i = 0; i < ctx->repo->max_stats; i++)
+		for (i = 0; i < ctx.repo->max_stats; i++)
 			html_option(fmt("%c", periods[i].code),
 				    periods[i].name, fmt("%c", period->code));
 		html("</select></td></tr>");
@@ -414,9 +413,9 @@ void cgit_show_stats(struct cgit_context *ctx)
 	html("</form>");
 	html("</div>");
 	htmlf("<h2>Commits per author per %s", period->name);
-	if (ctx->qry.path) {
+	if (ctx.qry.path) {
 		html(" (path '");
-		html_txt(ctx->qry.path);
+		html_txt(ctx.qry.path);
 		html("')");
 	}
 	html("</h2>");
diff --git a/ui-stats.h b/ui-stats.h
index f0761ba..341ab13 100644
--- a/ui-stats.h
+++ b/ui-stats.h
@@ -23,6 +23,6 @@ struct cgit_period {
 extern int cgit_find_stats_period(const char *expr, struct cgit_period **period);
 extern const char *cgit_find_stats_periodname(int idx);
 
-extern void cgit_show_stats(struct cgit_context *ctx);
+extern void cgit_show_stats(void);
 
 #endif /* UI_STATS_H */