Updated README
13 files changed, 2628 insertions(+), 1804 deletions(-) | |||
---|---|---|---|
M | LICENSE | +1 | -1 |
M | Makefile | +5 | -3 |
M | README.md | +14 | -10 |
M | example_create.sh | +4 | -3 |
M | favicon.png | +0 | -0 |
A | index-style.css | +3 | -0 |
D | logo.png | +0 | -0 |
A | me.webp | +0 | -0 |
A | post-receive | +74 | -0 |
D | profile_img.png | +0 | -0 |
M | stagit-index.c | +206 | -203 |
M | stagit.c | +1391 | -1307 |
M | style.css | +930 | -277 |
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, -01@@ -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("<", fp); break;
98- case '>': fputs(">", fp); break;
99- case '\'': fputs("'" , fp); break;
100- case '&': fputs("&", fp); break;
101- case '"': fputs(""", 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("<", fp);
111+ break;
112+ case '>':
113+ fputs(">", fp);
114+ break;
115+ case '\'':
116+ fputs("'", fp);
117+ break;
118+ case '&':
119+ fputs("&", fp);
120+ break;
121+ case '"':
122+ fputs(""", 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("<", fp); break;
686- case '>': fputs(">", fp); break;
687- case '\'': fputs("'", fp); break;
688- case '&': fputs("&", fp); break;
689- case '"': fputs(""", 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("<", fp);
699+ break;
700+ case '>':
701+ fputs(">", fp);
702+ break;
703+ case '\'':
704+ fputs("'", fp);
705+ break;
706+ case '&':
707+ fputs("&", fp);
708+ break;
709+ case '"':
710+ fputs(""", 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("<", fp); break;
728- case '>': fputs(">", fp); break;
729- case '\'': fputs("'", fp); break;
730- case '&': fputs("&", fp); break;
731- case '"': fputs(""", 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("<", fp);
743+ break;
744+ case '>':
745+ fputs(">", fp);
746+ break;
747+ case '\'':
748+ fputs("'", fp);
749+ break;
750+ case '&':
751+ fputs("&", fp);
752+ break;
753+ case '"':
754+ fputs(""", 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(" <<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>>\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(" <<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>>\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(" -> ", 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(" -> ", 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(" <", fp);
1709+ xmlencode(fp, ci->author->email, strlen(ci->author->email));
1710+ fputs(">\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(" <", fp);
1781- xmlencode(fp, ci->author->email, strlen(ci->author->email));
1782- fputs(">\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 }