"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
Re: gotwebd: introducing fcgi_printf
To:
Mark Jamsek <mark@jamsek.com>
Cc:
gameoftrees@openbsd.org, Tracey Emery <tracey@traceyemery.net>
Date:
Mon, 15 Aug 2022 17:09:41 +0200

Download raw body.

Thread
On 2022/08/15 21:46:38 +1000, Mark Jamsek <mark@jamsek.com> wrote:
> On 22-08-11 05:06PM, Omar Polo wrote:
> > On 2022/08/11 16:00:19 +0200, Omar Polo <op@omarpolo.com> wrote:
> > > as suggested by Tracey, I took a bit further my idea of adding a
> > > printf-like function in gotwebd.  Diff belows drops fcgi_gen_response
> > > (used to "print" a string) and replace everything with fcgi_printf.
> > > 
> > > If fcgi_gen_response is preferred as a name I can change it.  I went
> > > with a different name to make sure to catch and adapt every place
> > > where it's called.  (i prefer fcgi_printf as name though)
> > > 
> > > I have this running on my instance and it seems to work.  I can't
> > > visually spot differences in the pages and using the "View Page
> > > Source" menu in firefox the html seems correct too.

spoiler: i was wrong

> > > If I've missed some NULL checks I guess I'll know pretty soon, but
> > > after clicking around for a bit I haven't seen any "vprintf %s NULL"
> > > in /var/log/messages.

I haven't seen any of such logs after a few days of runtime, I'm quite
sure I caught them all :)

> > > The diff is pretty long to read and maybe even a bit boring, but on
> > > the bright side it drops a lot of code (564 +, 1430 -).
> > 
> > there was a typo in gotweb_render_tags
> 
> That was a long diff to read! My eyes were crossing before I was even
> halfway :)

Thanks for looking at it!

> It looks good to me. I haven't run it myself; it builds fine, but
> I don't have gotweb setup to test yet (I should do sometime this
> week). I have been playing with it on your site though--assuming it's
> running with this patch--and that's working great!

Yes, I'm running my instance with diff below (now updated.)  Feel free
to play with it and/or feed it to validators to spot errors in the
HTML!

(to be precise I'm running with also another small diff applied but
doesn't affect the rendering.  it should fix a double free, will send
the diff later.)

> A couple remarks:
> 
>   (1) I noticed in the diff lib we use the gcc extension to omit the
>       middle operand in ternary ifs in quite a few places.  I just did
>       a cursory scan of got and couldn't see it elsewhere _except_ it is
>       used in gotwebd/got_operations.c. I'm not sure if you or stsp have
>       a preference either way, but there are a few opportunities for it
>       in this diff so thought I'd mention it.

fwiw i agree with Stefan

>   (2) See inline comments regarding omitted '\n' from some tags in the
>       changed -fcgi_gen_response()/+fcgi_printf() lines. I'm pretty
>       sure it doesn't matter, but I thought I'd ask for the sake of
>       fidelity; please disregard if intentionally elided. Apart from
>       that, there might be a couple missing closing div tags that I've
>       also annotated inline.

I think I fixed them all.  Newlines aren't important but they make
debugging the HTML easier :)

> I'll setup gotweb this week and run the patch myself if someone doesn't
> ok before then. But I gave the diff a thorough read and it looks fine;
>
> I particularly like all the '-' lines :)
>
> [...]
>
> > @@ -1292,7 +1123,7 @@ render:
> >  	if (error)
> >  		goto done;
> >  div:
> > -	fcgi_gen_response(c, "</div>\n");
> > +	/* fcgi_printf(c, "<!-- extra? --></div>\n"); */

Dropped this leftover.  That div was indeed extra!

> > [...]
> </div> missing between div class='commit' and div class='navs_wrapper'?
> see below xxx

(replying to this but applies to the other "is </div> missing?")

I think they were redundant originally.  I've checked the generated
html with the W3C validator and the HTML is now fine.  (the validator
complains about some things that i'm planning to address after this
goes in, but the HTML is balanced now :-)

> > +		r = fcgi_printf(c, "<div class='navs_wrapper'>\n"
> > +		    "<div class='navs'>"
> > +		    "<a href='?index_page=%s&path=%s&action=diff&commit=%s>"

I forgot to close the ' in these two links, now fixed

> > [...]
> > +		    ,

I fixed also these strange commas on their own line.

> > +		    age ? age : "",
> > +		    index_page_str, qs->path, refname,
> > +		    refname,
> > +		    index_page_str, qs->path, refname,
> > +		    index_page_str, qs->path, refname,
> > +		    index_page_str, qs->path, refname);
> > +		if (r == -1)
> > [...]
> > -	fcgi_gen_response(c, "</div>\n");
> > -	fcgi_gen_response(c, "</div>\n");
> > +	fcgi_printf(c, "</div>\n"); /* #tree */
> > +	fcgi_printf(c, "</div>\n"); /* #tre_content */
> 
>                                        #tree_content

thanks for spotting the typo!

> > [...]
> > -	fcgi_gen_response(c, "</div>\n");
> > +	fcgi_printf(c, "</div>\n"); /* #diff */
> > +	fcgi_printf(c, "</div>\n"); /* #diff_content */
> 
>                         ^^^^^^^^ second </div> tag was after done:

I decided not to care about this (and the other places where I moved
the </div> before the done label.)  It's too difficult and it's just
not worth it to try to keep the HTML balance when errors occurs, and
these </div> after the done label didn't help with that anyway.

> >  done:
> > -	fcgi_gen_response(c, "</div>\n");
> 
>                               ^^^^^^^^ here
> 
> >  	free(age);
> >  	free(author);
> >  	return error;


-----------------------------------------------
commit ac899ae0f5cf700be62f3caf7d7a9b6544d2bf3a
from: Omar Polo <op@omarpolo.com>
date: Mon Aug 15 14:39:05 2022 UTC
 
 gotwebd: add fcgi_printf
 
diff 6dd6b25bb156f2610bd9d59c89bb355c9b178718 ac899ae0f5cf700be62f3caf7d7a9b6544d2bf3a
commit - 6dd6b25bb156f2610bd9d59c89bb355c9b178718
commit + ac899ae0f5cf700be62f3caf7d7a9b6544d2bf3a
blob - f6cb81448f5778d7872b537fd6b90838285c99ee
blob + ae872cfb0d22ad79db91ef2e1032bae6db071f17
--- gotwebd/fcgi.c
+++ gotwebd/fcgi.c
@@ -25,6 +25,7 @@
 #include <errno.h>
 #include <event.h>
 #include <imsg.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -308,6 +309,27 @@ fcgi_timeout(int fd, short events, void *arg)
 }
 
 int
+fcgi_printf(struct request *c, const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+	int r;
+
+	va_start(ap, fmt);
+	r = vasprintf(&str, fmt, ap);
+	va_end(ap);
+
+	if (r == -1) {
+		log_warn("%s: asprintf", __func__);
+		return -1;
+	}
+
+	r = fcgi_gen_binary_response(c, str, r);
+	free(str);
+	return r;
+}
+
+int
 fcgi_gen_binary_response(struct request *c, const uint8_t *data, int len)
 {
 	int r;
@@ -346,14 +368,6 @@ fcgi_gen_binary_response(struct request *c, const uint
 	return 0;
 }
 
-int
-fcgi_gen_response(struct request *c, const char *data)
-{
-	if (data == NULL || *data == '\0')
-		return 0;
-	return fcgi_gen_binary_response(c, data, strlen(data));
-}
-
 static int
 send_response(struct request *c, int type, const uint8_t *data,
     size_t len)
blob - 1c274b225d0e710d16528c4b9970348bb3bec74e
blob + c0128e61de47bce0c95159c89f49edb94b403449
--- gotwebd/got_operations.c
+++ gotwebd/got_operations.c
@@ -829,10 +829,10 @@ got_output_repo_tree(struct request *c)
 	struct got_reflist_head refs;
 	struct got_tree_object *tree = NULL;
 	struct repo_dir *repo_dir = t->repo_dir;
+	const char *name, *index_page_str, *folder;
 	char *id_str = NULL;
-	char *path = NULL, *in_repo_path = NULL, *build_folder = NULL;
-	char *modestr = NULL, *name = NULL;
-	int nentries, i;
+	char *path = NULL, *in_repo_path = NULL, *modestr = NULL;
+	int nentries, i, r;
 
 	TAILQ_INIT(&refs);
 
@@ -871,6 +871,9 @@ got_output_repo_tree(struct request *c)
 
 	nentries = got_object_tree_get_nentries(tree);
 
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
+	folder = qs->folder ? qs->folder : "";
+
 	for (i = 0; i < nentries; i++) {
 		struct got_tree_entry *te;
 		mode_t mode;
@@ -917,226 +920,55 @@ got_output_repo_tree(struct request *c)
 			}
 		}
 
