stagit-responsive

My mobile friendly fork of stagit
Log | Files | Refs | README | LICENSE

commit 1bc04c027e39cbd2ae4b93c216eb34a5cefb3055
parent a8318e0499e4de4b09846a99e04dec36c3c6ac0f
Author: Yohanes Bandung <hi@ybbond.dev>
Date:   Wed, 20 May 2020 13:29:09 +0700

refactor: use spaces instead of tabs, style enhancement for target

Diffstat:
Mreallocarray.c | 14+++++++-------
Mstagit-index.c | 286++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mstagit.c | 2108++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mstrlcat.c | 42+++++++++++++++++++++---------------------
Mstrlcpy.c | 36++++++++++++++++++------------------
Mstyle.css | 77+++++++++++++++++++++++++++++++++++++++--------------------------------------

6 files changed, 1282 insertions(+), 1281 deletions(-)

diff --git a/reallocarray.c b/reallocarray.c
@@ -25,15 +25,15 @@
  * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
  * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
  */
-#define MUL_NO_OVERFLOW	(1UL << (sizeof(size_t) * 4))
+#define MUL_NO_OVERFLOW  (1UL << (sizeof(size_t) * 4))
 
 void *
 reallocarray(void *optr, size_t nmemb, size_t size)
 {
-	if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
-	    nmemb > 0 && SIZE_MAX / nmemb < size) {
-		errno = ENOMEM;
-		return NULL;
-	}
-	return realloc(optr, size * nmemb);
+  if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+      nmemb > 0 && SIZE_MAX / nmemb < size) {
+    errno = ENOMEM;
+    return NULL;
+  }
+  return realloc(optr, size * nmemb);
 }
