stagit

Updated README

Arjun Choudhary contact@arjunchoudhary.com

commit: a3b214d parent: 780a44d
13 files changed, 2628 insertions(+), 1804 deletions(-)
MLICENSE+1-1
MMakefile+5-3
MREADME.md+14-10
Mexample_create.sh+4-3
Mfavicon.png+0-0
Aindex-style.css+3-0
Dlogo.png+0-0
Ame.webp+0-0
Apost-receive+74-0
Dprofile_img.png+0-0
Mstagit-index.c+206-203
Mstagit.c+1391-1307
Mstyle.css+930-277
M · LICENSE +1, -1
1@@ -1,7 +1,7 @@
2 MIT/X Consortium License
3 
4 (c) 2015-2022 Hiltjo Posthuma <hiltjo@codemadness.org>
5-(c) 2022-2023 Arjun Choudhary <contact@arjunchoudhary.com>
6+(c) 2022-2023 Arjun Choudhary <contact@arjun.lol>
7 
8 Permission is hereby granted, free of charge, to any person obtaining a
9 copy of this software and associated documentation files (the "Software"),
M · Makefile +5, -3
 1@@ -66,7 +66,7 @@ dist:
 2 	rm -rf ${NAME}-${VERSION}
 3 	mkdir -p ${NAME}-${VERSION}
 4 	cp -f ${MAN1} ${HDR} ${SRC} ${COMPATSRC} ${DOC} \
 5-		Makefile favicon.png logo.png style.css \
 6+		Makefile favicon.png me.webp style.css \
 7 		example_create.sh example_post-receive.sh \
 8 		${NAME}-${VERSION}
 9 	# make tarball
10@@ -93,8 +93,9 @@ install: all
11 	# installing example files.
12 	mkdir -p ${DESTDIR}${DOCPREFIX}
13 	cp -f style.css\
14+		index-style.css\
15 		favicon.png\
16-		logo.png\
17+		me.webp\
18 		example_create.sh\
19 		example_post-receive.sh\
20 		README.md\
21@@ -110,8 +111,9 @@ uninstall:
22 	# removing example files.
23 	rm -f \
24 		${DESTDIR}${DOCPREFIX}/style.css\
25+		${DESTDIR}${DOCPREFIX}/index-style.css\
26 		${DESTDIR}${DOCPREFIX}/favicon.png\
27-		${DESTDIR}${DOCPREFIX}/logo.png\
28+		${DESTDIR}${DOCPREFIX}/me.webp\
29 		${DESTDIR}${DOCPREFIX}/example_create.sh\
30 		${DESTDIR}${DOCPREFIX}/example_post-receive.sh\
31 		${DESTDIR}${DOCPREFIX}/README
M · README.md +14, -10
 1@@ -1,25 +1,29 @@
 2-# stagit
 3-
 4-static git page generator.
 5+Static git page generator.
 6 
 7 It generates static HTML pages for a git repository.
 8 
 9 ## Changes