-		name = strdup(got_tree_entry_get_name(te));
-		if (name == NULL) {
-			error = got_error_from_errno("strdup");
-			goto done;
-		}
 		if (S_ISDIR(mode)) {
-			if (asprintf(&build_folder, "%s/%s",
-			    qs->folder ? qs->folder : "",
-			    got_tree_entry_get_name(te)) == -1) {
-				error = got_error_from_errno("asprintf");
+			name = got_tree_entry_get_name(te);
+			r = fcgi_printf(c,
+			    "<div class='tree_wrapper'>\n"
+			    "<div class='tree_line'>"
+			    "<a class='diff_directory' "
+			    "href='?index_page=%s&path=%s&action=tree"
+			    "&commit=%s&folder=%s/%s'>%s%s</a>"
+			    "</div>\n" /* .tree_line */
+			    "<div class='tree_line_blank'>&nbsp;</div>\n"
+			    "</div>\n", /* .tree_wrapper */
+			    index_page_str, qs->path, rc->commit_id,
+			    folder, name, name, modestr);
+			if (r == -1)
 				goto done;
-			}
-
-			if (fcgi_gen_response(c,
-			    "<div class='tree_wrapper'>\n") == -1)
-			goto done;
-
-			if (fcgi_gen_response(c, "<div class='tree_line'>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "<a class='diff_directory' "
-			    "href='?index_page=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->index_page_str) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "&path=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->path) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "&action=tree") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "&commit=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, rc->commit_id) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "&folder=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, build_folder) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "'>") == -1)
-				goto done;
-			if (fcgi_gen_response(c, name) == -1)
-				goto done;
-			if (fcgi_gen_response(c, modestr) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</a>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "<div class='tree_line_blank'>") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "&nbsp;") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
-
 		} else {
-			free(name);
-			name = strdup(got_tree_entry_get_name(te));
-			if (name == NULL) {
-				error = got_error_from_errno("strdup");
+			name = got_tree_entry_get_name(te);
+			r = fcgi_printf(c,
+			    "<div class='tree_wrapper'>\n"
+			    "<div class='tree_line'>"
+			    "<a href='?index_page=%s&path=%s&action=blob"
+			    "&commit=%s&folder=%s&file=%s'>%s%s</a>"
+			    "</div>\n" /* .tree_line */
+			    "<div class='tree_line_blank'>"
+			    "<a href='?index_page=%s&path=%s&action=commits"
+			    "&commit=%s&folder=%s&file=%s'>commits</a>\n"
+			    " | \n"
+			    "<a href='?index_page=%s&path=%s&action=blame"
+			    "&commit=%s&folder=%s&file=%s'>blame</a>\n"
+			    "</div>\n"  /* .tree_line_blank */
+			    "</div>\n", /* .tree_wrapper */
+			    index_page_str, qs->path, rc->commit_id,
+			    folder, name, name, modestr,
+			    index_page_str, qs->path, rc->commit_id,
+			    folder, name,
+			    index_page_str, qs->path, rc->commit_id,
+			    folder, name);
+			if (r == -1)
 				goto done;
-			}
-
-			if (fcgi_gen_response(c,
-			    "<div class='tree_wrapper'>\n") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "<div class='tree_line'>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c,
-			    "<a href='?index_page=") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, qs->index_page_str) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&path=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->path) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&action=blob") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&commit=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, rc->commit_id) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&folder=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->folder) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&file=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, name) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "'>") == -1)
-				goto done;
-			if (fcgi_gen_response(c, name) == -1)
-				goto done;
-			if (fcgi_gen_response(c, modestr) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "</a>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "<div class='tree_line_blank'>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c,
-			    "<a href='?index_page=") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, qs->index_page_str) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&path=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->path) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&action=commits") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&commit=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, rc->commit_id) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&folder=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->folder) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&file=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, name) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "'>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "commits") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</a>\n") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, " | \n") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c,
-			    "<a href='?index_page=") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, qs->index_page_str) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&path=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->path) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&action=blame") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&commit=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, rc->commit_id) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&folder=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, qs->folder) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "&file=") == -1)
-				goto done;
-			if (fcgi_gen_response(c, name) == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "'>") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "blame") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</a>\n") == -1)
-				goto done;
-
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
 		}
 		free(id_str);
 		id_str = NULL;
-		free(build_folder);
-		build_folder = NULL;
-		free(name);
-		name = NULL;
 		free(modestr);
 		modestr = NULL;
 	}
 done:
 	free(id_str);
-	free(build_folder);
 	free(modestr);
 	free(path);
-	free(name);
 	got_ref_list_free(&refs);
 	if (commit)
 		got_object_commit_close(commit);
@@ -1286,11 +1118,13 @@ got_gotweb_blame_cb(void *arg, int nlines, int lineno,
 	struct transport *t = c->t;
 	struct querystring *qs = t->qs;
 	struct repo_dir *repo_dir = t->repo_dir;
+	const char *index_page_str;
 	char *line = NULL, *eline = NULL;
 	size_t linesize = 0;
 	off_t offset;
 	struct tm tm;
 	time_t committer_time;
+	int r;
 
 	if (nlines != a->nlines ||
 	    (lineno != -1 && lineno < 1) || lineno > a->nlines)
@@ -1334,10 +1168,10 @@ got_gotweb_blame_cb(void *arg, int nlines, int lineno,
 		goto done;
 	}
 
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
+
 	while (bline->annotated) {
-		int out_buff_size = 100;
 		char *smallerthan, *at, *nl, *committer;
-		char out_buff[out_buff_size];
 		size_t len;
 
 		if (getline(&line, &linesize, a->f) == -1) {
@@ -1361,68 +1195,29 @@ got_gotweb_blame_cb(void *arg, int nlines, int lineno,
 		if (nl)
 			*nl = '\0';
 
-		if (fcgi_gen_response(c, "<div class='blame_wrapper'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='blame_number'>") == -1)
-			goto done;
-		if (snprintf(out_buff, strlen(out_buff), "%.*d", a->nlines_prec,
-		    a->lineno_cur) < 0)
-			goto done;
-		if (fcgi_gen_response(c, out_buff) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='blame_hash'>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=diff&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, bline->id_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (snprintf(out_buff, 10, "%.8s", bline->id_str) < 0)
-			goto done;
-		if (fcgi_gen_response(c, out_buff) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a></div>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='blame_date'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, bline->datebuf) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='blame_author'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, committer) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='blame_code'>") == -1)
-			goto done;
 		err = gotweb_escape_html(&eline, line);
 		if (err)
 			goto done;
-		if (fcgi_gen_response(c, eline) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "</div>") == -1)
+		r = fcgi_printf(c, "<div class='blame_wrapper'>"
+		    "<div class='blame_number'>%.*d</div>"
+		    "<div class='blame_hash'>"
+		    "<a href='?index_page=%s&path=%s&action=diff&commit=%s'>"
+		    "%.8s</a>"
+		    "</div>"
+		    "<div class='blame_date'>%s</div>"
+		    "<div class='blame_author'>%s</div>"
+		    "<div class='blame_code'>%s</div>"
+		    "</div>",	/* .blame_wrapper */
+		    a->nlines_prec, a->lineno_cur,
+		    index_page_str, repo_dir->name, bline->id_str,
+		    bline->id_str,
+		    bline->datebuf,
+		    committer,
+		    eline);
+		if (r == -1)
 			goto done;
+
 		a->lineno_cur++;
 		bline = &a->lines[a->lineno_cur - 1];
 	}
@@ -1767,12 +1562,7 @@ got_output_repo_diff(struct request *c)
 				goto done;
 			}
 		}