diff --git a/stagit-index.c b/stagit-index.c
@@ -18,189 +18,189 @@ static char *name = "";
 void
 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
 {
-	int r;
+  int r;
 
-	r = snprintf(buf, bufsiz, "%s%s%s",
-		path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
-	if (r < 0 || (size_t)r >= bufsiz)
-		errx(1, "path truncated: '%s%s%s'",
-			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
+  r = snprintf(buf, bufsiz, "%s%s%s",
+    path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
+  if (r < 0 || (size_t)r >= bufsiz)
+    errx(1, "path truncated: '%s%s%s'",
+      path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 }
 
 /* Escape characters below as HTML 2.0 / XML 1.0. */
 void
 xmlencode(FILE *fp, const char *s, size_t len)
 {
-	size_t i;
-
-	for (i = 0; *s && i < len; s++, i++) {
-		switch(*s) {
-		case '<':  fputs("&lt;",   fp); break;
-		case '>':  fputs("&gt;",   fp); break;
-		case '\'': fputs("&#39;" , fp); break;
-		case '&':  fputs("&amp;",  fp); break;
-		case '"':  fputs("&quot;", fp); break;
-		default:   fputc(*s, fp);
-		}
-	}
+  size_t i;
+
+  for (i = 0; *s && i < len; s++, i++) {
+    switch(*s) {
+    case '<':  fputs("&lt;",   fp); break;
+    case '>':  fputs("&gt;",   fp); break;
+    case '\'': fputs("&#39;" , fp); break;
+    case '&':  fputs("&amp;",  fp); break;
+    case '"':  fputs("&quot;", fp); break;
+    default:   fputc(*s, fp);
+    }
+  }
 }
 
 void
 printtimeshort(FILE *fp, const git_time *intime)
 {
-	struct tm *intm;
-	time_t t;
-	char out[32];
-
-	t = (time_t)intime->time;
-	if (!(intm = gmtime(&t)))
-		return;
-	strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
-	fputs(out, fp);
+  struct tm *intm;
+  time_t t;
+  char out[32];
+
+  t = (time_t)intime->time;
+  if (!(intm = gmtime(&t)))
+    return;
+  strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
+  fputs(out, fp);
 }
 
 void
 writeheader(FILE *fp)
 {
-	fputs("<!DOCTYPE html>\n"
-		"<html>\n<head>\n"
+  fputs("<!DOCTYPE html>\n"
+    "<html>\n<head>\n"
     "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
-		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
-		"<title>", fp);
-	xmlencode(fp, description, strlen(description));
-	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"/favicon.png\" />\n");
-	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\" />\n");
-	fputs("</head>\n<body>\n", fp);
-	fprintf(fp, "<table>\n<tr><td><a href=\"https://ybbond.dev\">"
-					"<img src=\"/ybbond.png\" alt=\"\" width=\"32\" height=\"32\" /></a></td>\n"
-	        "<td><span class=\"desc\">");
-	xmlencode(fp, description, strlen(description));
-	fputs("</span></td></tr><tr><td></td><td>\n"
-		"</td></tr>\n</table>\n<hr/>\n<div id=\"content\">\n"
-		"<div id=\"table-scroll\"><table id=\"index\"><thead>\n"
-		"<tr><th>Name</th><th>Description</th>"
-		"<th>Last commit</th></tr>"
-		"</thead><tbody>\n", fp);
+    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
+    "<title>", fp);
+  xmlencode(fp, description, strlen(description));
+  fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"/favicon.png\" />\n");
+  fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\" />\n");
+  fputs("</head>\n<body>\n", fp);
+  fprintf(fp, "<table>\n<tr><td><a href=\"https://ybbond.dev\">"
+          "<img src=\"/ybbond.png\" alt=\"\" width=\"32\" height=\"32\" /></a></td>\n"
+          "<td><span class=\"desc\">");
+  xmlencode(fp, description, strlen(description));
+  fputs("</span></td></tr><tr><td></td><td>\n"
+    "</td></tr>\n</table>\n<hr/>\n<div id=\"content\">\n"
+    "<div id=\"table-scroll\"><table id=\"index\"><thead>\n"
+    "<tr><th>Name</th><th>Description</th>"
+    "<th>Last commit</th></tr>"
+    "</thead><tbody>\n", fp);
 }
 
 void
 writefooter(FILE *fp)
 {
-	fputs("</tbody>\n</table>\n</div>\n</div>\n</body>\n</html>\n", fp);
+  fputs("</tbody>\n</table>\n</div>\n</div>\n</body>\n</html>\n", fp);
 }
 
 int
 writelog(FILE *fp)
 {
-	git_commit *commit = NULL;
-	const git_signature *author;
-	git_revwalk *w = NULL;
-	git_oid id;
-	char *stripped_name = NULL, *p;
-	int ret = 0;
-
-	git_revwalk_new(&w, repo);
-	git_revwalk_push_head(w);
-	git_revwalk_simplify_first_parent(w);
-
-	if (git_revwalk_next(&id, w) ||
-	    git_commit_lookup(&commit, repo, &id)) {
-		ret = -1;
-		goto err;
-	}
-
-	author = git_commit_author(commit);
-
-	/* strip .git suffix */
-	if (!(stripped_name = strdup(name)))
-		err(1, "strdup");
-	if ((p = strrchr(stripped_name, '.')))
-		if (!strcmp(p, ".git"))
-			*p = '\0';
-
-	fputs("<tr><td><a href=\"", fp);
-	xmlencode(fp, stripped_name, strlen(stripped_name));
-	fputs("/index.html\">", fp);
-	xmlencode(fp, stripped_name, strlen(stripped_name));
-	fputs("</a></td><td>", fp);
-	xmlencode(fp, description, strlen(description));
-	fputs("</td><td>", fp);
-	if (author)
-		printtimeshort(fp, &(author->when));
-	fputs("</td></tr>", fp);
-
-	git_commit_free(commit);
+  git_commit *commit = NULL;
+  const git_signature *author;
+  git_revwalk *w = NULL;
+  git_oid id;
+  char *stripped_name = NULL, *p;
+  int ret = 0;
+
+  git_revwalk_new(&w, repo);
+  git_revwalk_push_head(w);
+  git_revwalk_simplify_first_parent(w);
+
+  if (git_revwalk_next(&id, w) ||
+      git_commit_lookup(&commit, repo, &id)) {
+    ret = -1;
+    goto err;
+  }
+
+  author = git_commit_author(commit);
+
+  /* strip .git suffix */
+  if (!(stripped_name = strdup(name)))
+    err(1, "strdup");
+  if ((p = strrchr(stripped_name, '.')))
+    if (!strcmp(p, ".git"))
+      *p = '\0';
+
+  fputs("<tr><td><a href=\"", fp);
+  xmlencode(fp, stripped_name, strlen(stripped_name));
+  fputs("/index.html\">", fp);
+  xmlencode(fp, stripped_name, strlen(stripped_name));
+  fputs("</a></td><td>", fp);
+  xmlencode(fp, description, strlen(description));
+  fputs("</td><td>", fp);
+  if (author)
+    printtimeshort(fp, &(author->when));
+  fputs("</td></tr>", fp);
+
+  git_commit_free(commit);
 err:
-	git_revwalk_free(w);
-	free(stripped_name);
+  git_revwalk_free(w);
+  free(stripped_name);
 
-	return ret;
+  return ret;
 }
 
 int
 main(int argc, char *argv[])
 {
-	FILE *fp;
-	char path[PATH_MAX], repodirabs[PATH_MAX + 1];
-	const char *repodir;
-	int i, ret = 0;
+  FILE *fp;
+  char path[PATH_MAX], repodirabs[PATH_MAX + 1];
+  const char *repodir;
+  int i, ret = 0;
 
-	if (argc < 2) {
-		fprintf(stderr, "%s [repodir...]\n", argv[0]);
-		return 1;
-	}
+  if (argc < 2) {
+    fprintf(stderr, "%s [repodir...]\n", argv[0]);
+    return 1;
+  }
 
-	git_libgit2_init();
+  git_libgit2_init();
 
 #ifdef __OpenBSD__
-	for (i = 1; i < argc; i++)
-		if (unveil(argv[i], "r") == -1)
-			err(1, "unveil: %s", argv[i]);
+  for (i = 1; i < argc; i++)
+    if (unveil(argv[i], "r") == -1)
+      err(1, "unveil: %s", argv[i]);
 
-	if (pledge("stdio rpath", NULL) == -1)
-		err(1, "pledge");
+  if (pledge("stdio rpath", NULL) == -1)
+    err(1, "pledge");
 #endif
 
-	writeheader(stdout);
-
-	for (i = 1; i < argc; i++) {
-		repodir = argv[i];
-		if (!realpath(repodir, repodirabs))
-			err(1, "realpath");
-
-		if (git_repository_open_ext(&repo, repodir,
-		    GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
-			fprintf(stderr, "%s: cannot open repository\n", argv[0]);
-			ret = 1;
-			continue;
-		}
-
-		/* use directory name as name */
-		if ((name = strrchr(repodirabs, '/')))
-			name++;
-		else
-			name = "";
-
-		/* read description or .git/description */
-		joinpath(path, sizeof(path), repodir, "description");
-		if (!(fp = fopen(path, "r"))) {
-			joinpath(path, sizeof(path), repodir, ".git/description");
-			fp = fopen(path, "r");
-		}
-		description[0] = '\0';
-		if (fp) {
-			if (!fgets(description, sizeof(description), fp))
-				description[0] = '\0';
-			fclose(fp);
-		}
-
-		writelog(stdout);
-	}
-	writefooter(stdout);
-
-	/* cleanup */
-	git_repository_free(repo);
-	git_libgit2_shutdown();
-
-	return ret;
+  writeheader(stdout);
+
+  for (i = 1; i < argc; i++) {
+    repodir = argv[i];
+    if (!realpath(repodir, repodirabs))
+      err(1, "realpath");
+
+    if (git_repository_open_ext(&repo, repodir,
+        GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
+      fprintf(stderr, "%s: cannot open repository\n", argv[0]);
+      ret = 1;
+      continue;
+    }
+
+    /* use directory name as name */
+    if ((name = strrchr(repodirabs, '/')))
+      name++;
+    else
+      name = "";
+
+    /* read description or .git/description */
+    joinpath(path, sizeof(path), repodir, "description");
+    if (!(fp = fopen(path, "r"))) {
+      joinpath(path, sizeof(path), repodir, ".git/description");
+      fp = fopen(path, "r");
+    }
+    description[0] = '\0';
+    if (fp) {
+      if (!fgets(description, sizeof(description), fp))
+        description[0] = '\0';
+      fclose(fp);
+    }
+
+    writelog(stdout);
+  }
+  writefooter(stdout);
+
+  /* cleanup */
+  git_repository_free(repo);
+  git_libgit2_shutdown();
+
+  return ret;
 }
diff --git a/stagit.c b/stagit.c
@@ -17,35 +17,35 @@
 #include "compat.h"
 
 struct deltainfo {
-	git_patch *patch;
+  git_patch *patch;
 
-	size_t addcount;
-	size_t delcount;
+  size_t addcount;
+  size_t delcount;
 };
 
 struct commitinfo {
-	const git_oid *id;
+  const git_oid *id;
 
-	char oid[GIT_OID_HEXSZ + 1];
-	char parentoid[GIT_OID_HEXSZ + 1];
+  char oid[GIT_OID_HEXSZ + 1];
+  char parentoid[GIT_OID_HEXSZ + 1];
 
-	const git_signature *author;
-	const git_signature *committer;
-	const char          *summary;
-	const char          *msg;
+  const git_signature *author;
+  const git_signature *committer;
+  const char          *summary;
+  const char          *msg;
 
-	git_diff   *diff;
-	git_commit *commit;
-	git_commit *parent;
-	git_tree   *commit_tree;
-	git_tree   *parent_tree;
+  git_diff   *diff;
+  git_commit *commit;
+  git_commit *parent;
+  git_tree   *commit_tree;
+  git_tree   *parent_tree;
 
-	size_t addcount;
-	size_t delcount;
-	size_t filecount;
+  size_t addcount;
+  size_t delcount;
+  size_t filecount;
 
-	struct deltainfo **deltas;
-	size_t ndeltas;
+  struct deltainfo **deltas;
+  size_t ndeltas;
 };
 
 static git_repository *repo;
@@ -73,1206 +73,1206 @@ static const char *cachefile;
 void
 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
 {
-	int r;
+  int r;
 
-	r = snprintf(buf, bufsiz, "%s%s%s",
-		path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
-	if (r < 0 || (size_t)r >= bufsiz)
-		errx(1, "path truncated: '%s%s%s'",
-			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
+  r = snprintf(buf, bufsiz, "%s%s%s",
+    path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
+  if (r < 0 || (size_t)r >= bufsiz)
+    errx(1, "path truncated: '%s%s%s'",
+      path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 }
 
 void
 deltainfo_free(struct deltainfo *di)
 {
-	if (!di)
-		return;
-	git_patch_free(di->patch);
-	memset(di, 0, sizeof(*di));
-	free(di);
+  if (!di)
+    return;
+  git_patch_free(di->patch);
+  memset(di, 0, sizeof(*di));
+  free(di);
 }
 
 int
 commitinfo_getstats(struct commitinfo *ci)
 {
-	struct deltainfo *di;
-	git_diff_options opts;
-	git_diff_find_options fopts;
-	const git_diff_delta *delta;
-	const git_diff_hunk *hunk;
-	const git_diff_line *line;
-	git_patch *patch = NULL;
-	size_t ndeltas, nhunks, nhunklines;
-	size_t i, j, k;
-
-	if (git_tree_lookup(&(ci->commit_tree), repo, git_commit_tree_id(ci->commit)))
-		goto err;
-	if (!git_commit_parent(&(ci->parent), ci->commit, 0)) {
-		if (git_tree_lookup(&(ci->parent_tree), repo, git_commit_tree_id(ci->parent))) {
-			ci->parent = NULL;
-			ci->parent_tree = NULL;
-		}
-	}
-
-	git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
-	opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH |
-	              GIT_DIFF_IGNORE_SUBMODULES |
-		      GIT_DIFF_INCLUDE_TYPECHANGE;
-	if (git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts))
-		goto err;
-
-	if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
-		goto err;
-	/* find renames and copies, exact matches (no heuristic) for renames. */
-	fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
-	               GIT_DIFF_FIND_EXACT_MATCH_ONLY;
-	if (git_diff_find_similar(ci->diff, &fopts))
-		goto err;
-
-	ndeltas = git_diff_num_deltas(ci->diff);
-	if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
-		err(1, "calloc");
-
-	for (i = 0; i < ndeltas; i++) {
-		if (git_patch_from_diff(&patch, ci->diff, i))
-			goto err;
-
-		if (!(di = calloc(1, sizeof(struct deltainfo))))
-			err(1, "calloc");
-		di->patch = patch;
-		ci->deltas[i] = di;
-
-		delta = git_patch_get_delta(patch);
-
-		/* skip stats for binary data */
-		if (delta->flags & GIT_DIFF_FLAG_BINARY)
-			continue;
-
-		nhunks = git_patch_num_hunks(patch);
-		for (j = 0; j < nhunks; j++) {
-			if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
-				break;
-			for (k = 0; ; k++) {
-				if (git_patch_get_line_in_hunk(&line, patch, j, k))
-					break;
-				if (line->old_lineno == -1) {
-					di->addcount++;
-					ci->addcount++;
-				} else if (line->new_lineno == -1) {
-					di->delcount++;
-					ci->delcount++;
-				}
-			}
-		}
-	}
-	ci->ndeltas = i;
-	ci->filecount = i;
-
-	return 0;
+  struct deltainfo *di;
+  git_diff_options opts;
+  git_diff_find_options fopts;
+  const git_diff_delta *delta;
+  const git_diff_hunk *hunk;
+  const git_diff_line *line;
+  git_patch *patch = NULL;
+  size_t ndeltas, nhunks, nhunklines;
+  size_t i, j, k;
+
+  if (git_tree_lookup(&(ci->commit_tree), repo, git_commit_tree_id(ci->commit)))
+    goto err;
+  if (!git_commit_parent(&(ci->parent), ci->commit, 0)) {
+    if (git_tree_lookup(&(ci->parent_tree), repo, git_commit_tree_id(ci->parent))) {
+      ci->parent = NULL;
+      ci->parent_tree = NULL;
+    }
+  }
+
+  git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
+  opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH |
+                GIT_DIFF_IGNORE_SUBMODULES |
+          GIT_DIFF_INCLUDE_TYPECHANGE;
+  if (git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts))
+    goto err;
+
+  if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
+    goto err;
+  /* find renames and copies, exact matches (no heuristic) for renames. */
+  fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
+                 GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+  if (git_diff_find_similar(ci->diff, &fopts))
+    goto err;
+
+  ndeltas = git_diff_num_deltas(ci->diff);
+  if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
+    err(1, "calloc");
+
+  for (i = 0; i < ndeltas; i++) {
+    if (git_patch_from_diff(&patch, ci->diff, i))
+      goto err;
+
+    if (!(di = calloc(1, sizeof(struct deltainfo))))
+      err(1, "calloc");
+    di->patch = patch;
+    ci->deltas[i] = di;
+
+    delta = git_patch_get_delta(patch);
+
+    /* skip stats for binary data */
+    if (delta->flags & GIT_DIFF_FLAG_BINARY)
+      continue;
+
+    nhunks = git_patch_num_hunks(patch);
+    for (j = 0; j < nhunks; j++) {
+      if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
+        break;
+      for (k = 0; ; k++) {
+        if (git_patch_get_line_in_hunk(&line, patch, j, k))
+          break;
+        if (line->old_lineno == -1) {
+          di->addcount++;
+          ci->addcount++;
+        } else if (line->new_lineno == -1) {
+          di->delcount++;
+          ci->delcount++;
+        }
+      }
+    }
+  }
+  ci->ndeltas = i;
+  ci->filecount = i;
+
+  return 0;
 
 err:
-	git_diff_free(ci->diff);
-	ci->diff = NULL;
-	git_tree_free(ci->commit_tree);
-	ci->commit_tree = NULL;
-	git_tree_free(ci->parent_tree);
-	ci->parent_tree = NULL;
-	git_commit_free(ci->parent);
-	ci->parent = NULL;
-
-	if (ci->deltas)
-		for (i = 0; i < ci->ndeltas; i++)
-			deltainfo_free(ci->deltas[i]);
-	free(ci->deltas);
-	ci->deltas = NULL;
-	ci->ndeltas = 0;
-	ci->addcount = 0;
-	ci->delcount = 0;
-	ci->filecount = 0;
-
-	return -1;
+  git_diff_free(ci->diff);
+  ci->diff = NULL;
+  git_tree_free(ci->commit_tree);
+  ci->commit_tree = NULL;
+  git_tree_free(ci->parent_tree);
+  ci->parent_tree = NULL;
+  git_commit_free(ci->parent);
+  ci->parent = NULL;
+
+  if (ci->deltas)
+    for (i = 0; i < ci->ndeltas; i++)
+      deltainfo_free(ci->deltas[i]);
+  free(ci->deltas);
+  ci->deltas = NULL;
+  ci->ndeltas = 0;
+  ci->addcount = 0;
+  ci->delcount = 0;
+  ci->filecount = 0;
+
+  return -1;
 }
 
 void
 commitinfo_free(struct commitinfo *ci)
 {
-	size_t i;
-
-	if (!ci)
-		return;
-	if (ci->deltas)
-		for (i = 0; i < ci->ndeltas; i++)
-			deltainfo_free(ci->deltas[i]);
-
-	free(ci->deltas);
-	git_diff_free(ci->diff);
-	git_tree_free(ci->commit_tree);
-	git_tree_free(ci->parent_tree);
-	git_commit_free(ci->commit);
-	git_commit_free(ci->parent);
-	memset(ci, 0, sizeof(*ci));
-	free(ci);
+  size_t i;
+
+  if (!ci)
+    return;
+  if (ci->deltas)
+    for (i = 0; i < ci->ndeltas; i++)
+      deltainfo_free(ci->deltas[i]);
+
+  free(ci->deltas);
+  git_diff_free(ci->diff);
+  git_tree_free(ci->commit_tree);
+  git_tree_free(ci->parent_tree);
+  git_commit_free(ci->commit);
+  git_commit_free(ci->parent);
+  memset(ci, 0, sizeof(*ci));
+  free(ci);
 }
 
 struct commitinfo *
 commitinfo_getbyoid(const git_oid *id)
 {
-	struct commitinfo *ci;
+  struct commitinfo *ci;
 
-	if (!(ci = calloc(1, sizeof(struct commitinfo))))
-		err(1, "calloc");
+  if (!(ci = calloc(1, sizeof(struct commitinfo))))
+    err(1, "calloc");
 
-	if (git_commit_lookup(&(ci->commit), repo, id))
-		goto err;
-	ci->id = id;
+  if (git_commit_lookup(&(ci->commit), repo, id))
+    goto err;
+  ci->id = id;
 
-	git_oid_tostr(ci->oid, sizeof(ci->oid), git_commit_id(ci->commit));
-	git_oid_tostr(ci->parentoid, sizeof(ci->parentoid), git_commit_parent_id(ci->commit, 0));
+  git_oid_tostr(ci->oid, sizeof(ci->oid), git_commit_id(ci->commit));
+  git_oid_tostr(ci->parentoid, sizeof(ci->parentoid), git_commit_parent_id(ci->commit, 0));
 
-	ci->author = git_commit_author(ci->commit);
-	ci->committer = git_commit_committer(ci->commit);
-	ci->summary = git_commit_summary(ci->commit);
-	ci->msg = git_commit_message(ci->commit);
+  ci->author = git_commit_author(ci->commit);
+  ci->committer = git_commit_committer(ci->commit);
+  ci->summary = git_commit_summary(ci->commit);
+  ci->msg = git_commit_message(ci->commit);
 
-	return ci;
+  return ci;
 
 err:
-	commitinfo_free(ci);
+  commitinfo_free(ci);
 
-	return NULL;
+  return NULL;
 }
 
 FILE *
 efopen(const char *name, const char *flags)
 {
-	FILE *fp;
+  FILE *fp;
 
-	if (!(fp = fopen(name, flags)))
-		err(1, "fopen: '%s'", name);
+  if (!(fp = fopen(name, flags)))
+    err(1, "fopen: '%s'", name);
 
-	return fp;
+  return fp;
 }
 
 /* Escape characters below as HTML 2.0 / XML 1.0. */
 void
 xmlencode(FILE *fp, const char *s, size_t len)
 {
-	size_t i;
-
-	for (i = 0; *s && i < len; s++, i++) {
-		switch(*s) {
-		case '<':  fputs("&lt;",   fp); break;
-		case '>':  fputs("&gt;",   fp); break;
-		case '\'': fputs("&#39;",  fp); break;
-		case '&':  fputs("&amp;",  fp); break;
-		case '"':  fputs("&quot;", fp); break;
-		default:   fputc(*s, fp);
-		}
-	}
+  size_t i;
+
+  for (i = 0; *s && i < len; s++, i++) {
+    switch(*s) {
+    case '<':  fputs("&lt;",   fp); break;
+    case '>':  fputs("&gt;",   fp); break;
+    case '\'': fputs("&#39;",  fp); break;
+    case '&':  fputs("&amp;",  fp); break;
+    case '"':  fputs("&quot;", fp); break;
+    default:   fputc(*s, fp);
+    }
+  }
 }
 
 int
 mkdirp(const char *path)
 {
-	char tmp[PATH_MAX], *p;
-
-	if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
-		errx(1, "path truncated: '%s'", path);
-	for (p = tmp + (tmp[0] == '/'); *p; p++) {
-		if (*p != '/')
-			continue;
-		*p = '\0';
-		if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
-			return -1;
-		*p = '/';
-	}
-	if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
-		return -1;
-	return 0;
+  char tmp[PATH_MAX], *p;
+
+  if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
+    errx(1, "path truncated: '%s'", path);
+  for (p = tmp + (tmp[0] == '/'); *p; p++) {
+    if (*p != '/')
+      continue;
+    *p = '\0';
+    if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
+      return -1;
+    *p = '/';
+  }
+  if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
+    return -1;
+  return 0;
 }
 
 void
 printtimez(FILE *fp, const git_time *intime)
 {
-	struct tm *intm;
-	time_t t;
-	char out[32];
-
-	t = (time_t)intime->time;
-	if (!(intm = gmtime(&t)))
-		return;
-	strftime(out, sizeof(out), "%Y-%m-%dT%H:%M:%SZ", intm);
-	fputs(out, fp);
+  struct tm *intm;
+  time_t t;
+  char out[32];
+
+  t = (time_t)intime->time;
+  if (!(intm = gmtime(&t)))
+    return;
+  strftime(out, sizeof(out), "%Y-%m-%dT%H:%M:%SZ", intm);
+  fputs(out, fp);
 }
 
 void
 printtime(FILE *fp, const git_time *intime)
 {
-	struct tm *intm;
-	time_t t;
-	char out[32];
-
-	t = (time_t)intime->time + (intime->offset * 60);
-	if (!(intm = gmtime(&t)))
-		return;
-	strftime(out, sizeof(out), "%a, %e %b %Y %H:%M:%S", intm);
-	if (intime->offset < 0)
-		fprintf(fp, "%s -%02d%02d", out,
-		            -(intime->offset) / 60, -(intime->offset) % 60);
-	else
-		fprintf(fp, "%s +%02d%02d", out,
-		            intime->offset / 60, intime->offset % 60);
+  struct tm *intm;
+  time_t t;
+  char out[32];
+
+  t = (time_t)intime->time + (intime->offset * 60);
+  if (!(intm = gmtime(&t)))
+    return;
+  strftime(out, sizeof(out), "%a, %e %b %Y %H:%M:%S", intm);
+  if (intime->offset < 0)
+    fprintf(fp, "%s -%02d%02d", out,
+                -(intime->offset) / 60, -(intime->offset) % 60);
+  else
+    fprintf(fp, "%s +%02d%02d", out,
+                intime->offset / 60, intime->offset % 60);
 }
 
 void
 printtimeshort(FILE *fp, const git_time *intime)
 {
-	struct tm *intm;
-	time_t t;
-	char out[32];
-
-	t = (time_t)intime->time;
-	if (!(intm = gmtime(&t)))
-		return;
-	strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
-	fputs(out, fp);
+  struct tm *intm;
+  time_t t;
+  char out[32];
+
+  t = (time_t)intime->time;
+  if (!(intm = gmtime(&t)))
+    return;
+  strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
+  fputs(out, fp);
 }
 
 void
 writeheader(FILE *fp, const char *title)
 {
-	fputs("<!DOCTYPE html>\n"
-		"<html>\n<head>\n"
+  fputs("<!DOCTYPE html>\n"
+    "<html>\n<head>\n"
     "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
-		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
-		"<title>", fp);
-	xmlencode(fp, title, strlen(title));
-	if (title[0] && strippedname[0])
-		fputs(" - ", fp);
-	xmlencode(fp, strippedname, strlen(strippedname));
-	if (description[0])
-		fputs(" - ", fp);
-	xmlencode(fp, description, strlen(description));
-	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"/favicon.png\" />\n");
-	fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
-		name, relpath);
-	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\" />\n");
-	fputs("</head>\n<body>\n<table><tr><td>", fp);
-	fprintf(fp, "<a href=\"../%s\"><img src=\"/logo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
-	        relpath);
-	fputs("</td><td><h1>", fp);
-	xmlencode(fp, strippedname, strlen(strippedname));
-	fputs("</h1><span class=\"desc\">", fp);
-	xmlencode(fp, description, strlen(description));
-	fputs("</span></td></tr>", fp);
-	if (cloneurl[0]) {
-		fputs("<tr class=\"url\"><td></td><td>git clone <a href=\"", fp);
-		xmlencode(fp, cloneurl, strlen(cloneurl));
-		fputs("\">", fp);
-		xmlencode(fp, cloneurl, strlen(cloneurl));
-		fputs("</a></td></tr>", fp);
-	}
-	fputs("<tr><td></td><td>\n", fp);
-	fprintf(fp, "<a href=\"%sindex.html\">Log</a> | ", relpath);
-	fprintf(fp, "<a href=\"%sfiles.html\">Files</a> | ", relpath);
-	fprintf(fp, "<a href=\"%srefs.html\">Refs</a>", relpath);
-	if (submodules)
-		fprintf(fp, " | <a href=\"%sfile/%s.html\">Submodules</a>",
-		        relpath, submodules);
-	if (readme)
-		fprintf(fp, " | <a href=\"%sfile/%s.html\">README</a>",
-		        relpath, readme);
-	if (license)
-		fprintf(fp, " | <a href=\"%sfile/%s.html\">LICENSE</a>",
-		        relpath, license);
-	fputs("</td></tr></table>\n<hr/>\n<div id=\"content\">\n", fp);
+    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
+    "<title>", fp);
+  xmlencode(fp, title, strlen(title));
+  if (title[0] && strippedname[0])
+    fputs(" - ", fp);
+  xmlencode(fp, strippedname, strlen(strippedname));
+  if (description[0])
+    fputs(" - ", fp);
+  xmlencode(fp, description, strlen(description));
+  fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"/favicon.png\" />\n");
+  fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
+    name, relpath);
+  fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\" />\n");
+  fputs("</head>\n<body>\n<table><tr><td>", fp);
+  fprintf(fp, "<a href=\"../%s\"><img src=\"/logo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
+          relpath);
+  fputs("</td><td><h1>", fp);
+  xmlencode(fp, strippedname, strlen(strippedname));
+  fputs("</h1><span class=\"desc\">", fp);
+  xmlencode(fp, description, strlen(description));
+  fputs("</span></td></tr>", fp);
+  if (cloneurl[0]) {
+    fputs("<tr class=\"url\"><td></td><td>git clone <a href=\"", fp);
+    xmlencode(fp, cloneurl, strlen(cloneurl));
+    fputs("\">", fp);
+    xmlencode(fp, cloneurl, strlen(cloneurl));
+    fputs("</a></td></tr>", fp);
+  }
+  fputs("<tr><td></td><td>\n", fp);
+  fprintf(fp, "<a href=\"%sindex.html\">Log</a> | ", relpath);
+  fprintf(fp, "<a href=\"%sfiles.html\">Files</a> | ", relpath);
+  fprintf(fp, "<a href=\"%srefs.html\">Refs</a>", relpath);
+  if (submodules)
+    fprintf(fp, " | <a href=\"%sfile/%s.html\">Submodules</a>",
+            relpath, submodules);
+  if (readme)
+    fprintf(fp, " | <a href=\"%sfile/%s.html\">README</a>",
+            relpath, readme);
+  if (license)
+    fprintf(fp, " | <a href=\"%sfile/%s.html\">LICENSE</a>",
+            relpath, license);
+  fputs("</td></tr></table>\n<hr/>\n<div id=\"content\">\n", fp);
 }
 
 void
 writefooter(FILE *fp)
 {
-	fputs("</div>\n</body>\n</html>\n", fp);
+  fputs("</div>\n</body>\n</html>\n", fp);
 }
 
 int
 writeblobhtml(FILE *fp, const git_blob *blob)
 {
-	size_t n = 0, i, prev;
-	const char *nfmt = "<a href=\"#l%d\" class=\"line\" id=\"l%d\">%7d</a> ";
-	const char *s = git_blob_rawcontent(blob);
-	git_off_t len = git_blob_rawsize(blob);
-
-	fputs("<pre id=\"blob\">\n", fp);
-
-	if (len > 0) {
-		for (i = 0, prev = 0; i < (size_t)len; i++) {
-			if (s[i] != '\n')
-				continue;
-			n++;
-			fprintf(fp, nfmt, n, n, n);
-			xmlencode(fp, &s[prev], i - prev + 1);
-			prev = i + 1;
-		}
-		/* trailing data */
-		if ((len - prev) > 0) {
-			n++;
-			fprintf(fp, nfmt, n, n, n);
-			xmlencode(fp, &s[prev], len - prev);
-		}
-	}
-
-	fputs("</pre>\n", fp);
-
-	return n;
+  size_t n = 0, i, prev;
+  const char *nfmt = "<a href=\"#l%d\" class=\"line\" id=\"l%d\">%7d</a> ";
+  const char *s = git_blob_rawcontent(blob);
+  git_off_t len = git_blob_rawsize(blob);
+
+  fputs("<pre id=\"blob\">\n", fp);
+
+  if (len > 0) {
+    for (i = 0, prev = 0; i < (size_t)len; i++) {
+      if (s[i] != '\n')
+        continue;
+      n++;
+      fprintf(fp, nfmt, n, n, n);
+      xmlencode(fp, &s[prev], i - prev + 1);
+      prev = i + 1;
+    }
+    /* trailing data */
+    if ((len - prev) > 0) {
+      n++;
+      fprintf(fp, nfmt, n, n, n);
+      xmlencode(fp, &s[prev], len - prev);
+    }
+  }
+
+  fputs("</pre>\n", fp);
+
+  return n;
 }
 
 void
 printcommit(FILE *fp, struct commitinfo *ci)
 {
-	fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n",
-		relpath, ci->oid, ci->oid);
-
-	if (ci->parentoid[0])
-		fprintf(fp, "<b>parent</b> <a href=\"%scommit/%s.html\">%s</a>\n",
-			relpath, ci->parentoid, ci->parentoid);
-
-	if (ci->author) {
-		fputs("<b>Author:</b> ", fp);
-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
-		fputs(" &lt;<a href=\"mailto:", fp);
-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
-		fputs("\">", fp);
-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
-		fputs("</a>&gt;\n<b>Date:</b>   ", fp);
-		printtime(fp, &(ci->author->when));
-		fputc('\n', fp);
-	}
-	if (ci->msg) {
-		fputc('\n', fp);
-		xmlencode(fp, ci->msg, strlen(ci->msg));
-		fputc('\n', fp);
-	}
+  fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n",
+    relpath, ci->oid, ci->oid);
+
+  if (ci->parentoid[0])
+    fprintf(fp, "<b>parent</b> <a href=\"%scommit/%s.html\">%s</a>\n",
+      relpath, ci->parentoid, ci->parentoid);
+
+  if (ci->author) {
+    fputs("<b>Author:</b> ", fp);
+    xmlencode(fp, ci->author->name, strlen(ci->author->name));
+    fputs(" &lt;<a href=\"mailto:", fp);
+    xmlencode(fp, ci->author->email, strlen(ci->author->email));
+    fputs("\">", fp);
+    xmlencode(fp, ci->author->email, strlen(ci->author->email));
+    fputs("</a>&gt;\n<b>Date:</b>   ", fp);
+    printtime(fp, &(ci->author->when));
+    fputc('\n', fp);
+  }
+  if (ci->msg) {
+    fputc('\n', fp);
+    xmlencode(fp, ci->msg, strlen(ci->msg));
+    fputc('\n', fp);
+  }
 }
 
 void
 printshowfile(FILE *fp, struct commitinfo *ci)
 {
-	const git_diff_delta *delta;
-	const git_diff_hunk *hunk;
-	const git_diff_line *line;
-	git_patch *patch;
-	size_t nhunks, nhunklines, changed, add, del, total, i, j, k;
-	char linestr[80];
-	int c;
-
-	printcommit(fp, ci);
-
-	if (!ci->deltas)
-		return;
-
-	if (ci->filecount > 1000   ||
-	    ci->ndeltas   > 1000   ||
-	    ci->addcount  > 100000 ||
-	    ci->delcount  > 100000) {
-		fputs("Diff is too large, output suppressed.\n", fp);
-		return;
-	}
-
-	/* diff stat */
-	fputs("<b>Diffstat:</b>\n<table>", fp);
-	for (i = 0; i < ci->ndeltas; i++) {
-		delta = git_patch_get_delta(ci->deltas[i]->patch);
-
-		switch (delta->status) {
-		case GIT_DELTA_ADDED:      c = 'A'; break;
-		case GIT_DELTA_COPIED:     c = 'C'; break;
-		case GIT_DELTA_DELETED:    c = 'D'; break;
-		case GIT_DELTA_MODIFIED:   c = 'M'; break;
-		case GIT_DELTA_RENAMED:    c = 'R'; break;
-		case GIT_DELTA_TYPECHANGE: c = 'T'; break;
-		default:                   c = ' '; break;
-		}
-		if (c == ' ')
-			fprintf(fp, "<tr><td>%c", c);
-		else
-			fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
-
-		fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
-		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
-		if (strcmp(delta->old_file.path, delta->new_file.path)) {
-			fputs(" -&gt; ", fp);
-			xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
-		}
-
-		add = ci->deltas[i]->addcount;
-		del = ci->deltas[i]->delcount;
-		changed = add + del;
-		total = sizeof(linestr) - 2;
-		if (changed > total) {
-			if (add)
-				add = ((float)total / changed * add) + 1;
-			if (del)
-				del = ((float)total / changed * del) + 1;
-		}
-		memset(&linestr, '+', add);
-		memset(&linestr[add], '-', del);
-
-		fprintf(fp, "</a></td><td> | </td><td class=\"num\">%zu</td><td><span class=\"i\">",
-		        ci->deltas[i]->addcount + ci->deltas[i]->delcount);
-		fwrite(&linestr, 1, add, fp);
-		fputs("</span><span class=\"d\">", fp);
-		fwrite(&linestr[add], 1, del, fp);
-		fputs("</span></td></tr>\n", fp);
-	}
-	fprintf(fp, "</table></pre></div><p>%zu file%s changed, %zu insertion%s(<span id=\"plus\">+</span>)"
-		", %zu deletion%s(<span id=\"min\">-</span>)</p>\n",
-					ci->filecount, ci->filecount == 1 ? "" : "s",
-	        ci->addcount,  ci->addcount  == 1 ? "" : "s",
-	        ci->delcount,  ci->delcount  == 1 ? "" : "s");
-
-	fputs("<div id=\"pre-scroll\">\n<pre>\n", fp);
-
-	for (i = 0; i < ci->ndeltas; i++) {
-		patch = ci->deltas[i]->patch;
-		delta = git_patch_get_delta(patch);
-		fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
-		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
-		fputs(".html\">", fp);
-		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
-		fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
-		xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
-		fprintf(fp, ".html\">");
-		xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
-		fprintf(fp, "</a></b>\n");
-
-		/* check binary data */
-		if (delta->flags & GIT_DIFF_FLAG_BINARY) {
-			fputs("Binary files differ.\n", fp);
-			continue;
-		}
-
-		nhunks = git_patch_num_hunks(patch);
-		for (j = 0; j < nhunks; j++) {
-			if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
-				break;
-
-			fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
-			xmlencode(fp, hunk->header, hunk->header_len);
-			fputs("</a>", fp);
-
-			for (k = 0; ; k++) {
-				if (git_patch_get_line_in_hunk(&line, patch, j, k))
-					break;
-				if (line->old_lineno == -1)
-					fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+",
-						i, j, k, i, j, k);
-				else if (line->new_lineno == -1)
-					fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-",
-						i, j, k, i, j, k);
-				else
-					fputc(' ', fp);
-				xmlencode(fp, line->content, line->content_len);
-				if (line->old_lineno == -1 || line->new_lineno == -1)
-					fputs("</a>", fp);
-			}
-		}
-	}
+  const git_diff_delta *delta;
+  const git_diff_hunk *hunk;
+  const git_diff_line *line;
+  git_patch *patch;
+  size_t nhunks, nhunklines, changed, add, del, total, i, j, k;
+  char linestr[80];
+  int c;
+
+  printcommit(fp, ci);
+
+  if (!ci->deltas)
+    return;
+
+  if (ci->filecount > 1000   ||
+      ci->ndeltas   > 1000   ||
+      ci->addcount  > 100000 ||
+      ci->delcount  > 100000) {
+    fputs("Diff is too large, output suppressed.\n", fp);
+    return;
+  }
+
+  /* diff stat */
+  fputs("<b>Diffstat:</b>\n<table>", fp);
+  for (i = 0; i < ci->ndeltas; i++) {
+    delta = git_patch_get_delta(ci->deltas[i]->patch);
+
+    switch (delta->status) {
+    case GIT_DELTA_ADDED:      c = 'A'; break;
+    case GIT_DELTA_COPIED:     c = 'C'; break;
+    case GIT_DELTA_DELETED:    c = 'D'; break;
+    case GIT_DELTA_MODIFIED:   c = 'M'; break;
+    case GIT_DELTA_RENAMED:    c = 'R'; break;
+    case GIT_DELTA_TYPECHANGE: c = 'T'; break;
+    default:                   c = ' '; break;
+    }
+    if (c == ' ')
+      fprintf(fp, "<tr><td>%c", c);
+    else
+      fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
+
+    fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
+    xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+    if (strcmp(delta->old_file.path, delta->new_file.path)) {
+      fputs(" -&gt; ", fp);
+      xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+    }
+
+    add = ci->deltas[i]->addcount;
+    del = ci->deltas[i]->delcount;
+    changed = add + del;
+    total = sizeof(linestr) - 2;
+    if (changed > total) {
+      if (add)
+        add = ((float)total / changed * add) + 1;
+      if (del)
+        del = ((float)total / changed * del) + 1;
+    }
+    memset(&linestr, '+', add);
+    memset(&linestr[add], '-', del);
+
+    fprintf(fp, "</a></td><td> | </td><td class=\"num\">%zu</td><td><span class=\"i\">",
+            ci->deltas[i]->addcount + ci->deltas[i]->delcount);
+    fwrite(&linestr, 1, add, fp);
+    fputs("</span><span class=\"d\">", fp);
+    fwrite(&linestr[add], 1, del, fp);
+    fputs("</span></td></tr>\n", fp);
+  }
+  fprintf(fp, "</table></pre></div><p>%zu file%s changed, %zu insertion%s(<span id=\"plus\">+</span>)"
+    ", %zu deletion%s(<span id=\"min\">-</span>)</p>\n",
+          ci->filecount, ci->filecount == 1 ? "" : "s",
+          ci->addcount,  ci->addcount  == 1 ? "" : "s",
+          ci->delcount,  ci->delcount  == 1 ? "" : "s");
+
+  fputs("<div id=\"pre-scroll\">\n<pre>\n", fp);
+
+  for (i = 0; i < ci->ndeltas; i++) {
+    patch = ci->deltas[i]->patch;
+    delta = git_patch_get_delta(patch);
+    fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
+    xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+    fputs(".html\">", fp);
+    xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+    fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
+    xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+    fprintf(fp, ".html\">");
+    xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+    fprintf(fp, "</a></b>\n");
+
+    /* check binary data */
+    if (delta->flags & GIT_DIFF_FLAG_BINARY) {
+      fputs("Binary files differ.\n", fp);
+      continue;
+    }
+
+    nhunks = git_patch_num_hunks(patch);
+    for (j = 0; j < nhunks; j++) {
+      if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
+        break;
+
+      fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
+      xmlencode(fp, hunk->header, hunk->header_len);
+      fputs("</a>", fp);
+
+      for (k = 0; ; k++) {
+        if (git_patch_get_line_in_hunk(&line, patch, j, k))
+          break;
+        if (line->old_lineno == -1)
+          fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+",
+            i, j, k, i, j, k);
+        else if (line->new_lineno == -1)
+          fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-",
+            i, j, k, i, j, k);
+        else
+          fputc(' ', fp);
+        xmlencode(fp, line->content, line->content_len);
+        if (line->old_lineno == -1 || line->new_lineno == -1)
+          fputs("</a>", fp);
+      }
+    }
+  }
 }
 
 void
 writelogline(FILE *fp, struct commitinfo *ci)
 {
-	fprintf(fp, "<tr id=\"%s\">"
-		"<td><a href=\"#%s\">#</a></td>"
-		"<td>", ci->oid, ci->oid);
-	if (ci->author)
-		printtimeshort(fp, &(ci->author->when));
-	fputs("</td><td>", fp);
-	if (ci->summary) {
-		fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
-		xmlencode(fp, ci->summary, strlen(ci->summary));
-		fputs("</a>", fp);
-	}
-	fputs("</td><td>", fp);
-	if (ci->author)
-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
-	fputs("</td><td class=\"num\">", fp);
-	fprintf(fp, "%zu", ci->filecount);
-	fputs("</td><td class=\"num\">", fp);
-	fprintf(fp, "+%zu", ci->addcount);
-	fputs("</td><td class=\"num\">", fp);
-	fprintf(fp, "-%zu", ci->delcount);
-	fputs("</td></tr>\n", fp);
+  fprintf(fp, "<tr id=\"%s\">"
+    "<td><a href=\"#%s\">#</a></td>"
+    "<td>", ci->oid, ci->oid);
+  if (ci->author)
+    printtimeshort(fp, &(ci->author->when));
+  fputs("</td><td>", fp);
+  if (ci->summary) {
+    fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
+    xmlencode(fp, ci->summary, strlen(ci->summary));
+    fputs("</a>", fp);
+  }
+  fputs("</td><td>", fp);
+  if (ci->author)
+    xmlencode(fp, ci->author->name, strlen(ci->author->name));
+  fputs("</td><td class=\"num\">", fp);
+  fprintf(fp, "%zu", ci->filecount);
+  fputs("</td><td class=\"num\">", fp);
+  fprintf(fp, "+%zu", ci->addcount);
+  fputs("</td><td class=\"num\">", fp);
+  fprintf(fp, "-%zu", ci->delcount);
+  fputs("</td></tr>\n", fp);
 }
 
 int
 writelog(FILE *fp, const git_oid *oid)
 {
-	struct commitinfo *ci;
-	git_revwalk *w = NULL;
-	git_oid id;
-	char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
-	FILE *fpfile;
-	int r;
-
-	git_revwalk_new(&w, repo);
-	git_revwalk_push(w, oid);
-	git_revwalk_simplify_first_parent(w);
-
-	while (!git_revwalk_next(&id, w)) {
-		relpath = "";
-
-		if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
-			break;
-
-		git_oid_tostr(oidstr, sizeof(oidstr), &id);
-		r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
-		if (r < 0 || (size_t)r >= sizeof(path))
-			errx(1, "path truncated: 'commit/%s.html'", oidstr);
-		r = access(path, F_OK);
-
-		/* optimization: if there are no log lines to write and
-		   the commit file already exists: skip the diffstat */
-		if (!nlogcommits && !r)
-			continue;
-
-		if (!(ci = commitinfo_getbyoid(&id)))
-			break;
-		/* diffstat: for stagit HTML required for the index.html line */
-		if (commitinfo_getstats(ci) == -1)
-			goto err;
-
-		if (nlogcommits < 0) {
-			writelogline(fp, ci);
-		} else if (nlogcommits > 0) {
-			writelogline(fp, ci);
-			nlogcommits--;
-			if (!nlogcommits && ci->parentoid[0])
-				fputs("<tr><td></td><td colspan=\"5\">"
-				      "More commits remaining [...]</td>"
-				      "</tr>\n", fp);
-		}
-
-		if (cachefile)
-			writelogline(wcachefp, ci);
-
-		/* check if file exists if so skip it */
-		if (r) {
-			relpath = "../";
-			fpfile = efopen(path, "w");
-			writeheader(fpfile, ci->summary);
-			fputs("<div id=\"pre-scroll\">\n<pre>", fpfile);
-			printshowfile(fpfile, ci);
-			fputs("</pre>\n</div>\n", fpfile);
-			writefooter(fpfile);
-			fclose(fpfile);
-		}
+  struct commitinfo *ci;
+  git_revwalk *w = NULL;
+  git_oid id;
+  char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
+  FILE *fpfile;
+  int r;
+
+  git_revwalk_new(&w, repo);
+  git_revwalk_push(w, oid);
+  git_revwalk_simplify_first_parent(w);
+
+  while (!git_revwalk_next(&id, w)) {
+    relpath = "";
+
+    if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
+      break;
+
+    git_oid_tostr(oidstr, sizeof(oidstr), &id);
+    r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
+    if (r < 0 || (size_t)r >= sizeof(path))
+      errx(1, "path truncated: 'commit/%s.html'", oidstr);
+    r = access(path, F_OK);
+
+    /* optimization: if there are no log lines to write and
+       the commit file already exists: skip the diffstat */
+    if (!nlogcommits && !r)
+      continue;
+
+    if (!(ci = commitinfo_getbyoid(&id)))
+      break;
+    /* diffstat: for stagit HTML required for the index.html line */
+    if (commitinfo_getstats(ci) == -1)
+      goto err;
+
+    if (nlogcommits < 0) {
+      writelogline(fp, ci);
+    } else if (nlogcommits > 0) {
+      writelogline(fp, ci);
+      nlogcommits--;
+      if (!nlogcommits && ci->parentoid[0])
+        fputs("<tr><td></td><td colspan=\"5\">"
+              "More commits remaining [...]</td>"
+              "</tr>\n", fp);
+    }
+
+    if (cachefile)
+      writelogline(wcachefp, ci);
+
+    /* check if file exists if so skip it */
+    if (r) {
+      relpath = "../";
+      fpfile = efopen(path, "w");
+      writeheader(fpfile, ci->summary);
+      fputs("<div id=\"pre-scroll\">\n<pre>", fpfile);
+      printshowfile(fpfile, ci);
+      fputs("</pre>\n</div>\n", fpfile);
+      writefooter(fpfile);
+      fclose(fpfile);
+    }
 err:
-		commitinfo_free(ci);
-	}
-	git_revwalk_free(w);
+    commitinfo_free(ci);
+  }
+  git_revwalk_free(w);
 
-	relpath = "";
+  relpath = "";
 
-	return 0;
+  return 0;
 }
 
 void
 printcommitatom(FILE *fp, struct commitinfo *ci)
 {
-	fputs("<entry>\n", fp);
-
-	fprintf(fp, "<id>%s</id>\n", ci->oid);
-	if (ci->author) {
-		fputs("<published>", fp);
-		printtimez(fp, &(ci->author->when));
-		fputs("</published>\n", fp);
-	}
-	if (ci->committer) {
-		fputs("<updated>", fp);
-		printtimez(fp, &(ci->committer->when));
-		fputs("</updated>\n", fp);
-	}
-	if (ci->summary) {
-		fputs("<title type=\"text\">", fp);
-		xmlencode(fp, ci->summary, strlen(ci->summary));
-		fputs("</title>\n", fp);
-	}
-	fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />\n",
-	        ci->oid);
-
-	if (ci->author) {
-		fputs("<author>\n<name>", fp);
-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
-		fputs("</name>\n<email>", fp);
-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
-		fputs("</email>\n</author>\n", fp);
-	}
-
-	fputs("<content type=\"text\">", fp);
-	fprintf(fp, "commit %s\n", ci->oid);
-	if (ci->parentoid[0])
-		fprintf(fp, "parent %s\n", ci->parentoid);
-	if (ci->author) {
-		fputs("Author: ", fp);
-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
-		fputs(" &lt;", fp);
-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
-		fputs("&gt;\nDate:   ", fp);
-		printtime(fp, &(ci->author->when));
-		fputc('\n', fp);
-	}
-	if (ci->msg) {
-		fputc('\n', fp);
-		xmlencode(fp, ci->msg, strlen(ci->msg));
-	}
-	fputs("\n</content>\n</entry>\n", fp);
+  fputs("<entry>\n", fp);
+
+  fprintf(fp, "<id>%s</id>\n", ci->oid);
+  if (ci->author) {
+    fputs("<published>", fp);
+    printtimez(fp, &(ci->author->when));
+    fputs("</published>\n", fp);
+  }
+  if (ci->committer) {
+    fputs("<updated>", fp);
+    printtimez(fp, &(ci->committer->when));
+    fputs("</updated>\n", fp);
+  }
+  if (ci->summary) {
+    fputs("<title type=\"text\">", fp);
+    xmlencode(fp, ci->summary, strlen(ci->summary));
+    fputs("</title>\n", fp);
+  }
+  fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />\n",
+          ci->oid);
+
+  if (ci->author) {
+    fputs("<author>\n<name>", fp);
+    xmlencode(fp, ci->author->name, strlen(ci->author->name));
+    fputs("</name>\n<email>", fp);
+    xmlencode(fp, ci->author->email, strlen(ci->author->email));
+    fputs("</email>\n</author>\n", fp);
+  }
+
+  fputs("<content type=\"text\">", fp);
+  fprintf(fp, "commit %s\n", ci->oid);
+  if (ci->parentoid[0])
+    fprintf(fp, "parent %s\n", ci->parentoid);
+  if (ci->author) {
+    fputs("Author: ", fp);
+    xmlencode(fp, ci->author->name, strlen(ci->author->name));
+    fputs(" &lt;", fp);
+    xmlencode(fp, ci->author->email, strlen(ci->author->email));
+    fputs("&gt;\nDate:   ", fp);
+    printtime(fp, &(ci->author->when));
+    fputc('\n', fp);
+  }
+  if (ci->msg) {
+    fputc('\n', fp);
+    xmlencode(fp, ci->msg, strlen(ci->msg));
+  }
+  fputs("\n</content>\n</entry>\n", fp);
 }
 
 int
 writeatom(FILE *fp)
 {
-	struct commitinfo *ci;
-	git_revwalk *w = NULL;
-	git_oid id;
-	size_t i, m = 100; /* last 'm' commits */
-
-	fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-	      "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>", fp);
-	xmlencode(fp, strippedname, strlen(strippedname));
-	fputs(", branch HEAD</title>\n<subtitle>", fp);
-	xmlencode(fp, description, strlen(description));
-	fputs("</subtitle>\n", fp);
-
-	git_revwalk_new(&w, repo);
-	git_revwalk_push_head(w);
-	git_revwalk_simplify_first_parent(w);
-
-	for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
-		if (!(ci = commitinfo_getbyoid(&id)))
-			break;
-		printcommitatom(fp, ci);
-		commitinfo_free(ci);
-	}
-	git_revwalk_free(w);
-
-	fputs("</feed>\n", fp);
-
-	return 0;
+  struct commitinfo *ci;
+  git_revwalk *w = NULL;
+  git_oid id;
+  size_t i, m = 100; /* last 'm' commits */
+
+  fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+        "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>", fp);
+  xmlencode(fp, strippedname, strlen(strippedname));
+  fputs(", branch HEAD</title>\n<subtitle>", fp);
+  xmlencode(fp, description, strlen(description));
+  fputs("</subtitle>\n", fp);
+
+  git_revwalk_new(&w, repo);
+  git_revwalk_push_head(w);
+  git_revwalk_simplify_first_parent(w);
+
+  for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
+    if (!(ci = commitinfo_getbyoid(&id)))
+      break;
+    printcommitatom(fp, ci);
+    commitinfo_free(ci);
+  }
+  git_revwalk_free(w);
+
+  fputs("</feed>\n", fp);
+
+  return 0;
 }
 
 int
 writeblob(git_object *obj, const char *fpath, const char *filename, git_off_t filesize)
 {
-	char tmp[PATH_MAX] = "", *d;
-	const char *p;
-	int lc = 0;
-	FILE *fp;
-
-	if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp))
-		errx(1, "path truncated: '%s'", fpath);
-	if (!(d = dirname(tmp)))
-		err(1, "dirname");
-	if (mkdirp(d))
-		return -1;
-
-	for (p = fpath, tmp[0] = '\0'; *p; p++) {
-		if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
-			errx(1, "path truncated: '../%s'", tmp);
-	}
-	relpath = tmp;
-
-	fp = efopen(fpath, "w");
-	writeheader(fp, filename);
-	fputs("<p> ", fp);
-	xmlencode(fp, filename, strlen(filename));
-	fprintf(fp, " (%juB)", (uintmax_t)filesize);
-	fputs("</p><hr/>", fp);
-
-	if (git_blob_is_binary((git_blob *)obj)) {
-		fputs("<p>Binary file.</p>\n", fp);
-	} else {
-		lc = writeblobhtml(fp, (git_blob *)obj);
-		if (ferror(fp))
-			err(1, "fwrite");
-	}
-	writefooter(fp);
-	fclose(fp);
-
-	relpath = "";
-
-	return lc;
+  char tmp[PATH_MAX] = "", *d;
+  const char *p;
+  int lc = 0;
+  FILE *fp;
+
+  if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp))
+    errx(1, "path truncated: '%s'", fpath);
+  if (!(d = dirname(tmp)))
+    err(1, "dirname");
+  if (mkdirp(d))
+    return -1;
+
+  for (p = fpath, tmp[0] = '\0'; *p; p++) {
+    if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
+      errx(1, "path truncated: '../%s'", tmp);
+  }
+  relpath = tmp;
+
+  fp = efopen(fpath, "w");
+  writeheader(fp, filename);
+  fputs("<p> ", fp);
+  xmlencode(fp, filename, strlen(filename));
+  fprintf(fp, " (%juB)", (uintmax_t)filesize);
+  fputs("</p><hr/>", fp);
+
+  if (git_blob_is_binary((git_blob *)obj)) {
+    fputs("<p>Binary file.</p>\n", fp);
+  } else {
+    lc = writeblobhtml(fp, (git_blob *)obj);
+    if (ferror(fp))
+      err(1, "fwrite");
+  }
+  writefooter(fp);
+  fclose(fp);
+
+  relpath = "";
+
+  return lc;
 }
 
 const char *
 filemode(git_filemode_t m)
 {
-	static char mode[11];
-
-	memset(mode, '-', sizeof(mode) - 1);
-	mode[10] = '\0';
-
-	if (S_ISREG(m))
-		mode[0] = '-';
-	else if (S_ISBLK(m))
-		mode[0] = 'b';
-	else if (S_ISCHR(m))
-		mode[0] = 'c';
-	else if (S_ISDIR(m))
-		mode[0] = 'd';
-	else if (S_ISFIFO(m))
-		mode[0] = 'p';
-	else if (S_ISLNK(m))
-		mode[0] = 'l';
-	else if (S_ISSOCK(m))
-		mode[0] = 's';
-	else
-		mode[0] = '?';
-
-	if (m & S_IRUSR) mode[1] = 'r';
-	if (m & S_IWUSR) mode[2] = 'w';
-	if (m & S_IXUSR) mode[3] = 'x';
-	if (m & S_IRGRP) mode[4] = 'r';
-	if (m & S_IWGRP) mode[5] = 'w';
-	if (m & S_IXGRP) mode[6] = 'x';
-	if (m & S_IROTH) mode[7] = 'r';
-	if (m & S_IWOTH) mode[8] = 'w';
-	if (m & S_IXOTH) mode[9] = 'x';
-
-	if (m & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S';
-	if (m & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S';
-	if (m & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T';
-
-	return mode;
+  static char mode[11];
+
+  memset(mode, '-', sizeof(mode) - 1);
+  mode[10] = '\0';
+
+  if (S_ISREG(m))
+    mode[0] = '-';
+  else if (S_ISBLK(m))
+    mode[0] = 'b';
+  else if (S_ISCHR(m))
+    mode[0] = 'c';
+  else if (S_ISDIR(m))
+    mode[0] = 'd';
+  else if (S_ISFIFO(m))
+    mode[0] = 'p';
+  else if (S_ISLNK(m))
+    mode[0] = 'l';
+  else if (S_ISSOCK(m))
+    mode[0] = 's';
+  else
+    mode[0] = '?';
+
+  if (m & S_IRUSR) mode[1] = 'r';
+  if (m & S_IWUSR) mode[2] = 'w';
+  if (m & S_IXUSR) mode[3] = 'x';
+  if (m & S_IRGRP) mode[4] = 'r';
+  if (m & S_IWGRP) mode[5] = 'w';
+  if (m & S_IXGRP) mode[6] = 'x';
+  if (m & S_IROTH) mode[7] = 'r';
+  if (m & S_IWOTH) mode[8] = 'w';
+  if (m & S_IXOTH) mode[9] = 'x';
+
+  if (m & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S';
+  if (m & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S';
+  if (m & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T';
+
+  return mode;
 }
 
 int
 writefilestree(FILE *fp, git_tree *tree, const char *path)
 {
-	const git_tree_entry *entry = NULL;
-	git_submodule *module = NULL;
-	git_object *obj = NULL;
-	git_off_t filesize;
-	const char *entryname;
-	char filepath[PATH_MAX], entrypath[PATH_MAX];
-	size_t count, i;
-	int lc, r, ret;
-
-	count = git_tree_entrycount(tree);
-	for (i = 0; i < count; i++) {
-		if (!(entry = git_tree_entry_byindex(tree, i)) ||
-		    !(entryname = git_tree_entry_name(entry)))
-			return -1;
-		joinpath(entrypath, sizeof(entrypath), path, entryname);
-
-		r = snprintf(filepath, sizeof(filepath), "file/%s.html",
-		         entrypath);
-		if (r < 0 || (size_t)r >= sizeof(filepath))
-			errx(1, "path truncated: 'file/%s.html'", entrypath);
-
-		if (!git_tree_entry_to_object(&obj, repo, entry)) {
-			switch (git_object_type(obj)) {
-			case GIT_OBJ_BLOB:
-				break;
-			case GIT_OBJ_TREE:
-				/* NOTE: recurses */
-				ret = writefilestree(fp, (git_tree *)obj,
-				                     entrypath);
-				git_object_free(obj);
-				if (ret)
-					return ret;
-				continue;
-			default:
-				git_object_free(obj);
-				continue;
-			}
-
-			filesize = git_blob_rawsize((git_blob *)obj);
-			lc = writeblob(obj, filepath, entryname, filesize);
-
-			fputs("<tr><td>", fp);
-			fputs(filemode(git_tree_entry_filemode(entry)), fp);
-			fprintf(fp, "</td><td><a href=\"%s", relpath);
-			xmlencode(fp, filepath, strlen(filepath));
-			fputs("\">", fp);
-			xmlencode(fp, entrypath, strlen(entrypath));
-			fputs("</a></td><td class=\"num\">", fp);
-			if (lc > 0)
-				fprintf(fp, "%dL", lc);
-			else
-				fprintf(fp, "%juB", (uintmax_t)filesize);
-			fputs("</td></tr>\n", fp);
-			git_object_free(obj);
-		} else if (!git_submodule_lookup(&module, repo, entryname)) {
-			fprintf(fp, "<tr><td>m---------</td><td><a href=\"%sfile/.gitmodules.html\">",
-				relpath);
-			xmlencode(fp, entrypath, strlen(entrypath));
-			git_submodule_free(module);
-			fputs("</a></td><td class=\"num\"></td></tr>\n", fp);
-		}
-	}
-
-	return 0;
+  const git_tree_entry *entry = NULL;
+  git_submodule *module = NULL;
+  git_object *obj = NULL;
+  git_off_t filesize;
+  const char *entryname;
+  char filepath[PATH_MAX], entrypath[PATH_MAX];
+  size_t count, i;
+  int lc, r, ret;
+
+  count = git_tree_entrycount(tree);
+  for (i = 0; i < count; i++) {
+    if (!(entry = git_tree_entry_byindex(tree, i)) ||
+        !(entryname = git_tree_entry_name(entry)))
+      return -1;
+    joinpath(entrypath, sizeof(entrypath), path, entryname);
+
+    r = snprintf(filepath, sizeof(filepath), "file/%s.html",
+             entrypath);
+    if (r < 0 || (size_t)r >= sizeof(filepath))
+      errx(1, "path truncated: 'file/%s.html'", entrypath);
+
+    if (!git_tree_entry_to_object(&obj, repo, entry)) {
+      switch (git_object_type(obj)) {
+      case GIT_OBJ_BLOB:
+        break;
+      case GIT_OBJ_TREE:
+        /* NOTE: recurses */
+        ret = writefilestree(fp, (git_tree *)obj,
+                             entrypath);
+        git_object_free(obj);
+        if (ret)
+          return ret;
+        continue;
+      default:
+        git_object_free(obj);
+        continue;
+      }
+
+      filesize = git_blob_rawsize((git_blob *)obj);
+      lc = writeblob(obj, filepath, entryname, filesize);
+
+      fputs("<tr><td>", fp);
+      fputs(filemode(git_tree_entry_filemode(entry)), fp);
+      fprintf(fp, "</td><td><a href=\"%s", relpath);
+      xmlencode(fp, filepath, strlen(filepath));
+      fputs("\">", fp);
+      xmlencode(fp, entrypath, strlen(entrypath));
+      fputs("</a></td><td class=\"num\">", fp);
+      if (lc > 0)
+        fprintf(fp, "%dL", lc);
+      else
+        fprintf(fp, "%juB", (uintmax_t)filesize);
+      fputs("</td></tr>\n", fp);
+      git_object_free(obj);
+    } else if (!git_submodule_lookup(&module, repo, entryname)) {
+      fprintf(fp, "<tr><td>m---------</td><td><a href=\"%sfile/.gitmodules.html\">",
+        relpath);
+      xmlencode(fp, entrypath, strlen(entrypath));
+      git_submodule_free(module);
+      fputs("</a></td><td class=\"num\"></td></tr>\n", fp);
+    }
+  }
+
+  return 0;
 }
 
 int
 writefiles(FILE *fp, const git_oid *id)
 {
-	git_tree *tree = NULL;
-	git_commit *commit = NULL;
-	int ret = -1;
+  git_tree *tree = NULL;
+  git_commit *commit = NULL;
+  int ret = -1;
 
-	fputs("<div id=\"table-scroll\"><table id=\"files\"><thead>\n<tr>"
-	      "<th>Mode</th><th>Name</th>"
-	      "<th class=\"num\">Size</th>"
-	      "</tr>\n</thead><tbody>\n", fp);
+  fputs("<div id=\"table-scroll\"><table id=\"files\"><thead>\n<tr>"
+        "<th>Mode</th><th>Name</th>"
+        "<th class=\"num\">Size</th>"
+        "</tr>\n</thead><tbody>\n", fp);
 
-	if (!git_commit_lookup(&commit, repo, id) &&
-	    !git_commit_tree(&tree, commit))
-		ret = writefilestree(fp, tree, "");
+  if (!git_commit_lookup(&commit, repo, id) &&
+      !git_commit_tree(&tree, commit))
+    ret = writefilestree(fp, tree, "");
 
-	fputs("</tbody></table></div>", fp);
+  fputs("</tbody></table></div>", fp);
 
-	git_commit_free(commit);
-	git_tree_free(tree);
+  git_commit_free(commit);
+  git_tree_free(tree);
 
-	return ret;
+  return ret;
 }
 
 int
 refs_cmp(const void *v1, const void *v2)
 {
-	git_reference *r1 = (*(git_reference **)v1);
-	git_reference *r2 = (*(git_reference **)v2);
-	int r;
+  git_reference *r1 = (*(git_reference **)v1);
+  git_reference *r2 = (*(git_reference **)v2);
+  int r;
 
-	if ((r = git_reference_is_branch(r1) - git_reference_is_branch(r2)))
-		return r;
+  if ((r = git_reference_is_branch(r1) - git_reference_is_branch(r2)))
+    return r;
 
-	return strcmp(git_reference_shorthand(r1),
-	              git_reference_shorthand(r2));
+  return strcmp(git_reference_shorthand(r1),
+                git_reference_shorthand(r2));
 }
 
 int
 writerefs(FILE *fp)
 {
-	struct commitinfo *ci;
-	const git_oid *id = NULL;
-	git_object *obj = NULL;
-	git_reference *dref = NULL, *r, *ref = NULL;
-	git_reference_iterator *it = NULL;
-	git_reference **refs = NULL;
-	size_t count, i, j, refcount;
-	const char *titles[] = { "Branches", "Tags" };
-	const char *ids[] = { "branches", "tags" };
-	const char *name;
-
-	if (git_reference_iterator_new(&it, repo))
-		return -1;
-
-	for (refcount = 0; !git_reference_next(&ref, it); refcount++) {
-		if (!(refs = reallocarray(refs, refcount + 1, sizeof(git_reference *))))
-			err(1, "realloc");
-		refs[refcount] = ref;
-	}
-	git_reference_iterator_free(it);
-
-	/* sort by type then shorthand name */
-	qsort(refs, refcount, sizeof(git_reference *), refs_cmp);
-
-	for (j = 0; j < 2; j++) {
-		for (i = 0, count = 0; i < refcount; i++) {
-			if (!(git_reference_is_branch(refs[i]) && j == 0) &&
-			    !(git_reference_is_tag(refs[i]) && j == 1))
-				continue;
-
-			switch (git_reference_type(refs[i])) {
-			case GIT_REF_SYMBOLIC:
-				if (git_reference_resolve(&dref, refs[i]))
-					goto err;
-				r = dref;
-				break;
-			case GIT_REF_OID:
-				r = refs[i];
-				break;
-			default:
-				continue;
-			}
-			if (!git_reference_target(r) ||
-			    git_reference_peel(&obj, r, GIT_OBJ_ANY))
-				goto err;
-			if (!(id = git_object_id(obj)))
-				goto err;
-			if (!(ci = commitinfo_getbyoid(id)))
-				break;
-
-			/* print header if it has an entry (first). */
-			if (++count == 1) {
-				fprintf(fp, "<h2>%s</h2><div id=\"table-scroll\"><table id=\"%s\">"
-			                "<thead>\n<tr><th>Name</th>"
-				        "<th>Last commit date</th>"
-				        "<th>Author</th>\n</tr>\n"
-				        "</thead><tbody>\n",
-				         titles[j], ids[j]);
-			}
-
-			relpath = "";
-			name = git_reference_shorthand(r);
-
-			fputs("<tr><td>", fp);
-			xmlencode(fp, name, strlen(name));
-			fputs("</td><td>", fp);
-			if (ci->author)
-				printtimeshort(fp, &(ci->author->when));
-			fputs("</td><td>", fp);
-			if (ci->author)
-				xmlencode(fp, ci->author->name, strlen(ci->author->name));
-			fputs("</td></tr>\n", fp);
-
-			relpath = "../";
-
-			commitinfo_free(ci);
-			git_object_free(obj);
-			obj = NULL;
-			git_reference_free(dref);
-			dref = NULL;
-		}
-		/* table footer */
-		if (count)
-			fputs("</tbody></table></div><br/>", fp);
-	}
+  struct commitinfo *ci;
+  const git_oid *id = NULL;
+  git_object *obj = NULL;
+  git_reference *dref = NULL, *r, *ref = NULL;
+  git_reference_iterator *it = NULL;
+  git_reference **refs = NULL;
+  size_t count, i, j, refcount;
+  const char *titles[] = { "Branches", "Tags" };
+  const char *ids[] = { "branches", "tags" };
+  const char *name;
+
+  if (git_reference_iterator_new(&it, repo))
+    return -1;
+
+  for (refcount = 0; !git_reference_next(&ref, it); refcount++) {
+    if (!(refs = reallocarray(refs, refcount + 1, sizeof(git_reference *))))
+      err(1, "realloc");
+    refs[refcount] = ref;
+  }
+  git_reference_iterator_free(it);
+
+  /* sort by type then shorthand name */
+  qsort(refs, refcount, sizeof(git_reference *), refs_cmp);
+
+  for (j = 0; j < 2; j++) {
+    for (i = 0, count = 0; i < refcount; i++) {
+      if (!(git_reference_is_branch(refs[i]) && j == 0) &&
+          !(git_reference_is_tag(refs[i]) && j == 1))
+        continue;
+
+      switch (git_reference_type(refs[i])) {
+      case GIT_REF_SYMBOLIC:
+        if (git_reference_resolve(&dref, refs[i]))
+          goto err;
+        r = dref;
+        break;
+      case GIT_REF_OID:
+        r = refs[i];
+        break;
+      default:
+        continue;
+      }
+      if (!git_reference_target(r) ||
+          git_reference_peel(&obj, r, GIT_OBJ_ANY))
+        goto err;
+      if (!(id = git_object_id(obj)))
+        goto err;
+      if (!(ci = commitinfo_getbyoid(id)))
+        break;
+
+      /* print header if it has an entry (first). */
+      if (++count == 1) {
+        fprintf(fp, "<h2>%s</h2><div id=\"table-scroll\"><table id=\"%s\">"
+                      "<thead>\n<tr><th>Name</th>"
+                "<th>Last commit date</th>"
+                "<th>Author</th>\n</tr>\n"
+                "</thead><tbody>\n",
+                 titles[j], ids[j]);
+      }
+
+      relpath = "";
+      name = git_reference_shorthand(r);
+
+      fputs("<tr><td>", fp);
+      xmlencode(fp, name, strlen(name));
+      fputs("</td><td>", fp);
+      if (ci->author)
+        printtimeshort(fp, &(ci->author->when));
+      fputs("</td><td>", fp);
+      if (ci->author)
+        xmlencode(fp, ci->author->name, strlen(ci->author->name));
+      fputs("</td></tr>\n", fp);
+
+      relpath = "../";
+
+      commitinfo_free(ci);
+      git_object_free(obj);
+      obj = NULL;
+      git_reference_free(dref);
+      dref = NULL;
+    }
+    /* table footer */
+    if (count)
+      fputs("</tbody></table></div><br/>", fp);
+  }
 
 err:
-	git_object_free(obj);
-	git_reference_free(dref);
+  git_object_free(obj);
+  git_reference_free(dref);
 
-	for (i = 0; i < refcount; i++)
-		git_reference_free(refs[i]);
-	free(refs);
+  for (i = 0; i < refcount; i++)
+    git_reference_free(refs[i]);
+  free(refs);
 
-	return 0;
+  return 0;
 }
 
 void
 usage(char *argv0)
 {
-	fprintf(stderr, "%s [-c cachefile | -l commits] repodir\n", argv0);
-	exit(1);
+  fprintf(stderr, "%s [-c cachefile | -l commits] repodir\n", argv0);
+  exit(1);
 }
 
 int
 main(int argc, char *argv[])
 {
-	git_object *obj = NULL;
-	const git_oid *head = NULL;
-	mode_t mask;
-	FILE *fp, *fpread;
-	char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
-	char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
-	size_t n;
-	int i, fd;
-
-	for (i = 1; i < argc; i++) {
-		if (argv[i][0] != '-') {
-			if (repodir)
-				usage(argv[0]);
-			repodir = argv[i];
-		} else if (argv[i][1] == 'c') {
-			if (nlogcommits > 0 || i + 1 >= argc)
-				usage(argv[0]);
-			cachefile = argv[++i];
-		} else if (argv[i][1] == 'l') {
-			if (cachefile || i + 1 >= argc)
-				usage(argv[0]);
-			errno = 0;
-			nlogcommits = strtoll(argv[++i], &p, 10);
-			if (argv[i][0] == '\0' || *p != '\0' ||
-			    nlogcommits <= 0 || errno)
-				usage(argv[0]);
-		}
-	}
-	if (!repodir)
-		usage(argv[0]);
-
-	if (!realpath(repodir, repodirabs))
-		err(1, "realpath");
-
-	git_libgit2_init();
+  git_object *obj = NULL;
+  const git_oid *head = NULL;
+  mode_t mask;
+  FILE *fp, *fpread;
+  char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
+  char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
+  size_t n;
+  int i, fd;
+
+  for (i = 1; i < argc; i++) {
+    if (argv[i][0] != '-') {
+      if (repodir)
+        usage(argv[0]);
+      repodir = argv[i];
+    } else if (argv[i][1] == 'c') {
+      if (nlogcommits > 0 || i + 1 >= argc)
+        usage(argv[0]);
+      cachefile = argv[++i];
+    } else if (argv[i][1] == 'l') {
+      if (cachefile || i + 1 >= argc)
+        usage(argv[0]);
+      errno = 0;
+      nlogcommits = strtoll(argv[++i], &p, 10);
+      if (argv[i][0] == '\0' || *p != '\0' ||
+          nlogcommits <= 0 || errno)
+        usage(argv[0]);
+    }
+  }
+  if (!repodir)
+    usage(argv[0]);
+
+  if (!realpath(repodir, repodirabs))
+    err(1, "realpath");
+
+  git_libgit2_init();
 
 #ifdef __OpenBSD__
-	if (unveil(repodir, "r") == -1)
-		err(1, "unveil: %s", repodir);
-	if (unveil(".", "rwc") == -1)
-		err(1, "unveil: .");
-	if (cachefile && unveil(cachefile, "rwc") == -1)
-		err(1, "unveil: %s", cachefile);
-
-	if (cachefile) {
-		if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
-			err(1, "pledge");
-	} else {
-		if (pledge("stdio rpath wpath cpath", NULL) == -1)
-			err(1, "pledge");
-	}
+  if (unveil(repodir, "r") == -1)
+    err(1, "unveil: %s", repodir);
+  if (unveil(".", "rwc") == -1)
+    err(1, "unveil: .");
+  if (cachefile && unveil(cachefile, "rwc") == -1)
+    err(1, "unveil: %s", cachefile);
+
+  if (cachefile) {
+    if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
+      err(1, "pledge");
+  } else {
+    if (pledge("stdio rpath wpath cpath", NULL) == -1)
+      err(1, "pledge");
+  }
 #endif
 
-	if (git_repository_open_ext(&repo, repodir,
-		GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
-		fprintf(stderr, "%s: cannot open repository\n", argv[0]);
-		return 1;
-	}
-
-	/* find HEAD */
-	if (!git_revparse_single(&obj, repo, "HEAD"))
-		head = git_object_id(obj);
-	git_object_free(obj);
-
-	/* use directory name as name */
-	if ((name = strrchr(repodirabs, '/')))
-		name++;
-	else
-		name = "";
-
-	/* strip .git suffix */
-	if (!(strippedname = strdup(name)))
-		err(1, "strdup");
-	if ((p = strrchr(strippedname, '.')))
-		if (!strcmp(p, ".git"))
-			*p = '\0';
-
-	/* read description or .git/description */
-	joinpath(path, sizeof(path), repodir, "description");
-	if (!(fpread = fopen(path, "r"))) {
-		joinpath(path, sizeof(path), repodir, ".git/description");
-		fpread = fopen(path, "r");
-	}
-	if (fpread) {
-		if (!fgets(description, sizeof(description), fpread))
-			description[0] = '\0';
-		fclose(fpread);
-	}
-
-	/* read url or .git/url */
-	joinpath(path, sizeof(path), repodir, "url");
-	if (!(fpread = fopen(path, "r"))) {
-		joinpath(path, sizeof(path), repodir, ".git/url");
-		fpread = fopen(path, "r");
-	}
-	if (fpread) {
-		if (!fgets(cloneurl, sizeof(cloneurl), fpread))
-			cloneurl[0] = '\0';
-		cloneurl[strcspn(cloneurl, "\n")] = '\0';
-		fclose(fpread);
-	}
-
-	/* check LICENSE */
-	for (i = 0; i < sizeof(licensefiles) / sizeof(*licensefiles) && !license; i++) {
-		if (!git_revparse_single(&obj, repo, licensefiles[i]) &&
-		    git_object_type(obj) == GIT_OBJ_BLOB)
-			license = licensefiles[i] + strlen("HEAD:");
-		git_object_free(obj);
-	}
-
-	/* check README */
-	for (i = 0; i < sizeof(readmefiles) / sizeof(*readmefiles) && !readme; i++) {
-		if (!git_revparse_single(&obj, repo, readmefiles[i]) &&
-		    git_object_type(obj) == GIT_OBJ_BLOB)
-			readme = readmefiles[i] + strlen("HEAD:");
-		git_object_free(obj);
-	}
-
-	if (!git_revparse_single(&obj, repo, "HEAD:.gitmodules") &&
-	    git_object_type(obj) == GIT_OBJ_BLOB)
-		submodules = ".gitmodules";
-	git_object_free(obj);
-
-	/* log for HEAD */
-	fp = efopen("index.html", "w");
-	relpath = "";
-	mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
-	writeheader(fp, "Log");
-	fputs("<div id=\"table-scroll\"><table id=\"log\"><thead>\n<tr><th>"
-				"<a href=\"#\">🔗</a></th><th>Date</th>"
-	      "<th>Commit message</th>"
-	      "<th>Author</th><td class=\"num\"><b>Files</th>"
-	      "<td class=\"num\" id=\"plus\"><b>+</th>"
-	      "<td class=\"num\" id=\"min\"><b>-</th></tr>\n</thead><tbody>\n", fp);
-
-	if (cachefile && head) {
-		/* read from cache file (does not need to exist) */
-		if ((rcachefp = fopen(cachefile, "r"))) {
-			if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
-				errx(1, "%s: no object id", cachefile);
-			if (git_oid_fromstr(&lastoid, lastoidstr))
-				errx(1, "%s: invalid object id", cachefile);
-		}
-
-		/* write log to (temporary) cache */
-		if ((fd = mkstemp(tmppath)) == -1)
-			err(1, "mkstemp");
-		if (!(wcachefp = fdopen(fd, "w")))
-			err(1, "fdopen: '%s'", tmppath);
-		/* write last commit id (HEAD) */
-		git_oid_tostr(buf, sizeof(buf), head);
-		fprintf(wcachefp, "%s\n", buf);
-
-		writelog(fp, head);
-
-		if (rcachefp) {
-			/* append previous log to index.html and the new cache */
-			while (!feof(rcachefp)) {
-				n = fread(buf, 1, sizeof(buf), rcachefp);
-				if (ferror(rcachefp))
-					err(1, "fread");
-				if (fwrite(buf, 1, n, fp) != n ||
-				    fwrite(buf, 1, n, wcachefp) != n)
-					err(1, "fwrite");
-			}
-			fclose(rcachefp);
-		}
-		fclose(wcachefp);
-	} else {
-		if (head)
-			writelog(fp, head);
-	}
-
-	fputs("</tbody></table></div>", fp);
-	writefooter(fp);
-	fclose(fp);
-
-	/* files for HEAD */
-	fp = efopen("files.html", "w");
-	writeheader(fp, "Files");
-	if (head)
-		writefiles(fp, head);
-	writefooter(fp);
-	fclose(fp);
-
-	/* summary page with branches and tags */
-	fp = efopen("refs.html", "w");
-	writeheader(fp, "Refs");
-	writerefs(fp);
-	writefooter(fp);
-	fclose(fp);
-
-	/* Atom feed */
-	fp = efopen("atom.xml", "w");
-	writeatom(fp);
-	fclose(fp);
-
-	/* rename new cache file on success */
-	if (cachefile && head) {
-		if (rename(tmppath, cachefile))
-			err(1, "rename: '%s' to '%s'", tmppath, cachefile);
-		umask((mask = umask(0)));
-		if (chmod(cachefile,
-		    (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mask))
-			err(1, "chmod: '%s'", cachefile);
-	}
-
-	/* cleanup */
-	git_repository_free(repo);
-	git_libgit2_shutdown();
-
-	return 0;
+  if (git_repository_open_ext(&repo, repodir,
+    GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
+    fprintf(stderr, "%s: cannot open repository\n", argv[0]);
+    return 1;
+  }
+
+  /* find HEAD */
+  if (!git_revparse_single(&obj, repo, "HEAD"))
+    head = git_object_id(obj);
+  git_object_free(obj);
+
+  /* use directory name as name */
+  if ((name = strrchr(repodirabs, '/')))
+    name++;
+  else
+    name = "";
+
+  /* strip .git suffix */
+  if (!(strippedname = strdup(name)))
+    err(1, "strdup");
+  if ((p = strrchr(strippedname, '.')))
+    if (!strcmp(p, ".git"))
+      *p = '\0';
+
+  /* read description or .git/description */
+  joinpath(path, sizeof(path), repodir, "description");
+  if (!(fpread = fopen(path, "r"))) {
+    joinpath(path, sizeof(path), repodir, ".git/description");
+    fpread = fopen(path, "r");
+  }
+  if (fpread) {
+    if (!fgets(description, sizeof(description), fpread))
+      description[0] = '\0';
+    fclose(fpread);
+  }
+
+  /* read url or .git/url */
+  joinpath(path, sizeof(path), repodir, "url");
+  if (!(fpread = fopen(path, "r"))) {
+    joinpath(path, sizeof(path), repodir, ".git/url");
+    fpread = fopen(path, "r");
+  }
+  if (fpread) {
+    if (!fgets(cloneurl, sizeof(cloneurl), fpread))
+      cloneurl[0] = '\0';
+    cloneurl[strcspn(cloneurl, "\n")] = '\0';
+    fclose(fpread);
+  }
+
+  /* check LICENSE */
+  for (i = 0; i < sizeof(licensefiles) / sizeof(*licensefiles) && !license; i++) {
+    if (!git_revparse_single(&obj, repo, licensefiles[i]) &&
+        git_object_type(obj) == GIT_OBJ_BLOB)
+      license = licensefiles[i] + strlen("HEAD:");
+    git_object_free(obj);
+  }
+
+  /* check README */
+  for (i = 0; i < sizeof(readmefiles) / sizeof(*readmefiles) && !readme; i++) {
+    if (!git_revparse_single(&obj, repo, readmefiles[i]) &&
+        git_object_type(obj) == GIT_OBJ_BLOB)
+      readme = readmefiles[i] + strlen("HEAD:");
+    git_object_free(obj);
+  }
+
+  if (!git_revparse_single(&obj, repo, "HEAD:.gitmodules") &&
+      git_object_type(obj) == GIT_OBJ_BLOB)
+    submodules = ".gitmodules";
+  git_object_free(obj);
+
+  /* log for HEAD */
+  fp = efopen("index.html", "w");
+  relpath = "";
+  mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
+  writeheader(fp, "Log");
+  fputs("<div id=\"table-scroll\"><table id=\"log\"><thead>\n<tr><th>"
+        "<a href=\"#\">🔗</a></th><th>Date</th>"
+        "<th>Commit message</th>"
+        "<th>Author</th><td class=\"num\"><b>Files</th>"
+        "<td class=\"num\" id=\"plus\"><b>+</th>"
+        "<td class=\"num\" id=\"min\"><b>-</th></tr>\n</thead><tbody>\n", fp);
+
+  if (cachefile && head) {
+    /* read from cache file (does not need to exist) */
+    if ((rcachefp = fopen(cachefile, "r"))) {
+      if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
+        errx(1, "%s: no object id", cachefile);
+      if (git_oid_fromstr(&lastoid, lastoidstr))
+        errx(1, "%s: invalid object id", cachefile);
+    }
+
+    /* write log to (temporary) cache */
+    if ((fd = mkstemp(tmppath)) == -1)
+      err(1, "mkstemp");
+    if (!(wcachefp = fdopen(fd, "w")))
+      err(1, "fdopen: '%s'", tmppath);
+    /* write last commit id (HEAD) */
+    git_oid_tostr(buf, sizeof(buf), head);
+    fprintf(wcachefp, "%s\n", buf);
+
+    writelog(fp, head);
+
+    if (rcachefp) {
+      /* append previous log to index.html and the new cache */
+      while (!feof(rcachefp)) {
+        n = fread(buf, 1, sizeof(buf), rcachefp);
+        if (ferror(rcachefp))
+          err(1, "fread");
+        if (fwrite(buf, 1, n, fp) != n ||
+            fwrite(buf, 1, n, wcachefp) != n)
+          err(1, "fwrite");
+      }
+      fclose(rcachefp);
+    }
+    fclose(wcachefp);
+  } else {
+    if (head)
+      writelog(fp, head);
+  }
+
+  fputs("</tbody></table></div>", fp);
+  writefooter(fp);
+  fclose(fp);
+
+  /* files for HEAD */
+  fp = efopen("files.html", "w");
+  writeheader(fp, "Files");
+  if (head)
+    writefiles(fp, head);
+  writefooter(fp);
+  fclose(fp);
+
+  /* summary page with branches and tags */
+  fp = efopen("refs.html", "w");
+  writeheader(fp, "Refs");
+  writerefs(fp);
+  writefooter(fp);
+  fclose(fp);
+
+  /* Atom feed */
+  fp = efopen("atom.xml", "w");
+  writeatom(fp);
+  fclose(fp);
+
+  /* rename new cache file on success */
+  if (cachefile && head) {
+    if (rename(tmppath, cachefile))
+      err(1, "rename: '%s' to '%s'", tmppath, cachefile);
+    umask((mask = umask(0)));
+    if (chmod(cachefile,
+        (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mask))
+      err(1, "chmod: '%s'", cachefile);
+  }
+
+  /* cleanup */
+  git_repository_free(repo);
+  git_libgit2_shutdown();
+
+  return 0;
 }
diff --git a/strlcat.c b/strlcat.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $	*/
+/*  $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $  */
 
 /*
  * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
@@ -31,27 +31,27 @@
 size_t
 strlcat(char *dst, const char *src, size_t dsize)
 {
-	const char *odst = dst;
-	const char *osrc = src;
-	size_t n = dsize;
-	size_t dlen;
+  const char *odst = dst;
+  const char *osrc = src;
+  size_t n = dsize;
+  size_t dlen;
 
-	/* Find the end of dst and adjust bytes left but don't go past end. */
-	while (n-- != 0 && *dst != '\0')
-		dst++;
-	dlen = dst - odst;
-	n = dsize - dlen;
+  /* Find the end of dst and adjust bytes left but don't go past end. */
+  while (n-- != 0 && *dst != '\0')
+    dst++;
+  dlen = dst - odst;
+  n = dsize - dlen;
 
-	if (n-- == 0)
-		return(dlen + strlen(src));
-	while (*src != '\0') {
-		if (n != 0) {
-			*dst++ = *src;
-			n--;
-		}
-		src++;
-	}
-	*dst = '\0';
+  if (n-- == 0)
+    return(dlen + strlen(src));
+  while (*src != '\0') {
+    if (n != 0) {
+      *dst++ = *src;
+      n--;
+    }
+    src++;
+  }
+  *dst = '\0';
 
-	return(dlen + (src - osrc));	/* count does not include NUL */
+  return(dlen + (src - osrc));  /* count does not include NUL */
 }
diff --git a/strlcpy.c b/strlcpy.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $	*/
+/*  $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $  */
 
 /*
  * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
@@ -29,24 +29,24 @@
 size_t
 strlcpy(char *dst, const char *src, size_t dsize)
 {
-	const char *osrc = src;
-	size_t nleft = dsize;
+  const char *osrc = src;
+  size_t nleft = dsize;
 
-	/* Copy as many bytes as will fit. */
-	if (nleft != 0) {
-		while (--nleft != 0) {
-			if ((*dst++ = *src++) == '\0')
-				break;
-		}
-	}
+  /* Copy as many bytes as will fit. */
+  if (nleft != 0) {
+    while (--nleft != 0) {
+      if ((*dst++ = *src++) == '\0')
+        break;
+    }
+  }
 
-	/* Not enough room in dst, add NUL and traverse rest of src. */
-	if (nleft == 0) {
-		if (dsize != 0)
-			*dst = '\0';		/* NUL-terminate dst */
-		while (*src++)
-			;
-	}
+  /* Not enough room in dst, add NUL and traverse rest of src. */
+  if (nleft == 0) {
+    if (dsize != 0)
+      *dst = '\0';    /* NUL-terminate dst */
+    while (*src++)
+      ;
+  }
 
-	return(src - osrc - 1);	/* count does not include NUL */
+  return(src - osrc - 1);  /* count does not include NUL */
 }
diff --git a/style.css b/style.css
@@ -1,74 +1,75 @@
 body {
-	color: #E3E0D7;
-	background-color: #242424;
-	font-family: monospace;
-	-webkit-text-size-adjust: none;
+  color: #E3E0D7;
+  background-color: #242424;
+  font-family: monospace;
+  -webkit-text-size-adjust: none;
 }
 
 h1, h2, h3, h4, h5, h6 {
-	font-size: 1em;
-	margin: 0;
+  font-size: 1em;
+  margin: 0;
 }
 
 img, h1, h2 {
-	vertical-align: middle;
+  vertical-align: middle;
 }
 
 img {
-	border: 0;
+  border: 0;
 }
 
 a {
-	color: #8dbfff;
+  color: #8dbfff;
 }
 
 min, a:hover {
-	color: #F9690E;
+  color: #F9690E;
 }
 
 a:target, tr:target {
-	background-color: #303030;
+  outline: 0.03em solid grey;
+  background-color: #303030;
 }
 
 a.d,
 a.h,
 a.i,
 a.line {
-	text-decoration: none;
+  text-decoration: none;
 }
 
 #blob a {
-	color: #777;
+  color: #777;
 }
 
 #blob a:hover {
-	color: #F9690E;
-	background-color: #303030;
-	text-decoration: none;
+  color: #F9690E;
+  background-color: #303030;
+  text-decoration: none;
 }
 
 table th {
-	font-weight: bold;
-	text-align: left;
+  font-weight: bold;
+  text-align: left;
 }
 @media screen and (min-width: 600px) {
-	table:not(#branches) th {
-		text-align: center;
-	}
+  table:not(#branches) th {
+    text-align: center;
+  }
 }
 
 table td, table th {
-	padding: 0 0.4em;
+  padding: 0 0.4em;
 }
 
 #blob, div#table-scroll, div#pre-scroll {
-	overflow: scroll;
-	padding-bottom: 3em;
+  overflow: scroll;
+  padding-bottom: 3em;
 }
 
 #content table td, #content table th {
-	vertical-align: top;
-	white-space: nowrap;
+  vertical-align: top;
+  white-space: nowrap;
 }
 
 #branches tr:hover td,
@@ -76,54 +77,54 @@ table td, table th {
 #index tr:hover td,
 #log tr:hover td,
 #files tr:hover td {
-	background-color: #303030;
+  background-color: #303030;
 }
 
 #index tr td:nth-child(2),
 #tags tr td:nth-child(3),
 #branches tr td:nth-child(3),
 #log tr td:nth-child(2) {
-	white-space: normal;
+  white-space: normal;
 }
 
 th.num, td.num {
-	text-align: right;
+  text-align: right;
 }
 
 .desc {
-	color: #777;
+  color: #777;
 }
 
 hr {
-	border: 0;
-	border-top: 1px solid #777;
-	height: 1px;
+  border: 0;
+  border-top: 1px solid #777;
+  height: 1px;
 }
 
 pre {
-	font-family: monospace;
+  font-family: monospace;
 }
 
 pre a.h {
-	color: #8DBFFF;
+  color: #8DBFFF;
 }
 
 .A,
 #plus,
 span.i,
 pre a.i {
-	color: #79D688;
+  color: #79D688;
 }
 
 .D,
 #min,
 span.d,
 pre a.d {
-	color: #F9690E;
+  color: #F9690E;
 }
 
 pre a.h:hover,
 pre a.i:hover,
 pre a.d:hover {
-	text-decoration: none;
+  text-decoration: none;
 }