10 This is my personal fork of stagit.    
11 It has been modified to fit my needs and some refs points
12-to cli apps on my web-server.
13+to cli apps on my web-server. Feel free to reach out and ask for help if 
14+you are stuck on some change.
15+
16+* Added functionality to view raw files
17+* Preserves the structure of directories in the files view
18+* Syntax highlighting using (chroma)[https://github.com/alecthomas/chroma] (pygments and linguist for both super slow and had other issues)
19+* The index page now links to the README.md file - hard dependency that the file needs to exist in the repo and be a valid .md file
20+* (md4c)[https://github.com/mity/md4c] has been added to parse markdown files
21+* Add classes and ids for better style targetting
22+* Draws elements (like the sidebar for example) to match the hugo site on the main domain
23+* Styling changes to improve responsive-ness and add personalized css 
24 
25-* Title of files while viewing is now a link to download the file
26-* The index page now links to the repository directory
27-* md4c has been added to parse markdown files
28-* Added classes and ids to some html tags to easier style them
29-* Changed style.css to a modern looking nord theme as well as markdown styling
30+Changes such as adding md4c and chroma were done while trying to preserve the speed of generating the static files.
31 
32 The link to download the file points to `/raw/?repository=&file=` 
33 where file is the full path to the file relative to the repository. 
34 It is also possible to specify which branch to fetch the file from.
35 
36-Make necessary changes to the web-server to serve the file    
37+Make necessary changes to the web-server to serve the file
38 
39 To view a file in a bare repository:
40 
M · example_create.sh +4, -3
 1@@ -16,7 +16,7 @@
 2 # - sh example_create.sh
 3 
 4 # path must be absolute.
 5-reposdir="/var/www/domains/git.codemadness.nl/home/src"
 6+reposdir="/var/www/git/"
 7 curdir="$(pwd)"
 8 
 9 # make index.
10@@ -31,13 +31,14 @@ for dir in "${reposdir}/"*/; do
11 
12 	mkdir -p "${curdir}/${d}"
13 	cd "${curdir}/${d}" || continue
14-	stagit -c ".cache" -u "https://git.codemadness.nl/$d/" "${reposdir}/${r}"
15+	stagit -c ".cache" -u "https://git.arjun.lol/$d/" "${reposdir}/${r}"
16 
17 	# symlinks
18 	ln -sf log.html index.html
19 	ln -sf ../style.css style.css
20-	ln -sf ../logo.png logo.png
21+	ln -sf ../me.webp me.webp
22 	ln -sf ../favicon.png favicon.png
23+	ln -sf ../style.css
24 
25 	echo "done"
26 done
M · favicon.png +0, -0
A · index-style.css +3, -0
1@@ -0,0 +1,3 @@
2+#repositories:before {
3+content: "🚀 ";
4+}
D · logo.png +0, -0
A · me.webp +0, -0
A · post-receive +74, -0
 1@@ -0,0 +1,74 @@
 2+#!/bin/sh
 3+# generic git post-receive hook.
 4+# change the config options below and call this script in your post-receive
 5+# hook or symlink it.
 6+#
 7+# usage: $0 [name]
 8+#
 9+# if name is not set the basename of the current directory is used,
10+# this is the directory of the repo when called from the post-receive script.
11+
12+# NOTE: needs to be set for correct locale (expects UTF-8) otherwise the
13+#       default is LC_CTYPE="POSIX".
14+export LC_CTYPE="en_US.UTF-8"
15+
16+name="$1"
17+if test "${name}" = ""; then
18+	name=$(basename "$(pwd)")
19+fi
20+
21+# config
22+# paths must be absolute.
23+reposdir="/var/www/git/"
24+dir="${reposdir}/${name}"
25+destdir="/var/www/git/"
26+cachefile=".cache"
27+# /config
28+
29+if ! test -d "${dir}"; then
30+	echo "${dir} does not exist" >&2
31+	exit 1
32+fi
33+cd "${dir}" || exit 1
34+
35+# detect git push -f
36+force=0
37+while read -r old new ref; do
38+	test "${old}" = "0000000000000000000000000000000000000000" && continue
39+	test "${new}" = "0000000000000000000000000000000000000000" && continue
40+
41+	hasrevs=$(git rev-list "${old}" "^${new}" | sed 1q)
42+	if test -n "${hasrevs}"; then
43+		force=1
44+		break
45+	fi
46+done
47+
48+# strip .git suffix.
49+r=$(basename "${name}")
50+d=$(basename "${name}" ".git")
51+printf "[%s] stagit HTML pages... " "${d}"
52+
53+mkdir -p "${destdir}/${d}"
54+cd "${destdir}/${d}" || exit 1
55+
56+# remove commits and ${cachefile} on git push -f, this recreated later on.
57+if test "${force}" = "1"; then
58+	rm -f "${cachefile}"
59+	rm -rf "commit"
60+fi
61+
62+# make index.
63+stagit-index "${reposdir}/"*/ > "${reposdir}/index.html"
64+
65+# make pages.
66+stagit -c "${cachefile}" -u "https://git.arjun.lol/$d/" "${reposdir}/${r}"
67+
68+# symlinks
69+ln -sf log.html index.html
70+ln -sf ../style.css style.css
71+ln -sf ../me.webp me.webp
72+ln -sf ../favicon.png favicon.png
73+
74+echo "done"
75+
D · profile_img.png +0, -0
M · stagit-index.c +206, -203
  1@@ -8,247 +8,250 @@
  2 
  3 #include <git2.h>
  4 
  5-static git_repository *repo;
  6+static git_repository* repo;
  7 
  8-static const char *relpath = "";
  9+static const char* relpath = "";
 10 
 11 static char description[255] = "Repositories";
 12-static char *name = "";
 13+static char* name = "";
 14 static char owner[255];
 15 
 16 /* Handle read or write errors for a FILE * stream */
 17-void
 18-checkfileerror(FILE *fp, const char *name, int mode)
 19+void checkfileerror(FILE* fp, const char* name, int mode)
 20 {
 21-	if (mode == 'r' && ferror(fp))
 22-		errx(1, "read error: %s", name);
 23-	else if (mode == 'w' && (fflush(fp) || ferror(fp)))
 24-		errx(1, "write error: %s", name);
 25+    if (mode == 'r' && ferror(fp))
 26+        errx(1, "read error: %s", name);
 27+    else if (mode == 'w' && (fflush(fp) || ferror(fp)))
 28+        errx(1, "write error: %s", name);
 29 }
 30 
 31-void
 32-joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
 33+void joinpath(char* buf, size_t bufsiz, const char* path, const char* path2)
 34 {
 35-	int r;
 36+    int r;
 37 
 38-	r = snprintf(buf, bufsiz, "%s%s%s",
 39-		path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 40-	if (r < 0 || (size_t)r >= bufsiz)
 41-		errx(1, "path truncated: '%s%s%s'",
 42-			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 43+    r = snprintf(buf, bufsiz, "%s%s%s",
 44+        path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 45+    if (r < 0 || (size_t)r >= bufsiz)
 46+        errx(1, "path truncated: '%s%s%s'",
 47+            path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 48 }
 49 
 50 /* Percent-encode, see RFC3986 section 2.1. */
 51-void
 52-percentencode(FILE *fp, const char *s, size_t len)
 53+void percentencode(FILE* fp, const char* s, size_t len)
 54 {
 55-	static char tab[] = "0123456789ABCDEF";
 56-	unsigned char uc;
 57-	size_t i;
 58-
 59-	for (i = 0; *s && i < len; s++, i++) {
 60-		uc = *s;
 61-		/* NOTE: do not encode '/' for paths or ",-." */
 62-		if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') ||
 63-		    uc == '[' || uc == ']') {
 64-			putc('%', fp);
 65-			putc(tab[(uc >> 4) & 0x0f], fp);
 66-			putc(tab[uc & 0x0f], fp);
 67-		} else {
 68-			putc(uc, fp);
 69-		}
 70-	}
 71+    static char tab[] = "0123456789ABCDEF";
 72+    unsigned char uc;
 73+    size_t i;
 74+
 75+    for (i = 0; *s && i < len; s++, i++) {
 76+        uc = *s;
 77+        /* NOTE: do not encode '/' for paths or ",-." */
 78+        if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') || uc == '[' || uc == ']') {
 79+            putc('%', fp);
 80+            putc(tab[(uc >> 4) & 0x0f], fp);
 81+            putc(tab[uc & 0x0f], fp);
 82+        } else {
 83+            putc(uc, fp);
 84+        }
 85+    }
 86 }
 87 
 88 /* Escape characters below as HTML 2.0 / XML 1.0. */
 89-void
 90-xmlencode(FILE *fp, const char *s, size_t len)
 91+void xmlencode(FILE* fp, const char* s, size_t len)
 92 {
 93-	size_t i;
 94-
 95-	for (i = 0; *s && i < len; s++, i++) {
 96-		switch(*s) {
 97-		case '<':  fputs("&lt;",   fp); break;
 98-		case '>':  fputs("&gt;",   fp); break;
 99-		case '\'': fputs("&#39;" , fp); break;
100-		case '&':  fputs("&amp;",  fp); break;
101-		case '"':  fputs("&quot;", fp); break;
102-		default:   putc(*s, fp);
103-		}
104-	}
105+    size_t i;
106+
107+    for (i = 0; *s && i < len; s++, i++) {
108+        switch (*s) {
109+        case '<':
110+            fputs("&lt;", fp);
111+            break;
112+        case '>':
113+            fputs("&gt;", fp);
114+            break;
115+        case '\'':
116+            fputs("&#39;", fp);
117+            break;
118+        case '&':
119+            fputs("&amp;", fp);
120+            break;
121+        case '"':
122+            fputs("&quot;", fp);
123+            break;
124+        default:
125+            putc(*s, fp);
126+        }
127+    }
128 }
129 
130-void
131-printtimeshort(FILE *fp, const git_time *intime)
132+void printtimeshort(FILE* fp, const git_time* intime)
133 {
134-	struct tm *intm;
135-	time_t t;
136-	char out[32];
137-
138-	t = (time_t)intime->time;
139-	if (!(intm = gmtime(&t)))
140-		return;
141-	strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
142-	fputs(out, fp);
143+    struct tm* intm;
144+    time_t t;
145+    char out[32];
146+
147+    t = (time_t)intime->time;
148+    if (!(intm = gmtime(&t)))
149+        return;
150+    strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
151+    fputs(out, fp);
152 }
153 
154-void
155-writeheader(FILE *fp)
156+void writeheader(FILE* fp)
157 {
158-	fputs("<!DOCTYPE html>\n"
159-		"<html>\n<head>\n"
160-		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
161-		"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
162-		"<title>", fp);
163-	xmlencode(fp, description, strlen(description));
164-	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
165-	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
166-	fputs("</head>\n<body>\n<div id=\"container\">\n<div id=\"sidebar\">\n<a id=\"logo\" href=\"https://git.arjunchoudhary.com\">\n<div id=\"typing\">ARJUN</div>\n<hr id=\"cursor\"/>\n</a>\n<img id=\"profile_img\" src=\"profile_img.png\" alt=\"my_profile_image\"/>\n<a id=\"contact\" href=\"mailto:contact@arjunchoudhary.com\">Contact</a>\n</div>\n<div id=\"main-view\">\n", fp);
167-	fprintf(fp, "<h1 id=\"repositories\">");
168-	xmlencode(fp, description, strlen(description));
169-	fputs("</h1></td></tr>\n</table>\n<div id=\"content\">\n"
170-		"<table id=\"index\"><thead>\n"
171-		"<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>"
172-		"<td><b>Last commit</b></td></tr>"
173-		"</thead><tbody>\n", fp);
174+    fputs("<!DOCTYPE html>\n"
175+          "<html>\n<head>\n"
176+          "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
177+          "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
178+          "<title>",
179+        fp);
180+    xmlencode(fp, description, strlen(description));
181+    fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
182+    fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
183+    fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sindex-style.css\" />\n", relpath);
184+    fputs("</head>\n<body>\n<div id=\"container\">\n<div id=\"sidebar\">\n<a id=\"logo\" href=\"https://arjun.lol\">\n<div id=\"typing\">ARJUN</div>\n<hr id=\"cursor\"/>\n</a>\n<img id=\"profile_img\" src=\"me.webp\" alt=\"my_profile_image\"/>\n<div><a id=\"contact\" href=\"mailto:contact@arjunchoudhary.com\">Contact</a> | <a id=\"contact\" href=\"https://arjun.lol/public_pgp\">PGP</a>\n</div></div>\n<div id=\"main-view\">\n", fp);
185+    fprintf(fp, "<div id=\"header\"><h1 id=\"repositories\">");
186+    xmlencode(fp, description, strlen(description));
187+    fputs("</h1><div id=\"subtitle\"><span class=\"desc\"> Somewhat sorted collection of some of my projects. | </span><a href=\"https://arjun.lol\">Back</a></div></div></td></tr>\n</table>\n<div id=\"content\">\n"
188+          "<table id=\"index\"><thead>\n"
189+          "<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>"
190+          "<td><b>Last commit</b></td></tr>"
191+          "</thead><tbody>\n",
192+        fp);
193 }
194 
195-void
196-writefooter(FILE *fp)
197+void writefooter(FILE* fp)
198 {
199-	fputs("</tbody>\n</table>\n</div>\n</div>\n</div>\n</body>\n</html>\n", fp);
200+    fputs("</tbody>\n</table>\n</div>\n</div>\n</div>\n</body>\n</html>\n", fp);
201 }
202 
203-int
204-writelog(FILE *fp)
205+int writelog(FILE* fp)
206 {
207-	git_commit *commit = NULL;
208-	const git_signature *author;
209-	git_revwalk *w = NULL;
210-	git_oid id;
211-	char *stripped_name = NULL, *p;
212-	int ret = 0;
213-
214-	git_revwalk_new(&w, repo);
215-	git_revwalk_push_head(w);
216-
217-	if (git_revwalk_next(&id, w) ||
218-	    git_commit_lookup(&commit, repo, &id)) {
219-		ret = -1;
220-		goto err;
221-	}
222-
223-	author = git_commit_author(commit);
224-
225-	/* strip .git suffix */
226-	if (!(stripped_name = strdup(name)))
227-		err(1, "strdup");
228-	if ((p = strrchr(stripped_name, '.')))
229-		if (!strcmp(p, ".git"))
230-			*p = '\0';
231-
232-	fputs("<tr><td><a href=\"", fp);
233-	percentencode(fp, stripped_name, strlen(stripped_name));
234-	fputs("/file/README.md.html\">", fp);
235-	xmlencode(fp, stripped_name, strlen(stripped_name));
236-	fputs("</a></td><td>", fp);
237-	xmlencode(fp, description, strlen(description));
238-	fputs("</td><td>", fp);
239-	xmlencode(fp, owner, strlen(owner));
240-	fputs("</td><td>", fp);
241-	if (author)
242-		printtimeshort(fp, &(author->when));
243-	fputs("</td></tr>", fp);
244-
245-	git_commit_free(commit);
246+    git_commit* commit = NULL;
247+    const git_signature* author;
248+    git_revwalk* w = NULL;
249+    git_oid id;
250+    char *stripped_name = NULL, *p;
251+    int ret = 0;
252+
253+    git_revwalk_new(&w, repo);
254+    git_revwalk_push_head(w);
255+
256+    if (git_revwalk_next(&id, w) || git_commit_lookup(&commit, repo, &id)) {
257+        ret = -1;
258+        goto err;
259+    }
260+
261+    author = git_commit_author(commit);
262+
263+    /* strip .git suffix */
264+    if (!(stripped_name = strdup(name)))
265+        err(1, "strdup");
266+    if ((p = strrchr(stripped_name, '.')))
267+        if (!strcmp(p, ".git"))
268+            *p = '\0';
269+
270+    fputs("<tr><td><a href=\"", fp);
271+    percentencode(fp, stripped_name, strlen(stripped_name));
272+    fputs("/file/README.md.html\">", fp);
273+    xmlencode(fp, stripped_name, strlen(stripped_name));
274+    fputs("</a></td><td>", fp);
275+    xmlencode(fp, description, strlen(description));
276+    fputs("</td><td>", fp);
277+    xmlencode(fp, owner, strlen(owner));
278+    fputs("</td><td>", fp);
279+    if (author)
280+        printtimeshort(fp, &(author->when));
281+    fputs("</td></tr>", fp);
282+
283+    git_commit_free(commit);
284 err:
285-	git_revwalk_free(w);
286-	free(stripped_name);
287+    git_revwalk_free(w);
288+    free(stripped_name);
289 
290-	return ret;
291+    return ret;
292 }
293 
294-int
295-main(int argc, char *argv[])
296+int main(int argc, char* argv[])
297 {
298-	FILE *fp;
299-	char path[PATH_MAX], repodirabs[PATH_MAX + 1];
300-	const char *repodir;
301-	int i, ret = 0;
302-
303-	if (argc < 2) {
304-		fprintf(stderr, "%s [repodir...]\n", argv[0]);
305-		return 1;
306-	}
307-
308-	/* do not search outside the git repository:
309-	   GIT_CONFIG_LEVEL_APP is the highest level currently */
310-	git_libgit2_init();
311-	for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++)
312-		git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "");
313+    FILE* fp;
314+    char path[PATH_MAX], repodirabs[PATH_MAX + 1];
315+    const char* repodir;
316+    int i, ret = 0;
317+
318+    if (argc < 2) {
319+        fprintf(stderr, "%s [repodir...]\n", argv[0]);
320+        return 1;
321+    }
322+
323+    /* do not search outside the git repository:
324+       GIT_CONFIG_LEVEL_APP is the highest level currently */
325+    git_libgit2_init();
326+    for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++)
327+        git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "");
328 
329 #ifdef __OpenBSD__
330-	if (pledge("stdio rpath", NULL) == -1)
331-		err(1, "pledge");
332+    if (pledge("stdio rpath", NULL) == -1)
333+        err(1, "pledge");
334 #endif
335 
336-	writeheader(stdout);
337-
338-	for (i = 1; i < argc; i++) {
339-		repodir = argv[i];
340-		if (!realpath(repodir, repodirabs))
341-			err(1, "realpath");
342-
343-		if (git_repository_open_ext(&repo, repodir,
344-		    GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
345-			fprintf(stderr, "%s: cannot open repository\n", argv[0]);
346-			ret = 1;
347-			continue;
348-		}
349-
350-		/* use directory name as name */
351-		if ((name = strrchr(repodirabs, '/')))
352-			name++;
353-		else
354-			name = "";
355-
356-		/* read description or .git/description */
357-		joinpath(path, sizeof(path), repodir, "description");
358-		if (!(fp = fopen(path, "r"))) {
359-			joinpath(path, sizeof(path), repodir, ".git/description");
360-			fp = fopen(path, "r");
361-		}
362-		description[0] = '\0';
363-		if (fp) {
364-			if (!fgets(description, sizeof(description), fp))
365-				description[0] = '\0';
366-			checkfileerror(fp, "description", 'r');
367-			fclose(fp);
368-		}
369-
370-		/* read owner or .git/owner */
371-		joinpath(path, sizeof(path), repodir, "owner");
372-		if (!(fp = fopen(path, "r"))) {
373-			joinpath(path, sizeof(path), repodir, ".git/owner");
374-			fp = fopen(path, "r");
375-		}
376-		owner[0] = '\0';
377-		if (fp) {
378-			if (!fgets(owner, sizeof(owner), fp))
379-				owner[0] = '\0';
380-			checkfileerror(fp, "owner", 'r');
381-			fclose(fp);
382-			owner[strcspn(owner, "\n")] = '\0';
383-		}
384-		writelog(stdout);
385-	}
386-	writefooter(stdout);
387-
388-	/* cleanup */
389-	git_repository_free(repo);
390-	git_libgit2_shutdown();
391-
392-	checkfileerror(stdout, "<stdout>", 'w');
393-
394-	return ret;
395+    writeheader(stdout);
396+
397+    for (i = 1; i < argc; i++) {
398+        repodir = argv[i];
399+        if (!realpath(repodir, repodirabs))
400+            err(1, "realpath");
401+
402+        if (git_repository_open_ext(&repo, repodir,
403+                GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
404+            fprintf(stderr, "%s: cannot open repository\n", argv[0]);
405+            ret = 1;
406+            continue;
407+        }
408+
409+        /* use directory name as name */
410+        if ((name = strrchr(repodirabs, '/')))
411+            name++;
412+        else
413+            name = "";
414+
415+        /* read description or .git/description */
416+        joinpath(path, sizeof(path), repodir, "description");
417+        if (!(fp = fopen(path, "r"))) {
418+            joinpath(path, sizeof(path), repodir, ".git/description");
419+            fp = fopen(path, "r");
420+        }
421+        description[0] = '\0';
422+        if (fp) {
423+            if (!fgets(description, sizeof(description), fp))
424+                description[0] = '\0';
425+            checkfileerror(fp, "description", 'r');
426+            fclose(fp);
427+        }
428+
429+        /* read owner or .git/owner */
430+        joinpath(path, sizeof(path), repodir, "owner");
431+        if (!(fp = fopen(path, "r"))) {
432+            joinpath(path, sizeof(path), repodir, ".git/owner");
433+            fp = fopen(path, "r");
434+        }
435+        owner[0] = '\0';
436+        if (fp) {
437+            if (!fgets(owner, sizeof(owner), fp))
438+                owner[0] = '\0';
439+            checkfileerror(fp, "owner", 'r');
440+            fclose(fp);
441+            owner[strcspn(owner, "\n")] = '\0';
442+        }
443+        writelog(stdout);
444+    }
445+    writefooter(stdout);
446+
447+    /* cleanup */
448+    git_repository_free(repo);
449+    git_libgit2_shutdown();
450+
451+    checkfileerror(stdout, "<stdout>", 'w');
452+
453+    return ret;
454 }
M · stagit.c +1391, -1307
   1@@ -5,8 +5,8 @@
   2 #include <errno.h>
   3 #include <libgen.h>
   4 #include <limits.h>
   5-#include <stdint.h>
   6 #include <stdbool.h>
   7+#include <stdint.h>
   8 #include <stdio.h>
   9 #include <stdlib.h>
  10 #include <string.h>
  11@@ -19,562 +19,564 @@
  12 
  13 #include "compat.h"
  14 
  15-#define LEN(s)    (sizeof(s)/sizeof(*s))
  16+#define LEN(s) (sizeof(s) / sizeof(*s))
  17 
  18 struct deltainfo {
  19-	git_patch *patch;
  20+    git_patch* patch;
  21 
  22-	size_t addcount;
  23-	size_t delcount;
  24+    size_t addcount;
  25+    size_t delcount;
  26 };
  27 
  28 struct commitinfo {
  29-	const git_oid *id;
  30+    const git_oid* id;
  31 
  32-	char oid[GIT_OID_HEXSZ + 1];
  33-	char parentoid[GIT_OID_HEXSZ + 1];
  34+    char oid[GIT_OID_HEXSZ + 1];
  35+    char parentoid[GIT_OID_HEXSZ + 1];
  36 
  37-	const git_signature *author;
  38-	const git_signature *committer;
  39-	const char          *summary;
  40-	const char          *msg;
  41+    const git_signature* author;
  42+    const git_signature* committer;
  43+    const char* summary;
  44+    const char* msg;
  45 
  46-	git_diff   *diff;
  47-	git_commit *commit;
  48-	git_commit *parent;
  49-	git_tree   *commit_tree;
  50-	git_tree   *parent_tree;
  51+    git_diff* diff;
  52+    git_commit* commit;
  53+    git_commit* parent;
  54+    git_tree* commit_tree;
  55+    git_tree* parent_tree;
  56 
  57-	size_t addcount;
  58-	size_t delcount;
  59-	size_t filecount;
  60+    size_t addcount;
  61+    size_t delcount;
  62+    size_t filecount;
  63 
  64-	struct deltainfo **deltas;
  65-	size_t ndeltas;
  66+    struct deltainfo** deltas;
  67+    size_t ndeltas;
  68 };
  69 
  70 /* reference and associated data for sorting */
  71 struct referenceinfo {
  72-	struct git_reference *ref;
  73-	struct commitinfo *ci;
  74+    struct git_reference* ref;
  75+    struct commitinfo* ci;
  76 };
  77 
  78-static git_repository *repo;
  79+static git_repository* repo;
  80 
  81-static const char *baseurl = ""; /* base URL to make absolute RSS/Atom URI */
  82-static const char *relpath = "";
  83-static const char *repodir;
  84+static const char* baseurl = ""; /* base URL to make absolute RSS/Atom URI */
  85+static const char* relpath = "";
  86+static const char* repodir;
  87 
  88-static char *name = "";
  89-static char *strippedname = "";
  90+static char* name = "";
  91+static char* strippedname = "";
  92 static char description[255];
  93 static char cloneurl[1024];
  94-static char *submodules;
  95-static char *licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:COPYING" };
  96-static char *license;
  97-static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" };
  98-static char *readme;
  99+static char* submodules;
 100+static char* licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:COPYING" };
 101+static char* license;
 102+static char* readmefiles[] = { "HEAD:README", "HEAD:README.md" };
 103+static char* readme;
 104 static long long nlogcommits = -1; /* -1 indicates not used */
 105 
 106 bool htmlized; /* true if markdoown converted to HTML */
 107 
 108-
 109 /* cache */
 110 static git_oid lastoid;
 111 static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */
 112 static FILE *rcachefp, *wcachefp;
 113-static const char *cachefile;
 114+static const char* cachefile;
 115 
 116 /* Handle read or write errors for a FILE * stream */
 117-void
 118-checkfileerror(FILE *fp, const char *name, int mode)
 119+void checkfileerror(FILE* fp, const char* name, int mode)
 120 {
 121-	if (mode == 'r' && ferror(fp))
 122-		errx(1, "read error: %s", name);
 123-	else if (mode == 'w' && (fflush(fp) || ferror(fp)))
 124-		errx(1, "write error: %s", name);
 125+    if (mode == 'r' && ferror(fp))
 126+        errx(1, "read error: %s", name);
 127+    else if (mode == 'w' && (fflush(fp) || ferror(fp)))
 128+        errx(1, "write error: %s", name);
 129 }
 130 
 131-void
 132-joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
 133+void joinpath(char* buf, size_t bufsiz, const char* path, const char* path2)
 134 {
 135-	int r;
 136+    int r;
 137 
 138-	r = snprintf(buf, bufsiz, "%s%s%s",
 139-		path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 140-	if (r < 0 || (size_t)r >= bufsiz)
 141-		errx(1, "path truncated: '%s%s%s'",
 142-			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 143+    r = snprintf(buf, bufsiz, "%s%s%s",
 144+        path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 145+    if (r < 0 || (size_t)r >= bufsiz)
 146+        errx(1, "path truncated: '%s%s%s'",
 147+            path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 148 }
 149 
 150-void
 151-deltainfo_free(struct deltainfo *di)
 152+void deltainfo_free(struct deltainfo* di)
 153 {
 154-	if (!di)
 155-		return;
 156-	git_patch_free(di->patch);
 157-	memset(di, 0, sizeof(*di));
 158-	free(di);
 159+    if (!di)
 160+        return;
 161+    git_patch_free(di->patch);
 162+    memset(di, 0, sizeof(*di));
 163+    free(di);
 164 }
 165 
 166-int
 167-commitinfo_getstats(struct commitinfo *ci)
 168+int commitinfo_getstats(struct commitinfo* ci)
 169 {
 170-	struct deltainfo *di;
 171-	git_diff_options opts;
 172-	git_diff_find_options fopts;
 173-	const git_diff_delta *delta;
 174-	const git_diff_hunk *hunk;
 175-	const git_diff_line *line;
 176-	git_patch *patch = NULL;
 177-	size_t ndeltas, nhunks, nhunklines;
 178-	size_t i, j, k;
 179-
 180-	if (git_tree_lookup(&(ci->commit_tree), repo, git_commit_tree_id(ci->commit)))
 181-		goto err;
 182-	if (!git_commit_parent(&(ci->parent), ci->commit, 0)) {
 183-		if (git_tree_lookup(&(ci->parent_tree), repo, git_commit_tree_id(ci->parent))) {
 184-			ci->parent = NULL;
 185-			ci->parent_tree = NULL;
 186-		}
 187-	}
 188-
 189-	git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
 190-	opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH |
 191-	              GIT_DIFF_IGNORE_SUBMODULES |
 192-		      GIT_DIFF_INCLUDE_TYPECHANGE;
 193-	if (git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts))
 194-		goto err;
 195-
 196-	if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
 197-		goto err;
 198-	/* find renames and copies, exact matches (no heuristic) for renames. */
 199-	fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
 200-	               GIT_DIFF_FIND_EXACT_MATCH_ONLY;
 201-	if (git_diff_find_similar(ci->diff, &fopts))
 202-		goto err;
 203-
 204-	ndeltas = git_diff_num_deltas(ci->diff);
 205-	if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
 206-		err(1, "calloc");
 207-
 208-	for (i = 0; i < ndeltas; i++) {
 209-		if (git_patch_from_diff(&patch, ci->diff, i))
 210-			goto err;
 211-
 212-		if (!(di = calloc(1, sizeof(struct deltainfo))))
 213-			err(1, "calloc");
 214-		di->patch = patch;
 215-		ci->deltas[i] = di;
 216-
 217-		delta = git_patch_get_delta(patch);
 218-
 219-		/* skip stats for binary data */
 220-		if (delta->flags & GIT_DIFF_FLAG_BINARY)
 221-			continue;
 222-
 223-		nhunks = git_patch_num_hunks(patch);
 224-		for (j = 0; j < nhunks; j++) {
 225-			if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
 226-				break;
 227-			for (k = 0; ; k++) {
 228-				if (git_patch_get_line_in_hunk(&line, patch, j, k))
 229-					break;
 230-				if (line->old_lineno == -1) {
 231-					di->addcount++;
 232-					ci->addcount++;
 233-				} else if (line->new_lineno == -1) {
 234-					di->delcount++;
 235-					ci->delcount++;
 236-				}
 237-			}
 238-		}
 239-	}
 240-	ci->ndeltas = i;
 241-	ci->filecount = i;
 242-
 243-	return 0;
 244+    struct deltainfo* di;
 245+    git_diff_options opts;
 246+    git_diff_find_options fopts;
 247+    const git_diff_delta* delta;
 248+    const git_diff_hunk* hunk;
 249+    const git_diff_line* line;
 250+    git_patch* patch = NULL;
 251+    size_t ndeltas, nhunks, nhunklines;
 252+    size_t i, j, k;
 253+
 254+    if (git_tree_lookup(&(ci->commit_tree), repo, git_commit_tree_id(ci->commit)))
 255+        goto err;
 256+    if (!git_commit_parent(&(ci->parent), ci->commit, 0)) {
 257+        if (git_tree_lookup(&(ci->parent_tree), repo, git_commit_tree_id(ci->parent))) {
 258+            ci->parent = NULL;
 259+            ci->parent_tree = NULL;
 260+        }
 261+    }
 262+
 263+    git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
 264+    opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_TYPECHANGE;
 265+    if (git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts))
 266+        goto err;
 267+
 268+    if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
 269+        goto err;
 270+    /* find renames and copies, exact matches (no heuristic) for renames. */
 271+    fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
 272+    if (git_diff_find_similar(ci->diff, &fopts))
 273+        goto err;
 274+
 275+    ndeltas = git_diff_num_deltas(ci->diff);
 276+    if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo*))))
 277+        err(1, "calloc");
 278+
 279+    for (i = 0; i < ndeltas; i++) {
 280+        if (git_patch_from_diff(&patch, ci->diff, i))
 281+            goto err;
 282+
 283+        if (!(di = calloc(1, sizeof(struct deltainfo))))
 284+            err(1, "calloc");
 285+        di->patch = patch;
 286+        ci->deltas[i] = di;
 287+
 288+        delta = git_patch_get_delta(patch);
 289+
 290+        /* skip stats for binary data */
 291+        if (delta->flags & GIT_DIFF_FLAG_BINARY)
 292+            continue;
 293+
 294+        nhunks = git_patch_num_hunks(patch);
 295+        for (j = 0; j < nhunks; j++) {
 296+            if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
 297+                break;
 298+            for (k = 0;; k++) {
 299+                if (git_patch_get_line_in_hunk(&line, patch, j, k))
 300+                    break;
 301+                if (line->old_lineno == -1) {
 302+                    di->addcount++;
 303+                    ci->addcount++;
 304+                } else if (line->new_lineno == -1) {
 305+                    di->delcount++;
 306+                    ci->delcount++;
 307+                }
 308+            }
 309+        }
 310+    }
 311+    ci->ndeltas = i;
 312+    ci->filecount = i;
 313+
 314+    return 0;
 315 
 316 err:
 317-	git_diff_free(ci->diff);
 318-	ci->diff = NULL;
 319-	git_tree_free(ci->commit_tree);
 320-	ci->commit_tree = NULL;
 321-	git_tree_free(ci->parent_tree);
 322-	ci->parent_tree = NULL;
 323-	git_commit_free(ci->parent);
 324-	ci->parent = NULL;
 325-
 326-	if (ci->deltas)
 327-		for (i = 0; i < ci->ndeltas; i++)
 328-			deltainfo_free(ci->deltas[i]);
 329-	free(ci->deltas);
 330-	ci->deltas = NULL;
 331-	ci->ndeltas = 0;
 332-	ci->addcount = 0;
 333-	ci->delcount = 0;
 334-	ci->filecount = 0;
 335-
 336-	return -1;
 337+    git_diff_free(ci->diff);
 338+    ci->diff = NULL;
 339+    git_tree_free(ci->commit_tree);
 340+    ci->commit_tree = NULL;
 341+    git_tree_free(ci->parent_tree);
 342+    ci->parent_tree = NULL;
 343+    git_commit_free(ci->parent);
 344+    ci->parent = NULL;
 345+
 346+    if (ci->deltas)
 347+        for (i = 0; i < ci->ndeltas; i++)
 348+            deltainfo_free(ci->deltas[i]);
 349+    free(ci->deltas);
 350+    ci->deltas = NULL;
 351+    ci->ndeltas = 0;
 352+    ci->addcount = 0;
 353+    ci->delcount = 0;
 354+    ci->filecount = 0;
 355+
 356+    return -1;
 357 }
 358 
 359-void
 360-commitinfo_free(struct commitinfo *ci)
 361+void commitinfo_free(struct commitinfo* ci)
 362 {
 363-	size_t i;
 364-
 365-	if (!ci)
 366-		return;
 367-	if (ci->deltas)
 368-		for (i = 0; i < ci->ndeltas; i++)
 369-			deltainfo_free(ci->deltas[i]);
 370-
 371-	free(ci->deltas);
 372-	git_diff_free(ci->diff);
 373-	git_tree_free(ci->commit_tree);
 374-	git_tree_free(ci->parent_tree);
 375-	git_commit_free(ci->commit);
 376-	git_commit_free(ci->parent);
 377-	memset(ci, 0, sizeof(*ci));
 378-	free(ci);
 379+    size_t i;
 380+
 381+    if (!ci)
 382+        return;
 383+    if (ci->deltas)
 384+        for (i = 0; i < ci->ndeltas; i++)
 385+            deltainfo_free(ci->deltas[i]);
 386+
 387+    free(ci->deltas);
 388+    git_diff_free(ci->diff);
 389+    git_tree_free(ci->commit_tree);
 390+    git_tree_free(ci->parent_tree);
 391+    git_commit_free(ci->commit);
 392+    git_commit_free(ci->parent);
 393+    memset(ci, 0, sizeof(*ci));
 394+    free(ci);
 395 }
 396 
 397-struct commitinfo *
 398-commitinfo_getbyoid(const git_oid *id)
 399+struct commitinfo*
 400+commitinfo_getbyoid(const git_oid* id)
 401 {
 402-	struct commitinfo *ci;
 403+    struct commitinfo* ci;
 404 
 405-	if (!(ci = calloc(1, sizeof(struct commitinfo))))
 406-		err(1, "calloc");
 407+    if (!(ci = calloc(1, sizeof(struct commitinfo))))
 408+        err(1, "calloc");
 409 
 410-	if (git_commit_lookup(&(ci->commit), repo, id))
 411-		goto err;
 412-	ci->id = id;
 413+    if (git_commit_lookup(&(ci->commit), repo, id))
 414+        goto err;
 415+    ci->id = id;
 416 
 417-	git_oid_tostr(ci->oid, sizeof(ci->oid), git_commit_id(ci->commit));
 418-	git_oid_tostr(ci->parentoid, sizeof(ci->parentoid), git_commit_parent_id(ci->commit, 0));
 419+    git_oid_tostr(ci->oid, sizeof(ci->oid), git_commit_id(ci->commit));
 420+    git_oid_tostr(ci->parentoid, sizeof(ci->parentoid), git_commit_parent_id(ci->commit, 0));
 421 
 422-	ci->author = git_commit_author(ci->commit);
 423-	ci->committer = git_commit_committer(ci->commit);
 424-	ci->summary = git_commit_summary(ci->commit);
 425-	ci->msg = git_commit_message(ci->commit);
 426+    ci->author = git_commit_author(ci->commit);
 427+    ci->committer = git_commit_committer(ci->commit);
 428+    ci->summary = git_commit_summary(ci->commit);
 429+    ci->msg = git_commit_message(ci->commit);
 430 
 431-	return ci;
 432+    return ci;
 433 
 434 err:
 435-	commitinfo_free(ci);
 436+    commitinfo_free(ci);
 437 
 438-	return NULL;
 439+    return NULL;
 440 }
 441 
 442-int
 443-refs_cmp(const void *v1, const void *v2)
 444+int refs_cmp(const void* v1, const void* v2)
 445 {
 446-	const struct referenceinfo *r1 = v1, *r2 = v2;
 447-	time_t t1, t2;
 448-	int r;
 449+    const struct referenceinfo *r1 = v1, *r2 = v2;
 450+    time_t t1, t2;
 451+    int r;
 452 
 453-	if ((r = git_reference_is_tag(r1->ref) - git_reference_is_tag(r2->ref)))
 454-		return r;
 455+    if ((r = git_reference_is_tag(r1->ref) - git_reference_is_tag(r2->ref)))
 456+        return r;
 457 
 458-	t1 = r1->ci->author ? r1->ci->author->when.time : 0;
 459-	t2 = r2->ci->author ? r2->ci->author->when.time : 0;
 460-	if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1)))
 461-		return r;
 462+    t1 = r1->ci->author ? r1->ci->author->when.time : 0;
 463+    t2 = r2->ci->author ? r2->ci->author->when.time : 0;
 464+    if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1)))
 465+        return r;
 466 
 467-	return strcmp(git_reference_shorthand(r1->ref),
 468-	              git_reference_shorthand(r2->ref));
 469+    return strcmp(git_reference_shorthand(r1->ref),
 470+        git_reference_shorthand(r2->ref));
 471 }
 472 
 473-int
 474-getrefs(struct referenceinfo **pris, size_t *prefcount)
 475+int getrefs(struct referenceinfo** pris, size_t* prefcount)
 476 {
 477-	struct referenceinfo *ris = NULL;
 478-	struct commitinfo *ci = NULL;
 479-	git_reference_iterator *it = NULL;
 480-	const git_oid *id = NULL;
 481-	git_object *obj = NULL;
 482-	git_reference *dref = NULL, *r, *ref = NULL;
 483-	size_t i, refcount;
 484-
 485-	*pris = NULL;
 486-	*prefcount = 0;
 487-
 488-	if (git_reference_iterator_new(&it, repo))
 489-		return -1;
 490-
 491-	for (refcount = 0; !git_reference_next(&ref, it); ) {
 492-		if (!git_reference_is_branch(ref) && !git_reference_is_tag(ref)) {
 493-			git_reference_free(ref);
 494-			ref = NULL;
 495-			continue;
 496-		}
 497-
 498-		switch (git_reference_type(ref)) {
 499-		case GIT_REF_SYMBOLIC:
 500-			if (git_reference_resolve(&dref, ref))
 501-				goto err;
 502-			r = dref;
 503-			break;
 504-		case GIT_REF_OID:
 505-			r = ref;
 506-			break;
 507-		default:
 508-			continue;
 509-		}
 510-		if (!git_reference_target(r) ||
 511-		    git_reference_peel(&obj, r, GIT_OBJ_ANY))
 512-			goto err;
 513-		if (!(id = git_object_id(obj)))
 514-			goto err;
 515-		if (!(ci = commitinfo_getbyoid(id)))
 516-			break;
 517-
 518-		if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
 519-			err(1, "realloc");
 520-		ris[refcount].ci = ci;
 521-		ris[refcount].ref = r;
 522-		refcount++;
 523-
 524-		git_object_free(obj);
 525-		obj = NULL;
 526-		git_reference_free(dref);
 527-		dref = NULL;
 528-	}
 529-	git_reference_iterator_free(it);
 530-
 531-	/* sort by type, date then shorthand name */
 532-	qsort(ris, refcount, sizeof(*ris), refs_cmp);
 533-
 534-	*pris = ris;
 535-	*prefcount = refcount;
 536-
 537-	return 0;
 538+    struct referenceinfo* ris = NULL;
 539+    struct commitinfo* ci = NULL;
 540+    git_reference_iterator* it = NULL;
 541+    const git_oid* id = NULL;
 542+    git_object* obj = NULL;
 543+    git_reference *dref = NULL, *r, *ref = NULL;
 544+    size_t i, refcount;
 545+
 546+    *pris = NULL;
 547+    *prefcount = 0;
 548+
 549+    if (git_reference_iterator_new(&it, repo))
 550+        return -1;
 551+
 552+    for (refcount = 0; !git_reference_next(&ref, it);) {
 553+        if (!git_reference_is_branch(ref) && !git_reference_is_tag(ref)) {
 554+            git_reference_free(ref);
 555+            ref = NULL;
 556+            continue;
 557+        }
 558+
 559+        switch (git_reference_type(ref)) {
 560+        case GIT_REF_SYMBOLIC:
 561+            if (git_reference_resolve(&dref, ref))
 562+                goto err;
 563+            r = dref;
 564+            break;
 565+        case GIT_REF_OID:
 566+            r = ref;
 567+            break;
 568+        default:
 569+            continue;
 570+        }
 571+        if (!git_reference_target(r) || git_reference_peel(&obj, r, GIT_OBJ_ANY))
 572+            goto err;
 573+        if (!(id = git_object_id(obj)))
 574+            goto err;
 575+        if (!(ci = commitinfo_getbyoid(id)))
 576+            break;
 577+
 578+        if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
 579+            err(1, "realloc");
 580+        ris[refcount].ci = ci;
 581+        ris[refcount].ref = r;
 582+        refcount++;
 583+
 584+        git_object_free(obj);
 585+        obj = NULL;
 586+        git_reference_free(dref);
 587+        dref = NULL;
 588+    }
 589+    git_reference_iterator_free(it);
 590+
 591+    /* sort by type, date then shorthand name */
 592+    qsort(ris, refcount, sizeof(*ris), refs_cmp);
 593+
 594+    *pris = ris;
 595+    *prefcount = refcount;
 596+
 597+    return 0;
 598 
 599 err:
 600-	git_object_free(obj);
 601-	git_reference_free(dref);
 602-	commitinfo_free(ci);
 603-	for (i = 0; i < refcount; i++) {
 604-		commitinfo_free(ris[i].ci);
 605-		git_reference_free(ris[i].ref);
 606-	}
 607-	free(ris);
 608-
 609-	return -1;
 610+    git_object_free(obj);
 611+    git_reference_free(dref);
 612+    commitinfo_free(ci);
 613+    for (i = 0; i < refcount; i++) {
 614+        commitinfo_free(ris[i].ci);
 615+        git_reference_free(ris[i].ref);
 616+    }
 617+    free(ris);
 618+
 619+    return -1;
 620 }
 621 
 622-FILE *
 623-efopen(const char *filename, const char *flags)
 624+FILE* efopen(const char* filename, const char* flags)
 625 {
 626-	FILE *fp;
 627+    FILE* fp;
 628 
 629-	if (!(fp = fopen(filename, flags)))
 630-		err(1, "fopen: '%s'", filename);
 631+    if (!(fp = fopen(filename, flags)))
 632+        err(1, "fopen: '%s'", filename);
 633 
 634-	return fp;
 635+    return fp;
 636 }
 637 
 638 /* Percent-encode, see RFC3986 section 2.1. */
 639-void
 640-percentencode(FILE *fp, const char *s, size_t len)
 641+void percentencode(FILE* fp, const char* s, size_t len)
 642 {
 643-	static char tab[] = "0123456789ABCDEF";
 644-	unsigned char uc;
 645-	size_t i;
 646-
 647-	for (i = 0; *s && i < len; s++, i++) {
 648-		uc = *s;
 649-		/* NOTE: do not encode '/' for paths or ",-." */
 650-		if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') ||
 651-		    uc == '[' || uc == ']') {
 652-			putc('%', fp);
 653-			putc(tab[(uc >> 4) & 0x0f], fp);
 654-			putc(tab[uc & 0x0f], fp);
 655-		} else {
 656-			putc(uc, fp);
 657-		}
 658-	}
 659+    static char tab[] = "0123456789ABCDEF";
 660+    unsigned char uc;
 661+    size_t i;
 662+
 663+    for (i = 0; *s && i < len; s++, i++) {
 664+        uc = *s;
 665+        /* NOTE: do not encode '/' for paths or ",-." */
 666+        if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') || uc == '[' || uc == ']') {
 667+            putc('%', fp);
 668+            putc(tab[(uc >> 4) & 0x0f], fp);
 669+            putc(tab[uc & 0x0f], fp);
 670+        } else {
 671+            putc(uc, fp);
 672+        }
 673+    }
 674 }
 675 
 676 /* Escape characters below as HTML 2.0 / XML 1.0. */
 677-void
 678-xmlencode(FILE *fp, const char *s, size_t len)
 679+void xmlencode(FILE* fp, const char* s, size_t len)
 680 {
 681-	size_t i;
 682-
 683-	for (i = 0; *s && i < len; s++, i++) {
 684-		switch(*s) {
 685-		case '<':  fputs("&lt;",   fp); break;
 686-		case '>':  fputs("&gt;",   fp); break;
 687-		case '\'': fputs("&#39;",  fp); break;
 688-		case '&':  fputs("&amp;",  fp); break;
 689-		case '"':  fputs("&quot;", fp); break;
 690-		default:   putc(*s, fp);
 691-		}
 692-	}
 693+    size_t i;
 694+
 695+    for (i = 0; *s && i < len; s++, i++) {
 696+        switch (*s) {
 697+        case '<':
 698+            fputs("&lt;", fp);
 699+            break;
 700+        case '>':
 701+            fputs("&gt;", fp);
 702+            break;
 703+        case '\'':
 704+            fputs("&#39;", fp);
 705+            break;
 706+        case '&':
 707+            fputs("&amp;", fp);
 708+            break;
 709+        case '"':
 710+            fputs("&quot;", fp);
 711+            break;
 712+        default:
 713+            putc(*s, fp);
 714+        }
 715+    }
 716 }
 717 
 718 /* Escape characters below as HTML 2.0 / XML 1.0, ignore printing '\r', '\n' */
 719-void
 720-xmlencodeline(FILE *fp, const char *s, size_t len)
 721+void xmlencodeline(FILE* fp, const char* s, size_t len)
 722 {
 723-	size_t i;
 724-
 725-	for (i = 0; *s && i < len; s++, i++) {
 726-		switch(*s) {
 727-		case '<':  fputs("&lt;",   fp); break;
 728-		case '>':  fputs("&gt;",   fp); break;
 729-		case '\'': fputs("&#39;",  fp); break;
 730-		case '&':  fputs("&amp;",  fp); break;
 731-		case '"':  fputs("&quot;", fp); break;
 732-		case '\r': break; /* ignore CR */
 733-		case '\n': break; /* ignore LF */
 734-		default:   putc(*s, fp);
 735-		}
 736-	}
 737+    size_t i;
 738+
 739+    for (i = 0; *s && i < len; s++, i++) {
 740+        switch (*s) {
 741+        case '<':
 742+            fputs("&lt;", fp);
 743+            break;
 744+        case '>':
 745+            fputs("&gt;", fp);
 746+            break;
 747+        case '\'':
 748+            fputs("&#39;", fp);
 749+            break;
 750+        case '&':
 751+            fputs("&amp;", fp);
 752+            break;
 753+        case '"':
 754+            fputs("&quot;", fp);
 755+            break;
 756+        case '\r':
 757+            break; /* ignore CR */
 758+        case '\n':
 759+            break; /* ignore LF */
 760+        default:
 761+            putc(*s, fp);
 762+        }
 763+    }
 764 }
 765 
 766-int
 767-mkdirp(const char *path)
 768+int mkdirp(const char* path)
 769 {
 770-	char tmp[PATH_MAX], *p;
 771-
 772-	if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
 773-		errx(1, "path truncated: '%s'", path);
 774-	for (p = tmp + (tmp[0] == '/'); *p; p++) {
 775-		if (*p != '/')
 776-			continue;
 777-		*p = '\0';
 778-		if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
 779-			return -1;
 780-		*p = '/';
 781-	}
 782-	if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
 783-		return -1;
 784-	return 0;
 785+    char tmp[PATH_MAX], *p;
 786+
 787+    if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
 788+        errx(1, "path truncated: '%s'", path);
 789+    for (p = tmp + (tmp[0] == '/'); *p; p++) {
 790+        if (*p != '/')
 791+            continue;
 792+        *p = '\0';
 793+        if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
 794+            return -1;
 795+        *p = '/';
 796+    }
 797+    if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
 798+        return -1;
 799+    return 0;
 800 }
 801 
 802-void
 803-printtimez(FILE *fp, const git_time *intime)
 804+void printtimez(FILE* fp, const git_time* intime)
 805 {
 806-	struct tm *intm;
 807-	time_t t;
 808-	char out[32];
 809-
 810-	t = (time_t)intime->time;
 811-	if (!(intm = gmtime(&t)))
 812-		return;
 813-	strftime(out, sizeof(out), "%Y-%m-%dT%H:%M:%SZ", intm);
 814-	fputs(out, fp);
 815+    struct tm* intm;
 816+    time_t t;
 817+    char out[32];
 818+
 819+    t = (time_t)intime->time;
 820+    if (!(intm = gmtime(&t)))
 821+        return;
 822+    strftime(out, sizeof(out), "%Y-%m-%dT%H:%M:%SZ", intm);
 823+    fputs(out, fp);
 824 }
 825 
 826-void
 827-printtime(FILE *fp, const git_time *intime)
 828+void printtime(FILE* fp, const git_time* intime)
 829 {
 830-	struct tm *intm;
 831-	time_t t;
 832-	char out[32];
 833-
 834-	t = (time_t)intime->time + (intime->offset * 60);
 835-	if (!(intm = gmtime(&t)))
 836-		return;
 837-	strftime(out, sizeof(out), "%a, %e %b %Y %H:%M:%S", intm);
 838-	if (intime->offset < 0)
 839-		fprintf(fp, "%s -%02d%02d", out,
 840-		            -(intime->offset) / 60, -(intime->offset) % 60);
 841-	else
 842-		fprintf(fp, "%s +%02d%02d", out,
 843-		            intime->offset / 60, intime->offset % 60);
 844+    struct tm* intm;
 845+    time_t t;
 846+    char out[32];
 847+
 848+    t = (time_t)intime->time + (intime->offset * 60);
 849+    if (!(intm = gmtime(&t)))
 850+        return;
 851+    strftime(out, sizeof(out), "%a, %e %b %Y %H:%M:%S", intm);
 852+    if (intime->offset < 0)
 853+        fprintf(fp, "%s -%02d%02d", out,
 854+            -(intime->offset) / 60, -(intime->offset) % 60);
 855+    else
 856+        fprintf(fp, "%s +%02d%02d", out,
 857+            intime->offset / 60, intime->offset % 60);
 858 }
 859 
 860-void
 861-printtimeshort(FILE *fp, const git_time *intime)
 862+void printtimeshort(FILE* fp, const git_time* intime)
 863 {
 864-	struct tm *intm;
 865-	time_t t;
 866-	char out[32];
 867-
 868-	t = (time_t)intime->time;
 869-	if (!(intm = gmtime(&t)))
 870-		return;
 871-	strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
 872-	fputs(out, fp);
 873+    struct tm* intm;
 874+    time_t t;
 875+    char out[32];
 876+
 877+    t = (time_t)intime->time;
 878+    if (!(intm = gmtime(&t)))
 879+        return;
 880+    strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
 881+    fputs(out, fp);
 882 }
 883 
 884-void
 885-writeheader(FILE *fp, const char *title)
 886+void writeheader(FILE* fp, const char* title)
 887 {
 888-	fputs("<!DOCTYPE html>\n"
 889-		"<html>\n<head>\n"
 890-		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
 891-		"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
 892-		"<title>", fp);
 893-	xmlencode(fp, title, strlen(title));
 894-	if (title[0] && strippedname[0])
 895-		fputs(" - ", fp);
 896-	xmlencode(fp, strippedname, strlen(strippedname));
 897-	if (description[0])
 898-		fputs(" - ", fp);
 899-	xmlencode(fp, description, strlen(description));
 900-	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
 901-	fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
 902-	xmlencode(fp, name, strlen(name));
 903-	fprintf(fp, " Atom Feed\" href=\"%satom.xml\" />\n", relpath);
 904-	fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
 905-	xmlencode(fp, name, strlen(name));
 906-	fprintf(fp, " Atom Feed (tags)\" href=\"%stags.xml\" />\n", relpath);
 907-	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
 908-	fputs("</head>\n<body>\n<div id=\"container\">\n<div id=\"sidebar\">\n<a id=\"logo\" href=\"https://git.arjunchoudhary.com\">\n<div id=\"typing\">ARJUN</div>\n<hr id=\"cursor\"/>\n</a>\n<img id=\"profile_img\" src=\"profile_img.png\" alt=\"my_profile_image\"/>\n<a id=\"contact\" href=\"mailto:contact@arjunchoudhary.com\">Contact</a>\n</div>\n<div id=\"main-view\">\n", fp);
 909-	fputs("<div id=\"header\">", fp);
 910-	fputs("<h1>", fp);
 911-	xmlencode(fp, strippedname, strlen(strippedname));
 912-	fputs("</h1><div id=\"subtitle\"><span class=\"desc\">", fp);
 913-	xmlencode(fp, description, strlen(description));
 914-	fputs("</span>", fp);
 915-	fprintf(fp, " <a href=\"../%s\">Back</a></div>", relpath);
 916-	if (cloneurl[0]) {
 917-		fputs("<span id=\"cloneurl\"> git clone <a href=\"", fp);
 918-		xmlencode(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */
 919-		fputs("\">", fp);
 920-		xmlencode(fp, cloneurl, strlen(cloneurl));
 921-		fputs("</a></span>", fp);
 922-	}
 923-	fputs("<div id=\"navbar\">", fp);
 924-	fprintf(fp, "<a href=\"%slog.html\">Log |</a> ", relpath);
 925-	fprintf(fp, "<a href=\"%sfiles.html\">Files |</a> ", relpath);
 926-	fprintf(fp, "<a href=\"%srefs.html\">Refs |</a> ", relpath);
 927-	if (submodules)
 928-		fprintf(fp, " <a href=\"%sfile/%s.html\">Submodules</a>",
 929-		        relpath, submodules);
 930-	if (readme)
 931-		fprintf(fp, " <a href=\"%sfile/%s.html\">README |</a>",
 932-		        relpath, readme);
 933-	if (license)
 934-		fprintf(fp, " <a href=\"%sfile/%s.html\">LICENSE</a>",
 935-		        relpath, license);
 936-	fputs("</div>", fp);
 937-	fputs("</div>", fp);
 938-	fputs("<div id=\"content\">\n", fp);
 939+    fputs("<!DOCTYPE html>\n"
 940+          "<html>\n<head>\n"
 941+          "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
 942+          "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
 943+          "<title>",
 944+        fp);
 945+    xmlencode(fp, title, strlen(title));
 946+    if (title[0] && strippedname[0])
 947+        fputs(" - ", fp);
 948+    xmlencode(fp, strippedname, strlen(strippedname));
 949+    if (description[0])
 950+        fputs(" - ", fp);
 951+    xmlencode(fp, description, strlen(description));
 952+    fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
 953+    fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
 954+    xmlencode(fp, name, strlen(name));
 955+    fprintf(fp, " Atom Feed\" href=\"%satom.xml\" />\n", relpath);
 956+    fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
 957+    xmlencode(fp, name, strlen(name));
 958+    fprintf(fp, " Atom Feed (tags)\" href=\"%stags.xml\" />\n", relpath);
 959+    fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
 960+    fputs("</head>\n<body>\n<div id=\"container\">\n<div id=\"sidebar\">\n<a id=\"logo\" href=\"https://git.arjun.lol\">\n<div id=\"typing\">ARJUN</div>\n<hr id=\"cursor\"/>\n</a>\n<img id=\"profile_img\" src=\"me.webp\" alt=\"my_profile_image\"/>\n<div><a id=\"contact\" href=\"mailto:contact@arjunchoudhary.com\">Contact</a> | <a id=\"contact\" href=\"https://arjun.lol/public_pgp\">PGP</a>\n</div></div>\n<div id=\"main-view\">\n", fp);
 961+    fputs("<div id=\"header\">", fp);
 962+    fputs("<h1>", fp);
 963+    xmlencode(fp, strippedname, strlen(strippedname));
 964+    fputs("</h1><div id=\"subtitle\"><span class=\"desc\">", fp);
 965+    xmlencode(fp, description, strlen(description));
 966+    fputs("|", fp);
 967+    fputs("</span>", fp);
 968+    fprintf(fp, " <a href=\"../%s\">Back</a></div>", relpath);
 969+    if (cloneurl[0]) {
 970+        fputs("<span id=\"cloneurl\"> git clone <a href=\"", fp);
 971+        xmlencode(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */
 972+        fputs("\">", fp);
 973+        xmlencode(fp, cloneurl, strlen(cloneurl));
 974+        fputs("</a></span>", fp);
 975+    }
 976+    fputs("<div id=\"navbar\">", fp);
 977+    fprintf(fp, "<a href=\"%slog.html\">Log</a> ", relpath);
 978+    fprintf(fp, "<a href=\"%sfiles.html\">Files</a> ", relpath);
 979+    fprintf(fp, "<a href=\"%srefs.html\">Refs</a> ", relpath);
 980+    if (submodules)
 981+        fprintf(fp, " <a href=\"%sfile/%s.html\">Submodules</a>",
 982+            relpath, submodules);
 983+    if (readme)
 984+        fprintf(fp, " <a href=\"%sfile/%s.html\">README</a>",
 985+            relpath, readme);
 986+    if (license)
 987+        fprintf(fp, " <a href=\"%sfile/%s.html\">LICENSE</a>",
 988+            relpath, license);
 989+    fputs("</div>", fp);
 990+    fputs("</div>", fp);
 991+    fputs("<div id=\"content\">\n", fp);
 992 }
 993 
 994-void
 995-writefooter(FILE *fp)
 996+void writefooter(FILE* fp)
 997 {
 998-	fputs("</div>\n</div>\n</div>\n</body>\n</html>\n", fp);
 999+    fputs("</div>\n</div>\n</div>\n<script nonce=\"TGV0c0Z1Y2tpbmdHb0xtYW9w\" >for (var i = 0; i < document.links.length; i++) { if (document.links[i].href === document.URL) {document.links[i].className = 'current';}}</script>\n</body>\n</html>\n", fp);
1000 }
1001 
1002-void
1003-processmd(const char* output, unsigned int len, void *fp)
1004+void processmd(const char* output, unsigned int len, void* fp)
1005 {
1006-    fprintf((FILE *)fp, "%.*s", len, output);
1007+    fprintf((FILE*)fp, "%.*s", len, output);
1008 }
1009 
1010 size_t
1011-writeblobmd(FILE *fp, const git_blob *blob)
1012+writeblobmd(FILE* fp, const git_blob* blob)
1013 {
1014     size_t n = 0, i, len, prev, ret;
1015-    const char *s = git_blob_rawcontent(blob);
1016+    const char* s = git_blob_rawcontent(blob);
1017     len = git_blob_rawsize(blob);
1018     fputs("<div id=\"md\">\n", fp);
1019     /* Counting lines in the file*/
1020@@ -588,939 +590,1021 @@ writeblobmd(FILE *fp, const git_blob *blob)
1021         if ((len - prev) > 0) {
1022             n++;
1023         }
1024-        ret = md_html(s, len, processmd, fp, MD_FLAG_TABLES | MD_FLAG_TASKLISTS | 
1025-                MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS, 0);
1026+        ret = md_html(s, len, processmd, fp, MD_FLAG_TABLES | MD_FLAG_TASKLISTS | MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS, 0);
1027     }
1028 
1029     fputs("</div>\n", fp);
1030     return n;
1031 }
1032 
1033-int
1034-syntax_highlight(const char *filename, FILE *fp, const char *s, size_t len)
1035+int syntax_highlight(const char* filename, FILE* fp, const char* s, size_t len)
1036 {
1037-	// Flush HTML-file
1038-	fflush(fp);
1039-	// Copy STDOUT
1040-	int stdout_copy = dup(1);
1041-	// Redirect STDOUT
1042-	dup2(fileno(fp), 1);
1043-
1044-	char cmd[255] = "chroma --html --html-only --html-lines --html-lines-table --filename ";
1045-
1046-	strncat(cmd, filename, strlen(filename) + 1);
1047-	FILE *child = popen(cmd, "w");
1048-	if (child == NULL) {
1049-		printf("child is null: %s", strerror(errno));
1050-		exit(1);
1051-	}
1052-
1053-	// Give filename through STDIN:
1054-	// fprintf(child, "%s\n", filename);
1055-
1056-	// Give code to highlight through STDIN:
1057-	int lc;
1058-	size_t i;
1059-	for (i = 0; *s && i < len; s++, i++) {
1060-		if (*s == '\n') lc++;
1061-		fprintf(child, "%c", *s);
1062-	}
1063-
1064-	pclose(child);
1065-	fflush(stdout);
1066-	// Give back STDOUT.
1067-	dup2(stdout_copy, 1);
1068-	return lc;
1069+    // Flush HTML-file
1070+    fflush(fp);
1071+    // Copy STDOUT
1072+    int stdout_copy = dup(1);
1073+    // Redirect STDOUT
1074+    dup2(fileno(fp), 1);
1075+
1076+    char cmd[255] = "chroma --html --html-only --html-lines --html-lines-table --filename ";
1077+
1078+    strncat(cmd, filename, strlen(filename) + 1);
1079+    FILE* child = popen(cmd, "w");
1080+    if (child == NULL) {
1081+        printf("child is null: %s", strerror(errno));
1082+        exit(1);
1083+    }
1084+
1085+    // Give filename through STDIN:
1086+    // fprintf(child, "%s\n", filename);
1087+
1088+    // Give code to highlight through STDIN:
1089+    int lc;
1090+    size_t i;
1091+    for (i = 0; *s && i < len; s++, i++) {
1092+        if (*s == '\n')
1093+            lc++;
1094+        fprintf(child, "%c", *s);
1095+    }
1096+
1097+    pclose(child);
1098+    fflush(stdout);
1099+    // Give back STDOUT.
1100+    dup2(stdout_copy, 1);
1101+    return lc;
1102 }
1103 
1104 size_t
1105-writeblobhtml(const char *filename, FILE *fp, const git_blob *blob)
1106-{	
1107-	int lc = 0;
1108-	size_t n = 0, i, len, prev;
1109-	const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%zu</a>";
1110-	const char *s = git_blob_rawcontent(blob);
1111-
1112-	len = git_blob_rawsize(blob);
1113-	fputs("<pre id=\"blob\">\n", fp);
1114-
1115-	if (len > 0) {
1116-		syntax_highlight(filename, fp, s, len);
1117-		/* Uncomment to render the original file (no syntax highlight) */
1118-/* 		for (i = 0, prev = 0; i < len; i++) {
1119-			if (s[i] != '\n')
1120-				continue;
1121-			n++;
1122-			fprintf(fp, nfmt, n, n, n);
1123-			xmlencodeline(fp, &s[prev], i - prev + 1);
1124-			putc('\n', fp);
1125-			prev = i + 1;
1126-		} */
1127-		/* trailing data */
1128-/* 		if ((len - prev) > 0) {
1129-			n++;
1130-			fprintf(fp, nfmt, n, n, n);
1131-			xmlencodeline(fp, &s[prev], len - prev);
1132-		} */
1133-	}
1134-	fputs("</pre>\n", fp);
1135-
1136-	return n;
1137+writeblobhtml(const char* filename, FILE* fp, const git_blob* blob)
1138+{
1139+    int lc = 0;
1140+    size_t n = 0, i, len, prev;
1141+    const char* nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%zu</a>";
1142+    const char* s = git_blob_rawcontent(blob);
1143+
1144+    len = git_blob_rawsize(blob);
1145+    fputs("<pre id=\"blob\">\n", fp);
1146+
1147+    if (len > 0) {
1148+        syntax_highlight(filename, fp, s, len);
1149+        /* Uncomment to render the original file (no syntax highlight) */
1150+        /* 		for (i = 0, prev = 0; i < len; i++) {
1151+                                if (s[i] != '\n')
1152+                                        continue;
1153+                                n++;
1154+                                fprintf(fp, nfmt, n, n, n);
1155+                                xmlencodeline(fp, &s[prev], i - prev + 1);
1156+                                putc('\n', fp);
1157+                                prev = i + 1;
1158+                        } */
1159+        /* trailing data */
1160+        /* 		if ((len - prev) > 0) {
1161+                                n++;
1162+                                fprintf(fp, nfmt, n, n, n);
1163+                                xmlencodeline(fp, &s[prev], len - prev);
1164+                        } */
1165+    }
1166+    fputs("</pre>\n", fp);
1167+
1168+    return n;
1169+}
1170+
1171+void printcommit(FILE* fp, struct commitinfo* ci)
1172+{
1173+    fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n",
1174+        relpath, ci->oid, ci->oid);
1175+
1176+    if (ci->parentoid[0])
1177+        fprintf(fp, "<b>parent</b> <a href=\"%scommit/%s.html\">%s</a>\n",
1178+            relpath, ci->parentoid, ci->parentoid);
1179+
1180+    if (ci->author) {
1181+        fputs("<b>Author:</b> ", fp);
1182+        xmlencode(fp, ci->author->name, strlen(ci->author->name));
1183+        fputs(" &lt;<a href=\"mailto:", fp);
1184+        xmlencode(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */
1185+        fputs("\">", fp);
1186+        xmlencode(fp, ci->author->email, strlen(ci->author->email));
1187+        fputs("</a>&gt;\n<b>Date:</b>   ", fp);
1188+        printtime(fp, &(ci->author->when));
1189+        putc('\n', fp);
1190+    }
1191+    if (ci->msg) {
1192+        putc('\n', fp);
1193+        xmlencode(fp, ci->msg, strlen(ci->msg));
1194+        putc('\n', fp);
1195+    }
1196 }
1197 
1198-void
1199-printcommit(FILE *fp, struct commitinfo *ci)
1200+void printshowfile(FILE* fp, struct commitinfo* ci)
1201 {
1202-	fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n",
1203-		relpath, ci->oid, ci->oid);
1204-
1205-	if (ci->parentoid[0])
1206-		fprintf(fp, "<b>parent</b> <a href=\"%scommit/%s.html\">%s</a>\n",
1207-			relpath, ci->parentoid, ci->parentoid);
1208-
1209-	if (ci->author) {
1210-		fputs("<b>Author:</b> ", fp);
1211-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
1212-		fputs(" &lt;<a href=\"mailto:", fp);
1213-		xmlencode(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */
1214-		fputs("\">", fp);
1215-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
1216-		fputs("</a>&gt;\n<b>Date:</b>   ", fp);
1217-		printtime(fp, &(ci->author->when));
1218-		putc('\n', fp);
1219-	}
1220-	if (ci->msg) {
1221-		putc('\n', fp);
1222-		xmlencode(fp, ci->msg, strlen(ci->msg));
1223-		putc('\n', fp);
1224-	}
1225+    const git_diff_delta* delta;
1226+    const git_diff_hunk* hunk;
1227+    const git_diff_line* line;
1228+    git_patch* patch;
1229+    size_t nhunks, nhunklines, changed, add, del, total, i, j, k;
1230+    char linestr[80];
1231+    int c;
1232+
1233+    printcommit(fp, ci);
1234+
1235+    if (!ci->deltas)
1236+        return;
1237+
1238+    if (ci->filecount > 1000 || ci->ndeltas > 1000 || ci->addcount > 100000 || ci->delcount > 100000) {
1239+        fputs("Diff is too large, output suppressed.\n", fp);
1240+        return;
1241+    }
1242+
1243+    /* diff stat */
1244+    fputs("<b>Diffstat:</b>\n<table>", fp);
1245+    for (i = 0; i < ci->ndeltas; i++) {
1246+        delta = git_patch_get_delta(ci->deltas[i]->patch);
1247+
1248+        switch (delta->status) {
1249+        case GIT_DELTA_ADDED:
1250+            c = 'A';
1251+            break;
1252+        case GIT_DELTA_COPIED:
1253+            c = 'C';
1254+            break;
1255+        case GIT_DELTA_DELETED:
1256+            c = 'D';
1257+            break;
1258+        case GIT_DELTA_MODIFIED:
1259+            c = 'M';
1260+            break;
1261+        case GIT_DELTA_RENAMED:
1262+            c = 'R';
1263+            break;
1264+        case GIT_DELTA_TYPECHANGE:
1265+            c = 'T';
1266+            break;
1267+        default:
1268+            c = ' ';
1269+            break;
1270+        }
1271+        if (c == ' ')
1272+            fprintf(fp, "<tr><td>%c", c);
1273+        else
1274+            fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
1275+
1276+        fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
1277+        xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
1278+        if (strcmp(delta->old_file.path, delta->new_file.path)) {
1279+            fputs(" -&gt; ", fp);
1280+            xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
1281+        }
1282+
1283+        add = ci->deltas[i]->addcount;
1284+        del = ci->deltas[i]->delcount;
1285+        changed = add + del;
1286+        total = sizeof(linestr) - 2;
1287+        if (changed > total) {
1288+            if (add)
1289+                add = ((float)total / changed * add) + 1;
1290+            if (del)
1291+                del = ((float)total / changed * del) + 1;
1292+        }
1293+        memset(&linestr, '+', add);
1294+        memset(&linestr[add], '-', del);
1295+
1296+        fprintf(fp, "</a></td><td> | </td><td class=\"num\">%zu</td><td><span class=\"i\">",
1297+            ci->deltas[i]->addcount + ci->deltas[i]->delcount);
1298+        fwrite(&linestr, 1, add, fp);
1299+        fputs("</span><span class=\"d\">", fp);
1300+        fwrite(&linestr[add], 1, del, fp);
1301+        fputs("</span></td></tr>\n", fp);
1302+    }
1303+    fprintf(fp, "</table></pre><pre>%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
1304+        ci->filecount, ci->filecount == 1 ? "" : "s",
1305+        ci->addcount, ci->addcount == 1 ? "" : "s",
1306+        ci->delcount, ci->delcount == 1 ? "" : "s");
1307+
1308+    for (i = 0; i < ci->ndeltas; i++) {
1309+        patch = ci->deltas[i]->patch;
1310+        delta = git_patch_get_delta(patch);
1311+        fprintf(fp, "<b id=\"diff\">diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
1312+        percentencode(fp, delta->old_file.path, strlen(delta->old_file.path));
1313+        fputs(".html\">", fp);
1314+        xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
1315+        fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
1316+        percentencode(fp, delta->new_file.path, strlen(delta->new_file.path));
1317+        fprintf(fp, ".html\">");
1318+        xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
1319+        fprintf(fp, "</a></b>\n");
1320+
1321+        /* check binary data */
1322+        if (delta->flags & GIT_DIFF_FLAG_BINARY) {
1323+            fputs("Binary files differ.\n", fp);
1324+            continue;
1325+        }
1326+
1327+        nhunks = git_patch_num_hunks(patch);
1328+        for (j = 0; j < nhunks; j++) {
1329+            if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
1330+                break;
1331+
1332+            fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
1333+            xmlencode(fp, hunk->header, hunk->header_len);
1334+            fputs("</a>", fp);
1335+
1336+            for (k = 0;; k++) {
1337+                if (git_patch_get_line_in_hunk(&line, patch, j, k))
1338+                    break;
1339+                if (line->old_lineno == -1)
1340+                    fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+",
1341+                        i, j, k, i, j, k);
1342+                else if (line->new_lineno == -1)
1343+                    fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-",
1344+                        i, j, k, i, j, k);
1345+                else
1346+                    putc(' ', fp);
1347+                xmlencodeline(fp, line->content, line->content_len);
1348+                putc('\n', fp);
1349+                if (line->old_lineno == -1 || line->new_lineno == -1)
1350+                    fputs("</a>", fp);
1351+            }
1352+        }
1353+    }
1354 }
1355 
1356-void
1357-printshowfile(FILE *fp, struct commitinfo *ci)
1358+void writelogline(FILE* fp, struct commitinfo* ci)
1359 {
1360-	const git_diff_delta *delta;
1361-	const git_diff_hunk *hunk;
1362-	const git_diff_line *line;
1363-	git_patch *patch;
1364-	size_t nhunks, nhunklines, changed, add, del, total, i, j, k;
1365-	char linestr[80];
1366-	int c;
1367-
1368-	printcommit(fp, ci);
1369-
1370-	if (!ci->deltas)
1371-		return;
1372-
1373-	if (ci->filecount > 1000   ||
1374-	    ci->ndeltas   > 1000   ||
1375-	    ci->addcount  > 100000 ||
1376-	    ci->delcount  > 100000) {
1377-		fputs("Diff is too large, output suppressed.\n", fp);
1378-		return;
1379-	}
1380-
1381-	/* diff stat */
1382-	fputs("<b>Diffstat:</b>\n<table>", fp);
1383-	for (i = 0; i < ci->ndeltas; i++) {
1384-		delta = git_patch_get_delta(ci->deltas[i]->patch);
1385-
1386-		switch (delta->status) {
1387-		case GIT_DELTA_ADDED:      c = 'A'; break;
1388-		case GIT_DELTA_COPIED:     c = 'C'; break;
1389-		case GIT_DELTA_DELETED:    c = 'D'; break;
1390-		case GIT_DELTA_MODIFIED:   c = 'M'; break;
1391-		case GIT_DELTA_RENAMED:    c = 'R'; break;
1392-		case GIT_DELTA_TYPECHANGE: c = 'T'; break;
1393-		default:                   c = ' '; break;
1394-		}
1395-		if (c == ' ')
1396-			fprintf(fp, "<tr><td>%c", c);
1397-		else
1398-			fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
1399-
1400-		fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
1401-		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
1402-		if (strcmp(delta->old_file.path, delta->new_file.path)) {
1403-			fputs(" -&gt; ", fp);
1404-			xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
1405-		}
1406-
1407-		add = ci->deltas[i]->addcount;
1408-		del = ci->deltas[i]->delcount;
1409-		changed = add + del;
1410-		total = sizeof(linestr) - 2;
1411-		if (changed > total) {
1412-			if (add)
1413-				add = ((float)total / changed * add) + 1;
1414-			if (del)
1415-				del = ((float)total / changed * del) + 1;
1416-		}
1417-		memset(&linestr, '+', add);
1418-		memset(&linestr[add], '-', del);
1419-
1420-		fprintf(fp, "</a></td><td> | </td><td class=\"num\">%zu</td><td><span class=\"i\">",
1421-		        ci->deltas[i]->addcount + ci->deltas[i]->delcount);
1422-		fwrite(&linestr, 1, add, fp);
1423-		fputs("</span><span class=\"d\">", fp);
1424-		fwrite(&linestr[add], 1, del, fp);
1425-		fputs("</span></td></tr>\n", fp);
1426-	}
1427-	fprintf(fp, "</table></pre><pre>%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
1428-		ci->filecount, ci->filecount == 1 ? "" : "s",
1429-	        ci->addcount,  ci->addcount  == 1 ? "" : "s",
1430-	        ci->delcount,  ci->delcount  == 1 ? "" : "s");
1431-
1432-	//fputs("<hr/>", fp);
1433-	//wtf
1434-
1435-	for (i = 0; i < ci->ndeltas; i++) {
1436-		patch = ci->deltas[i]->patch;
1437-		delta = git_patch_get_delta(patch);
1438-		fprintf(fp, "<b id=\"diff\">diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
1439-		percentencode(fp, delta->old_file.path, strlen(delta->old_file.path));
1440-		fputs(".html\">", fp);
1441-		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
1442-		fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
1443-		percentencode(fp, delta->new_file.path, strlen(delta->new_file.path));
1444-		fprintf(fp, ".html\">");
1445-		xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
1446-		fprintf(fp, "</a></b>\n");
1447-
1448-		/* check binary data */
1449-		if (delta->flags & GIT_DIFF_FLAG_BINARY) {
1450-			fputs("Binary files differ.\n", fp);
1451-			continue;
1452-		}
1453-
1454-		nhunks = git_patch_num_hunks(patch);
1455-		for (j = 0; j < nhunks; j++) {
1456-			if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
1457-				break;
1458-
1459-			fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
1460-			xmlencode(fp, hunk->header, hunk->header_len);
1461-			fputs("</a>", fp);
1462-
1463-			for (k = 0; ; k++) {
1464-				if (git_patch_get_line_in_hunk(&line, patch, j, k))
1465-					break;
1466-				if (line->old_lineno == -1)
1467-					fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+",
1468-						i, j, k, i, j, k);
1469-				else if (line->new_lineno == -1)
1470-					fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-",
1471-						i, j, k, i, j, k);
1472-				else
1473-					putc(' ', fp);
1474-				xmlencodeline(fp, line->content, line->content_len);
1475-				putc('\n', fp);
1476-				if (line->old_lineno == -1 || line->new_lineno == -1)
1477-					fputs("</a>", fp);
1478-			}
1479-		}
1480-	}
1481+    fputs("<tr>", fp);
1482+    fputs("<td id=\"commit-message\">", fp);
1483+    if (ci->summary) {
1484+        fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
1485+        xmlencode(fp, ci->summary, strlen(ci->summary));
1486+        fputs("</a>", fp);
1487+    }
1488+    fputs("</td><td>", fp);
1489+    if (ci->author)
1490+        printtimeshort(fp, &(ci->author->when));
1491+    fputs("</td><td>", fp);
1492+    if (ci->author)
1493+        xmlencode(fp, ci->author->name, strlen(ci->author->name));
1494+    fputs("</td></tr>\n", fp);
1495 }
1496 
1497-void
1498-writelogline(FILE *fp, struct commitinfo *ci)
1499+int writelog(FILE* fp, const git_oid* oid)
1500 {
1501-	fputs("<tr><td>", fp);
1502-	if (ci->author)
1503-		printtimeshort(fp, &(ci->author->when));
1504-	fputs("</td><td id=\"commit-message\">", fp);
1505-	if (ci->summary) {
1506-		fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
1507-		xmlencode(fp, ci->summary, strlen(ci->summary));
1508-		fputs("</a>", fp);
1509-	}
1510-	fputs("</td><td>", fp);
1511-	if (ci->author)
1512-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
1513-	fputs("</td><td class=\"num\" align=\"right\">", fp);
1514-	fprintf(fp, "%zu", ci->filecount);
1515-	fputs("</td><td class=\"num\" align=\"right\">", fp);
1516-	fprintf(fp, "+%zu", ci->addcount);
1517-	fputs("</td><td class=\"num\" align=\"right\">", fp);
1518-	fprintf(fp, "-%zu", ci->delcount);
1519-	fputs("</td></tr>\n", fp);
1520+    struct commitinfo* ci;
1521+    git_revwalk* w = NULL;
1522+    git_oid id;
1523+    char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
1524+    FILE* fpfile;
1525+    size_t remcommits = 0;
1526+    int r;
1527+
1528+    git_revwalk_new(&w, repo);
1529+    git_revwalk_push(w, oid);
1530+
1531+    while (!git_revwalk_next(&id, w)) {
1532+        relpath = "";
1533+
1534+        if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
1535+            break;
1536+
1537+        git_oid_tostr(oidstr, sizeof(oidstr), &id);
1538+        r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
1539+        if (r < 0 || (size_t)r >= sizeof(path))
1540+            errx(1, "path truncated: 'commit/%s.html'", oidstr);
1541+        r = access(path, F_OK);
1542+
1543+        /* optimization: if there are no log lines to write and
1544+           the commit file already exists: skip the diffstat */
1545+        if (!nlogcommits) {
1546+            remcommits++;
1547+            if (!r)
1548+                continue;
1549+        }
1550+
1551+        if (!(ci = commitinfo_getbyoid(&id)))
1552+            break;
1553+        /* diffstat: for stagit HTML required for the log.html line */
1554+        if (commitinfo_getstats(ci) == -1)
1555+            goto err;
1556+
1557+        if (nlogcommits != 0) {
1558+            writelogline(fp, ci);
1559+            if (nlogcommits > 0)
1560+                nlogcommits--;
1561+        }
1562+
1563+        if (cachefile)
1564+            writelogline(wcachefp, ci);
1565+
1566+        /* check if file exists if so skip it */
1567+        if (r) {
1568+            relpath = "../";
1569+            fpfile = efopen(path, "w");
1570+            writeheader(fpfile, ci->summary);
1571+            fputs("<pre>", fpfile);
1572+            printshowfile(fpfile, ci);
1573+            fputs("</pre>\n", fpfile);
1574+            writefooter(fpfile);
1575+            checkfileerror(fpfile, path, 'w');
1576+            fclose(fpfile);
1577+        }
1578+    err:
1579+        commitinfo_free(ci);
1580+    }
1581+    git_revwalk_free(w);
1582+
1583+    if (nlogcommits == 0 && remcommits != 0) {
1584+        fprintf(fp, "<tr><td></td><td colspan=\"5\">"
1585+                    "%zu more commits remaining, fetch the repository"
1586+                    "</td></tr>\n",
1587+            remcommits);
1588+    }
1589+
1590+    relpath = "";
1591+
1592+    return 0;
1593 }
1594 
1595-int
1596-writelog(FILE *fp, const git_oid *oid)
1597+void printcommitatom(FILE* fp, struct commitinfo* ci, const char* tag)
1598 {
1599-	struct commitinfo *ci;
1600-	git_revwalk *w = NULL;
1601-	git_oid id;
1602-	char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
1603-	FILE *fpfile;
1604-	size_t remcommits = 0;
1605-	int r;
1606-
1607-	git_revwalk_new(&w, repo);
1608-	git_revwalk_push(w, oid);
1609-
1610-	while (!git_revwalk_next(&id, w)) {
1611-		relpath = "";
1612-
1613-		if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
1614-			break;
1615-
1616-		git_oid_tostr(oidstr, sizeof(oidstr), &id);
1617-		r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
1618-		if (r < 0 || (size_t)r >= sizeof(path))
1619-			errx(1, "path truncated: 'commit/%s.html'", oidstr);
1620-		r = access(path, F_OK);
1621-
1622-		/* optimization: if there are no log lines to write and
1623-		   the commit file already exists: skip the diffstat */
1624-		if (!nlogcommits) {
1625-			remcommits++;
1626-			if (!r)
1627-				continue;
1628-		}
1629-
1630-		if (!(ci = commitinfo_getbyoid(&id)))
1631-			break;
1632-		/* diffstat: for stagit HTML required for the log.html line */
1633-		if (commitinfo_getstats(ci) == -1)
1634-			goto err;
1635-
1636-		if (nlogcommits != 0) {
1637-			writelogline(fp, ci);
1638-			if (nlogcommits > 0)
1639-				nlogcommits--;
1640-		}
1641-
1642-		if (cachefile)
1643-			writelogline(wcachefp, ci);
1644-
1645-		/* check if file exists if so skip it */
1646-		if (r) {
1647-			relpath = "../";
1648-			fpfile = efopen(path, "w");
1649-			writeheader(fpfile, ci->summary);
1650-			fputs("<pre>", fpfile);
1651-			printshowfile(fpfile, ci);
1652-			fputs("</pre>\n", fpfile);
1653-			writefooter(fpfile);
1654-			checkfileerror(fpfile, path, 'w');
1655-			fclose(fpfile);
1656-		}
1657-err:
1658-		commitinfo_free(ci);
1659-	}
1660-	git_revwalk_free(w);
1661+    fputs("<entry>\n", fp);
1662 
1663-	if (nlogcommits == 0 && remcommits != 0) {
1664-		fprintf(fp, "<tr><td></td><td colspan=\"5\">"
1665-		        "%zu more commits remaining, fetch the repository"
1666-		        "</td></tr>\n", remcommits);
1667-	}
1668+    fprintf(fp, "<id>%s</id>\n", ci->oid);
1669+    if (ci->author) {
1670+        fputs("<published>", fp);
1671+        printtimez(fp, &(ci->author->when));
1672+        fputs("</published>\n", fp);
1673+    }
1674+    if (ci->committer) {
1675+        fputs("<updated>", fp);
1676+        printtimez(fp, &(ci->committer->when));
1677+        fputs("</updated>\n", fp);
1678+    }
1679+    if (ci->summary) {
1680+        fputs("<title type=\"text\">", fp);
1681+        if (tag && tag[0]) {
1682+            fputs("[", fp);
1683+            xmlencode(fp, tag, strlen(tag));
1684+            fputs("] ", fp);
1685+        }
1686+        xmlencode(fp, ci->summary, strlen(ci->summary));
1687+        fputs("</title>\n", fp);
1688+    }
1689+    fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"%scommit/%s.html\" />\n",
1690+        baseurl, ci->oid);
1691+
1692+    if (ci->author) {
1693+        fputs("<author>\n<name>", fp);
1694+        xmlencode(fp, ci->author->name, strlen(ci->author->name));
1695+        fputs("</name>\n<email>", fp);
1696+        xmlencode(fp, ci->author->email, strlen(ci->author->email));
1697+        fputs("</email>\n</author>\n", fp);
1698+    }
1699 
1700-	relpath = "";
1701+    fputs("<content type=\"text\">", fp);
1702+    fprintf(fp, "commit %s\n", ci->oid);
1703+    if (ci->parentoid[0])
1704+        fprintf(fp, "parent %s\n", ci->parentoid);
1705+    if (ci->author) {
1706+        fputs("Author: ", fp);
1707+        xmlencode(fp, ci->author->name, strlen(ci->author->name));
1708+        fputs(" &lt;", fp);
1709+        xmlencode(fp, ci->author->email, strlen(ci->author->email));
1710+        fputs("&gt;\nDate:   ", fp);
1711+        printtime(fp, &(ci->author->when));
1712+        putc('\n', fp);
1713+    }
1714+    if (ci->msg) {
1715+        putc('\n', fp);
1716+        xmlencode(fp, ci->msg, strlen(ci->msg));
1717+    }
1718+    fputs("\n</content>\n</entry>\n", fp);
1719+}
1720 
1721-	return 0;
1722+int mkdirfile(const char* path)
1723+{
1724+    char* d;
1725+    char tmp[PATH_MAX];
1726+    if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
1727+        errx(1, "path truncated: '%s'", path);
1728+    if (!(d = dirname(tmp)))
1729+        err(1, "dirname");
1730+    if (mkdirp(d))
1731+        return -1;
1732+    return 0;
1733 }
1734 
1735-void
1736-printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
1737+void writeblobraw(const git_blob* blob, const char* fpath, const char* filename, git_off_t filesize)
1738 {
1739-	fputs("<entry>\n", fp);
1740-
1741-	fprintf(fp, "<id>%s</id>\n", ci->oid);
1742-	if (ci->author) {
1743-		fputs("<published>", fp);
1744-		printtimez(fp, &(ci->author->when));
1745-		fputs("</published>\n", fp);
1746-	}
1747-	if (ci->committer) {
1748-		fputs("<updated>", fp);
1749-		printtimez(fp, &(ci->committer->when));
1750-		fputs("</updated>\n", fp);
1751-	}
1752-	if (ci->summary) {
1753-		fputs("<title type=\"text\">", fp);
1754-		if (tag && tag[0]) {
1755-			fputs("[", fp);
1756-			xmlencode(fp, tag, strlen(tag));
1757-			fputs("] ", fp);
1758-		}
1759-		xmlencode(fp, ci->summary, strlen(ci->summary));
1760-		fputs("</title>\n", fp);
1761-	}
1762-	fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"%scommit/%s.html\" />\n",
1763-	        baseurl, ci->oid);
1764-
1765-	if (ci->author) {
1766-		fputs("<author>\n<name>", fp);
1767-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
1768-		fputs("</name>\n<email>", fp);
1769-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
1770-		fputs("</email>\n</author>\n", fp);
1771-	}
1772-
1773-	fputs("<content type=\"text\">", fp);
1774-	fprintf(fp, "commit %s\n", ci->oid);
1775-	if (ci->parentoid[0])
1776-		fprintf(fp, "parent %s\n", ci->parentoid);
1777-	if (ci->author) {
1778-		fputs("Author: ", fp);
1779-		xmlencode(fp, ci->author->name, strlen(ci->author->name));
1780-		fputs(" &lt;", fp);
1781-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
1782-		fputs("&gt;\nDate:   ", fp);
1783-		printtime(fp, &(ci->author->when));
1784-		putc('\n', fp);
1785-	}
1786-	if (ci->msg) {
1787-		putc('\n', fp);
1788-		xmlencode(fp, ci->msg, strlen(ci->msg));
1789-	}
1790-	fputs("\n</content>\n</entry>\n", fp);
1791+    char tmp[PATH_MAX] = "";
1792+    const char* p;
1793+    int lc = 0;
1794+    FILE* fp;
1795+
1796+    mkdirfile(fpath);
1797+
1798+    if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp))
1799+        errx(1, "path truncated: '%s'", fpath);
1800+
1801+    for (p = fpath, tmp[0] = '\0'; *p; p++) {
1802+        if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
1803+            errx(1, "path truncated: '../%s'", tmp);
1804+    }
1805+
1806+    fp = efopen(fpath, "w");
1807+    fwrite(git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob), 1, fp);
1808+    fclose(fp);
1809 }
1810 
1811-int
1812-writeatom(FILE *fp, int all)
1813+int writeatom(FILE* fp, int all)
1814 {
1815-	struct referenceinfo *ris = NULL;
1816-	size_t refcount = 0;
1817-	struct commitinfo *ci;
1818-	git_revwalk *w = NULL;
1819-	git_oid id;
1820-	size_t i, m = 100; /* last 'm' commits */
1821-
1822-	fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1823-	      "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>", fp);
1824-	xmlencode(fp, strippedname, strlen(strippedname));
1825-	fputs(", branch HEAD</title>\n<subtitle>", fp);
1826-	xmlencode(fp, description, strlen(description));
1827-	fputs("</subtitle>\n", fp);
1828-
1829-	/* all commits or only tags? */
1830-	if (all) {
1831-		git_revwalk_new(&w, repo);
1832-		git_revwalk_push_head(w);
1833-		for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
1834-			if (!(ci = commitinfo_getbyoid(&id)))
1835-				break;
1836-			printcommitatom(fp, ci, "");
1837-			commitinfo_free(ci);
1838-		}
1839-		git_revwalk_free(w);
1840-	} else if (getrefs(&ris, &refcount) != -1) {
1841-		/* references: tags */
1842-		for (i = 0; i < refcount; i++) {
1843-			if (git_reference_is_tag(ris[i].ref))
1844-				printcommitatom(fp, ris[i].ci,
1845-				                git_reference_shorthand(ris[i].ref));
1846-
1847-			commitinfo_free(ris[i].ci);
1848-			git_reference_free(ris[i].ref);
1849-		}
1850-		free(ris);
1851-	}
1852-
1853-	fputs("</feed>\n", fp);
1854-
1855-	return 0;
1856+    struct referenceinfo* ris = NULL;
1857+    size_t refcount = 0;
1858+    struct commitinfo* ci;
1859+    git_revwalk* w = NULL;
1860+    git_oid id;
1861+    size_t i, m = 100; /* last 'm' commits */
1862+
1863+    fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1864+          "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>",
1865+        fp);
1866+    xmlencode(fp, strippedname, strlen(strippedname));
1867+    fputs(", branch HEAD</title>\n<subtitle>", fp);
1868+    xmlencode(fp, description, strlen(description));
1869+    fputs("</subtitle>\n", fp);
1870+
1871+    /* all commits or only tags? */
1872+    if (all) {
1873+        git_revwalk_new(&w, repo);
1874+        git_revwalk_push_head(w);
1875+        for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
1876+            if (!(ci = commitinfo_getbyoid(&id)))
1877+                break;
1878+            printcommitatom(fp, ci, "");
1879+            commitinfo_free(ci);
1880+        }
1881+        git_revwalk_free(w);
1882+    } else if (getrefs(&ris, &refcount) != -1) {
1883+        /* references: tags */
1884+        for (i = 0; i < refcount; i++) {
1885+            if (git_reference_is_tag(ris[i].ref))
1886+                printcommitatom(fp, ris[i].ci,
1887+                    git_reference_shorthand(ris[i].ref));
1888+
1889+            commitinfo_free(ris[i].ci);
1890+            git_reference_free(ris[i].ref);
1891+        }
1892+        free(ris);
1893+    }
1894+
1895+    fputs("</feed>\n", fp);
1896+
1897+    return 0;
1898 }
1899 
1900-int
1901-file_is_md(const char *filename)
1902+int file_is_md(const char* filename)
1903 {
1904     int i = strlen(filename) - 3;
1905-    if (filename[i++] == '.' &&
1906-            filename[i++] == 'm' &&
1907-            filename[i] == 'd')
1908+    if (filename[i++] == '.' && filename[i++] == 'm' && filename[i] == 'd')
1909         return 1;
1910     return 0;
1911-    
1912 }
1913 
1914 size_t
1915-writeblob(git_object *obj, const char *fpath, const char *filename, size_t filesize)
1916+writeblob(git_object* obj, const char* fpath, const char* filename, size_t filesize, const char* rpath)
1917 {
1918-	char tmp[PATH_MAX] = "", *d;
1919-	const char *p;
1920-	size_t lc = 0;
1921-	FILE *fp;
1922-
1923-	if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp))
1924-		errx(1, "path truncated: '%s'", fpath);
1925-	if (!(d = dirname(tmp)))
1926-		err(1, "dirname");
1927-	if (mkdirp(d))
1928-		return -1;
1929-
1930-	for (p = fpath, tmp[0] = '\0'; *p; p++) {
1931-		if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
1932-			errx(1, "path truncated: '../%s'", tmp);
1933-	}
1934-	relpath = tmp;
1935-
1936-	fp = efopen(fpath, "w");
1937-	writeheader(fp, filename);
1938-	fputs("<p> ", fp);
1939-	xmlencode(fp, filename, strlen(filename));
1940-	fprintf(fp, " (%zuB)", filesize);
1941-	
1942-	if (git_blob_is_binary((git_blob *)obj)) {
1943-		fputs("<p>Binary file.</p>\n", fp);
1944+    char tmp[PATH_MAX] = "", *d;
1945+    const char *p, *oldrelpath;
1946+    size_t lc = 0;
1947+    FILE* fp;
1948+
1949+    mkdirfile(fpath);
1950+
1951+    if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp))
1952+        errx(1, "path truncated: '%s'", fpath);
1953+    if (!(d = dirname(tmp)))
1954+        err(1, "dirname");
1955+    if (mkdirp(d))
1956+        return -1;
1957+
1958+    for (p = fpath, tmp[0] = '\0'; *p; p++) {
1959+        if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
1960+            errx(1, "path truncated: '../%s'", tmp);
1961+    }
1962+
1963+    oldrelpath = relpath;
1964+    relpath = tmp;
1965+
1966+    fp = efopen(fpath, "w");
1967+    writeheader(fp, filename);
1968+    fputs("<p> ", fp);
1969+    xmlencode(fp, filename, strlen(filename));
1970+    fprintf(fp, " (%zuB)", filesize);
1971+    fprintf(fp, " - <a href=\"%s%s\">raw</a></p>", relpath, rpath);
1972+
1973+    if (git_blob_is_binary((git_blob*)obj)) {
1974+        fputs("<p>Binary file.</p>\n", fp);
1975     } else if (file_is_md(filename)) {
1976-        lc = writeblobmd(fp, (git_blob *)obj);
1977+        lc = writeblobmd(fp, (git_blob*)obj);
1978         if (ferror(fp))
1979             err(1, "md parse fail");
1980-	} else {
1981-		lc = writeblobhtml(filename, fp, (git_blob *)obj);
1982-		if (ferror(fp))
1983-			err(1, "fwrite");
1984-	}
1985+    } else {
1986+        lc = writeblobhtml(filename, fp, (git_blob*)obj);
1987+        if (ferror(fp))
1988+            err(1, "fwrite");
1989+    }
1990 
1991-	writefooter(fp);
1992-	checkfileerror(fp, fpath, 'w');
1993-	fclose(fp);
1994+    writefooter(fp);
1995+    checkfileerror(fp, fpath, 'w');
1996+    fclose(fp);
1997 
1998-	relpath = "";
1999+    relpath = oldrelpath;
2000 
2001-	return lc;
2002+    return lc;
2003 }
2004 
2005-const char *
2006+const char*
2007 filemode(git_filemode_t m)
2008 {
2009-	static char mode[11];
2010-
2011-	memset(mode, '-', sizeof(mode) - 1);
2012-	mode[10] = '\0';
2013-
2014-	if (S_ISREG(m))
2015-		mode[0] = '-';
2016-	else if (S_ISBLK(m))
2017-		mode[0] = 'b';
2018-	else if (S_ISCHR(m))
2019-		mode[0] = 'c';
2020-	else if (S_ISDIR(m))
2021-		mode[0] = 'd';
2022-	else if (S_ISFIFO(m))
2023-		mode[0] = 'p';
2024-	else if (S_ISLNK(m))
2025-		mode[0] = 'l';
2026-	else if (S_ISSOCK(m))
2027-		mode[0] = 's';
2028-	else
2029-		mode[0] = '?';
2030-
2031-	if (m & S_IRUSR) mode[1] = 'r';
2032-	if (m & S_IWUSR) mode[2] = 'w';
2033-	if (m & S_IXUSR) mode[3] = 'x';
2034-	if (m & S_IRGRP) mode[4] = 'r';
2035-	if (m & S_IWGRP) mode[5] = 'w';
2036-	if (m & S_IXGRP) mode[6] = 'x';
2037-	if (m & S_IROTH) mode[7] = 'r';
2038-	if (m & S_IWOTH) mode[8] = 'w';
2039-	if (m & S_IXOTH) mode[9] = 'x';
2040-
2041-	if (m & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S';
2042-	if (m & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S';
2043-	if (m & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T';
2044-
2045-	return mode;
2046+    static char mode[11];
2047+
2048+    memset(mode, '-', sizeof(mode) - 1);
2049+    mode[10] = '\0';
2050+
2051+    if (S_ISREG(m))
2052+        mode[0] = '-';
2053+    else if (S_ISBLK(m))
2054+        mode[0] = 'b';
2055+    else if (S_ISCHR(m))
2056+        mode[0] = 'c';
2057+    else if (S_ISDIR(m))
2058+        mode[0] = 'd';
2059+    else if (S_ISFIFO(m))
2060+        mode[0] = 'p';
2061+    else if (S_ISLNK(m))
2062+        mode[0] = 'l';
2063+    else if (S_ISSOCK(m))
2064+        mode[0] = 's';
2065+    else
2066+        mode[0] = '?';
2067+
2068+    if (m & S_IRUSR)
2069+        mode[1] = 'r';
2070+    if (m & S_IWUSR)
2071+        mode[2] = 'w';
2072+    if (m & S_IXUSR)
2073+        mode[3] = 'x';
2074+    if (m & S_IRGRP)
2075+        mode[4] = 'r';
2076+    if (m & S_IWGRP)
2077+        mode[5] = 'w';
2078+    if (m & S_IXGRP)
2079+        mode[6] = 'x';
2080+    if (m & S_IROTH)
2081+        mode[7] = 'r';
2082+    if (m & S_IWOTH)
2083+        mode[8] = 'w';
2084+    if (m & S_IXOTH)
2085+        mode[9] = 'x';
2086+
2087+    if (m & S_ISUID)
2088+        mode[3] = (mode[3] == 'x') ? 's' : 'S';
2089+    if (m & S_ISGID)
2090+        mode[6] = (mode[6] == 'x') ? 's' : 'S';
2091+    if (m & S_ISVTX)
2092+        mode[9] = (mode[9] == 'x') ? 't' : 'T';
2093+
2094+    return mode;
2095 }
2096 
2097-int
2098-writefilestree(FILE *fp, git_tree *tree, const char *path)
2099+int writefilestree(FILE* fp, git_tree* tree, const char* path)
2100 {
2101-	const git_tree_entry *entry = NULL;
2102-	git_object *obj = NULL;
2103-	const char *entryname;
2104-	char filepath[PATH_MAX], entrypath[PATH_MAX], oid[8];
2105-	size_t count, i, lc, filesize;
2106-	int r, ret;
2107-
2108-	count = git_tree_entrycount(tree);
2109-	for (i = 0; i < count; i++) {
2110-		if (!(entry = git_tree_entry_byindex(tree, i)) ||
2111-		    !(entryname = git_tree_entry_name(entry)))
2112-			return -1;
2113-		joinpath(entrypath, sizeof(entrypath), path, entryname);
2114-
2115-		r = snprintf(filepath, sizeof(filepath), "file/%s.html",
2116-		         entrypath);
2117-		if (r < 0 || (size_t)r >= sizeof(filepath))
2118-			errx(1, "path truncated: 'file/%s.html'", entrypath);
2119-
2120-		if (!git_tree_entry_to_object(&obj, repo, entry)) {
2121-			switch (git_object_type(obj)) {
2122-			case GIT_OBJ_BLOB:
2123-				break;
2124-			case GIT_OBJ_TREE:
2125-				/* NOTE: recurses */
2126-				ret = writefilestree(fp, (git_tree *)obj,
2127-				                     entrypath);
2128-				git_object_free(obj);
2129-				if (ret)
2130-					return ret;
2131-				continue;
2132-			default:
2133-				git_object_free(obj);
2134-				continue;
2135-			}
2136-
2137-			filesize = git_blob_rawsize((git_blob *)obj);
2138-			lc = writeblob(obj, filepath, entryname, filesize);
2139-
2140-			fputs("<tr><td>", fp);
2141-			fputs(filemode(git_tree_entry_filemode(entry)), fp);
2142-			fprintf(fp, "</td><td><a href=\"%s", relpath);
2143-			percentencode(fp, filepath, strlen(filepath));
2144-			fputs("\">", fp);
2145-			xmlencode(fp, entrypath, strlen(entrypath));
2146-			fputs("</a></td><td class=\"num\" align=\"right\">", fp);
2147-			if (lc > 0)
2148-				fprintf(fp, "%zuL", lc);
2149-			else
2150-				fprintf(fp, "%zuB", filesize);
2151-			fputs("</td></tr>\n", fp);
2152-			git_object_free(obj);
2153-		} else if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) {
2154-			/* commit object in tree is a submodule */
2155-			fprintf(fp, "<tr><td>m---------</td><td><a href=\"%sfile/.gitmodules.html\">",
2156-				relpath);
2157-			xmlencode(fp, entrypath, strlen(entrypath));
2158-			fputs("</a> @ ", fp);
2159-			git_oid_tostr(oid, sizeof(oid), git_tree_entry_id(entry));
2160-			xmlencode(fp, oid, strlen(oid));
2161-			fputs("</td><td class=\"num\" align=\"right\"></td></tr>\n", fp);
2162-		}
2163-	}
2164-
2165-	return 0;
2166-}
2167+    const git_tree_entry* entry = NULL;
2168+    git_object* obj = NULL;
2169+    git_off_t filesize;
2170+    FILE* fp_subtree;
2171+    const char *entryname, *oldrelpath;
2172+    char filepath[PATH_MAX], rawpath[PATH_MAX], entrypath[PATH_MAX], tmp[PATH_MAX], tmp2[PATH_MAX];
2173+    char* parent;
2174+    size_t count, i;
2175+    int lc, r, rf, ret;
2176+
2177+    fputs("<table id=\"files\"><thead>\n<tr>"
2178+          "<td><b>Name</b></td>"
2179+          "<td class=\"num\" align=\"right\"><b>Size</b></td>"
2180+          "</tr>\n</thead><tbody>\n",
2181+        fp);
2182+
2183+    if (strlen(path) > 0) {
2184+        if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
2185+            errx(1, "path truncated: '%s'", path);
2186+        parent = strrchr(tmp, '/');
2187+        if (parent == NULL)
2188+            parent = "files";
2189+        else {
2190+            *parent = '\0';
2191+            parent = strrchr(tmp, '/');
2192+            if (parent == NULL)
2193+                parent = tmp;
2194+            else
2195+                ++parent;
2196+        }
2197+        fputs("<tr><td><a class=\"dir\" href=\"../", fp);
2198+        xmlencode(fp, parent, strlen(parent));
2199+        fputs(".html\">..</a></td><td class=\"num\" align=\"right\"></td></tr>\n", fp);
2200+    }
2201 
2202-int
2203-writefiles(FILE *fp, const git_oid *id)
2204-{
2205-	git_tree *tree = NULL;
2206-	git_commit *commit = NULL;
2207-	int ret = -1;
2208+    count = git_tree_entrycount(tree);
2209+    for (i = 0; i < count; i++) {
2210+        if (!(entry = git_tree_entry_byindex(tree, i)) || !(entryname = git_tree_entry_name(entry)))
2211+            return -1;
2212+        joinpath(entrypath, sizeof(entrypath), path, entryname);
2213+
2214+        r = snprintf(filepath, sizeof(filepath), "file/%s.html",
2215+            entrypath);
2216+        if (r < 0 || (size_t)r >= sizeof(filepath))
2217+            errx(1, "path truncated: '%s.html'", entrypath);
2218+        rf = snprintf(rawpath, sizeof(rawpath), "raw/%s",
2219+            entrypath);
2220+        if (rf < 0 || (size_t)rf >= sizeof(rawpath))
2221+            errx(1, "path truncated: 'raw/%s'", entrypath);
2222+
2223+        if (!git_tree_entry_to_object(&obj, repo, entry)) {
2224+            switch (git_object_type(obj)) {
2225+            case GIT_OBJ_BLOB:
2226+                filesize = git_blob_rawsize((git_blob*)obj);
2227+                lc = writeblob(obj, filepath, entryname, filesize, rawpath);
2228+                writeblobraw((git_blob*)obj, rawpath, entryname, filesize);
2229+                break;
2230+            case GIT_OBJ_TREE:
2231+                mkdirfile(filepath);
2232+
2233+                if (strlcpy(tmp, relpath, sizeof(tmp)) >= sizeof(tmp))
2234+                    errx(1, "path truncated: '%s'", relpath);
2235+                if (strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
2236+                    errx(1, "path truncated: '../%s'", tmp);
2237+                oldrelpath = relpath;
2238+                relpath = tmp;
2239+                fp_subtree = efopen(filepath, "w");
2240+                strlcpy(tmp2, "Files - ", sizeof(tmp2));
2241+                if (strlcat(tmp2, entrypath, sizeof(tmp2)) >= sizeof(tmp2))
2242+                    errx(1, "path truncated: '%s'", tmp2);
2243+                writeheader(fp_subtree, tmp2);
2244+                /* NOTE: recurses */
2245+                ret = writefilestree(fp_subtree, (git_tree*)obj,
2246+                    entrypath);
2247+                writefooter(fp_subtree);
2248+                relpath = oldrelpath;
2249+                lc = -1;
2250+                if (ret)
2251+                    return ret;
2252+                break;
2253+            default:
2254+                git_object_free(obj);
2255+                continue;
2256+            }
2257+
2258+            fputs("<tr>", fp);
2259+            fputs("<td><a ", fp);
2260+            if (git_object_type(obj) == GIT_OBJ_TREE)
2261+                fputs("class=\"dir\" ", fp);
2262+            fprintf(fp, "href=\"%s", relpath);
2263+            xmlencode(fp, filepath, strlen(filepath));
2264+            fputs("\">", fp);
2265+            xmlencode(fp, entryname, strlen(entryname));
2266+            fputs("</a></td><td class=\"num\" align=\"right\">", fp);
2267+            if (lc > 0)
2268+                fprintf(fp, "%dL", lc);
2269+            else if (lc == 0)
2270+                fprintf(fp, "%juB", (uintmax_t)filesize);
2271+            fputs("</td></tr>\n", fp);
2272+            git_object_free(obj);
2273+        } else if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) {
2274+            /* commit object in tree is a submodule */
2275+            fprintf(fp, "<tr><td><a href=\"%sfile/.gitmodules.html\">",
2276+                relpath);
2277+            xmlencode(fp, entrypath, strlen(entrypath));
2278+            fputs("</a> @ ", fp);
2279+            const git_oid* oid = git_tree_entry_id(entry);
2280+            char oidstr[8];
2281+            git_oid_tostr(oidstr, sizeof(oidstr), oid);
2282+            fprintf(fp, "%s</td><td class=\"num\" align=\"right\"></td></tr>\n",
2283+                oidstr);
2284+        }
2285+    }
2286 
2287-	fputs("<table id=\"files\"><thead>\n<tr>"
2288-	      "<td><b>Mode</b></td><td><b>Name</b></td>"
2289-	      "<td class=\"num\" align=\"right\"><b>Size</b></td>"
2290-	      "</tr>\n</thead><tbody>\n", fp);
2291+    fputs("</tbody></table>", fp);
2292+    return 0;
2293+}
2294 
2295-	if (!git_commit_lookup(&commit, repo, id) &&
2296-	    !git_commit_tree(&tree, commit))
2297-		ret = writefilestree(fp, tree, "");
2298+int writefiles(FILE* fp, const git_oid* id)
2299+{
2300+    git_tree* tree = NULL;
2301+    git_commit* commit = NULL;
2302+    int ret = -1;
2303 
2304-	fputs("</tbody></table>", fp);
2305+    if (!git_commit_lookup(&commit, repo, id) && !git_commit_tree(&tree, commit))
2306+        ret = writefilestree(fp, tree, "");
2307 
2308-	git_commit_free(commit);
2309-	git_tree_free(tree);
2310+    git_commit_free(commit);
2311+    git_tree_free(tree);
2312 
2313-	return ret;
2314+    return ret;
2315 }
2316 
2317-int
2318-writerefs(FILE *fp)
2319+int writerefs(FILE* fp)
2320 {
2321-	struct referenceinfo *ris = NULL;
2322-	struct commitinfo *ci;
2323-	size_t count, i, j, refcount;
2324-	const char *titles[] = { "Branches", "Tags" };
2325-	const char *ids[] = { "branches", "tags" };
2326-	const char *s;
2327-
2328-	if (getrefs(&ris, &refcount) == -1)
2329-		return -1;
2330-
2331-	for (i = 0, j = 0, count = 0; i < refcount; i++) {
2332-		if (j == 0 && git_reference_is_tag(ris[i].ref)) {
2333-			if (count)
2334-				fputs("</tbody></table><br/>\n", fp);
2335-			count = 0;
2336-			j = 1;
2337-		}
2338-
2339-		/* print header if it has an entry (first). */
2340-		if (++count == 1) {
2341-			fprintf(fp, "<h2>%s</h2><table id=\"%s\">"
2342-		                "<thead>\n<tr><td><b>Name</b></td>"
2343-			        "<td><b>Last commit date</b></td>"
2344-			        "<td><b>Author</b></td>\n</tr>\n"
2345-			        "</thead><tbody>\n",
2346-			         titles[j], ids[j]);
2347-		}
2348-
2349-		ci = ris[i].ci;
2350-		s = git_reference_shorthand(ris[i].ref);
2351-
2352-		fputs("<tr><td>", fp);
2353-		xmlencode(fp, s, strlen(s));
2354-		fputs("</td><td>", fp);
2355-		if (ci->author)
2356-			printtimeshort(fp, &(ci->author->when));
2357-		fputs("</td><td>", fp);
2358-		if (ci->author)
2359-			xmlencode(fp, ci->author->name, strlen(ci->author->name));
2360-		fputs("</td></tr>\n", fp);
2361-	}
2362-	/* table footer */
2363-	if (count)
2364-		fputs("</tbody></table><br/>\n", fp);
2365-
2366-	for (i = 0; i < refcount; i++) {
2367-		commitinfo_free(ris[i].ci);
2368-		git_reference_free(ris[i].ref);
2369-	}
2370-	free(ris);
2371-
2372-	return 0;
2373+    struct referenceinfo* ris = NULL;
2374+    struct commitinfo* ci;
2375+    size_t count, i, j, refcount;
2376+    const char* titles[] = { "Branches", "Tags" };
2377+    const char* ids[] = { "branches", "tags" };
2378+    const char* s;
2379+
2380+    if (getrefs(&ris, &refcount) == -1)
2381+        return -1;
2382+
2383+    for (i = 0, j = 0, count = 0; i < refcount; i++) {
2384+        if (j == 0 && git_reference_is_tag(ris[i].ref)) {
2385+            if (count)
2386+                fputs("</tbody></table><br/>\n", fp);
2387+            count = 0;
2388+            j = 1;
2389+        }
2390+
2391+        /* print header if it has an entry (first). */
2392+        if (++count == 1) {
2393+            fprintf(fp, "<h2>%s</h2><table id=\"%s\">"
2394+                        "<thead>\n<tr><td><b>Name</b></td>"
2395+                        "<td><b>Last commit date</b></td>"
2396+                        "<td><b>Author</b></td>\n</tr>\n"
2397+                        "</thead><tbody>\n",
2398+                titles[j], ids[j]);
2399+        }
2400+
2401+        ci = ris[i].ci;
2402+        s = git_reference_shorthand(ris[i].ref);
2403+
2404+        fputs("<tr><td>", fp);
2405+        xmlencode(fp, s, strlen(s));
2406+        fputs("</td><td>", fp);
2407+        if (ci->author)
2408+            printtimeshort(fp, &(ci->author->when));
2409+        fputs("</td><td>", fp);
2410+        if (ci->author)
2411+            xmlencode(fp, ci->author->name, strlen(ci->author->name));
2412+        fputs("</td></tr>\n", fp);
2413+    }
2414+    /* table footer */
2415+    if (count)
2416+        fputs("</tbody></table><br/>\n", fp);
2417+
2418+    for (i = 0; i < refcount; i++) {
2419+        commitinfo_free(ris[i].ci);
2420+        git_reference_free(ris[i].ref);
2421+    }
2422+    free(ris);
2423+
2424+    return 0;
2425 }
2426 
2427-void
2428-usage(char *argv0)
2429+void usage(char* argv0)
2430 {
2431-	fprintf(stderr, "%s [-c cachefile | -l commits] "
2432-	        "[-u baseurl] repodir\n", argv0);
2433-	exit(1);
2434+    fprintf(stderr, "%s [-c cachefile | -l commits] "
2435+                    "[-u baseurl] repodir\n",
2436+        argv0);
2437+    exit(1);
2438 }
2439 
2440-int
2441-main(int argc, char *argv[])
2442+int main(int argc, char* argv[])
2443 {
2444-	git_object *obj = NULL;
2445-	const git_oid *head = NULL;
2446-	mode_t mask;
2447-	FILE *fp, *fpread;
2448-	char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
2449-	char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
2450-	size_t n;
2451-	int i, fd;
2452-
2453-	for (i = 1; i < argc; i++) {
2454-		if (argv[i][0] != '-') {
2455-			if (repodir)
2456-				usage(argv[0]);
2457-			repodir = argv[i];
2458-		} else if (argv[i][1] == 'c') {
2459-			if (nlogcommits > 0 || i + 1 >= argc)
2460-				usage(argv[0]);
2461-			cachefile = argv[++i];
2462-		} else if (argv[i][1] == 'l') {
2463-			if (cachefile || i + 1 >= argc)
2464-				usage(argv[0]);
2465-			errno = 0;
2466-			nlogcommits = strtoll(argv[++i], &p, 10);
2467-			if (argv[i][0] == '\0' || *p != '\0' ||
2468-			    nlogcommits <= 0 || errno)
2469-				usage(argv[0]);
2470-		} else if (argv[i][1] == 'u') {
2471-			if (i + 1 >= argc)
2472-				usage(argv[0]);
2473-			baseurl = argv[++i];
2474-		}
2475-	}
2476-	if (!repodir)
2477-		usage(argv[0]);
2478-
2479-	if (!realpath(repodir, repodirabs))
2480-		err(1, "realpath");
2481-
2482-	/* do not search outside the git repository:
2483-	   GIT_CONFIG_LEVEL_APP is the highest level currently */
2484-	git_libgit2_init();
2485-	for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++)
2486-		git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "");
2487+    git_object* obj = NULL;
2488+    const git_oid* head = NULL;
2489+    mode_t mask;
2490+    FILE *fp, *fpread;
2491+    char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
2492+    char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
2493+    size_t n;
2494+    int i, fd;
2495+
2496+    for (i = 1; i < argc; i++) {
2497+        if (argv[i][0] != '-') {
2498+            if (repodir)
2499+                usage(argv[0]);
2500+            repodir = argv[i];
2501+        } else if (argv[i][1] == 'c') {
2502+            if (nlogcommits > 0 || i + 1 >= argc)
2503+                usage(argv[0]);
2504+            cachefile = argv[++i];
2505+        } else if (argv[i][1] == 'l') {
2506+            if (cachefile || i + 1 >= argc)
2507+                usage(argv[0]);
2508+            errno = 0;
2509+            nlogcommits = strtoll(argv[++i], &p, 10);
2510+            if (argv[i][0] == '\0' || *p != '\0' || nlogcommits <= 0 || errno)
2511+                usage(argv[0]);
2512+        } else if (argv[i][1] == 'u') {
2513+            if (i + 1 >= argc)
2514+                usage(argv[0]);
2515+            baseurl = argv[++i];
2516+        }
2517+    }
2518+    if (!repodir)
2519+        usage(argv[0]);
2520+
2521+    if (!realpath(repodir, repodirabs))
2522+        err(1, "realpath");
2523+
2524+    /* do not search outside the git repository:
2525+       GIT_CONFIG_LEVEL_APP is the highest level currently */
2526+    git_libgit2_init();
2527+    for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++)
2528+        git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "");
2529 
2530 #ifdef __OpenBSD__
2531-	if (unveil(repodir, "r") == -1)
2532-		err(1, "unveil: %s", repodir);
2533-	if (unveil(".", "rwc") == -1)
2534-		err(1, "unveil: .");
2535-	if (cachefile && unveil(cachefile, "rwc") == -1)
2536-		err(1, "unveil: %s", cachefile);
2537-
2538-	if (cachefile) {
2539-		if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
2540-			err(1, "pledge");
2541-	} else {
2542-		if (pledge("stdio rpath wpath cpath", NULL) == -1)
2543-			err(1, "pledge");
2544-	}
2545+    if (unveil(repodir, "r") == -1)
2546+        err(1, "unveil: %s", repodir);
2547+    if (unveil(".", "rwc") == -1)
2548+        err(1, "unveil: .");
2549+    if (cachefile && unveil(cachefile, "rwc") == -1)
2550+        err(1, "unveil: %s", cachefile);
2551+
2552+    if (cachefile) {
2553+        if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
2554+            err(1, "pledge");
2555+    } else {
2556+        if (pledge("stdio rpath wpath cpath", NULL) == -1)
2557+            err(1, "pledge");
2558+    }
2559 #endif
2560 
2561-	if (git_repository_open_ext(&repo, repodir,
2562-		GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
2563-		fprintf(stderr, "%s: cannot open repository\n", argv[0]);
2564-		return 1;
2565-	}
2566-
2567-	/* find HEAD */
2568-	if (!git_revparse_single(&obj, repo, "HEAD"))
2569-		head = git_object_id(obj);
2570-	git_object_free(obj);
2571-
2572-	/* use directory name as name */
2573-	if ((name = strrchr(repodirabs, '/')))
2574-		name++;
2575-	else
2576-		name = "";
2577-
2578-	/* strip .git suffix */
2579-	if (!(strippedname = strdup(name)))
2580-		err(1, "strdup");
2581-	if ((p = strrchr(strippedname, '.')))
2582-		if (!strcmp(p, ".git"))
2583-			*p = '\0';
2584-
2585-	/* read description or .git/description */
2586-	joinpath(path, sizeof(path), repodir, "description");
2587-	if (!(fpread = fopen(path, "r"))) {
2588-		joinpath(path, sizeof(path), repodir, ".git/description");
2589-		fpread = fopen(path, "r");
2590-	}
2591-	if (fpread) {
2592-		if (!fgets(description, sizeof(description), fpread))
2593-			description[0] = '\0';
2594-		checkfileerror(fpread, path, 'r');
2595-		fclose(fpread);
2596-	}
2597-
2598-	/* read url or .git/url */
2599-	joinpath(path, sizeof(path), repodir, "url");
2600-	if (!(fpread = fopen(path, "r"))) {
2601-		joinpath(path, sizeof(path), repodir, ".git/url");
2602-		fpread = fopen(path, "r");
2603-	}
2604-	if (fpread) {
2605-		if (!fgets(cloneurl, sizeof(cloneurl), fpread))
2606-			cloneurl[0] = '\0';
2607-		checkfileerror(fpread, path, 'r');
2608-		fclose(fpread);
2609-		cloneurl[strcspn(cloneurl, "\n")] = '\0';
2610-	}
2611-
2612-	/* check LICENSE */
2613-	for (i = 0; i < LEN(licensefiles) && !license; i++) {
2614-		if (!git_revparse_single(&obj, repo, licensefiles[i]) &&
2615-		    git_object_type(obj) == GIT_OBJ_BLOB)
2616-			license = licensefiles[i] + strlen("HEAD:");
2617-		git_object_free(obj);
2618-	}
2619-
2620-	/* check README */
2621-	for (i = 0; i < LEN(readmefiles) && !readme; i++) {
2622-		if (!git_revparse_single(&obj, repo, readmefiles[i]) &&
2623-		    git_object_type(obj) == GIT_OBJ_BLOB)
2624-			readme = readmefiles[i] + strlen("HEAD:");
2625-		git_object_free(obj);
2626-	}
2627-
2628-	if (!git_revparse_single(&obj, repo, "HEAD:.gitmodules") &&
2629-	    git_object_type(obj) == GIT_OBJ_BLOB)
2630-		submodules = ".gitmodules";
2631-	git_object_free(obj);
2632-
2633-	/* log for HEAD */
2634-	fp = efopen("log.html", "w");
2635-	relpath = "";
2636-	mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
2637-	writeheader(fp, "Log");
2638-	fputs("<table id=\"log\"><thead>\n<tr><td><b>Date</b></td>"
2639-	      "<td><b>Commit message</b></td>"
2640-	      "<td><b>Author</b></td><td class=\"num\" align=\"right\"><b>Files</b></td>"
2641-	      "<td class=\"num\" align=\"right\"><b>+</b></td>"
2642-	      "<td class=\"num\" align=\"right\"><b>-</b></td></tr>\n</thead><tbody>\n", fp);
2643-
2644-	if (cachefile && head) {
2645-		/* read from cache file (does not need to exist) */
2646-		if ((rcachefp = fopen(cachefile, "r"))) {
2647-			if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
2648-				errx(1, "%s: no object id", cachefile);
2649-			if (git_oid_fromstr(&lastoid, lastoidstr))
2650-				errx(1, "%s: invalid object id", cachefile);
2651-		}
2652-
2653-		/* write log to (temporary) cache */
2654-		if ((fd = mkstemp(tmppath)) == -1)
2655-			err(1, "mkstemp");
2656-		if (!(wcachefp = fdopen(fd, "w")))
2657-			err(1, "fdopen: '%s'", tmppath);
2658-		/* write last commit id (HEAD) */
2659-		git_oid_tostr(buf, sizeof(buf), head);
2660-		fprintf(wcachefp, "%s\n", buf);
2661-
2662-		writelog(fp, head);
2663-
2664-		if (rcachefp) {
2665-			/* append previous log to log.html and the new cache */
2666-			while (!feof(rcachefp)) {
2667-				n = fread(buf, 1, sizeof(buf), rcachefp);
2668-				if (ferror(rcachefp))
2669-					break;
2670-				if (fwrite(buf, 1, n, fp) != n ||
2671-				    fwrite(buf, 1, n, wcachefp) != n)
2672-					    break;
2673-			}
2674-			checkfileerror(rcachefp, cachefile, 'r');
2675-			fclose(rcachefp);
2676-		}
2677-		checkfileerror(wcachefp, tmppath, 'w');
2678-		fclose(wcachefp);
2679-	} else {
2680-		if (head)
2681-			writelog(fp, head);
2682-	}
2683-
2684-	fputs("</tbody></table>", fp);
2685-	writefooter(fp);
2686-	checkfileerror(fp, "log.html", 'w');
2687-	fclose(fp);
2688-
2689-	/* files for HEAD */
2690-	fp = efopen("files.html", "w");
2691-	writeheader(fp, "Files");
2692-	if (head)
2693-		writefiles(fp, head);
2694-	writefooter(fp);
2695-	checkfileerror(fp, "files.html", 'w');
2696-	fclose(fp);
2697-
2698-	/* summary page with branches and tags */
2699-	fp = efopen("refs.html", "w");
2700-	writeheader(fp, "Refs");
2701-	writerefs(fp);
2702-	writefooter(fp);
2703-	checkfileerror(fp, "refs.html", 'w');
2704-	fclose(fp);
2705-
2706-	/* Atom feed */
2707-	fp = efopen("atom.xml", "w");
2708-	writeatom(fp, 1);
2709-	checkfileerror(fp, "atom.xml", 'w');
2710-	fclose(fp);
2711-
2712-	/* Atom feed for tags / releases */
2713-	fp = efopen("tags.xml", "w");
2714-	writeatom(fp, 0);
2715-	checkfileerror(fp, "tags.xml", 'w');
2716-	fclose(fp);
2717-
2718-	/* rename new cache file on success */
2719-	if (cachefile && head) {
2720-		if (rename(tmppath, cachefile))
2721-			err(1, "rename: '%s' to '%s'", tmppath, cachefile);
2722-		umask((mask = umask(0)));
2723-		if (chmod(cachefile,
2724-		    (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mask))
2725-			err(1, "chmod: '%s'", cachefile);
2726-	}
2727-
2728-	/* cleanup */
2729-	git_repository_free(repo);
2730-	git_libgit2_shutdown();
2731-
2732-	return 0;
2733+    if (git_repository_open_ext(&repo, repodir,
2734+            GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)
2735+        < 0) {
2736+        fprintf(stderr, "%s: cannot open repository\n", argv[0]);
2737+        return 1;
2738+    }
2739+
2740+    /* find HEAD */
2741+    if (!git_revparse_single(&obj, repo, "HEAD"))
2742+        head = git_object_id(obj);
2743+    git_object_free(obj);
2744+
2745+    /* use directory name as name */
2746+    if ((name = strrchr(repodirabs, '/')))
2747+        name++;
2748+    else
2749+        name = "";
2750+
2751+    /* strip .git suffix */
2752+    if (!(strippedname = strdup(name)))
2753+        err(1, "strdup");
2754+    if ((p = strrchr(strippedname, '.')))
2755+        if (!strcmp(p, ".git"))
2756+            *p = '\0';
2757+
2758+    /* read description or .git/description */
2759+    joinpath(path, sizeof(path), repodir, "description");
2760+    if (!(fpread = fopen(path, "r"))) {
2761+        joinpath(path, sizeof(path), repodir, ".git/description");
2762+        fpread = fopen(path, "r");
2763+    }
2764+    if (fpread) {
2765+        if (!fgets(description, sizeof(description), fpread))
2766+            description[0] = '\0';
2767+        checkfileerror(fpread, path, 'r');
2768+        fclose(fpread);
2769+    }
2770+
2771+    /* read url or .git/url */
2772+    joinpath(path, sizeof(path), repodir, "url");
2773+    if (!(fpread = fopen(path, "r"))) {
2774+        joinpath(path, sizeof(path), repodir, ".git/url");
2775+        fpread = fopen(path, "r");
2776+    }
2777+    if (fpread) {
2778+        if (!fgets(cloneurl, sizeof(cloneurl), fpread))
2779+            cloneurl[0] = '\0';
2780+        checkfileerror(fpread, path, 'r');
2781+        fclose(fpread);
2782+        cloneurl[strcspn(cloneurl, "\n")] = '\0';
2783+    }
2784+
2785+    /* check LICENSE */
2786+    for (i = 0; i < LEN(licensefiles) && !license; i++) {
2787+        if (!git_revparse_single(&obj, repo, licensefiles[i]) && git_object_type(obj) == GIT_OBJ_BLOB)
2788+            license = licensefiles[i] + strlen("HEAD:");
2789+        git_object_free(obj);
2790+    }
2791+
2792+    /* check README */
2793+    for (i = 0; i < LEN(readmefiles) && !readme; i++) {
2794+        if (!git_revparse_single(&obj, repo, readmefiles[i]) && git_object_type(obj) == GIT_OBJ_BLOB)
2795+            readme = readmefiles[i] + strlen("HEAD:");
2796+        git_object_free(obj);
2797+    }
2798+
2799+    if (!git_revparse_single(&obj, repo, "HEAD:.gitmodules") && git_object_type(obj) == GIT_OBJ_BLOB)
2800+        submodules = ".gitmodules";
2801+    git_object_free(obj);
2802+
2803+    /* log for HEAD */
2804+    fp = efopen("log.html", "w");
2805+    relpath = "";
2806+    mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
2807+    writeheader(fp, "Log");
2808+    fputs("<table id=\"log\"><thead>\n<tr>"
2809+          "<td><b>Commit message</b></td>"
2810+          "<td><b>Date</b></td>"
2811+          "<td><b>Author</b></td>"
2812+          "</tr>\n</thead><tbody>\n",
2813+        fp);
2814+
2815+    if (cachefile && head) {
2816+        /* read from cache file (does not need to exist) */
2817+        if ((rcachefp = fopen(cachefile, "r"))) {
2818+            if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
2819+                errx(1, "%s: no object id", cachefile);
2820+            if (git_oid_fromstr(&lastoid, lastoidstr))
2821+                errx(1, "%s: invalid object id", cachefile);
2822+        }
2823+
2824+        /* write log to (temporary) cache */
2825+        if ((fd = mkstemp(tmppath)) == -1)
2826+            err(1, "mkstemp");
2827+        if (!(wcachefp = fdopen(fd, "w")))
2828+            err(1, "fdopen: '%s'", tmppath);
2829+        /* write last commit id (HEAD) */
2830+        git_oid_tostr(buf, sizeof(buf), head);
2831+        fprintf(wcachefp, "%s\n", buf);
2832+
2833+        writelog(fp, head);
2834+
2835+        if (rcachefp) {
2836+            /* append previous log to log.html and the new cache */
2837+            while (!feof(rcachefp)) {
2838+                n = fread(buf, 1, sizeof(buf), rcachefp);
2839+                if (ferror(rcachefp))
2840+                    break;
2841+                if (fwrite(buf, 1, n, fp) != n || fwrite(buf, 1, n, wcachefp) != n)
2842+                    break;
2843+            }
2844+            checkfileerror(rcachefp, cachefile, 'r');
2845+            fclose(rcachefp);
2846+        }
2847+        checkfileerror(wcachefp, tmppath, 'w');
2848+        fclose(wcachefp);
2849+    } else {
2850+        if (head)
2851+            writelog(fp, head);
2852+    }
2853+
2854+    fputs("</tbody></table>", fp);
2855+    writefooter(fp);
2856+    checkfileerror(fp, "log.html", 'w');
2857+    fclose(fp);
2858+
2859+    /* files for HEAD */
2860+    fp = efopen("files.html", "w");
2861+    writeheader(fp, "Files");
2862+    if (head)
2863+        writefiles(fp, head);
2864+    writefooter(fp);
2865+    checkfileerror(fp, "files.html", 'w');
2866+    fclose(fp);
2867+
2868+    /* summary page with branches and tags */
2869+    fp = efopen("refs.html", "w");
2870+    writeheader(fp, "Refs");
2871+    writerefs(fp);
2872+    writefooter(fp);
2873+    checkfileerror(fp, "refs.html", 'w');
2874+    fclose(fp);
2875+
2876+    /* Atom feed */
2877+    fp = efopen("atom.xml", "w");
2878+    writeatom(fp, 1);
2879+    checkfileerror(fp, "atom.xml", 'w');
2880+    fclose(fp);
2881+
2882+    /* Atom feed for tags / releases */
2883+    fp = efopen("tags.xml", "w");
2884+    writeatom(fp, 0);
2885+    checkfileerror(fp, "tags.xml", 'w');
2886+    fclose(fp);
2887+
2888+    /* rename new cache file on success */
2889+    if (cachefile && head) {
2890+        if (rename(tmppath, cachefile))
2891+            err(1, "rename: '%s' to '%s'", tmppath, cachefile);
2892+        umask((mask = umask(0)));
2893+        if (chmod(cachefile,
2894+                (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) & ~mask))
2895+            err(1, "chmod: '%s'", cachefile);
2896+    }
2897+
2898+    /* cleanup */
2899+    git_repository_free(repo);
2900+    git_libgit2_shutdown();
2901+
2902+    return 0;
2903 }
M · style.css +930, -277
   1@@ -1,97 +1,155 @@
   2-@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@500&text=ARJUN&display=swap");
   3+@font-face {
   4+    font-family: "Inter";
   5+    font-style: normal;
   6+    font-weight: 400;
   7+    font-display: swap;
   8+    src: url("../fonts/inter-regular-latin.woff2") format("woff2");
   9+}
  10+
  11+@font-face {
  12+    font-family: "Inter";
  13+    font-style: normal;
  14+    font-weight: 500;
  15+    font-display: swap;
  16+    src: url("../fonts/inter-medium-latin.woff2") format("woff2");
  17+}
  18+
  19+@font-face {
  20+    font-family: "Inter";
  21+    font-style: normal;
  22+    font-weight: 600;
  23+    font-display: swap;
  24+    src: url("../fonts/inter-semi-bold-latin.woff2") format("woff2");
  25+}
  26+
  27+@font-face {
  28+    font-family: "JetBrains Mono";
  29+    font-style: normal;
  30+    font-weight: 500;
  31+    font-display: swap;
  32+    src: url("../fonts/jetbrains-mono-nl-medium-latin.woff2") format("woff2");
  33+}
  34+
  35+:root {
  36+    --link: #0969da;
  37+    --scrollbar: #dedede;
  38+    --text: #1d1d1d;
  39+    --background: #fafafa;
  40+    --border: #e8e8e8;
  41+}
  42+
  43+::-webkit-scrollbar {
  44+    width: 10px;
  45+}
  46+
  47+::-webkit-scrollbar-thumb {
  48+    width: 10px;
  49+}
  50+
  51+::-webkit-scrollbar-thumb {
  52+    background: var(--scrollbar);
  53+}
  54+
  55+::-webkit-scrollbar-track {
  56+    background: transparent;
  57+}
  58+
  59+/* Firefox scrollbar*/
  60+* {
  61+    scrollbar-width: thin;
  62+    scrollbar-color: var(--scrollbar) transparent;
  63+}
  64 
  65 html {
  66-	font-size: calc(1rem + 0.25vw);
  67+    font-size: calc(16px + 0.15vw);
  68 }
  69 
  70 body {
  71-	margin: 0;
  72-	color: #000;
  73-	background-color: #fff;
  74-	font-family: sans-serif;
  75+    margin: 0;
  76+    color: var(--text);
  77+    background-color: var(--background);
  78+    font-family: "Inter", sans-serif;
  79+    overflow-x: hidden;
  80 }
  81 
  82 *::before,
  83 *::after {
  84-  box-sizing: border-box;
  85+    box-sizing: border-box;
  86 }
  87 
  88 #container {
  89-	box-sizing: border-box;
  90-	display: flex;
  91-	height: 100vh;
  92+    box-sizing: border-box;
  93+    display: flex;
  94+    height: 100vh;
  95 }
  96 
  97-#repositories {
  98-  font-weight: 600;
  99-  margin-top: 1.5rem;
 100-  margin-bottom: 1.5rem;
 101-  font-size: 2rem;
 102-  line-height: 1;
 103+a {
 104+    overflow-wrap: break-word;
 105+    text-decoration: none;
 106+    color: var(--link);
 107 }
 108 
 109-#header > h1 {
 110-  font-weight: 600;
 111-  margin-top: 1.5rem;
 112-  margin-bottom: 1.5rem;
 113-  font-size: 2rem;
 114-  line-height: 1;
 115+a:hover {
 116+    text-decoration: underline;
 117 }
 118 
 119-::-webkit-scrollbar {
 120-  width: 10px;
 121+#navbar > a {
 122+    padding: 0.55em 0.85em;
 123+    line-height: normal;
 124+    border-radius: 10px;
 125+    color: currentColor;
 126 }
 127- 
 128-::-webkit-scrollbar-thumb {
 129-  background: #666;
 130-  border-radius: 20px;
 131+
 132+#navbar .current {
 133+    color: white;
 134+    background: grey;
 135 }
 136 
 137-::-webkit-scrollbar-track {
 138-  background: transparent;
 139+#repositories {
 140+    font-weight: 600;
 141+    margin-top: 1.5rem;
 142+    margin-bottom: 1.5rem;
 143+    font-size: 2rem;
 144+    line-height: 1;
 145 }
 146 
 147-#sidebar {
 148-  display: flex;
 149-  flex-direction: column;
 150-  align-items: center;
 151-  justify-content: center;
 152-  width: 25%;
 153-  max-width: 30%;
 154-  padding: 10px;
 155-  overflow-y: hidden;
 156-  overflow-x: hidden;
 157-  margin: 5em 0em;
 158-  box-sizing: border-box;
 159+#header > h1 {
 160+    font-weight: 600;
 161+    margin-top: 1.5rem;
 162+    margin-bottom: 0.6rem;
 163+    font-size: 2rem;
 164+    line-height: 1;
 165 }
 166 
 167-#profile_img {
 168-  width: fit-content;
 169-  width: 50%;
 170-  max-width: 350px;
 171-  margin: 2em 1em;
 172-  border: 0px solid;
 173-  border-radius: 25px;
 174+#sidebar {
 175+    display: flex;
 176+    flex-direction: column;
 177+    align-items: center;
 178+    justify-content: center;
 179+    width: 25%;
 180+    max-width: 25%;
 181+    padding: 10px;
 182+    overflow-y: hidden;
 183+    overflow-x: hidden;
 184+    margin: 5em 0em;
 185+    box-sizing: border-box;
 186 }
 187 
 188-@keyframes typing-animation {
 189-  from {
 190-    width: 0%;
 191-  }
 192-  to {
 193-    width: 100%;
 194-  }
 195+#profile_img {
 196+    width: fit-content;
 197+    width: 50%;
 198+    max-width: 350px;
 199+    margin: 2em 1em;
 200+    border: 0px solid;
 201+    border-radius: 15%;
 202 }
 203 
 204 #typing {
 205-  white-space: nowrap;
 206-  overflow: hidden;
 207-  /* margin-top: 1.5rem; */
 208-  letter-spacing: 0.25em;
 209-  font-size: 1.85rem;
 210-  animation: typing-animation 0.85s steps(6, end);
 211-  font-weight: 500;
 212-  font-family: "IBM Plex Sans", sans-serif;
 213+    white-space: nowrap;
 214+    overflow: hidden;
 215+    letter-spacing: 0.25em;
 216+    font-size: 1.85rem;
 217+    font-weight: 600;
 218 }
 219 
 220 @keyframes cursor-blinking-animation {
 221@@ -105,33 +163,47 @@ body {
 222 }
 223 
 224 #cursor {
 225-  background-color: currentColor;
 226-  color: currentColor;
 227-  animation: cursor-blinking-animation 1s step-end infinite;
 228-  height: 0.15rem;
 229-  width: 1em;
 230-  margin-bottom: 0.5em;
 231+    background-color: var(--text);
 232+    color: var(--text);
 233+    animation: cursor-blinking-animation 1.2s step-end infinite;
 234+    border: none;
 235+    height: 2.2em;
 236+    vertical-align: top;
 237+    width: 1em;
 238+    margin-bottom: 11px;
 239+    margin-left: 3px;
 240 }
 241 
 242 #logo {
 243   position: absolute;
 244+  color: currentColor;
 245   top: 0;
 246   display: flex;
 247-  align-items: flex-end;
 248+  align-items: center;
 249   cursor: pointer;
 250   justify-content: center;
 251   text-decoration: none;
 252   margin-top: 1.5rem;
 253 }
 254 
 255+.logo:hover {
 256+    color: currentColor;
 257+    text-decoration: none;
 258+}
 259+
 260 #contact {
 261-	font-size: 1.2rem;
 262+    font-size: 1.2rem;
 263+    color: currentColor;
 264 }
 265 
 266 #main-view {
 267-	overflow-y: auto;
 268-	width: 100%;
 269-	line-height: 1.5;
 270+    overflow-y: auto;
 271+    width: 100%;
 272+    line-height: 1.5;
 273+}
 274+
 275+#subtitle {
 276+	padding-left: 2px;
 277 }
 278 
 279 #branches,
 280@@ -139,143 +211,173 @@ body {
 281 #index,
 282 #log,
 283 #files {
 284-	font-family: sans-serif;
 285+    width: 100%;
 286+    font-family: "Inter", sans-serif;
 287 }
 288 
 289-@media screen and (min-width: 1000px) {
 290-	#content {
 291-		display: flex;
 292-		flex-direction: column;
 293-		justify-content: flex-start;
 294-		align-items: center;
 295-	}
 296+td > a {
 297+    text-decoration: underline;
 298 }
 299 
 300-#head, #content, .table-container {
 301-	width: auto;
 302-	overflow-x: auto;
 303-	margin: auto;
 304+#md {
 305+    max-width: 70ch;
 306 }
 307 
 308-#log, #index {
 309-	min-width: 600px;
 310-	width: 100%;
 311-	overflow-x: auto;
 312+#md > pre {
 313+    padding: 20px;
 314+    border-radius: 15px;
 315+    border: 1px solid #d2d2d2;
 316 }
 317 
 318-pre:not(#about) {
 319-	overflow-x: auto;
 320-	border-radius: 4px;
 321-	padding: 10px;
 322+@media screen and (min-width: 1000px) {
 323+    #content {
 324+        display: flex;
 325+        flex-direction: column;
 326+        justify-content: flex-start;
 327+        align-items: center;
 328+    }
 329 }
 330 
 331-a {
 332-	color: #000;
 333+@media screen and (max-width: 1000px) {
 334+	html {
 335+		font-size: 17px;
 336+	}
 337 }
 338 
 339-a:hover {
 340-	color: #00c;
 341+#head,
 342+#content,
 343+.table-container {
 344+    width: auto;
 345+    overflow-x: auto;
 346+    margin: auto;
 347+    padding: 0px 25px 0px 0px;
 348+}
 349+
 350+#log,
 351+#index {
 352+    min-width: 600px;
 353+    width: 100%;
 354+    overflow-x: auto;
 355+}
 356+
 357+pre:not(#about) {
 358+    overflow-x: auto;
 359 }
 360 
 361 #head table {
 362-	margin-top: 0.5em;
 363-	margin-bottom: 0.5em;
 364+    margin-top: 0.5em;
 365+    margin-bottom: 0.5em;
 366 }
 367 
 368 #head table td:first-child {
 369-	padding: 0 0.4em 0 0;
 370+    padding: 0 0.4em 0 0;
 371 }
 372 
 373 #head table td:last-child {
 374-	padding: 0 0 0 0.4em;
 375+    padding: 0 0 0 0.4em;
 376 }
 377 
 378 #head p {
 379-	margin: 0;
 380+    margin: 0;
 381 }
 382 
 383 #files .dir {
 384-	font-weight: bold;
 385-	text-decoration: none;
 386+    font-weight: bold;
 387+    text-decoration: none;
 388 }
 389 
 390-.url {
 391-	font-family: monospace;
 392+#cloneurl {
 393+    font-family: "JetBrains Mono", monospace;
 394+    background-color: var(--border);
 395+    border: 0px solid transparent;
 396+    border-radius: 8px;
 397+    padding: 0.15em;
 398 }
 399 
 400 #home h1 {
 401-	font-size: 1.5em;
 402-	text-align: center;
 403-	margin-top: 1em;
 404+    font-size: 1.5em;
 405+    text-align: center;
 406+    margin-top: 1em;
 407 }
 408 
 409 #home h2 {
 410-	font-size: 1.25em;
 411-	margin-top: 2.5em;
 412-	margin-bottom: 1em;
 413+    font-size: 1.25em;
 414+    margin-top: 2.5em;
 415+    margin-bottom: 1em;
 416 }
 417 
 418 .md h1 {
 419-	font-size: 1.5em;
 420+    font-size: 1.5em;
 421 }
 422 
 423 .md h2 {
 424-	font-size: 1.25em;
 425+    font-size: 1.25em;
 426 }
 427 
 428-img, svg, h1, h2 {
 429-	vertical-align: middle;
 430+img,
 431+svg,
 432+h1,
 433+h2 {
 434+    vertical-align: middle;
 435 }
 436 
 437-img, svg {
 438-	border: 0;
 439+img,
 440+svg {
 441+    border: 0;
 442 }
 443 
 444 a:target {
 445-	background-cchor: #ccc;
 446+    background-color: #ccc;
 447 }
 448 
 449 a.d,
 450 a.h,
 451 a.i,
 452 a.line {
 453-	margin-right: 10px;
 454-	text-decoration: none;
 455+    margin-right: 10px;
 456+    text-decoration: none;
 457 }
 458 
 459 pre {
 460-	display: block;
 461+    max-width: 100%;
 462+    display: block;
 463 }
 464 
 465 #blob a {
 466-	color: #777;
 467+    color: #777;
 468 }
 469 #blob a:hover {
 470-	color: blue;
 471-	text-decoration: none;
 472+    color: blue;
 473+    text-decoration: none;
 474 }
 475 
 476-pre > #diff {
 477-	line-height: 3em;
 478-	font-size: 1.2em;
 479+#blob {
 480+	max-width: 100%;
 481 }
 482 
 483-table thead td {
 484-	font-weight: bold;
 485+table {
 486+    border-collapse: collapse;
 487+    border-spacing: 0;
 488 }
 489 
 490 table td {
 491-	padding: 0 0.4em;
 492+    padding: 0 0.4em;
 493 }
 494 
 495 #content table td {
 496-	vertical-align: top;
 497-	white-space: nowrap;
 498+    vertical-align: middle;
 499+    white-space: normal;
 500 }
 501 
 502 #commit-message {
 503-        min-width: 300px;
 504-        white-space: normal !important;
 505+    min-width: 300px;
 506+    white-space: normal !important;
 507+}
 508+
 509+tr:not(:last-child) {
 510+    border-color: var(--border);
 511+    border-style: solid;
 512+    border-width: 0px 0px 1px 0px;
 513 }
 514 
 515 #branches tr td,
 516@@ -283,15 +385,7 @@ table td {
 517 #index tr td,
 518 #log tr td,
 519 #files tr td {
 520-	padding: 0.75em 0.25em;
 521-} 
 522-
 523-#branches tr:nth-child(even),
 524-#tags tr:nth-child(even),
 525-#index tr:nth-child(even),
 526-#log tr:nth-child(even),
 527-#files tr:nth-child(even) {
 528-	background-color: #f2f2f2;
 529+    padding: 0.75em 0.25em;
 530 }
 531 
 532 #branches tr:hover td,
 533@@ -299,180 +393,739 @@ table td {
 534 #index tr:hover td,
 535 #log tr:hover td,
 536 #files tr:hover td {
 537-	background-color: #eee;
 538-}
 539-
 540-#index tr td:nth-child(2),
 541-#tags tr td:nth-child(3),
 542-#branches tr td:nth-child(3),
 543-#log tr td:nth-child(2) {
 544+    background-color: var(--border);
 545 }
 546 
 547 td.num {
 548-	text-align: right;
 549+    text-align: right;
 550 }
 551 
 552-.desc {
 553-	color: #777;
 554+#navbar {
 555+    font-weight: 600;
 556+    display: flex;
 557+    width: 100%;
 558+    margin: 10px 0px;
 559+    align-items: space-evenly;
 560+    justify-content: space-evenly;
 561 }
 562 
 563 hr {
 564-	border: 0;
 565-	border-top: 1px solid #777;
 566-	height: 1px;
 567+    border: 0;
 568+    border-top: 1px solid var(--border);
 569+    height: 1px;
 570+}
 571+
 572+#md pre {
 573+    margin: 0;
 574+    width: 90%;
 575+    font-family: "JetBrains Mono", monospace;
 576 }
 577 
 578-pre {	
 579-	width: 90%;
 580-	font-family: monospace;
 581+pre > code {
 582+    font-family: "JetBrains Mono", monospace;
 583 }
 584 
 585 .A,
 586 span.i,
 587 pre a.i {
 588-	color: #181;
 589+    color: #181;
 590 }
 591 
 592 .D,
 593 span.d,
 594 pre a.d {
 595-	color: #e02;
 596+    color: #e02;
 597 }
 598 
 599 pre a.h:hover,
 600 pre a.i:hover,
 601 pre a.d:hover {
 602-	text-decoration: none;
 603+    text-decoration: none;
 604 }
 605 
 606 .md table {
 607-	border-collapse: collapse;
 608-	margin: 1em 1em;
 609-	border: 1px solid #d2d2d2;
 610+    border-collapse: collapse;
 611+    margin: 1em 1em;
 612+    border: 1px solid #d2d2d2;
 613 }
 614 
 615 .md table td,
 616 .md table th {
 617-	padding: 0.25em 1em;
 618-	border: 1px solid #d2d2d2;
 619+    padding: 0.25em 1em;
 620+    border: 1px solid #d2d2d2;
 621 }
 622 
 623 #index .cat td {
 624-	font-style: italic;
 625+    font-style: italic;
 626 }
 627 
 628 #index .repo td:first-child {
 629-	padding-left: 1.5em;
 630+    padding-left: 1.5em;
 631 }
 632 
 633-@media screen and (max-width: 1000px) {
 634-        #sidebar {
 635-		justify-content: space-between;
 636-		align-items: center;
 637-		max-height: 60vh;
 638-    		width: 100%;
 639-    		max-width: 100%;
 640-		padding: 1em;
 641-		overflow-y: hidden;
 642-		overflow-x: hidden;
 643-		margin: 0;
 644-	}
 645-	#logo {
 646-		position: relative;
 647-		margin-bottom: 0rem;
 648-	}
 649-	#profile_img {
 650-		max-width: 250px;
 651-		margin: 1em;
 652-	}
 653-	#header {
 654-		padding: 15px;
 655-	}
 656-        #content {
 657-                display: block;
 658-		padding: 0px 10px;
 659-        }
 660-	#main-view {
 661-	
 662-	}
 663-	#header > h1 {
 664-		font-size: 1.75rem;
 665-	}
 666-	#header > h2 {
 667-		font-size: 1.5rem;
 668-	}
 669-	#header > h3 {
 670-		font-size: 1.25rem;
 671-	}
 672-	#container {
 673-		display: block;
 674-		height: auto;
 675-	}
 676+.chroma {
 677+	font-family: "JetBrains Mono", monospace;
 678 }
 679 
 680-@media (prefers-color-scheme: dark) {
 681-	body {
 682-		background-color: #010509;
 683-		color: #fff;
 684-	}
 685-	hr {
 686-		border-color: #555;
 687-	}
 688+/* Syntax (light) */
 689+.chroma:not(pre) {
 690+    max-width: 100%;
 691+    border: 1px solid #d2d2d2;
 692+    padding: 5px;
 693+    border-radius: 15px;
 694+}
 695+/* LineTableTD */
 696+.lntd {
 697+    vertical-align: top;
 698+    padding: 0em 0em;
 699+    margin: 0;
 700+    border: 0;
 701+}
 702+/* LineTable */
 703+.lntable {
 704+    border-spacing: 0;
 705+    padding: 0px;
 706+    margin: 0;
 707+    border: 0;
 708+    width: inherit;
 709+    overflow: auto;
 710+    display: block;
 711+}
 712+/* LineHighlight */
 713+.hl {
 714+    display: block;
 715+    width: 100%;
 716+    background-color: #ffffcc;
 717+}
 718+/* LineNumbersTable */
 719+.lnt {
 720+    margin-right: 0.4em;
 721+    padding: 0 0.4em 0 0.4em;
 722+    color: #7f7f7f;
 723+}
 724+/* LineNumbers */
 725+.ln {
 726+    margin-right: 0.4em;
 727+    padding: 0 0.4em 0 0.4em;
 728+    color: #7f7f7f;
 729+}
 730+/* Keyword */
 731+.k {
 732+    color: #aa22ff;
 733+}
 734+/* KeywordConstant */
 735+.kc {
 736+    color: #aa22ff;
 737+}
 738+/* KeywordDeclaration */
 739+.kd {
 740+    color: #aa22ff;
 741+}
 742+/* KeywordNamespace */
 743+.kn {
 744+    color: #aa22ff;
 745+}
 746+/* KeywordPseudo */
 747+.kp {
 748+    color: #aa22ff;
 749+}
 750+/* KeywordReserved */
 751+.kr {
 752+    color: #aa22ff;
 753+}
 754+/* KeywordType */
 755+.kt {
 756+    color: #00bb00;
 757+}
 758 
 759-	a {
 760-		color: #fff;
 761-	}
 762+/* NameAttribute */
 763+.na {
 764+    color: #bb4444;
 765+}
 766+/* NameBuiltin */
 767+.nb {
 768+    color: #aa22ff;
 769+}
 770 
 771-	a:hover {
 772-		color: #4d80b3;
 773-	}
 774-	a:target {
 775-		background-color: #222;
 776-	}
 777-	.desc {
 778-		color: #aaa;
 779-	}
 780-	#blob a {
 781-		color: #555;
 782-	}
 783-	#blob a:target {
 784-		color: #eee;
 785-	}
 786-	#blob a:hover {
 787-		color: #56c8ff;
 788-	}
 789-	pre a.h {
 790-		color: #0bb;
 791-	}
 792-	.A,
 793-	span.i,
 794-	pre a.i {
 795-		color: #1a1;
 796-	}
 797-	.D,
 798-	span.d,
 799-	pre a.d {
 800-		color: #e44;
 801-	}
 802-	#branches tr:hover td,
 803-	#tags tr:hover td,
 804-	#index tr:hover td,
 805-	#log tr:hover td,
 806-	#files tr:hover td {
 807-		background-color: #111;
 808-	}
 809-	pre:not(#about),
 810-	.md table,
 811-	.md table td,
 812-	.md table th {
 813-		border-color: #555;
 814-	}
 815-	#branches tr:nth-child(even),
 816-	#tags tr:nth-child(even),
 817-	#index tr:nth-child(even),
 818-	#log tr:nth-child(even),
 819-	#files tr:nth-child(even) {
 820-        	background-color: #1a1a1a;
 821-	}
 822+/* NameClass */
 823+.nc {
 824+    color: #0000ff;
 825+}
 826+/* NameConstant */
 827+.no {
 828+    color: #880000;
 829+}
 830+/* NameDecorator */
 831+.nd {
 832+    color: #aa22ff;
 833+}
 834+/* NameEntity */
 835+.ni {
 836+    color: #999999;
 837+}
 838+/* NameException */
 839+.ne {
 840+    color: #d2413a;
 841+}
 842+/* NameFunction */
 843+.nf {
 844+    color: #00a000;
 845+}
 846+
 847+/* NameLabel */
 848+.nl {
 849+    color: #a0a000;
 850+}
 851+/* NameNamespace */
 852+.nn {
 853+    color: #0000ff;
 854+}
 855 
 856+/* NameTag */
 857+.nt {
 858+    color: #008000;
 859+}
 860+/* NameVariable */
 861+.nv {
 862+    color: #b8860b;
 863+}
 864+
 865+/* LiteralString */
 866+.s {
 867+    color: #bb4444;
 868+}
 869+/* LiteralStringAffix */
 870+.sa {
 871+    color: #bb4444;
 872+}
 873+/* LiteralStringBacktick */
 874+.sb {
 875+    color: #bb4444;
 876+}
 877+/* LiteralStringChar */
 878+.sc {
 879+    color: #bb4444;
 880+}
 881+/* LiteralStringDelimiter */
 882+.dl {
 883+    color: #bb4444;
 884+}
 885+/* LiteralStringDoc */
 886+.sd {
 887+    color: #bb4444;
 888+    font-style: italic;
 889+}
 890+/* LiteralStringDouble */
 891+.s2 {
 892+    color: #bb4444;
 893+}
 894+/* LiteralStringEscape */
 895+.se {
 896+    color: #bb6622;
 897+}
 898+/* LiteralStringHeredoc */
 899+.sh {
 900+    color: #bb4444;
 901+}
 902+/* LiteralStringInterpol */
 903+.si {
 904+    color: #bb6688;
 905+}
 906+/* LiteralStringOther */
 907+.sx {
 908+    color: #008000;
 909+}
 910+/* LiteralStringRegex */
 911+.sr {
 912+    color: #bb6688;
 913+}
 914+/* LiteralStringSingle */
 915+.s1 {
 916+    color: #bb4444;
 917+}
 918+/* LiteralStringSymbol */
 919+.ss {
 920+    color: #b8860b;
 921+}
 922+/* LiteralNumber */
 923+.m {
 924+    color: #666666;
 925+}
 926+/* LiteralNumberBin */
 927+.mb {
 928+    color: #666666;
 929+}
 930+/* LiteralNumberFloat */
 931+.mf {
 932+    color: #666666;
 933+}
 934+/* LiteralNumberHex */
 935+.mh {
 936+    color: #666666;
 937+}
 938+/* LiteralNumberInteger */
 939+.mi {
 940+    color: #666666;
 941+}
 942+/* LiteralNumberIntegerLong */
 943+.il {
 944+    color: #666666;
 945+}
 946+/* LiteralNumberOct */
 947+.mo {
 948+    color: #666666;
 949+}
 950+/* Operator */
 951+.o {
 952+    color: #666666;
 953+}
 954+/* OperatorWord */
 955+.ow {
 956+    color: #aa22ff;
 957+}
 958+
 959+/* Comment */
 960+.c {
 961+    color: #008800;
 962+    font-style: italic;
 963+}
 964+/* CommentHashbang */
 965+.ch {
 966+    color: #008800;
 967+    font-style: italic;
 968+}
 969+/* CommentMultiline */
 970+.cm {
 971+    color: #008800;
 972+    font-style: italic;
 973+}
 974+/* CommentSingle */
 975+.c1 {
 976+    color: #008800;
 977+    font-style: italic;
 978+}
 979+/* CommentSpecial */
 980+.cs {
 981+    color: #008800;
 982+}
 983+/* CommentPreproc */
 984+.cp {
 985+    color: #008800;
 986+}
 987+/* CommentPreprocFile */
 988+.cpf {
 989+    color: #008800;
 990+}
 991+
 992+/* GenericDeleted */
 993+.gd {
 994+    color: #a00000;
 995+}
 996+/* GenericEmph */
 997+.ge {
 998+    font-style: italic;
 999+}