-		if (fcgi_gen_response(c, "<div class='diff_line' class='") == -1)
-			goto done;
-		if (fcgi_gen_response(c, color ? color : "") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
+
 		newline = strchr(line, '\n');
 		if (newline)
 			*newline = '\0';
@@ -1780,13 +1570,12 @@ got_output_repo_diff(struct request *c)
 		error = gotweb_escape_html(&eline, line);
 		if (error)
 			goto done;
-		if (fcgi_gen_response(c, eline) == -1)
-			goto done;
+
+		fcgi_printf(c, "<div class='diff_line %s'>%s</div>\n",
+		    color ? color : "", eline);
 		free(eline);
 		eline = NULL;
 
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 		if (linelen > 0)
 			wrlen = wrlen + linelen;
 		free(color);
blob - 4b3729bd8c2a9dba3031daeca4b3fea398ba2f7f
blob + f332e5d6085b45092bae5d9094c76128da56fc32
--- gotwebd/gotweb.c
+++ gotwebd/gotweb.c
@@ -118,7 +118,7 @@ gotweb_process_request(struct request *c)
 	struct querystring *qs = NULL;
 	struct repo_dir *repo_dir = NULL;
 	uint8_t err[] = "gotwebd experienced an error: ";
-	int html = 0;
+	int r, html = 0;
 
 	/* init the transport */
 	error = gotweb_init_transport(&c->t);
@@ -270,29 +270,27 @@ render:
 		break;
 	case ERR:
 	default:
-		if (fcgi_gen_response(c, "<div id='err_content'>") == -1)
+		r = fcgi_printf(c, "<div id='err_content'>%s</div>\n",
+		    "Erorr: Bad Querystring");
+		if (r == -1)
 			goto err;
-		if (fcgi_gen_response(c, "Error: Bad Querystring\n") == -1)
-			goto err;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto err;
 		break;
 	}
 
 	goto done;
 err:
-	if (html && fcgi_gen_response(c, "<div id='err_content'>") == -1)
+	if (html && fcgi_printf(c, "<div id='err_content'>") == -1)
 		return;
-	if (fcgi_gen_response(c, err) == -1)
+	if (fcgi_printf(c, "%s", err) == -1)
 		return;
 	if (error) {
-		if (fcgi_gen_response(c, (uint8_t *)error->msg) == -1)
+		if (fcgi_printf(c, "%s", error->msg) == -1)
 			return;
 	} else {
-		if (fcgi_gen_response(c, "see daemon logs for details") == -1)
+		if (fcgi_printf(c, "see daemon logs for details") == -1)
 			return;
 	}
-	if (html && fcgi_gen_response(c, "</div>\n") == -1)
+	if (html && fcgi_printf(c, "</div>\n") == -1)
 		return;
 done:
 	if (c->t->repo != NULL && qs->action != INDEX)
@@ -628,205 +626,124 @@ gotweb_free_transport(struct transport *t)
 const struct got_error *
 gotweb_render_content_type(struct request *c, const uint8_t *type)
 {
-	const struct got_error *error = NULL;
-	char *h = NULL;
-
-	if (asprintf(&h, "Content-type: %s\r\n\r\n", type) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
-
-	fcgi_gen_response(c, h);
-done:
-	free(h);
-
-	return error;
+	fcgi_printf(c, "Content-Type: %s\r\n\r\n", type);
+	return NULL;
 }
 
 const struct got_error *
 gotweb_render_content_type_file(struct request *c, const uint8_t *type,
     char *file)
 {
-	const struct got_error *error = NULL;
-	char *h = NULL;
-
-	if (asprintf(&h, "Content-type: %s\r\n"
+	fcgi_printf(c, "Content-type: %s\r\n"
 	    "Content-disposition: attachment; filename=%s\r\n\r\n",
-	    type, file) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
-
-	fcgi_gen_response(c, h);
-done:
-	free(h);
-
-	return error;
+	    type, file);
+	return NULL;
 }
 
 static const struct got_error *
 gotweb_render_header(struct request *c)
 {
-	const struct got_error *error = NULL;
 	struct server *srv = c->srv;
 	struct querystring *qs = c->t->qs;
-	char *title = NULL, *droot = NULL, *css = NULL, *gotlink = NULL;
-	char *gotimg = NULL, *sitelink = NULL, *summlink = NULL;
+	char droot[PATH_MAX];
+	int r;
 
 	if (strlen(c->document_root) > 0) {
-		if (asprintf(&droot, "/%s/", c->document_root) == -1) {
-			error = got_error_from_errno2("%s: asprintf", __func__);
-			goto done;
-		}
-	} else {
-		if (asprintf(&droot, "/") == -1) {
-			error = got_error_from_errno2("%s: asprintf", __func__);
-			goto done;
-		}
-	}
+		r = snprintf(droot, sizeof(droot), "/%s/", c->document_root);
+		if (r < 0 || (size_t)r >= sizeof(droot))
+			return got_error(GOT_ERR_NO_SPACE);
+	} else
+		strlcpy(droot, "/", sizeof(droot));
 
-	if (asprintf(&title, "<title>%s</title>\n", srv->site_name) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
+	r = fcgi_printf(c, "<!doctype html>\n"
+	    "<html>\n"
+	    "<head>\n"
+	    "<title>%s</title>\n"
+	    "<meta charset='utf-8' />\n"
+	    "<meta name='viewport' content='initial-scale=.75' />\n"
+	    "<meta name='msapplication-TileColor' content='#da532c' />\n"
+	    "<meta name='theme-color' content='#ffffff'/>\n"
+	    "<link rel='apple-touch-icon' sizes='180x180'"
+	    " href='/apple-touch-icon.png' />\n"
+	    "<link rel='icon' type='image/png' sizes='32x32'"
+	    " href='/favicon-32x32.png' />\n"
+	    "<link rel='icon' type='image/png' sizes='16x16'"
+	    " href='/favicon-16x16.png' />\n"
+	    "<link rel='manifest' href='/site.webmanifest'/>\n"
+	    "<link rel='mask-icon' href='/safari-pinned-tab.svg' />\n"
+	    "<link rel='stylesheet' type='text/css' href='%s%s' />\n"
+	    "</head>\n"
+	    "<body>\n"
+	    "<div id='gw_body'>\n"
+	    "<div id='header'>\n"
+	    "<div id='got_link'>"
+	    "<a href='%s' target='_sotd'>"
+	    "<img src='%s%s' alt='logo' id='logo' />"
+	    "</a>\n"
+	    "</div>\n"		/* #got_link */
+	    "</div>\n"		/* #header */
+	    "<div id='site_path'>\n"
+	    "<div id='site_link'>\n"
+	    "<a href='/%s?index_page=%d' alt='sitelink'>%s</a>",
+	    srv->site_name,
+	    droot, srv->custom_css,
+	    srv->logo_url,
+	    droot, srv->logo,
+	    c->document_root, qs->index_page, srv->site_link);
+	if (r == -1)
 		goto done;
-	}
-	if (asprintf(&css,
-	    "<link rel='stylesheet' type='text/css' href='%s%s'/>\n",
-	    droot, srv->custom_css) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
-	if (asprintf(&gotlink, "<a href='%s' target='_sotd'>",
-	    srv->logo_url) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
-	if (asprintf(&gotimg, "<img src='%s%s' alt='logo' id='logo'/></a>",
-	    droot, srv->logo) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
-	if (asprintf(&sitelink, "<a href='/%s?index_page=%d' "
-	    "alt='sitelink'>%s</a>", c->document_root, qs->index_page,
-	    srv->site_link) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
-	if (asprintf(&summlink, "<a href='/%s?index_page=%d&path=%s"
-	    "&action=summary' alt='summlink'>%s</a>", c->document_root,
-	    qs->index_page, qs->path, qs->path) == -1) {
-		error = got_error_from_errno2("%s: asprintf", __func__);
-		goto done;
-	}
 
-	if (fcgi_gen_response(c, "<!DOCTYPE html>\n<head>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, title) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<meta name='viewport' "
-	    "content='initial-scale=.75, user-scalable=yes'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<meta charset='utf-8'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<meta name='msapplication-TileColor' "
-	    "content='#da532c'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c,
-	    "<meta name='theme-color' content='#ffffff'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<link rel='apple-touch-icon' sizes='180x180' "
-	    "href='/apple-touch-icon.png'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c,
-	    "<link rel='icon' type='image/png' sizes='32x32' "
-	    "href='/favicon-32x32.png'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<link rel='icon' type='image/png' "
-	    "sizes='16x16' href='/favicon-16x16.png'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<link rel='manifest' "
-	    "href='/site.webmanifest'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<link rel='mask-icon' "
-	    "href='/safari-pinned-tab.svg'/>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, css) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</head>\n<body>\n<div id='gw_body'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='header'>\n<div id='got_link'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, gotlink) == -1)
-		goto done;
-	if (fcgi_gen_response(c, gotimg) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='site_path'>\n<div id='site_link'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, sitelink) == -1)
-		goto done;
 	if (qs != NULL) {
 		if (qs->path != NULL) {
-			if (fcgi_gen_response(c, " / ") == -1)
+			r = fcgi_printf(c, " / "
+			    "<a href='/%s?index_page=%d&path=%s&action=summary'"
+			    " alt='summlink'>%s</a>",
+			    c->document_root, qs->index_page, qs->path,
+			    qs->path);
+			if (r == -1)
 				goto done;
-			if (fcgi_gen_response(c, summlink) == -1)
-				goto done;
 		}
 		if (qs->action != INDEX) {
-			if (fcgi_gen_response(c, " / ") == -1)
-				goto done;
-			switch(qs->action) {
-			case(BLAME):
-				if (fcgi_gen_response(c, "blame") == -1)
-					goto done;
+			const char *action = "";
+
+			switch (qs->action) {
+			case BLAME:
+				action = "blame";
 				break;
-			case(BRIEFS):
-				if (fcgi_gen_response(c, "briefs") == -1)
-					goto done;
+			case BRIEFS:
+				action = "briefs";
 				break;
-			case(COMMITS):
-				if (fcgi_gen_response(c, "commits") == -1)
-					goto done;
+			case COMMITS:
+				action = "commits";
 				break;
-			case(DIFF):
-				if (fcgi_gen_response(c, "diff") == -1)
-					goto done;
+			case DIFF:
+				action = "diff";
 				break;
-			case(SUMMARY):
-				if (fcgi_gen_response(c, "summary") == -1)
-					goto done;
+			case SUMMARY:
+				action = "summary";
 				break;
-			case(TAG):
-				if (fcgi_gen_response(c, "tag") == -1)
-					goto done;
+			case TAG:
+				action = "tag";
 				break;
-			case(TAGS):
-				if (fcgi_gen_response(c, "tags") == -1)
-					goto done;
+			case TAGS:
+				action = "tags";
 				break;
-			case(TREE):
-				if (fcgi_gen_response(c, "tree") == -1)
-					goto done;
+			case TREE:
+				action = "tree";
 				break;
-			default:
-				break;
 			}
+
+			if (fcgi_printf(c, " / %s", action) == -1)
+				goto done;
 		}
-
 	}
-	fcgi_gen_response(c, "</div>\n</div>\n<div id='content'>\n");
+
+	fcgi_printf(c, "</div>\n"	/* #site_path */
+	    "</div>\n"			/* #site_link */
+	    "<div id='content'>\n");
+
 done:
-	free(title);
-	free(droot);
-	free(css);
-	free(gotlink);
-	free(gotimg);
-	free(sitelink);
-	free(summlink);
-
-	return error;
+	return NULL;
 }
 
 static const struct got_error *
@@ -834,26 +751,25 @@ gotweb_render_footer(struct request *c)
 {
 	const struct got_error *error = NULL;
 	struct server *srv = c->srv;
-	char *siteowner = NULL;
+	const char *siteowner = "&nbsp;";
+	char *escaped_owner = NULL;
 
-	if (fcgi_gen_response(c, "<div id='site_owner_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='site_owner'>") == -1)
-		goto done;
 	if (srv->show_site_owner) {
-		error = gotweb_escape_html(&siteowner, srv->site_owner);
+		error = gotweb_escape_html(&escaped_owner, srv->site_owner);
 		if (error)
-			goto done;
-		if (fcgi_gen_response(c, siteowner) == -1)
-			goto done;
-	} else
-		if (fcgi_gen_response(c, "&nbsp;") == -1)
-			goto done;
-	fcgi_gen_response(c, "</div>\n</div>\n</div>\n</body>\n</html>");
-done:
-	free(siteowner);
+			return error;
+		siteowner = escaped_owner;
+	}
 
-	return error;
+	fcgi_printf(c, "<div id='site_owner_wrapper'>\n"
+	    "<div id='site_owner'>%s</div>\n"
+	    "</div>\n"		/* #site_owner_wrapper */
+	    "</div>\n"		/* #content */
+	    "</div>\n"		/* #gw_body */
+	    "</body>\n</html>\n", siteowner);
+
+	free(escaped_owner);
+	return NULL;
 }
 
 static const struct got_error *
@@ -864,12 +780,11 @@ gotweb_render_navs(struct request *c)
 	struct querystring *qs = t->qs;
 	struct server *srv = c->srv;
 	char *nhref = NULL, *phref = NULL;
-	int disp = 0;
+	int r, disp = 0;
 
-	if (fcgi_gen_response(c, "<div id='np_wrapper'>\n") == -1)
+	r = fcgi_printf(c, "<div id='np_wrapper'>\n<div id='nav_prev'>\n");
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c, "<div id='nav_prev'>") == -1)
-		goto done;
 
 	switch(qs->action) {
 	case INDEX:
@@ -933,20 +848,17 @@ gotweb_render_navs(struct request *c)
 	}
 
 	if (disp) {
-		if (fcgi_gen_response(c, "<a href='?") == -1)
-			goto  done;
-		if (fcgi_gen_response(c, phref) == -1)
+		r = fcgi_printf(c, "<a href='?%s'>Previous</a>", phref);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, "'>Previous</a>") == -1)
-			goto done;
 	}
-	if (fcgi_gen_response(c, "</div>\n") == -1)
+
+	r = fcgi_printf(c, "</div>\n"	/* #nav_prev */
+	    "<div id='nav_next'>");
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c, "<div id='nav_next'>") == -1)
-		goto done;
 
 	disp = 0;
-
 	switch(qs->action) {
 	case INDEX:
 		if (t->next_disp == srv->max_repos_display &&
@@ -1007,14 +919,12 @@ gotweb_render_navs(struct request *c)
 		break;
 	}
 	if (disp) {
-		if (fcgi_gen_response(c, "<a href='?") == -1)
+		r = fcgi_printf(c, "<a href='?%s'>Next</a>", nhref);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, nhref) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>Next</a>") == -1)
-			goto done;
 	}
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"); /* #nav_next */
+	fcgi_printf(c, "</div>\n"); /* #np_wrapper */
 done:
 	free(t->next_id);
 	t->next_id = NULL;
@@ -1035,10 +945,14 @@ gotweb_render_index(struct request *c)
 	struct repo_dir *repo_dir = NULL;
 	DIR *d;
 	struct dirent **sd_dent;
+	const char *index_page_str;
 	char *c_path = NULL;
 	struct stat st;
 	unsigned int d_cnt, d_i, d_disp = 0;
+	int r;
 
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
+
 	d = opendir(srv->repos_path);
 	if (d == NULL) {
 		error = got_error_from_errno2("opendir", srv->repos_path);
@@ -1070,24 +984,24 @@ gotweb_render_index(struct request *c)
 		c_path = NULL;
 	}
 
-	if (fcgi_gen_response(c, "<div id='index_header'>\n") == -1)
+	r = fcgi_printf(c, "<div id='index_header'>\n"
+	    "<div id='index_header_project'>Project</div>\n");
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='index_header_project'>Project</div>\n") == -1)
-		goto done;
+
 	if (srv->show_repo_description)