1000+/* GenericError */
1001+.gr {
1002+    color: #ff0000;
1003+}
1004+/* GenericHeading */
1005+.gh {
1006+    color: #000080;
1007+}
1008+/* GenericInserted */
1009+.gi {
1010+    color: #00a000;
1011+}
1012+/* GenericOutput */
1013+.go {
1014+    color: #888888;
1015+}
1016+/* GenericPrompt */
1017+.gp {
1018+    color: #000080;
1019+}
1020+/* GenericStrong */
1021+.gs {
1022+}
1023+/* GenericSubheading */
1024+.gu {
1025+    color: #800080;
1026+}
1027+/* GenericTraceback */
1028+.gt {
1029+    color: #0044dd;
1030+}
1031+/* GenericUnderline */
1032+.gl {
1033+    text-decoration: underline;
1034+}
1035+/* TextWhitespace */
1036+.w {
1037+    color: #bbbbbb;
1038+}
1039+
1040+@media screen and (max-width: 1300px) {
1041+        ::-webkit-scrollbar {
1042+        width: 5px;
1043+    }
1044+
1045+    ::-webkit-scrollbar-thumb {
1046+        width: 0px;
1047+    }
1048+
1049+	#sidebar {
1050+        justify-content: space-between;
1051+        align-items: center;
1052+        max-height: 60vh;
1053+        width: 100vw;
1054+        max-width: 100vw;
1055+        padding: 1em;
1056+        overflow-y: hidden;
1057+        overflow-x: hidden;
1058+        margin: 0;
1059+    }
1060+
1061+    #logo {
1062+        position: relative;
1063+        margin-bottom: 0rem;
1064+    }
1065+
1066+    #profile_img {
1067+        max-width: 150px;
1068+        margin: 1em;
1069+    }
1070+
1071+    #header {
1072+        padding: 15px;
1073+    }
1074+
1075+    #content {
1076+        display: block;
1077+        padding: 0px 10px;
1078+    }
1079+
1080+    #header > h1 {
1081+        font-size: 1.75rem;
1082+    }
1083+
1084+    #header > h2 {
1085+        font-size: 1.5rem;
1086+    }
1087+
1088+    #header > h3 {
1089+        font-size: 1.25rem;
1090+    }
1091+
1092+    #container {
1093+        display: block;
1094+        height: auto;
1095+    }
1096+}
1097+
1098+@media (prefers-color-scheme: dark) {
1099+    :root {
1100+        --link: #58a6ff;
1101+        --scrollbar: #4f4f4f;
1102+        --text: #ededed;
1103+        --background: #0f0f0f;
1104+        --border: #292929;
1105+    }
1106+
1107+    pre a.h {
1108+        color: #0bb;
1109+    }
1110+    .A,
1111+    span.i,
1112+    pre a.i {
1113+        color: #1a1;
1114+    }
1115+    .D,
1116+    span.d,
1117+    pre a.d {
1118+        color: #e44;
1119+    }
1120+
1121+    td.linenos .normal {
1122+        color: #37474f;
1123+        background-color: var(--code-background);
1124+        padding-left: 5px;
1125+        padding-right: 5px;
1126+    }
1127+    span.linenos {
1128+        color: #37474f;
1129+        background-color: var(--code-background);
1130+        padding-left: 5px;
1131+        padding-right: 5px;
1132+    }
1133+    td.linenos .special {
1134+        color: #607a86;
1135+        background-color: var(--code-background);
1136+        padding-left: 5px;
1137+        padding-right: 5px;
1138+    }
1139+    span.linenos.special {
1140+        color: #607a86;
1141+        background-color: var(--code-background);
1142+        padding-left: 5px;
1143+        padding-right: 5px;
1144+    }
1145+	
1146+    .chroma:not(pre) {
1147+    	border: 1px solid #292929;
1148+    }
1149+
1150+    #md > pre {
1151+        border: 1px solid #292929;
1152+    }
1153+
1154+    .md table {
1155+        border: 1px solid #292929;
1156+    }
1157+    
1158+    .md table td,
1159+    .md table th {
1160+        border: 1px solid #292929;
1161+    }
1162+
1163+
1164+
1165+    .hll {
1166+        background-color: #212121;
1167+    }
1168+    .c {
1169+        color: #546e7a;
1170+        font-style: italic;
1171+    } /* Comment */
1172+    .err {
1173+        color: #ff5370;
1174+    } /* Error */
1175+    .esc {
1176+        color: #89ddff;
1177+    } /* Escape */
1178+    .g {
1179+        color: #eeffff;
1180+    } /* Generic */
1181+    .k {
1182+        color: #bb80b3;
1183+    } /* Keyword */
1184+    .l {
1185+        color: #c3e88d;
1186+    } /* Literal */
1187+    .n {
1188+        color: #eeffff;
1189+    } /* Name */
1190+    .o {
1191+        color: #89ddff;
1192+    } /* Operator */
1193+    .p {
1194+        color: #89ddff;
1195+    } /* Punctuation */
1196+    .ch {
1197+        color: #546e7a;
1198+        font-style: italic;
1199+    } /* Comment.Hashbang */
1200+    .cm {
1201+        color: #546e7a;
1202+        font-style: italic;
1203+    } /* Comment.Multiline */
1204+    .cp {
1205+        color: #7f7f7f;
1206+        font-style: italic;
1207+    } /* Comment.Preproc */
1208+    .cpf {
1209+        color: #546e7a;
1210+        font-style: italic;
1211+    } /* Comment.PreprocFile */
1212+    .c1 {
1213+        color: #7f7f7f;
1214+        font-style: italic;
1215+    } /* Comment.Single */
1216+    .cs {
1217+        color: #546e7a;
1218+        font-style: italic;
1219+    } /* Comment.Special */
1220+    .gd {
1221+        color: #ff5370;
1222+    } /* Generic.Deleted */
1223+    .ge {
1224+        color: #89ddff;
1225+    } /* Generic.Emph */
1226+    .gr {
1227+        color: #ff5370;
1228+    } /* Generic.Error */
1229+    .gh {
1230+        color: #c3e88d;
1231+    } /* Generic.Heading */
1232+    .gi {
1233+        color: #c3e88d;
1234+    } /* Generic.Inserted */
1235+    .go {
1236+        color: #546e7a;
1237+    } /* Generic.Output */
1238+    .gp {
1239+        color: #ffcb6b;
1240+    } /* Generic.Prompt */
1241+    .gs {
1242+        color: #ff5370;
1243+    } /* Generic.Strong */
1244+    .gu {
1245+        color: #89ddff;
1246+    } /* Generic.Subheading */
1247+    .gt {
1248+        color: #ff5370;
1249+    } /* Generic.Traceback */
1250+    .kc {
1251+        color: #89ddff;
1252+    } /* Keyword.Constant */
1253+    .kd {
1254+        color: #bb80b3;
1255+    } /* Keyword.Declaration */
1256+    .kn {
1257+        color: #89ddff;
1258+        font-style: italic;
1259+    } /* Keyword.Namespace */
1260+    .kp {
1261+        color: #89ddff;
1262+    } /* Keyword.Pseudo */
1263+    .kr {
1264+        color: #bb80b3;
1265+    } /* Keyword.Reserved */
1266+    .kt {
1267+        color: #bb80b3;
1268+    } /* Keyword.Type */
1269+    .ld {
1270+        color: #c3e88d;
1271+    } /* Literal.Date */
1272+    .m {
1273+        color: #f78c6c;
1274+    } /* Literal.Number */
1275+    .s {
1276+        color: #c3e88d;
1277+    } /* Literal.String */
1278+    .na {
1279+        color: #bb80b3;
1280+    } /* Name.Attribute */
1281+    .nb {
1282+        color: #82aaff;
1283+    } /* Name.Builtin */
1284+    .nc {
1285+        color: #ffcb6b;
1286+    } /* Name.Class */
1287+    .no {
1288+        color: #eeffff;
1289+    } /* Name.Constant */
1290+    .nd {
1291+        color: #82aaff;
1292+    } /* Name.Decorator */
1293+    .ni {
1294+        color: #89ddff;
1295+    } /* Name.Entity */
1296+    .ne {
1297+        color: #ffcb6b;
1298+    } /* Name.Exception */
1299+    .nf {
1300+        color: #82aaff;
1301+    } /* Name.Function */
1302+    .nl {
1303+        color: #82aaff;
1304+    } /* Name.Label */
1305+    .nn {
1306+        color: #ffcb6b;
1307+    } /* Name.Namespace */
1308+    .nx {
1309+        color: #eeffff;
1310+    } /* Name.Other */
1311+    .py {
1312+        color: #ffcb6b;
1313+    } /* Name.Property */
1314+    .nt {
1315+        color: #ff5370;
1316+    } /* Name.Tag */
1317+    .nv {
1318+        color: #89ddff;
1319+    } /* Name.Variable */
1320+    .ow {
1321+        color: #89ddff;
1322+        font-style: italic;
1323+    } /* Operator.Word */
1324+    .w {
1325+        color: #eeffff;
1326+    } /* Text.Whitespace */
1327+    .mb {
1328+        color: #f78c6c;
1329+    } /* Literal.Number.Bin */
1330+    .mf {
1331+        color: #f78c6c;
1332+    } /* Literal.Number.Float */
1333+    .mh {
1334+        color: #f78c6c;
1335+    } /* Literal.Number.Hex */
1336+    .mi {
1337+        color: #f78c6c;
1338+    } /* Literal.Number.Integer */
1339+    .mo {
1340+        color: #f78c6c;
1341+    } /* Literal.Number.Oct */
1342+    .sa {
1343+        color: #bb80b3;
1344+    } /* Literal.String.Affix */
1345+    .sb {
1346+        color: #c3e88d;
1347+    } /* Literal.String.Backtick */
1348+    .sc {
1349+        color: #c3e88d;
1350+    } /* Literal.String.Char */
1351+    .dl {
1352+        color: #eeffff;
1353+    } /* Literal.String.Delimiter */
1354+    .sd {
1355+        color: #546e7a;
1356+        font-style: italic;
1357+    } /* Literal.String.Doc */
1358+    .s2 {
1359+        color: #c3e88d;
1360+    } /* Literal.String.Double */
1361+    .se {
1362+        color: #eeffff;
1363+    } /* Literal.String.Escape */
1364+    .sh {
1365+        color: #c3e88d;
1366+    } /* Literal.String.Heredoc */
1367+    .si {
1368+        color: #89ddff;
1369+    } /* Literal.String.Interpol */
1370+    .sx {
1371+        color: #c3e88d;
1372+    } /* Literal.String.Other */
1373+    .sr {
1374+        color: #89ddff;
1375+    } /* Literal.String.Regex */
1376+    .s1 {
1377+        color: #c3e88d;
1378+    } /* Literal.String.Single */
1379+    .ss {
1380+        color: #89ddff;
1381+    } /* Literal.String.Symbol */
1382+    .bp {
1383+        color: #89ddff;
1384+    } /* Name.Builtin.Pseudo */
1385+    .fm {
1386+        color: #82aaff;
1387+    } /* Name.Function.Magic */
1388+    .vc {
1389+        color: #89ddff;
1390+    } /* Name.Variable.Class */
1391+    .vg {
1392+        color: #89ddff;
1393+    } /* Name.Variable.Global */
1394+    .vi {
1395+        color: #89ddff;
1396+    } /* Name.Variable.Instance */
1397+    .vm {
1398+        color: #82aaff;
1399+    } /* Name.Variable.Magic */
1400+    .il {
1401+        color: #f78c6c;
1402+    }
1403 }