-		if (fcgi_gen_response(c, "<div id='index_header_description'>"
+		if (fcgi_printf(c, "<div id='index_header_description'>"
 		    "Description</div>\n") == -1)
 			goto done;
 	if (srv->show_repo_owner)
-		if (fcgi_gen_response(c, "<div id='index_header_owner'>"
+		if (fcgi_printf(c, "<div id='index_header_owner'>"
 		    "Owner</div>\n") == -1)
 			goto done;
 	if (srv->show_repo_age)
-		if (fcgi_gen_response(c, "<div id='index_header_age'>"
+		if (fcgi_printf(c, "<div id='index_header_age'>"
 		    "Last Change</div>\n") == -1)
 			goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
+	if (fcgi_printf(c, "</div>\n") == -1) /* #index_header */
 		goto done;
 
 	for (d_i = 0; d_i < d_cnt; d_i++) {
@@ -1128,149 +1042,69 @@ gotweb_render_index(struct request *c)
 render:
 		d_disp++;
 		t->prev_disp++;
-		if (fcgi_gen_response(c, "<div class='index_wrapper'>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='index_project'>") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
+		r = fcgi_printf(c, "<div class='index_wrapper'>\n"
+		    "<div class='index_project'>"
+		    "<a href='?index_page=%s&path=%s&action=summary'>"
+		    " %s "
+		    "</a>"
+		    "</div>",	/* .index_project */
+		    index_page_str, repo_dir->name,
+		    repo_dir->name);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=summary'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
 		if (srv->show_repo_description) {
-			if (fcgi_gen_response(c,
-			    "<div class='index_project_description'>\n") == -1)
+			r = fcgi_printf(c,
+			    "<div class='index_project_description'>\n"
+			    "%s</div>\n", repo_dir->description);
+			if (r == -1)
 				goto done;
-			if (fcgi_gen_response(c, repo_dir->description) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
 		}
 
 		if (srv->show_repo_owner) {
-			if (fcgi_gen_response(c,
-			    "<div class='index_project_owner'>") == -1)
+			r = fcgi_printf(c, "<div class='index_project_owner'>"
+			    "%s</div>\n", repo_dir->owner);
+			if (r == -1)
 				goto done;
-			if (fcgi_gen_response(c, repo_dir->owner) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
 		}
 
 		if (srv->show_repo_age) {
-			if (fcgi_gen_response(c,
-			    "<div class='index_project_age'>") == -1)
+			r = fcgi_printf(c, "<div class='index_project_age'>"
+			    "%s</div>\n", repo_dir->age);
+			if (r == -1)
 				goto done;
-			if (fcgi_gen_response(c, repo_dir->age) == -1)
-				goto done;
-			if (fcgi_gen_response(c, "</div>\n") == -1)
-				goto done;
 		}
 
-		if (fcgi_gen_response(c, "<div class='navs_wrapper'>") == -1)
+		r = fcgi_printf(c, "<div class='navs_wrapper'>"
+		    "<div class='navs'>"
+		    "<a href='?index_page=%s&path=%s&action=summary'>"
+		    "summary"
+		    "</a> | "
+		    "<a href='?index_page=%s&path=%s&action=briefs'>"
+		    "commit briefs"
+		    "</a> | "
+		    "<a href='?index_page=%s&path=%s&action=commits'>"
+		    "commits"
+		    "</a> | "
+		    "<a href='?index_page=%s&path=%s&action=tags'>"
+		    "tags"
+		    "</a> | "
+		    "<a href='?index_page=%s&path=%s&action=tree'>"
+		    "tree"
+		    "</a>"
+		    "</div>"	/* .navs */
+		    "<div class='dotted_line'></div>\n"
+		    "</div>\n"	/* .navs_wrapper */
+		    "</div>\n",	/* .index_wrapper */
+		    index_page_str, repo_dir->name,
+		    index_page_str, repo_dir->name,
+		    index_page_str, repo_dir->name,
+		    index_page_str, repo_dir->name,
+		    index_page_str, repo_dir->name);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, "<div class='navs'>") == -1)
-			goto done;;
 
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=summary'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "summary") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a> | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=briefs'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "commit briefs") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a> | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=commits'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "commits") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a> | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=tags'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "tags") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a> | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=tree'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "tree") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "</div>") == -1)
-			goto done;
-		if (fcgi_gen_response(c,
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
 		gotweb_free_repo_dir(repo_dir);
 		repo_dir = NULL;
 		error = got_repo_close(t->repo);
@@ -1281,18 +1115,16 @@ render:
 			break;
 	}
 	if (srv->max_repos_display == 0)
-		goto div;
+		goto done;
 	if (srv->max_repos > 0 && srv->max_repos < srv->max_repos_display)
-		goto div;
+		goto done;
 	if (t->repos_total <= srv->max_repos ||
 	    t->repos_total <= srv->max_repos_display)
-		goto div;
+		goto done;
 
 	error = gotweb_render_navs(c);
 	if (error)
 		goto done;
-div:
-	fcgi_gen_response(c, "</div>\n");
 done:
 	if (d != NULL && closedir(d) == EOF && error == NULL)
 		error = got_error_from_errno("closedir");
@@ -1306,6 +1138,7 @@ gotweb_render_blame(struct request *c)
 	struct transport *t = c->t;
 	struct repo_commit *rc = NULL;
 	char *age = NULL;
+	int r;
 
 	error = got_get_repo_commits(c, 1);
 	if (error)
@@ -1317,58 +1150,32 @@ gotweb_render_blame(struct request *c)
 	if (error)
 		goto done;
 
-	if (fcgi_gen_response(c, "<div id='blame_title_wrapper'>\n") == -1)
+	r = fcgi_printf(c, "<div id='blame_title_wrapper'>\n"
+	    "<div id='blame_title'>Blame</div>\n"
+	    "</div>\n"		/* #blame_title_wrapper */
+	    "<div id='blame_content'>\n"
+	    "<div id='blame_header_wrapper'>\n"
+	    "<div id='blame_header'>\n"
+	    "<div class='header_age_title'>Date:</div>\n"
+	    "<div class='header_age'>%s</div>\n"
+	    "<div id='header_commit_msg_title'>Message:</div>\n"
+	    "<div id='header_commit_msg'>%s</div>\n"
+	    "</div>\n"		/* #blame_header */
+	    "</div>\n"		/* #blame_header_wrapper */
+	    "<div class='dotted_line'></div>\n"
+	    "<div id='blame'>\n",
+	    age ? age : "",
+	    rc->commit_msg);
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c, "<div id='blame_title'>Blame</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
 
-	if (fcgi_gen_response(c, "<div id='blame_content'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='blame_header_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='blame_header'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_age_title'>Date:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_age'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, age ? age : "") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_commit_msg_title'>Message:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_commit_msg'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->commit_msg) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='dotted_line'></div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='blame'>\n") == -1)
-		goto done;
-
 	error = got_output_file_blame(c);
 	if (error)
 		goto done;
 
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"	/* #blame */
+	    "</div>\n");		/* #blame_content */
 done:
-	fcgi_gen_response(c, "</div>\n");
 	return error;
 }
 
@@ -1381,18 +1188,18 @@ gotweb_render_briefs(struct request *c)
 	struct transport *t = c->t;
 	struct querystring *qs = t->qs;
 	struct repo_dir *repo_dir = t->repo_dir;
+	const char *index_page_str;
 	char *smallerthan, *newline;
 	char *age = NULL;
+	int r;
 
-	if (fcgi_gen_response(c, "<div id='briefs_title_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='briefs_title'>Commit Briefs</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
 
-	if (fcgi_gen_response(c, "<div id='briefs_content'>\n") == -1)
+	r = fcgi_printf(c, "<div id='briefs_title_wrapper'>\n"
+	    "<div id='briefs_title'>Commit Briefs</div>\n"
+	    "</div>\n"	/* #briefs_title_wrapper */
+	    "<div id='briefs_content'>\n");
+	if (r == -1)
 		goto done;
 
 	if (qs->action == SUMMARY) {
@@ -1407,123 +1214,52 @@ gotweb_render_briefs(struct request *c)
 		error = gotweb_get_time_str(&age, rc->committer_time, TM_DIFF);
 		if (error)
 			goto done;
-		if (fcgi_gen_response(c, "<div class='briefs_age'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, age ? age : "") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "<div class='briefs_author'>") == -1)
-			goto done;
 		smallerthan = strchr(rc->author, '<');
 		if (smallerthan)
 			*smallerthan = '\0';
-		if (fcgi_gen_response(c, rc->author) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "<div class='briefs_log'>") == -1)
-			goto done;
 		newline = strchr(rc->commit_msg, '\n');
 		if (newline)
 			*newline = '\0';
 
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
+		r = fcgi_printf(c, "<div class='briefs_age'>%s</div>\n"
+		    "<div class='briefs_author'>%s</div>\n"
+		    "<div class='briefs_log'>"
+		    "<a href='?index_page=%s&path=%s&action=diff&commit=%s"
+		    "&headref=%s'>%s</a>",
+		    age ? age : "",
+		    rc->author,
+		    index_page_str, repo_dir->name, rc->commit_id, qs->headref,
+		    rc->commit_msg);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=diff&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->headref) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_msg) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
+
 		if (rc->refs_str) {
-			if (fcgi_gen_response(c,
-			    " <span class='refs_str'>(") == -1)
+			r = fcgi_printf(c,
+			    " <span class='refs_str'>(%s)</span>",
+			    rc->refs_str);
+			if (r == -1)
 				goto done;
-			if (fcgi_gen_response(c, rc->refs_str) == -1)
-				goto done;
-			if (fcgi_gen_response(c, ")</span>") == -1)
-				goto done;
 		}
-		if (fcgi_gen_response(c, "</div>\n") == -1)
+		if (fcgi_printf(c, "</div>\n") == -1) /* .briefs_log */
 			goto done;
 
-		if (fcgi_gen_response(c, "<div class='navs_wrapper'>\n") == -1)
+		r = fcgi_printf(c, "<div class='navs_wrapper'>\n"
+		    "<div class='navs'>"
+		    "<a href='?index_page=%s&path=%s&action=diff&commit=%s"
+		    "&headref=%s'>diff</a>"
+		    " | "
+		    "<a href='?index_page=%s&path=%s&action=tree&commit=%s"
+		    "&headref=%s'>tree</a>"
+		    "</div>\n"	/* .navs */
+		    "</div>\n"	/* .navs_wrapper */
+		    "<div class='dotted_line'></div>\n",
+		    index_page_str, repo_dir->name, rc->commit_id, qs->headref,
+		    index_page_str, repo_dir->name, rc->commit_id, qs->headref);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, "<div class='navs'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=diff&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->headref) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "diff") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, " | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=tree&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->headref) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "tree") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c,
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
-
 		free(age);
 		age = NULL;
 	}
@@ -1533,7 +1269,7 @@ gotweb_render_briefs(struct request *c)
 		if (error)
 			goto done;
 	}
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"); /* #briefs_content */
 done:
 	free(age);
 	return error;
@@ -1548,18 +1284,17 @@ gotweb_render_commits(struct request *c)
 	struct transport *t = c->t;
 	struct querystring *qs = t->qs;
 	struct repo_dir *repo_dir = t->repo_dir;
+	const char *index_page_str;
 	char *age = NULL, *author = NULL;
-	/* int commit_found = 0; */
+	int r;
 
-	if (fcgi_gen_response(c, "<div class='commits_title_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c,
-	    "<div class='commits_title'>Commits</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
 
-	if (fcgi_gen_response(c, "<div class='commits_content'>\n") == -1)
+	r = fcgi_printf(c, "<div class='commits_title_wrapper'>\n"
+	    "<div class='commits_title'>Commits</div>\n"
+	    "</div>\n"		/* .commits_title_wrapper */
+	    "<div class='commits_content'>\n");
+	if (r == -1)
 		goto done;
 
 	error = got_get_repo_commits(c, srv->max_commits_display);
@@ -1574,113 +1309,38 @@ gotweb_render_commits(struct request *c)
 		if (error)
 			goto done;
 
-		if (fcgi_gen_response(c,
-		    "<div class='commits_header_wrapper'>\n") == -1)
+		r = fcgi_printf(c, "<div class='commits_header_wrapper'>\n"
+		    "<div class='commits_header'>\n"
+		    "<div class='header_commit_title'>Commit:</div>\n"
+		    "<div class='header_commit'>%s</div>\n"
+		    "<div class='header_author_title'>Author:</div>\n"
+		    "<div class='header_author'>%s</div>\n"
+		    "<div class='header_age_title'>Date:</div>\n"
+		    "<div class='header_age'>%s</div>\n"
+		    "</div>\n"	/* .commits_header */
+		    "</div>\n"	/* .commits_header_wrapper */
+		    "<div class='dotted_line'></div>\n"
+		    "<div class='commit'>\n%s</div>\n",
+		    rc->commit_id,
+		    author ? author : "",
+		    age ? age : "",
+		    rc->commit_msg);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, "<div class='commits_header'>\n") == -1)
-			goto done;
 
+		r = fcgi_printf(c, "<div class='navs_wrapper'>\n"
+		    "<div class='navs'>"
+		    "<a href='?index_page=%s&path=%s&action=diff&commit=%s'>"
+		    "diff</a>"
+		    " | "
+		    "<a href='?index_page=%s&path=%s&action=tree&commit=%s'>"
+		    "tree</a>"
+		    "</div>\n"	/* .navs */
+		    "</div>\n"	/* .navs_wrapper */
+		    "<div class='dotted_line'></div>\n",
+		    index_page_str, repo_dir->name, rc->commit_id,
+		    index_page_str, repo_dir->name, rc->commit_id);
 
-		if (fcgi_gen_response(c, "<div class='header_commit_title'>Commit:"
-		    "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='header_commit'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='header_author_title'>Author:"
-		    "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='header_author'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, author ? author : "") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='header_age_title'>Date:"
-		    "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='header_age'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, age ? age : "") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c,
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='commit'>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, rc->commit_msg) == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='navs_wrapper'>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='navs'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=diff&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "diff") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, " | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=tree&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rc->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "tree") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c,
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
 		free(age);
 		age = NULL;
 		free(author);
@@ -1692,8 +1352,7 @@ gotweb_render_commits(struct request *c)
 		if (error)
 			goto done;
 	}
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
+	fcgi_printf(c, "</div>\n"); /* .commits_content */
 done:
 	free(age);
 	return error;
@@ -1708,8 +1367,12 @@ gotweb_render_branches(struct request *c)
 	struct transport *t = c->t;
 	struct querystring *qs = t->qs;
 	struct got_repository *repo = t->repo;
+	const char *index_page_str;
 	char *age = NULL;
+	int r;
 
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
+
 	TAILQ_INIT(&refs);
 
 	error = got_ref_list(&refs, repo, "refs/heads",
@@ -1717,17 +1380,13 @@ gotweb_render_branches(struct request *c)
 	if (error)
 		goto done;
 
-	if (fcgi_gen_response(c, "<div id='branches_title_wrapper'>\n") == -1)
+	r = fcgi_printf(c, "<div id='branches_title_wrapper'>\n"
+	    "<div id='branches_title'>Branches</div>\n"
+	    "</div>\n"	/* #branches_title_wrapper */
+	    "<div id='branches_content'>\n");
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='branches_title'>Branches</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
 
-	if (fcgi_gen_response(c, "<div id='branches_content'>\n") == -1)
-		goto done;
-
 	TAILQ_FOREACH(re, &refs, entry) {
 		char *refname = NULL;
 
@@ -1750,132 +1409,41 @@ gotweb_render_branches(struct request *c)
 		if (strncmp(refname, "refs/heads/", 11) == 0)
 			refname += 11;
 
-		if (fcgi_gen_response(c, "<div class='branches_wrapper'>") == -1)
+		r = fcgi_printf(c, "<div class='branches_wrapper'>\n"
+		    "<div class='branches_age'>%s</div>\n"
+		    "<div class='branches_space'>&nbsp;</div>\n"
+		    "<div class='branch'>"
+		    "<a href='?index_page=%s&path=%s&action=summary&headref=%s'>"
+		    "%s</a>"
+		    "</div>\n"	/* .branch */
+		    "<div class='navs_wrapper'>\n"
+		    "<div class='navs'>"
+		    "<a href='?index_page=%s&path=%s&action=summary&headref=%s'>"
+		    "summary</a>"
+		    " | "
+		    "<a href='?index_page=%s&path=%s&action=briefs&headref=%s'>"
+		    "commit briefs</a>"
+		    " | "
+		    "<a href='?index_page=%s&path=%s&action=commits&headref=%s'>"
+		    "commits</a>"
+		    "</div>\n"	/* .navs */
+		    "</div>\n"	/* .navs_wrapper */
+		    "<div class='dotted_line'></div>\n"
+		    "</div>\n",	/* .branches_wrapper */
+		    age ? age : "",
+		    index_page_str, qs->path, refname,
+		    refname,
+		    index_page_str, qs->path, refname,
+		    index_page_str, qs->path, refname,
+		    index_page_str, qs->path, refname);
+		if (r == -1)
 			goto done;
 
-		if (fcgi_gen_response(c, "<div class='branches_age'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, age ? age : "") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='branches_space'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&nbsp;") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='branch'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->path) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=summary&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, refname) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, refname) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<div class='navs_wrapper'>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='navs'>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->path) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=summary&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, refname) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "summary") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, " | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->path) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=briefs&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, refname) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "commit briefs") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, " | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->path) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=commits&headref=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, refname) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "commits") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c,
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
-
-		/* branches_wrapper */
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-
 		free(age);
 		age = NULL;
 
 	}
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"); /* #branches_content */
 done:
 	return error;
 }
@@ -1887,6 +1455,7 @@ gotweb_render_tree(struct request *c)
 	struct transport *t = c->t;
 	struct repo_commit *rc = NULL;
 	char *age = NULL;
+	int r;
 
 	error = got_get_repo_commits(c, 1);
 	if (error)
@@ -1898,67 +1467,34 @@ gotweb_render_tree(struct request *c)
 	if (error)
 		goto done;
 
-	if (fcgi_gen_response(c, "<div id='tree_title_wrapper'>\n") == -1)
+	r = fcgi_printf(c, "<div id='tree_title_wrapper'>\n"
+	    "<div id='tree_title'>Tree</div>\n"
+	    "</div>\n"		/* #tree_title_wrapper */
+	    "<div id='tree_content'>\n"
+	    "<div id='tree_header_wrapper'>\n"
+	    "<div id='tree_header'>\n"
+	    "<div id='header_tree_title'>Tree:</div>\n"
+	    "<div id='header_tree'>%s</div>\n"
+	    "<div class='header_age_title'>Date:</div>\n"
+	    "<div class='header_age'>%s</div>\n"
+	    "<div id='header_commit_msg_title'>Message:</div>\n"
+	    "<div id='header_commit_msg'>%s</div>\n"
+	    "</div>\n"		/* #tree_header */
+	    "</div>\n"		/* #tree_header_wrapper */
+	    "<div class='dotted_line'></div>\n"
+	    "<div id='tree'>\n",
+	    rc->tree_id,
+	    age ? age : "",
+	    rc->commit_msg);
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c, "<div id='tree_title'>Tree</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
 
-	if (fcgi_gen_response(c, "<div id='tree_content'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='tree_header_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='tree_header'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_tree_title'>Tree:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_tree'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->tree_id) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_age_title'>Date:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_age'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, age ? age : "") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_commit_msg_title'>Message:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_commit_msg'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->commit_msg) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='dotted_line'></div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='tree'>\n") == -1)
-		goto done;
-
 	error = got_output_repo_tree(c);
 	if (error)
 		goto done;
 
-	fcgi_gen_response(c, "</div>\n");
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"); /* #tree */
+	fcgi_printf(c, "</div>\n"); /* #tree_content */
 done:
 	return error;
 }
@@ -1970,6 +1506,7 @@ gotweb_render_diff(struct request *c)
 	struct transport *t = c->t;
 	struct repo_commit *rc = NULL;
 	char *age = NULL, *author = NULL;
+	int r;
 
 	error = got_get_repo_commits(c, 1);
 	if (error)
@@ -1984,101 +1521,44 @@ gotweb_render_diff(struct request *c)
 	if (error)
 		goto done;
 
-	if (fcgi_gen_response(c, "<div id='diff_title_wrapper'>\n") == -1)
+	r = fcgi_printf(c, "<div id='diff_title_wrapper'>\n"
+	    "<div id='diff_title'>Commit Diff</div>\n"
+	    "</div>\n"		/* #diff_title_wrapper */
+	    "<div id='diff_content'>\n"
+	    "<div id='diff_header_wrapper'>\n"
+	    "<div id='diff_header'>\n"
+	    "<div id='header_diff_title'>Diff:</div>\n"
+	    "<div id='header_diff'>%s<br />%s</div>\n"
+	    "<div class='header_commit_title'>Commit:</div>\n"
+	    "<div class='header_commit'>%s</div>\n"
+	    "<div id='header_tree_title'>Tree:</div>\n"
+	    "<div id='header_tree'>%s</div>\n"
+	    "<div class='header_author_title'>Author:</div>\n"
+	    "<div class='header_author'>%s</div>\n"
+	    "<div class='header_age_title'>Date:</div>\n"
+	    "<div class='header_age'>%s</div>\n"
+	    "<div id='header_commit_msg_title'>Message:</div>\n"
+	    "<div id='header_commit_msg'>%s</div>\n"
+	    "</div>\n"		/* #diff_header */
+	    "</div>\n"		/* #diff_header_wrapper */
+	    "<div class='dotted_line'></div>\n"
+	    "<div id='diff'>\n",
+	    rc->parent_id, rc->commit_id,
+	    rc->commit_id,
+	    rc->tree_id,
+	    author ? author : "",
+	    age ? age : "",
+	    rc->commit_msg);
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='diff_title'>Commit Diff</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
 
-	if (fcgi_gen_response(c, "<div id='diff_content'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='diff_header_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='diff_header'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_diff_title'>Diff:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_diff'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->parent_id) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<br />") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->commit_id) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_commit_title'>Commit:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_commit'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->commit_id) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_tree_title'>Tree:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_tree'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->tree_id) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_author_title'>Author:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_author'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, author ? author : "") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_age_title'>Date:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_age'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, age ? age : "") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_commit_msg_title'>Message:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_commit_msg'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rc->commit_msg) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='dotted_line'></div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='diff'>\n") == -1)
-		goto done;
-
 	error = got_output_repo_diff(c);
 	if (error)
 		goto done;
 
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"); /* #diff */
+	fcgi_printf(c, "</div>\n"); /* #diff_content */
 done:
-	fcgi_gen_response(c, "</div>\n");
 	free(age);
 	free(author);
 	return error;
@@ -2090,66 +1570,50 @@ gotweb_render_summary(struct request *c)
 	const struct got_error *error = NULL;
 	struct transport *t = c->t;
 	struct server *srv = c->srv;
+	int r;
 
-	if (fcgi_gen_response(c, "<div id='summary_wrapper'>\n") == -1)
+	if (fcgi_printf(c, "<div id='summary_wrapper'>\n") == -1)
 		goto done;
 
-	if (!srv->show_repo_description)
-		goto owner;
+	if (srv->show_repo_description) {
+		r = fcgi_printf(c,
+		    "<div id='description_title'>Description:</div>\n"
+		    "<div id='description'>%s</div>\n",
+		    t->repo_dir->description);
+		if (r == -1)
+			goto done;
+	}
 
-	if (fcgi_gen_response(c, "<div id='description_title'>"
-	    "Description:</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='description'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, t->repo_dir->description) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-owner:
-	if (!srv->show_repo_owner)
-		goto last_change;
+	if (srv->show_repo_owner) {
+		r = fcgi_printf(c,
+		    "<div id='repo_owner_title'>Owner:</div>\n"
+		    "<div id='repo_owner'>%s</div>\n",
+		    t->repo_dir->owner);
+		if (r == -1)
+			goto done;
+	}
 
-	if (fcgi_gen_response(c, "<div id='repo_owner_title'>"
-	    "Owner:</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='repo_owner'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, t->repo_dir->owner) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-last_change:
-	if (!srv->show_repo_age)
-		goto clone_url;
+	if (srv->show_repo_age) {
+		r = fcgi_printf(c,
+		    "<div id='last_change_title'>Last Change:</div>\n"
+		    "<div id='last_change'>%s</div>\n",
+		    t->repo_dir->age);
+		if (r == -1)
+			goto done;
+	}
 
-	if (fcgi_gen_response(c, "<div id='last_change_title'>"
-	    "Last Change:</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='last_change'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, t->repo_dir->age) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-clone_url:
-	if (!srv->show_repo_cloneurl)
-		goto content;
+	if (srv->show_repo_cloneurl) {
+		r = fcgi_printf(c,
+		    "<div id='cloneurl_title'>Clone URL:</div>\n"
+		    "<div id='cloneurl'>%s</div>\n",
+		    t->repo_dir->url ? t->repo_dir->url : "");
+		if (r == -1)
+			goto done;
+	}
 
-	if (fcgi_gen_response(c, "<div id='cloneurl_title'>"
-	    "Clone URL:</div>\n") == -1)
+	r = fcgi_printf(c, "</div>\n"); /* #summary_wrapper */
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c, "<div id='cloneurl'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, t->repo_dir->url) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-content:
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
 
 	error = gotweb_render_briefs(c);
 	if (error) {
@@ -2197,84 +1661,35 @@ gotweb_render_tag(struct request *c)
 	if (error)
 		goto done;
 
-	if (fcgi_gen_response(c, "<div id='tags_title_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='tags_title'>Tag</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='tags_content'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='tag_header_wrapper'>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='tag_header'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_commit_title'>Commit:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_commit'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rt->commit_id) == -1)
-		goto done;
-
 	if (strncmp(rt->tag_name, "refs/", 5) == 0)
 		rt->tag_name += 5;
 
-	if (fcgi_gen_response(c, " <span class='refs_str'>(") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rt->tag_name) == -1)
-		goto done;
-	if (fcgi_gen_response(c, ")</span>") == -1)
-		goto done;
+	fcgi_printf(c, "<div id='tags_title_wrapper'>\n"
+	    "<div id='tags_title'>Tag</div>\n"
+	    "</div>\n"		/* #tags_title_wrapper */
+	    "<div id='tags_content'>\n"
+	    "<div id='tag_header_wrapper'>\n"
+	    "<div id='tag_header'>\n"
+	    "<div class='header_commit_title'>Commit:</div>\n"
+	    "<div class='header_commit'>%s"
+	    " <span class='refs_str'>(%s)</span></div>\n"
+	    "<div class='header_author_title'>Tagger:</div>\n"
+	    "<div class='header_author'>%s</div>\n"
+	    "<div class='header_age_title'>Date:</div>\n"
+	    "<div class='header_age'>%s</div>\n"
+	    "<div id='header_commit_msg_title'>Message:</div>\n"
+	    "<div id='header_commit_msg'>%s</div>\n"
+	    "</div>\n"		/* #tag_header */
+	    "<div class='dotted_line'></div>\n"
+	    "<div id='tag_commit'>\n%s</div>"
+	    "</div>",		/* tag_header_wrapper */
+	    rt->commit_id,
+	    rt->tag_name,
+	    author ? author : "",
+	    age ? age : "",
+	    rt->commit_msg,
+	    rt->tag_commit);
 
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_author_title'>Tagger:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_author'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, author ? author : "") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='header_age_title'>Date:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div class='header_age'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, age ? age : "") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div id='header_commit_msg_title'>Message:"
-	    "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='header_commit_msg'>") == -1)
-		goto done;
-	if (fcgi_gen_response(c, rt->commit_msg) == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "<div class='dotted_line'></div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "<div id='tag_commit'>\n") == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, rt->tag_commit) == -1)
-		goto done;
-
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
-	fcgi_gen_response(c, "</div>\n");
 done:
 	free(age);
 	free(author);
@@ -2290,10 +1705,13 @@ gotweb_render_tags(struct request *c)
 	struct transport *t = c->t;
 	struct querystring *qs = t->qs;
 	struct repo_dir *repo_dir = t->repo_dir;
+	const char *index_page_str;
 	char *newline;
 	char *age = NULL;
-	int commit_found = 0;
+	int r, commit_found = 0;
 
+	index_page_str = qs->index_page_str ? qs->index_page_str : "";
+
 	if (qs->action == BRIEFS) {
 		qs->action = TAGS;
 		error = got_get_repo_tags(c, D_MAXSLCOMMDISP);
@@ -2302,27 +1720,18 @@ gotweb_render_tags(struct request *c)
 	if (error)
 		goto done;
 
-	if (fcgi_gen_response(c, "<div id='tags_title_wrapper'>\n") == -1)
+	r = fcgi_printf(c, "<div id='tags_title_wrapper'>\n"
+	    "<div id='tags_title'>Tags</div>\n"
+	    "</div>\n"		/* #tags_title_wrapper */
+	    "<div id='tags_content'>\n");
+	if (r == -1)
 		goto done;
-	if (fcgi_gen_response(c,
-	    "<div id='tags_title'>Tags</div>\n") == -1)
-		goto done;
-	if (fcgi_gen_response(c, "</div>\n") == -1)
-		goto done;
 
-	if (fcgi_gen_response(c, "<div id='tags_content'>\n") == -1)
-		goto done;
-
 	if (t->tag_count == 0) {
-		if (fcgi_gen_response(c, "<div id='err_content'>") == -1)
+		r = fcgi_printf(c, "<div id='err_content'>%s\n</div>\n",
+		    "This repository contains no tags");
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c,
-		    "This repository contains no tags\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 	}
 
 	TAILQ_FOREACH(rt, &t->repo_tags, entry) {
@@ -2335,128 +1744,45 @@ gotweb_render_tags(struct request *c)
 		error = gotweb_get_time_str(&age, rt->tagger_time, TM_DIFF);
 		if (error)
 			goto done;
-		if (fcgi_gen_response(c, "<div class='tag_age'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, age ? age : "") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "<div class='tag'>") == -1)
-			goto done;
 		if (strncmp(rt->tag_name, "refs/tags/", 10) == 0)
 			rt->tag_name += 10;
-		if (fcgi_gen_response(c, rt->tag_name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "<div class='tag_log'>") == -1)
-			goto done;
 		if (rt->tag_commit != NULL) {
 			newline = strchr(rt->tag_commit, '\n');
 			if (newline)
 				*newline = '\0';
 		}
 
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
+		r = fcgi_printf(c, "<div class='tag_age'>%s</div>\n"
+		    "<div class='tag'>%s</div>\n"
+		    "<div class='tag_log'>"
+		    "<a href='?index_page=%s&path=%s&action=tag&commit=%s'>"
+		    "%s</a>"
+		    "</div>\n"	/* .tag_log */
+		    "<div class='navs_wrapper'>\n"
+		    "<div class='navs'>"
+		    "<a href='?index_page=%s&path=%s&action=tag&commit=%s'>"
+		    "tag</a>"
+		    " | "
+		    "<a href='?index_page=%s&path=%s&action=briefs&commit=%s'>"
+		    "commit briefs</a>"
+		    " | "
+		    "<a href='?index_page=%s&path=%s&action=commits&commit=%s'>"
+		    "commits</a>"
+		    "</div>\n"	/* .navs */
+		    "</div>\n"	/* .navs_wrapper */
+		    "<div class='dotted_line'></div>\n",
+		    age ? age : "",
+		    rt->tag_name,
+		    index_page_str, repo_dir->name, rt->commit_id,
+		    rt->tag_commit ? rt->tag_commit : "",
+		    index_page_str, repo_dir->name, rt->commit_id,
+		    index_page_str, repo_dir->name, rt->commit_id,
+		    index_page_str, repo_dir->name, rt->commit_id);
+		if (r == -1)
 			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=tag&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rt->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (rt->tag_commit != NULL &&
-		    fcgi_gen_response(c, rt->tag_commit) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
 
-		if (fcgi_gen_response(c, "<div class='navs_wrapper'>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "<div class='navs'>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=tag&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rt->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "tag") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, " | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=briefs&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rt->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "commit briefs") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, " | ") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "<a href='?index_page=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, qs->index_page_str) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&path=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, repo_dir->name) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "&action=commits&commit=") == -1)
-			goto done;
-		if (fcgi_gen_response(c, rt->commit_id) == -1)
-			goto done;
-		if (fcgi_gen_response(c, "'>") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "commits") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</a>") == -1)
-			goto done;
-
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c, "</div>\n") == -1)
-			goto done;
-		if (fcgi_gen_response(c,
-		    "<div class='dotted_line'></div>\n") == -1)
-			goto done;
-
 		free(age);
 		age = NULL;
 	}
@@ -2465,7 +1791,7 @@ gotweb_render_tags(struct request *c)
 		if (error)
 			goto done;
 	}
-	fcgi_gen_response(c, "</div>\n");
+	fcgi_printf(c, "</div>\n"); /* #tags_content */
 done:
 	free(age);
 	return error;
blob - b58b1e7be0421dac9ff6c9717bacb59208ad1bbb
blob + 26479ad26fb6fd2bf7bbccfb3e717f0aa521b7bd
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
@@ -438,7 +438,9 @@ void fcgi_timeout(int, short, void *);
 void fcgi_cleanup_request(struct request *);
 void fcgi_create_end_record(struct request *);
 void dump_fcgi_record(const char *, struct fcgi_record_header *);
-int fcgi_gen_response(struct request *, const char *);
+int fcgi_printf(struct request *, const char *, ...)
+	__attribute__((__format__(printf, 2, 3)))
+	__attribute__((__nonnull__(2)));
 int fcgi_gen_binary_response(struct request *, const uint8_t *, int);
 
 /* got_operations.c */