aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2023-04-14 14:07:43 -0300
committerEuAndreh <eu@euandre.org>2023-04-14 14:08:18 -0300
commit7afce21a30355068a4bdd186b44d79dd37d94937 (patch)
tree13dbb2350efc192a64ee7e738d5d4dd343fa789b
parentrm -rf aux/ (diff)
downloadgistatic-7afce21a30355068a4bdd186b44d79dd37d94937.tar.gz
gistatic-7afce21a30355068a4bdd186b44d79dd37d94937.tar.xz
Cleanup repo, reinit in sh
-rw-r--r--Makefile44
-rw-r--r--TODOs.md21
-rwxr-xr-xdoc/manpages.sh24
-rw-r--r--po/LC_MESSAGES/CHANGELOG.en.md/eo.po6
-rw-r--r--po/LC_MESSAGES/CHANGELOG.en.md/fr.po6
-rw-r--r--po/LC_MESSAGES/CHANGELOG.en.md/pt.po6
-rw-r--r--po/LC_MESSAGES/README.en.md/eo.po176
-rw-r--r--po/LC_MESSAGES/README.en.md/fr.po176
-rw-r--r--po/LC_MESSAGES/README.en.md/pt.po176
-rw-r--r--po/LC_MESSAGES/doc/gistatic.en.1.in/eo.po18
-rw-r--r--po/LC_MESSAGES/doc/gistatic.en.1.in/fr.po18
-rw-r--r--po/LC_MESSAGES/doc/gistatic.en.1.in/pt.po18
-rw-r--r--src/config.h.in9
l---------src/gistatic.h1
-rwxr-xr-xsrc/gistatic.in700
-rw-r--r--src/lib.c2424
-rw-r--r--src/lib.h12
-rw-r--r--src/lib.msg61
-rw-r--r--src/logerr.c67
-rw-r--r--src/logerr.h34
-rw-r--r--src/main.c22
-rw-r--r--src/static/style.css133
-rw-r--r--src/static/styles.css133
-rw-r--r--src/tar.c103
-rw-r--r--src/tar.h17
-rw-r--r--src/templates/files.html610
-rw-r--r--src/templates/log.html142
-rw-r--r--src/templates/refs.html414
-rw-r--r--src/tests-lib.c29
-rw-r--r--src/tests-lib.h13
-rwxr-xr-xtests/assert-catgets.sh67
-rwxr-xr-xtests/c-lint.sh65
-rwxr-xr-xtests/dev-integration.sh19
-rw-r--r--[-rwxr-xr-x]tests/lib.sh4
34 files changed, 1350 insertions, 4418 deletions
diff --git a/Makefile b/Makefile
index 48a52ef..96d228f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,12 @@
.POSIX:
-PREFIX = /usr/local
-MANPREFIX = '$(PREFIX)/share/man'
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
DATE = 1970-01-01
VERSION = 0.1.0
NAME = gistatic
MAILING_LIST = public-inbox
TRANSLATIONS = pt fr eo
CONTRIBLANGS =
-LDLIBS = -lgit2
.SUFFIXES:
@@ -21,12 +20,6 @@ LDLIBS = -lgit2
< $< > $@
if [ -x $< ]; then chmod +x $@; fi
-.c.o:
- $(CC) $(CFLAGS) -o $@ -c $<
-
-.c.to:
- $(CC) $(CFLAGS) -DTEST -o $@ -c $<
-
manpages.en.in = \
doc/gistatic.en.1.in
@@ -36,37 +29,9 @@ manpages.in = $(manpages.en.in) \
doc/gistatic.eo.1.in
manpages = $(manpages.in:.in=)
-sources = \
- src/logerr.c \
- src/tar.c \
- src/lib.c
-lib-objects = $(sources:.c=.o)
-all-objects = $(lib-objects) src/main.o
-t-objects = $(sources:.c=.to) src/tests-lib.to src/main.to
-
-
-all: libgistatic.a gistatic src/gistatic $(manpages)
-
-libgistatic.a: $(lib-objects)
- $(AR) $(ARFLAGS) $@ $(lib-objects)
-
-gistatic: libgistatic.a src/main.o
- $(CC) $(LDFLAGS) -o $@ src/main.o libgistatic.a $(LDLIBS)
-gistatic-tests: $(t-objects)
- $(CC) $(LDFLAGS) -o $@ $(t-objects) $(LDLIBS)
-$(all-objects) $(t-objects): src/config.h
-src/tests-lib.to: src/tests-lib.h
-src/logerr.o src/logerr.to: src/logerr.h
-src/tar.o src/tar.to: src/tar.h
-src/tar.o: src/logerr.o
-src/tar.to: src/logerr.to src/tests-lib.to
-src/lib.o src/lib.to: src/lib.h
-src/lib.o: src/logerr.o src/tar.o
-src/lib.to: src/logerr.to src/tar.to src/tests-lib.to
-src/main.o: src/logerr.o src/tar.o src/lib.o
-src/main.to: src/logerr.to src/tar.to src/lib.to src/tests-lib.to
+all: src/gistatic $(manpages)
tests/resources/repositories/repo-1/.git \
@@ -77,9 +42,6 @@ check: all gistatic-tests \
tests/resources/repositories/repo-1/.git \
tests/resources/repositories/repo-2/.git
sh tests/build-sample.sh
- ./gistatic-tests
- sh tests/assert-catgets.sh src/*.c
- sh tests/c-lint.sh src/*.c
sh tests/integration.sh
clean:
diff --git a/TODOs.md b/TODOs.md
index 435c6ce..61e566f 100644
--- a/TODOs.md
+++ b/TODOs.md
@@ -128,6 +128,8 @@ Add both to the `README.md` and to the manpages.
Current items:
- can't compare commits: this
requires `O(n!)` code and HTML to be generated;
+- can't display human-friendly date formats like "3 days ago", only timestamps;
+- can't handle big repositories, only small and medium ones;
## TODO Integrate `fallible` or other form of fault injection into the test suite {#task-ca8d80ae-9d31-5dfa-03de-b3cfccf2a8b2}
- TODO in 2021-07-31
@@ -144,9 +146,9 @@ Current items:
---
-- <input type="checkbox" disabled /> `README.md`
-- <input type="checkbox" disabled /> `CHANGELOG.md`
-- <input type="checkbox" disabled /> `src/gistatic.msg`
+- [ ] `README.md`
+- [ ] `CHANGELOG.md`
+- [ ] `src/gistatic.msg`
## TODO Add Valgrind, sanitizers and fuzzers to the test suit {#task-a6bdaeb0-7099-c728-2b7c-b080aa2fba33}
- TODO in 2021-07-31
@@ -331,3 +333,16 @@ This isn't the case for this library.
# Scratch
+
+https://stackoverflow.com/questions/424839/what-were-the-main-disadvantages-of-cgi-bin-based-web-development
+https://www.careerride.com/mchoice/cgi-disadvantages-servlets-925.aspx
+https://www.geeksforgeeks.org/common-gateway-interface-cgi/
+https://stackoverflow.com/questions/28907546/disadvantages-of-using-cgi-scripts-written-in-c-for-web-server-side
+https://www.quora.com/What-are-the-advantages-and-disadvantages-of-CGI-programming?share=1
+https://www.nginx.com/resources/wiki/start/topics/examples/fastcgiexample/
+
+
+i18n
+https://github.com/projectfluent/fluent/wiki/Fluent-and-L20n
+https://github.com/projectfluent/fluent/wiki/Fluent-vs-gettext
+https://github.com/projectfluent/fluent/wiki/Fluent-and-ICU-MessageFormat
diff --git a/doc/manpages.sh b/doc/manpages.sh
index 5c83a37..8b30c0f 100755
--- a/doc/manpages.sh
+++ b/doc/manpages.sh
@@ -1,7 +1,7 @@
#!/bin/sh
set -eu
-while getopts 'iuHo:p:' flag; do
+while getopts 'iup:' flag; do
case "$flag" in
i)
ACTION=install
@@ -9,12 +9,6 @@ while getopts 'iuHo:p:' flag; do
u)
ACTION=uninstall
;;
- H)
- ACTION=html
- ;;
- o)
- OUTDIR="$OPTARG"
- ;;
p)
MANPREFIX="$OPTARG"
;;
@@ -32,30 +26,22 @@ assert_arg() {
fi
}
-assert_arg "${ACTION:-}" '-[iuH] for choosing action'
+assert_arg "${ACTION:-}" '-[iu] for choosing action'
+assert_arg "${MANPREFIX:-}" '-p MANPREFIX'
for f in "$@"; do
l="$(echo "$f" | awk -F. '{print $(NF-1)}')"
n="$(echo "$f" | awk -F. '{print $NF}')"
case "$ACTION" in
- html)
- assert_arg "${OUTDIR:-}" '-o OUTDIR'
- to_name="$(basename "${f%.$l.$n}.$n.html")"
- mkdir -p "$OUTDIR/$l"
- pandoc -s -r man -w html --metadata "lang=$l" \
- < "$f" > "$OUTDIR/$l/$to_name"
- ;;
install)
- assert_arg "${MANPREFIX:-}" '-p MANPREFIX'
- to_name="$(basename "${f%.$l.$n}.$n")"
+ to_name="$(basename "${f%."$l"."$n"}.$n")"
mkdir -p "$MANPREFIX/$l/man$n" "$MANPREFIX/man$n"
cp "$f" "$MANPREFIX/$l/man$n/$to_name"
ln -fs "../en/man$n/$to_name" \
"$MANPREFIX/man$n/$to_name"
;;
uninstall)
- assert_arg "${MANPREFIX:-}" '-p MANPREFIX'
- to_name="$(basename "${f%.$l.$n}.$n")"
+ to_name="$(basename "${f%."$l"."$n"}.$n")"
rm -f \
"$MANPREFIX/$l/man$n/$to_name" \
"$MANPREFIX/man$n/$to_name"
diff --git a/po/LC_MESSAGES/CHANGELOG.en.md/eo.po b/po/LC_MESSAGES/CHANGELOG.en.md/eo.po
deleted file mode 100644
index 92c03c1..0000000
--- a/po/LC_MESSAGES/CHANGELOG.en.md/eo.po
+++ /dev/null
@@ -1,6 +0,0 @@
-#
-msgid ""
-msgstr ""
-
-msgid "Changelog for [gistatic](https://euandreh.xyz/gistatic/en/)."
-msgstr ""
diff --git a/po/LC_MESSAGES/CHANGELOG.en.md/fr.po b/po/LC_MESSAGES/CHANGELOG.en.md/fr.po
deleted file mode 100644
index 92c03c1..0000000
--- a/po/LC_MESSAGES/CHANGELOG.en.md/fr.po
+++ /dev/null
@@ -1,6 +0,0 @@
-#
-msgid ""
-msgstr ""
-
-msgid "Changelog for [gistatic](https://euandreh.xyz/gistatic/en/)."
-msgstr ""
diff --git a/po/LC_MESSAGES/CHANGELOG.en.md/pt.po b/po/LC_MESSAGES/CHANGELOG.en.md/pt.po
deleted file mode 100644
index 92c03c1..0000000
--- a/po/LC_MESSAGES/CHANGELOG.en.md/pt.po
+++ /dev/null
@@ -1,6 +0,0 @@
-#
-msgid ""
-msgstr ""
-
-msgid "Changelog for [gistatic](https://euandreh.xyz/gistatic/en/)."
-msgstr ""
diff --git a/po/LC_MESSAGES/README.en.md/eo.po b/po/LC_MESSAGES/README.en.md/eo.po
deleted file mode 100644
index ace966d..0000000
--- a/po/LC_MESSAGES/README.en.md/eo.po
+++ /dev/null
@@ -1,176 +0,0 @@
-#
-msgid ""
-msgstr ""
-
-msgid "gistatic"
-msgstr ""
-
-msgid "Static Git repository generator"
-msgstr ""
-
-msgid ""
-"Generate HTML pages for a Git repository. The generated pages are static and"
-" offline-first, and can be made available online as static assets."
-msgstr ""
-
-msgid "Usage"
-msgstr ""
-
-msgid "Generate static HTML website for Git repository:"
-msgstr ""
-
-msgid "$ gistatic path/to/git/ -o public/\n"
-msgstr ""
-
-msgid "Installation"
-msgstr ""
-
-msgid "Get the latest tarball and install it:"
-msgstr ""
-
-msgid ""
-"wget https://euandreh.xyz/gistatic/tarballs/gistatic-main.tar.gz\n"
-"tar xvf gistatic-main.tar.gz\n"
-"cd gistatic-main/\n"
-"make\n"
-"[sudo] make install\n"
-msgstr ""
-
-msgid "The requirements are Perl with dependencies:"
-msgstr ""
-
-msgid "[Git::Raw][Git::Raw]: Perl bindings to [libgit2][libgit2];"
-msgstr ""
-
-msgid ""
-"[Mustache::Simple][Mustache::Simple]: template library for generating output"
-" files;"
-msgstr ""
-
-msgid "[CommonMark][cm-p]: [CommonMark][cm-spec] renderer."
-msgstr ""
-
-msgid "Documentation"
-msgstr ""
-
-msgid ""
-"Documentation available via installed manpages, also available online: "
-"[`gistatic.1`](gistatic.1.html) and [`gistatic-template.5`](gistatic-"
-"template.5.html)."
-msgstr ""
-
-msgid "Contributing"
-msgstr ""
-
-msgid "Extra tools used for development are:"
-msgstr ""
-
-msgid "[Perl::Critic][Perl::Critic] and [B::Lint][B::Lint] for linting;"
-msgstr ""
-
-msgid "[perltidy][perltidy] for code formatting;"
-msgstr ""
-
-msgid "[ShellCheck][ShellCheck] for validating scripts;"
-msgstr ""
-
-msgid "[pandoc][pandoc] for generating the documentation HTML and website."
-msgstr ""
-
-msgid "For running the extra development-only checks, run:"
-msgstr ""
-
-msgid "$ make dev-check\n"
-msgstr ""
-
-msgid "and for generating the documentation HTML and website, run:"
-msgstr ""
-
-msgid "$ make public\n"
-msgstr ""
-
-msgid ""
-"Send contributions to the [mailing list][mailing list] via [`git send-"
-"email`](https://git-send-email.io/)."
-msgstr ""
-
-msgid "Links"
-msgstr ""
-
-msgid "[homepage](https://euandreh.xyz/gistatic/en/)"
-msgstr ""
-
-msgid "[source code](https://git.euandreh.xyz/gistatic/)"
-msgstr ""
-
-msgid "[bug tracking](https://euandreh.xyz/gistatic/TODOs.html)"
-msgstr ""
-
-msgid "[mailing list][mailing list]"
-msgstr ""
-
-msgid "[CI logs](https://euandreh.xyz/gistatic/ci.html)"
-msgstr ""
-
-msgid "[CHANGELOG](https://euandreh.xyz/gistatic/en/CHANGELOG.html)"
-msgstr ""
-
-msgid "License"
-msgstr ""
-
-msgid ""
-"The code is licensed under [GNU Affero General Public License v3.0 or "
-"later][AGPL-3.0-or-later] (AGPL-3.0-or-later)."
-msgstr ""
-
-#, fuzzy
-msgid "[Git::Raw]: https://metacpan.org/pod/Git::Raw"
-msgstr "[Git::Raw]: https://metacpan.org/pod/Git::Raw"
-
-#, fuzzy
-msgid "[libgit2]: https://libgit2.org/"
-msgstr "[libgit2]: https://libgit2.org/"
-
-#, fuzzy
-msgid "[Mustache::Simple]: https://metacpan.org/pod/Mustache::Simple"
-msgstr "[Mustache::Simple]: https://metacpan.org/pod/Mustache::Simple"
-
-#, fuzzy
-msgid "[cm-p]: https://metacpan.org/pod/CommonMark"
-msgstr "[cm-p]: https://metacpan.org/pod/CommonMark"
-
-#, fuzzy
-msgid "[cm-spec]: https://commonmark.org/"
-msgstr "[cm-spec]: https://commonmark.org/"
-
-#, fuzzy
-msgid "[Perl::Critic]: https://metacpan.org/pod/Perl::Critic"
-msgstr "[Perl::Critic]: https://metacpan.org/pod/Perl::Critic"
-
-#, fuzzy
-msgid "[B::Lint]: https://metacpan.org/pod/B::Lint"
-msgstr "[B::Lint]: https://metacpan.org/pod/B::Lint"
-
-#, fuzzy
-msgid "[perltidy]: https://metacpan.org/pod/perltidy"
-msgstr "[perltidy]: https://metacpan.org/pod/perltidy"
-
-#, fuzzy
-msgid "[ShellCheck]: https://www.shellcheck.net/"
-msgstr "[ShellCheck]: https://www.shellcheck.net/"
-
-#, fuzzy
-msgid "[pandoc]: https://pandoc.org/"
-msgstr "[pandoc]: https://pandoc.org/"
-
-#, fuzzy
-msgid ""
-"[mailing list]: https://lists.sr.ht/~euandreh/public-"
-"inbox?search=%5Bgistatic%5D"
-msgstr ""
-"[mailing list]: https://lists.sr.ht/~euandreh/public-"
-"inbox?search=%5Bgistatic%5D"
-
-#, fuzzy
-msgid "[AGPL-3.0-or-later]: https://git.euandreh.xyz/gistatic/tree/COPYING"
-msgstr "[AGPL-3.0-or-later]: https://git.euandreh.xyz/gistatic/tree/COPYING"
diff --git a/po/LC_MESSAGES/README.en.md/fr.po b/po/LC_MESSAGES/README.en.md/fr.po
deleted file mode 100644
index ace966d..0000000
--- a/po/LC_MESSAGES/README.en.md/fr.po
+++ /dev/null
@@ -1,176 +0,0 @@
-#
-msgid ""
-msgstr ""
-
-msgid "gistatic"
-msgstr ""
-
-msgid "Static Git repository generator"
-msgstr ""
-
-msgid ""
-"Generate HTML pages for a Git repository. The generated pages are static and"
-" offline-first, and can be made available online as static assets."
-msgstr ""
-
-msgid "Usage"
-msgstr ""
-
-msgid "Generate static HTML website for Git repository:"
-msgstr ""
-
-msgid "$ gistatic path/to/git/ -o public/\n"
-msgstr ""
-
-msgid "Installation"
-msgstr ""
-
-msgid "Get the latest tarball and install it:"
-msgstr ""
-
-msgid ""
-"wget https://euandreh.xyz/gistatic/tarballs/gistatic-main.tar.gz\n"
-"tar xvf gistatic-main.tar.gz\n"
-"cd gistatic-main/\n"
-"make\n"
-"[sudo] make install\n"
-msgstr ""
-
-msgid "The requirements are Perl with dependencies:"
-msgstr ""
-
-msgid "[Git::Raw][Git::Raw]: Perl bindings to [libgit2][libgit2];"
-msgstr ""
-
-msgid ""
-"[Mustache::Simple][Mustache::Simple]: template library for generating output"
-" files;"
-msgstr ""
-
-msgid "[CommonMark][cm-p]: [CommonMark][cm-spec] renderer."
-msgstr ""
-
-msgid "Documentation"
-msgstr ""
-
-msgid ""
-"Documentation available via installed manpages, also available online: "
-"[`gistatic.1`](gistatic.1.html) and [`gistatic-template.5`](gistatic-"
-"template.5.html)."
-msgstr ""
-
-msgid "Contributing"
-msgstr ""
-
-msgid "Extra tools used for development are:"
-msgstr ""
-
-msgid "[Perl::Critic][Perl::Critic] and [B::Lint][B::Lint] for linting;"
-msgstr ""
-
-msgid "[perltidy][perltidy] for code formatting;"
-msgstr ""
-
-msgid "[ShellCheck][ShellCheck] for validating scripts;"
-msgstr ""
-
-msgid "[pandoc][pandoc] for generating the documentation HTML and website."
-msgstr ""
-
-msgid "For running the extra development-only checks, run:"
-msgstr ""
-
-msgid "$ make dev-check\n"
-msgstr ""
-
-msgid "and for generating the documentation HTML and website, run:"
-msgstr ""
-
-msgid "$ make public\n"
-msgstr ""
-
-msgid ""
-"Send contributions to the [mailing list][mailing list] via [`git send-"
-"email`](https://git-send-email.io/)."
-msgstr ""
-
-msgid "Links"
-msgstr ""
-
-msgid "[homepage](https://euandreh.xyz/gistatic/en/)"
-msgstr ""
-
-msgid "[source code](https://git.euandreh.xyz/gistatic/)"
-msgstr ""
-
-msgid "[bug tracking](https://euandreh.xyz/gistatic/TODOs.html)"
-msgstr ""
-
-msgid "[mailing list][mailing list]"
-msgstr ""
-
-msgid "[CI logs](https://euandreh.xyz/gistatic/ci.html)"
-msgstr ""
-
-msgid "[CHANGELOG](https://euandreh.xyz/gistatic/en/CHANGELOG.html)"
-msgstr ""
-
-msgid "License"
-msgstr ""
-
-msgid ""
-"The code is licensed under [GNU Affero General Public License v3.0 or "
-"later][AGPL-3.0-or-later] (AGPL-3.0-or-later)."
-msgstr ""
-
-#, fuzzy
-msgid "[Git::Raw]: https://metacpan.org/pod/Git::Raw"
-msgstr "[Git::Raw]: https://metacpan.org/pod/Git::Raw"
-
-#, fuzzy
-msgid "[libgit2]: https://libgit2.org/"
-msgstr "[libgit2]: https://libgit2.org/"
-
-#, fuzzy
-msgid "[Mustache::Simple]: https://metacpan.org/pod/Mustache::Simple"
-msgstr "[Mustache::Simple]: https://metacpan.org/pod/Mustache::Simple"
-
-#, fuzzy
-msgid "[cm-p]: https://metacpan.org/pod/CommonMark"
-msgstr "[cm-p]: https://metacpan.org/pod/CommonMark"
-
-#, fuzzy
-msgid "[cm-spec]: https://commonmark.org/"
-msgstr "[cm-spec]: https://commonmark.org/"
-
-#, fuzzy
-msgid "[Perl::Critic]: https://metacpan.org/pod/Perl::Critic"
-msgstr "[Perl::Critic]: https://metacpan.org/pod/Perl::Critic"
-
-#, fuzzy
-msgid "[B::Lint]: https://metacpan.org/pod/B::Lint"
-msgstr "[B::Lint]: https://metacpan.org/pod/B::Lint"
-
-#, fuzzy
-msgid "[perltidy]: https://metacpan.org/pod/perltidy"
-msgstr "[perltidy]: https://metacpan.org/pod/perltidy"
-
-#, fuzzy
-msgid "[ShellCheck]: https://www.shellcheck.net/"
-msgstr "[ShellCheck]: https://www.shellcheck.net/"
-
-#, fuzzy
-msgid "[pandoc]: https://pandoc.org/"
-msgstr "[pandoc]: https://pandoc.org/"
-
-#, fuzzy
-msgid ""
-"[mailing list]: https://lists.sr.ht/~euandreh/public-"
-"inbox?search=%5Bgistatic%5D"
-msgstr ""
-"[mailing list]: https://lists.sr.ht/~euandreh/public-"
-"inbox?search=%5Bgistatic%5D"
-
-#, fuzzy
-msgid "[AGPL-3.0-or-later]: https://git.euandreh.xyz/gistatic/tree/COPYING"
-msgstr "[AGPL-3.0-or-later]: https://git.euandreh.xyz/gistatic/tree/COPYING"
diff --git a/po/LC_MESSAGES/README.en.md/pt.po b/po/LC_MESSAGES/README.en.md/pt.po
deleted file mode 100644
index ace966d..0000000
--- a/po/LC_MESSAGES/README.en.md/pt.po
+++ /dev/null
@@ -1,176 +0,0 @@
-#
-msgid ""
-msgstr ""
-
-msgid "gistatic"
-msgstr ""
-
-msgid "Static Git repository generator"
-msgstr ""
-
-msgid ""
-"Generate HTML pages for a Git repository. The generated pages are static and"
-" offline-first, and can be made available online as static assets."
-msgstr ""
-
-msgid "Usage"
-msgstr ""
-
-msgid "Generate static HTML website for Git repository:"
-msgstr ""
-
-msgid "$ gistatic path/to/git/ -o public/\n"
-msgstr ""
-
-msgid "Installation"
-msgstr ""
-
-msgid "Get the latest tarball and install it:"
-msgstr ""
-
-msgid ""
-"wget https://euandreh.xyz/gistatic/tarballs/gistatic-main.tar.gz\n"
-"tar xvf gistatic-main.tar.gz\n"
-"cd gistatic-main/\n"
-"make\n"
-"[sudo] make install\n"
-msgstr ""
-
-msgid "The requirements are Perl with dependencies:"
-msgstr ""
-
-msgid "[Git::Raw][Git::Raw]: Perl bindings to [libgit2][libgit2];"
-msgstr ""
-
-msgid ""
-"[Mustache::Simple][Mustache::Simple]: template library for generating output"
-" files;"
-msgstr ""
-
-msgid "[CommonMark][cm-p]: [CommonMark][cm-spec] renderer."
-msgstr ""
-
-msgid "Documentation"
-msgstr ""
-
-msgid ""
-"Documentation available via installed manpages, also available online: "
-"[`gistatic.1`](gistatic.1.html) and [`gistatic-template.5`](gistatic-"
-"template.5.html)."
-msgstr ""
-
-msgid "Contributing"
-msgstr ""
-
-msgid "Extra tools used for development are:"
-msgstr ""
-
-msgid "[Perl::Critic][Perl::Critic] and [B::Lint][B::Lint] for linting;"
-msgstr ""
-
-msgid "[perltidy][perltidy] for code formatting;"
-msgstr ""
-
-msgid "[ShellCheck][ShellCheck] for validating scripts;"
-msgstr ""
-
-msgid "[pandoc][pandoc] for generating the documentation HTML and website."
-msgstr ""
-
-msgid "For running the extra development-only checks, run:"
-msgstr ""
-
-msgid "$ make dev-check\n"
-msgstr ""
-
-msgid "and for generating the documentation HTML and website, run:"
-msgstr ""
-
-msgid "$ make public\n"
-msgstr ""
-
-msgid ""
-"Send contributions to the [mailing list][mailing list] via [`git send-"
-"email`](https://git-send-email.io/)."
-msgstr ""
-
-msgid "Links"
-msgstr ""
-
-msgid "[homepage](https://euandreh.xyz/gistatic/en/)"
-msgstr ""
-
-msgid "[source code](https://git.euandreh.xyz/gistatic/)"
-msgstr ""
-
-msgid "[bug tracking](https://euandreh.xyz/gistatic/TODOs.html)"
-msgstr ""
-
-msgid "[mailing list][mailing list]"
-msgstr ""
-
-msgid "[CI logs](https://euandreh.xyz/gistatic/ci.html)"
-msgstr ""
-
-msgid "[CHANGELOG](https://euandreh.xyz/gistatic/en/CHANGELOG.html)"
-msgstr ""
-
-msgid "License"
-msgstr ""
-
-msgid ""
-"The code is licensed under [GNU Affero General Public License v3.0 or "
-"later][AGPL-3.0-or-later] (AGPL-3.0-or-later)."
-msgstr ""
-
-#, fuzzy
-msgid "[Git::Raw]: https://metacpan.org/pod/Git::Raw"
-msgstr "[Git::Raw]: https://metacpan.org/pod/Git::Raw"
-
-#, fuzzy
-msgid "[libgit2]: https://libgit2.org/"
-msgstr "[libgit2]: https://libgit2.org/"
-
-#, fuzzy
-msgid "[Mustache::Simple]: https://metacpan.org/pod/Mustache::Simple"
-msgstr "[Mustache::Simple]: https://metacpan.org/pod/Mustache::Simple"
-
-#, fuzzy
-msgid "[cm-p]: https://metacpan.org/pod/CommonMark"
-msgstr "[cm-p]: https://metacpan.org/pod/CommonMark"
-
-#, fuzzy
-msgid "[cm-spec]: https://commonmark.org/"
-msgstr "[cm-spec]: https://commonmark.org/"
-
-#, fuzzy
-msgid "[Perl::Critic]: https://metacpan.org/pod/Perl::Critic"
-msgstr "[Perl::Critic]: https://metacpan.org/pod/Perl::Critic"
-
-#, fuzzy
-msgid "[B::Lint]: https://metacpan.org/pod/B::Lint"
-msgstr "[B::Lint]: https://metacpan.org/pod/B::Lint"
-
-#, fuzzy
-msgid "[perltidy]: https://metacpan.org/pod/perltidy"
-msgstr "[perltidy]: https://metacpan.org/pod/perltidy"
-
-#, fuzzy
-msgid "[ShellCheck]: https://www.shellcheck.net/"
-msgstr "[ShellCheck]: https://www.shellcheck.net/"
-
-#, fuzzy
-msgid "[pandoc]: https://pandoc.org/"
-msgstr "[pandoc]: https://pandoc.org/"
-
-#, fuzzy
-msgid ""
-"[mailing list]: https://lists.sr.ht/~euandreh/public-"
-"inbox?search=%5Bgistatic%5D"
-msgstr ""
-"[mailing list]: https://lists.sr.ht/~euandreh/public-"
-"inbox?search=%5Bgistatic%5D"
-
-#, fuzzy
-msgid "[AGPL-3.0-or-later]: https://git.euandreh.xyz/gistatic/tree/COPYING"
-msgstr "[AGPL-3.0-or-later]: https://git.euandreh.xyz/gistatic/tree/COPYING"
diff --git a/po/LC_MESSAGES/doc/gistatic.en.1.in/eo.po b/po/LC_MESSAGES/doc/gistatic.en.1.in/eo.po
deleted file mode 100644
index a2c9416..0000000
--- a/po/LC_MESSAGES/doc/gistatic.en.1.in/eo.po
+++ /dev/null
@@ -1,18 +0,0 @@
-# SOME DESCRIPTIVE TITLE
-# Copyright (C) YEAR Free Software Foundation, Inc.
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2021-07-27 14:28-0300\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
diff --git a/po/LC_MESSAGES/doc/gistatic.en.1.in/fr.po b/po/LC_MESSAGES/doc/gistatic.en.1.in/fr.po
deleted file mode 100644
index a2c9416..0000000
--- a/po/LC_MESSAGES/doc/gistatic.en.1.in/fr.po
+++ /dev/null
@@ -1,18 +0,0 @@
-# SOME DESCRIPTIVE TITLE
-# Copyright (C) YEAR Free Software Foundation, Inc.
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2021-07-27 14:28-0300\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
diff --git a/po/LC_MESSAGES/doc/gistatic.en.1.in/pt.po b/po/LC_MESSAGES/doc/gistatic.en.1.in/pt.po
deleted file mode 100644
index a2c9416..0000000
--- a/po/LC_MESSAGES/doc/gistatic.en.1.in/pt.po
+++ /dev/null
@@ -1,18 +0,0 @@
-# SOME DESCRIPTIVE TITLE
-# Copyright (C) YEAR Free Software Foundation, Inc.
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2021-07-27 14:28-0300\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
diff --git a/src/config.h.in b/src/config.h.in
deleted file mode 100644
index 5f7b398..0000000
--- a/src/config.h.in
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef CONFIG_H
-#define CONFIG_H
-
-#define _POSIX_C_SOURCE 200809L
-#define VERSION "@VERSION@"
-#define DATE "@DATE@"
-#define PROGNAME "@NAME@"
-
-#endif
diff --git a/src/gistatic.h b/src/gistatic.h
deleted file mode 120000
index a564cc1..0000000
--- a/src/gistatic.h
+++ /dev/null
@@ -1 +0,0 @@
-lib.h \ No newline at end of file
diff --git a/src/gistatic.in b/src/gistatic.in
index 3f005ee..21980a5 100755
--- a/src/gistatic.in
+++ b/src/gistatic.in
@@ -1,25 +1,27 @@
#!/bin/sh
# shellcheck disable=2034 disable=2059
+# FIXME: remove these ^^^
set -eu
MSG_USAGE_EN='Usage:
- @NAME@ -i -o DIRECTORY REPOSITORY...
- @NAME@ -o DIRECTORY -u CLONE_URL REPOSITORY
- @NAME@ [-hV]
-'
-
-MSG_HELP_EN='Options:
- -i build the index page of the repositories
- -u CLONE_URL address to be shown alongside "git clone"
- -o DIRECTORY output where to place the generated files
- -h, --help show this help
- -V, --version print the version information
-
-See "man @NAME@" for more information.'
-
-MSG_MISSING_CLIARG_EN='Missing %s\n'
-MSG_INCOMPATIBLE_OPTIONS_EN='Incompatible options -u and -i'
-MSG_MISSING_ARGS_EN='Missing [PATH | [PATHS]]'
+ gistatic -i -o DIRECTORY REPOSITORY...
+ gistatic -o DIRECTORY -u CLONE_URL [-b MAIN_BRANCH] REPOSITORY
+ gistatic [-hV]'
+
+MSG_HELP_EN='
+Options:
+ -i build the index page of the repositories
+ -u CLONE_URL address to be shown alongside "git clone"
+ -o DIRECTORY output where to place the generated files
+ -b MAIN_BRANCH the default branch of the repository (default: main)
+ -h, --help show this help
+ -V, --version print the version information
+
+See "man gistatic" for more information.'
+
+MSG_MISSING_CLIARG_EN='Missing %s'
+MSG_INCOMPATIBLE_OPTIONS_EN='Incompatible options: '
+MSG_MISSING_ARGS_EN='[PATH | [PATHS]]'
MSG_LANGNAME_EN='en'
MSG_INDEX_DESCRIPTION_EN='Index of repositories'
MSG_DEFAULT_TITLE_EN='Repositories'
@@ -28,7 +30,6 @@ MSG_LOGO_ALT_REPOSITORY_EN='Logo image of the repository'
MSG_NAME_EN='Name'
MSG_DESCRIPTION_EN='Description'
MSG_LAST_COMMIT_EN='Last commit'
-MSG_FOOTER_TEMPLATE_EN='Generated with <a href="https://euandreh.xyz/@NAME@/">@NAME@</a>.'
MSG_COMMIT_FEED_EN='commit feed'
MSG_TAGS_FEED_EN='tags feed'
MSG_NAV_FILES_EN='files'
@@ -40,6 +41,10 @@ MSG_THEAD_AUTHOR_EN='Author'
MSG_THEAD_DATE_EN='Date'
MSG_THEAD_TAG_EN='Tag'
MSG_TITLE_REFS_EN='refs'
+MSG_GENERATING_LOG_EN='Generating %s...'
+
+# HTML escaped messages
+MSG_ESCAPED_FOOTER_TEMPLATE_EN='Generated with <a href="https://euandreh.xyz/gistatic/">gistatic</a>.'
set_lang() {
lang="$1"
@@ -57,7 +62,6 @@ set_lang() {
MSG_NAME=\$MSG_NAME_$lang
MSG_DESCRIPTION=\$MSG_DESCRIPTION_$lang
MSG_LAST_COMMIT=\$MSG_LAST_COMMIT_$lang
- MSG_FOOTER_TEMPLATE=\$MSG_FOOTER_TEMPLATE_$lang
MSG_COMMIT_FEED=\$MSG_COMMIT_FEED_$lang
MSG_TAGS_FEED=\$MSG_TAGS_FEED_$lang
MSG_NAV_FILES=\$MSG_NAV_FILES_$lang
@@ -69,6 +73,9 @@ set_lang() {
MSG_THEAD_DATE=\$MSG_THEAD_DATE_$lang
MSG_THEAD_TAG=\$MSG_THEAD_TAG_$lang
MSG_TITLE_REFS=\$MSG_TITLE_REFS_$lang
+ MSG_GENERATING_LOG=\$MSG_GENERATING_LOG_$lang
+
+ MSG_ESCAPED_FOOTER_TEMPLATE=\$MSG_ESCAPED_FOOTER_TEMPLATE_$lang
"
}
@@ -76,7 +83,7 @@ get_lang() {
# LC_MESSAGES="ll_CC.CODESET@modifier" -> ll_CC, where quotes
# are optional
locale 2>/dev/null |
- grep LC_MESSAGES |
+ grep ^LC_MESSAGES |
cut -d. -f1 |
cut -d\" -f2 |
cut -d= -f2
@@ -93,6 +100,10 @@ esac
# Utilities
#
+tac() {
+ sed '1!G;h;$!d'
+}
+
escape() {
echo "$1" |
sed -e 's|&|\&amp;|g;s|<|\&lt;|g;s|>|\&gt;|g;s|"|\&quot;|g' \
@@ -119,7 +130,7 @@ help() {
}
version() {
- printf '@NAME@-@VERSION@ @DATE@\n'
+ printf 'gistatic-0.1.0 1970-01-01\n'
}
@@ -199,16 +210,22 @@ print_style() {
:root {
--color: black;
--background-color: white;
+ --background-contrast-color: hsl(0, 0%, 98%);
--hover-color: hsl(0, 0%, 93%);
--nav-color: hsl(0, 0%, 87%);
+ --selected-color: hsl(0, 0%, 80%);
+ --diff-added-color: hsl(120, 100%, 23%);
+ --diff-removed-color: hsl(0, 100%, 47%);
}
@media(prefers-color-scheme: dark) {
:root {
--color: white;
--background-color: black;
+ --background-contrast-color: hsl(0, 0%, 2%);
--hover-color: hsl(0, 0%, 7%);
--nav-color: hsl(0, 0%, 13%);
+ --selected-color: hsl(0, 0%, 20%);
}
body {
@@ -293,6 +310,69 @@ td {
padding-right: 1em;
}
+
+/* commit page */
+
+.diff-added, .diff-removed {
+ text-decoration: none;
+}
+
+.diff-added:target, .diff-removed:target {
+ background-color: var(--selected-color);
+}
+
+.diff-added, .diff-added:visited {
+ color: var(--diff-added-color);
+}
+
+.diff-removed, .diff-removed:visited {
+ color: var(--diff-removed-color);
+}
+
+
+/* log page */
+
+.log-commit-box {
+ padding: 1em;
+ margin: 1em;
+ background-color: var(--background-contrast-color);
+}
+
+.log-commit-tag {
+ padding: 2px;
+ border: 1px solid;
+ color: var(--color);
+}
+
+.log-head-highlight {
+ background-color: #ff8888; /* FIXME: hsl + dark-mode */
+}
+
+.log-branch-highlight {
+ background-color: #88ff88; /* FIXME: hsl + dark-mode */
+}
+
+.log-tag-highlight {
+ background-color: #ffff88; /* FIXME: hsl + dark-mode */
+}
+
+.pre-wrapping {
+ overflow: auto;
+ margin: 1em;
+}
+
+.log-notes {
+ /* FIXME: yellow box goes until the end of the screen */
+ padding: 1em;
+ background-color: #ffd; /* FIXME: hsl + dark-mode */
+}
+
+.log-pagination {
+ text-align: center;
+ margin: 2em;
+}
+
+
footer {
text-align: center;
}
@@ -369,7 +449,7 @@ print_index_footer() {
<footer>
<hr />
<p>
- $(escape "$MSG_FOOTER_TEMPLATE")
+ $MSG_ESCAPED_FOOTER_TEMPLATE
</p>
</footer>
</body>
@@ -389,6 +469,72 @@ index_write() {
print_style > "$OUTDIR/style.css"
}
+genlog() {
+ if [ "$VERBOSE" = true ]; then
+ printf "$MSG_GENERATING_LOG\n" "$1" >&2
+ fi
+}
+
+parallel_n=0
+cached_run() {
+ TARGET_PATH="$1"
+ shift # drop $TARGET_PATH
+ shift # drop --
+
+ if [ -e "$OUTDIR/$TARGET_PATH" ]; then
+ return
+ fi
+
+ if [ -e "$CACHE_DIR/$TARGET_PATH" ]; then
+ cp "$CACHE_DIR/$TARGET_PATH" "$OUTDIR/$TARGET_PATH"
+ else
+ {
+ genlog "$OUTDIR/$TARGET_PATH"
+ "$@"
+ cp "$CACHE_DIR/$TARGET_PATH" "$OUTDIR/$TARGET_PATH"
+ } &
+ parallel_n=$((parallel_n + 1))
+ if [ "$parallel_n" = "$MAX_JOBS" ]; then
+ wait
+ parallel_n=0
+ fi
+ fi
+}
+
+repo_tarballs_write() {
+ repo="$1"
+ mkdir -p "$OUTDIR/tarballs" "$CACHE_DIR/tarballs"
+ local_parallel_n=0
+ for branch in $(git branch --format '%(refname:lstrip=2)'); do
+ TARBALL_PATH="$OUTDIR/tarballs/$repo-$branch.tar.gz"
+ genlog "$TARBALL_PATH"
+ git archive --prefix "$repo-$branch/" "$branch" \
+ -o "$TARBALL_PATH" &
+ local_parallel_n=$((local_parallel_n + 1))
+ if [ "$local_parallel_n" = "$MAX_JOBS" ]; then
+ wait
+ local_parallel_n=0
+ fi
+ done
+ wait
+
+ for tag in $(git tag); do
+ TARBALL_PATH="tarballs/$repo-$tag.tar.gz"
+ cached_run "$TARBALL_PATH" -- \
+ git archive --prefix "$repo-$tag/" "$tag" \
+ -o "$CACHE_DIR/$TARBALL_PATH"
+
+ SIGNATURE_PATH="tarballs/$repo-$tag.tar.gz.asc"
+ if git notes --ref=refs/notes/signatures/tar.gz show "$tag" \
+ 1>/dev/null 2>&1; then
+ git notes --ref=refs/notes/signatures/tar.gz \
+ show "$tag" > "$OUTDIR/$SIGNATURE_PATH"
+ fi
+ done
+
+ wait
+}
+
print_repo_refs() {
repo="$1"
description="$2"
@@ -409,7 +555,7 @@ print_repo_refs() {
<header>
<div class="header-horizontal-grouping">
<a href="../">
- <img alt="$(escape "$MSG_LOGO_ALT_REPOSITORY")" class="logo" src="logo.svg" />
+ <img alt="$(escape "$MSG_LOGO_ALT_INDEX")" class="logo" src="logo.svg" />
</a>
<div class="header-description">
<h1>
@@ -428,12 +574,12 @@ print_repo_refs() {
<nav>
<ul>
<li>
- <a href="files.html">
+ <a href="./">
$(escape "$MSG_NAV_FILES")
</a>
</li>
<li>
- <a href="log.html">
+ <a href="log/$(escape "$MAIN_BRANCH").html">
$(escape "$MSG_NAV_LOG")
</a>
</li>
@@ -478,7 +624,7 @@ EOF
</td>
<td>
<a href="commit/$(escape "$sha").html">
- $(escape "$(git log -1 --format=%B "$sha")")
+ $(escape "$(git log -1 --format=%s "$sha")")
</a>
</td>
<td>
@@ -494,6 +640,13 @@ EOF
cat <<EOF
</tbody>
</table>
+EOF
+ PRINTED_TABLE_OPEN=false
+
+ for tag in $(git tag | tac); do
+ if [ "$PRINTED_TABLE_OPEN" = false ]; then
+ PRINTED_TABLE_OPEN=true
+ cat <<EOF
<table>
<thead>
<tr>
@@ -510,14 +663,15 @@ EOF
</thead>
<tbody>
EOF
+ fi
- for tag in $(git tag | sed '1!G;h;$!d'); do
if [ -e "$CACHE_DIR/tarballs/$repo-$tag.tar.gz.asc" ]; then
tarball_link="(<a href=\"tarballs/$(escape "$repo")-$(escape "$tag").tar.gz\">tarball</a>,
<a href=\"tarballs/$(escape "$repo")-$(escape "$tag").tar.gz.asc\">sig</a>)"
else
tarball_link="(<a href=\"tarballs/$(escape "$repo")-$(escape "$tag").tar.gz\">tarball</a>)"
fi
+
cat <<EOF
<tr>
<td>
@@ -536,14 +690,19 @@ EOF
EOF
done
- cat <<EOF
+ if [ "$PRINTED_TABLE_OPEN" = true ]; then
+ cat <<EOF
</tbody>
</table>
+EOF
+ fi
+
+ cat <<EOF
</main>
<footer>
<hr />
<p>
- $(escape "$MSG_FOOTER_TEMPLATE")
+ $MSG_ESCAPED_FOOTER_TEMPLATE
</p>
</footer>
</body>
@@ -553,13 +712,64 @@ EOF
print_formatted_diff() {
sha="$1"
- printf '<pre>'
- git show -p "$sha"
+ previous_sha="$2"
+ printf '<pre class="pre-wrapping">'
+ # git show -p --stat "$sha"
+ escape "$(git show -p "$sha")" | awk \
+ -v SHA="$sha" -v PREVIOUS_SHA="$previous_sha" '
+ BEGIN {
+ diff_init = 0
+ diffl = 0
+ }
+
+ /^(diff|index) / {
+ diff_init = 1
+ print $0
+ next
+ }
+
+ diff_init == 1 && /^--- a\// {
+ fname = substr($0, 7)
+ printf "--- a/<a href=\"../tree/%s/%s.html\">%s</a>\n",
+ PREVIOUS_SHA, fname, fname
+ next
+ }
+
+ diff_init == 1 && /^\+\+\+ b\// {
+ fname = substr($0, 7)
+ printf "--- b/<a href=\"../tree/%s/%s.html\">%s</a>\n",
+ SHA, fname, fname
+ next
+ }
+
+ { diff_init = 0 }
+
+ /^-/ {
+ diffl++
+ printf "<a id=\"l%s\" href=\"#l%s\" class=\"diff-removed\">%s</a>\n",
+ diffl, diffl, $0
+ next
+ }
+
+ /^\+/ {
+ diffl++
+ printf "<a id=\"l%s\" href=\"#l%s\" class=\"diff-added\">%s</a>\n",
+ diffl, diffl, $0
+ next
+ }
+
+ {
+ print $0
+ }
+ '
printf '</pre>\n'
}
print_repo_commit_page() {
- sha="$2"
+ repo="$1"
+ description="$2"
+ sha="$3"
+ previous_sha="$(git rev-parse "$sha^" 2>/dev/null ||:)"
summary="$(git log -1 --format=%B "$sha")"
cat <<EOF
<!DOCTYPE html>
@@ -597,17 +807,17 @@ print_repo_commit_page() {
<nav>
<ul>
<li>
- <a href="files.html">
+ <a href="../">
$(escape "$MSG_NAV_FILES")
</a>
</li>
<li>
- <a href="log.html">
+ <a href="../log/$(escape "$MAIN_BRANCH").html">
$(escape "$MSG_NAV_LOG")
</a>
</li>
<li>
- <a href="refs.html">
+ <a href="../refs.html">
$(escape "$MSG_NAV_REFS")
</a>
</li>
@@ -616,12 +826,12 @@ print_repo_commit_page() {
<hr />
</header>
<main>
-$(print_formatted_diff "$sha")
+$(print_formatted_diff "$sha" "$previous_sha")
</main>
<footer>
<hr />
<p>
- $(escape "$MSG_FOOTER_TEMPLATE")
+ $MSG_ESCAPED_FOOTER_TEMPLATE
</p>
</footer>
</body>
@@ -629,76 +839,352 @@ $(print_formatted_diff "$sha")
EOF
}
+repo_commits_write() {
+ repo="$1"
+ description="$2"
+ mkdir -p "$OUTDIR/commit" "$CACHE_DIR/commit"
+ for branch in $(git branch --format '%(refname:lstrip=2)'); do
+ for commit in $(git rev-list "$branch"); do
+ COMMIT_PATH="commit/$commit.html"
-cached_run() {
- TARGET_PATH="$1"
- shift # drop $TARGET_PATH
- shift # drop --
+ if [ -e "$OUTDIR/$COMMIT_PATH" ]; then
+ # Assume all previous history exists, too
+ break
+ fi
- if [ -e "$OUTDIR/$TARGET_PATH" ]; then
- echo "$OUTDIR/$TARGET_PATH"
- return
- fi
- if [ ! -e "$CACHE_DIR/$TARGET_PATH" ]; then
- "$@"
- fi
- cp "$CACHE_DIR/$TARGET_PATH" "$OUTDIR/$TARGET_PATH"
+ cached_run "$COMMIT_PATH" -- \
+ eval "print_repo_commit_page '$repo' '$description' '$commit' \
+ > '$CACHE_DIR/$COMMIT_PATH'"
+ done
+ done
+
+ wait
}
-repo_tarballs_write() {
+NEWLINE="$(printf '\n')"
+print_repo_log_page() {
repo="$1"
- mkdir -p "$OUTDIR/tarballs" "$CACHE_DIR/tarballs"
- for branch in $(git branch --format '%(refname:lstrip=2)'); do
- git archive --prefix "$repo-$branch/" "$branch" \
- -o "$OUTDIR/tarballs/$repo-$branch.tar.xz"
- done
+ description="$2"
+ newer_page_name="$3"
+ older_page_name="$4"
+ shift
+ shift
+ shift
+ shift
+
+ title='FIXME'
+
+ if [ -z "$newer_page_name" ]; then
+ escaped_newer_page_link=''
+ else
+ escaped_newer_page_link=" <a href=\"$(escape "$newer_page_name")\">newer FIXME i18n</a>"'
+'
+ fi
- for tag in $(git tag); do
- TARBALL_PATH="tarballs/$repo-$tag.tar.gz"
- cached_run "$TARBALL_PATH" -- \
- git archive --prefix "$repo-$tag/" "$tag" \
- -o "$CACHE_DIR/$TARBALL_PATH"
+ if [ -z "$older_page_name" ]; then
+ escaped_older_page_link=''
+ else
+ escaped_older_page_link=" <a href=\"$(escape "$older_page_name")\">older FIXME i18n</a>"'
+'
+ fi
- SIGNATURE_PATH="tarballs/$repo-$tag.tar.gz.asc"
- if git notes --ref=refs/notes/signatures/tar.gz show "$tag" \
- 1>/dev/null 2>&1; then
- git notes --ref=refs/notes/signatures/tar.gz show "$tag" \
- > "$OUTDIR/$SIGNATURE_PATH"
+ if [ -n "$newer_page_name" ] || [ -n "$older_page_name" ]; then
+ escaped_pagination_open=' <section class="log-pagination">
+'
+ escaped_pagination_close=' </section>
+'
+ else
+ escaped_pagination_open=''
+ escaped_pagination_close=''
+ fi
+
+ if [ -n "$newer_page_name" ] && [ -n "$older_page_name" ]; then
+ escaped_pagination_separator=' |
+'
+ else
+ escaped_pagination_separator=''
+ fi
+
+ cat <<EOF
+<!DOCTYPE html>
+<html lang="$(escape "$MSG_LANGNAME")">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="description" content="$(escape "$description")" />
+ <link rel="icon" type="image/svg+xml" href="../logo.svg" />
+ <link rel="stylesheet" type="text/css" href="../style.css" />
+ <link rel="alternate" type="application/atom+xml" href="../commits.xml" title="$(escape "$repo") - $(escape "$MSG_COMMIT_FEED")" hreflang="$(escape "$MSG_LANGNAME")" />
+ <link rel="alternate" type="application/atom+xml" href="../tags.xml" title="$(escape "$repo") - $(escape "$MSG_TAGS_FEED")" hreflang="$(escape "$MSG_LANGNAME")" />
+ <title>$(escape "$repo") - $(escape "$title")</title>
+ </head>
+ <body>
+ <header>
+ <div class="header-horizontal-grouping">
+ <a href="../../">
+ <img alt="$(escape "$MSG_LOGO_ALT_INDEX")" class="logo" src="../logo.svg" />
+ </a>
+ <div class="header-description">
+ <h1>
+ <a href="../">
+ $(escape "$repo")
+ </a>
+ </h1>
+ <h2>
+ $(escape "$description")
+ </h2>
+ <code>
+ git clone $(escape "$CLONE_URL")
+ </code>
+ </div>
+ </div>
+ <nav>
+ <ul>
+ <li>
+ <a href="../">
+ $(escape "$MSG_NAV_FILES")
+ </a>
+ </li>
+ <li class="selected-nav-item">
+ <a href="../log/$(escape "$MAIN_BRANCH").html">
+ $(escape "$MSG_NAV_LOG")
+ </a>
+ </li>
+ <li>
+ <a href="../refs.html">
+ $(escape "$MSG_NAV_REFS")
+ </a>
+ </li>
+ </ul>
+ </nav>
+ <hr />
+ </header>
+ <main>
+EOF
+
+ for commit in "$@"; do
+ body_txt="$(git log -1 --format=%b "$commit")"
+ if [ "$body_txt" = "$NEWLINE" ]; then
+ body=''
+ else
+ body="
+
+$body_txt"
fi
+
+ notes_txt="$(git log -1 --format=%N "$commit")"
+
+ if [ "$notes_txt" = "$NEWLINE" ]; then
+ escaped_notes=''
+ else
+ escaped_notes="
+ <pre class=\"pre-wrapping log-notes\">$(escape "$notes_txt")</pre>"
+ fi
+
+ # FIXME: show if commit is in branch, tag or HEAD
+ # <a href="#" class="log-commit-tag log-head-highlight">HEAD</a>
+ # <a href="#" class="log-commit-tag log-branch-highlight">main</a>
+ # <a href="#" class="log-commit-tag log-tag-highlight">v0.2.1</a>
+
+ cat <<EOF
+ <section class="log-commit-box">
+ <p>
+ <a href="../commit/$(escape "$commit").html">$(escape "$commit")</a>
+ |
+ $(escape "$(git log -1 --format=%an "$commit")")
+ </p>
+ <pre class="pre-wrapping"><strong>$(escape "$(git log -1 --format=%s "$commit")")</strong>$(escape "$body")</pre>$escaped_notes
+ </section>
+EOF
done
+
+ cat <<EOF
+$escaped_pagination_open$escaped_newer_page_link$escaped_pagination_separator$escaped_older_page_link$escaped_pagination_close </main>
+ <footer>
+ <hr />
+ <p>
+ $MSG_ESCAPED_FOOTER_TEMPLATE
+ </p>
+ </footer>
+ </body>
+</html>
+EOF
}
-repo_commits_write() {
- mkdir -p "$OUTDIR/commit" "$CACHE_DIR/commit"
- for branch in $(git branch --format '%(refname:lstrip=2)'); do
- for commit in $(git rev-list "$branch"); do
- COMMIT_PATH="commit/$commit.html"
- if [ -e "$OUTDIR/$COMMIT_PATH" ]; then
- # Assume all previous history exists, too
- break
+# FIXME: use log/main/1.html over diffs?
+LOG_PAGE_MAX=50
+repo_log_write() {
+ repo="$1"
+ description="$2"
+ mkdir -p "$OUTDIR/log" "$CACHE_DIR/log"
+ for ref in $(git branch --format '%(refname:lstrip=2)') $(git tag); do
+ # FIXME: add bail out of loop when the first page already
+ # exists, so nothing after that needs to be ran
+
+ mkdir -p "$CACHE_DIR/log_tmp/$ref"
+ PAGE_SIZE=0
+ PAGE_INDEX=0
+ for commit in $(git rev-list "$ref" | tac); do
+ if [ "$PAGE_SIZE" = 0 ]; then
+ FIRST="$commit"
fi
- if [ ! -e "CACHE_DIR/$COMMIT_PATH" ]; then
- print_repo_commit_page "$repo" "$commit" \
- > "$CACHE_DIR/$COMMIT_PATH"
+ LAST="$commit"
+ PAGE_NAME="$FIRST..$LAST.html"
+ PAGE_SIZE=$((PAGE_SIZE + 1))
+
+ if [ "$PAGE_SIZE" != "$LOG_PAGE_MAX" ]; then
+ continue
fi
- cp "$CACHE_DIR/$COMMIT_PATH" "$OUTDIR/$COMMIT_PATH"
- # FIXME: WIP
- return
+ echo "$PAGE_NAME" > "$CACHE_DIR/log_tmp/$ref/$PAGE_INDEX"
+
+ PAGE_INDEX=$((PAGE_INDEX + 1))
+ PAGE_SIZE=0
done
+ if [ "$PAGE_SIZE" != 0 ]; then
+ echo "$PAGE_NAME" > "$CACHE_DIR/log_tmp/$ref/$PAGE_INDEX"
+ fi
+
+ PAGE_COMMITS=''
+ PAGE_SIZE=0
+ PAGE_INDEX=0
+ for commit in $(git rev-list "$ref" | tac); do
+ PAGE_COMMITS="$commit $PAGE_COMMITS"
+ PAGE_SIZE=$((PAGE_SIZE + 1))
+
+ if [ "$PAGE_SIZE" != "$LOG_PAGE_MAX" ]; then
+ continue
+ fi
+
+ # FIXME: 80 columns
+ NEWER_PAGE_NAME="$(cat "$CACHE_DIR/log_tmp/$ref/$((PAGE_INDEX + 1))" 2>/dev/null ||:)"
+ CURRT_PAGE_NAME="$(cat "$CACHE_DIR/log_tmp/$ref/$((PAGE_INDEX + 0))" 2>/dev/null ||:)"
+ OLDER_PAGE_NAME="$(cat "$CACHE_DIR/log_tmp/$ref/$((PAGE_INDEX - 1))" 2>/dev/null ||:)"
+ PAGE_PATH="log/$CURRT_PAGE_NAME"
+
+ cached_run "$PAGE_PATH" -- eval "print_repo_log_page \
+ '$repo' '$description' \
+ '$NEWER_PAGE_NAME' '$OLDER_PAGE_NAME' \
+ $PAGE_COMMITS \
+ > '$CACHE_DIR/$PAGE_PATH'"
+ PAGE_COMMITS=''
+ PAGE_SIZE=0
+ PAGE_INDEX=$((PAGE_INDEX + 1))
+ done
+ if [ "$PAGE_SIZE" != 0 ]; then
+ # FIXME: 80 columns
+ NEWER_PAGE_NAME="$(cat "$CACHE_DIR/log_tmp/$ref/$((PAGE_INDEX + 1))" 2>/dev/null ||:)"
+ CURRT_PAGE_NAME="$(cat "$CACHE_DIR/log_tmp/$ref/$((PAGE_INDEX + 0))" 2>/dev/null ||:)"
+ OLDER_PAGE_NAME="$(cat "$CACHE_DIR/log_tmp/$ref/$((PAGE_INDEX - 1))" 2>/dev/null ||:)"
+ PAGE_PATH="log/$CURRT_PAGE_NAME"
+
+ cached_run "$PAGE_PATH" -- eval "print_repo_log_page \
+ '$repo' '$description' \
+ '$NEWER_PAGE_NAME' '$OLDER_PAGE_NAME' \
+ $PAGE_COMMITS \
+ > '$CACHE_DIR/$PAGE_PATH'"
+ fi
+ ln -fs "$CURRT_PAGE_NAME" "$OUTDIR/log/$ref.html"
done
+ rm -rf "$CACHE_DIR/log_tmp"
+
+ wait
+}
+
+print_repo_index_page() {
+ repo="$1"
+ description="$2"
+
+ cat <<EOF
+<!DOCTYPE html>
+<html lang="$(escape "$MSG_LANGNAME")">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="description" content="$(escape "$description")" />
+ <link rel="icon" type="image/svg+xml" href="log.svg" />
+ <link rel="stylesheet" type="text/css" href="style.css" />
+ <link rel="alternate" type="application/atom+xml" href="commits.xml" title="$(escape "$repo") - $(escape "$MSG_COMMIT_FEED")" hreflang="$(escape "$MSG_LANGNAME")" />
+ <link rel="alternate" type="application/atom+xml" href="tags.xml" title="$(escape "$repo") - $(escape "$MSG_TAGS_FEED")" hreflang="$(escape "$MSG_LANGNAME")" />
+ <title>$(escape "$repo") - FIXME</title>
+ </head>
+ <body>
+ <header>
+ <div class="header-horizontal-grouping">
+ <a href="../">
+ <img alt="$(escape "$MSG_LOGO_ALT_INDEX")" class="logo" src="logo.svg" />
+ </a>
+ <div class="header-description">
+ <h1>
+ <a href="./">
+ $(escape "$repo")
+ </a>
+ </h1>
+ <h2>
+ $(escape "$description")
+ </h2>
+ <code>
+ git clone <a href="$(escape "$CLONE_URL")">$(escape "$CLONE_URL")</a>
+ </code>
+ </div>
+ </div>
+ <nav>
+ <ul>
+ <li class="selected-nav-item">
+ <a href="./">
+ $(escape "$MSG_NAV_FILES")
+ </a>
+ </li>
+ <li>
+ <a href="log/$(escape "$MAIN_BRANCH").html">
+ $(escape "$MSG_NAV_LOG")
+ </a>
+ </li>
+ <li>
+ <a href="refs.html">
+ $(escape "$MSG_NAV_REFS")
+ </a>
+ </li>
+ </ul>
+ </nav>
+ <hr />
+ </header>
+ <main>
+ <section>
+ Os arquivos vão aqui.
+ </section>
+ <section>
+ O README vai aqui.
+ </section>
+ </main>
+ <footer>
+ <hr />
+ <p>
+ $MSG_ESCAPED_FOOTER_TEMPLATE
+ </p>
+ </footer>
+ </body>
+</html>
+EOF
+}
+
+repo_trees_write() {
+ echo
}
repo_write() {
cd "$1"
+ # FIXME: use $REPO and $DESCRIPTION
repo="$(basename "$(realpath "${1%.git}")")"
description="$(cat "$1/description" 2>/dev/null ||:)"
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/gistatic/$repo"
repo_tarballs_write "$repo"
print_repo_refs "$repo" "$description" > "$OUTDIR/refs.html"
-
- repo_commits_write "$repo"
+ repo_commits_write "$repo" "$description"
+ repo_log_write "$repo" "$description"
+ print_repo_index_page "$repo" "$description" > "$OUTDIR/index.html"
+ repo_trees_write "$repo" "$description"
print_logo > "$OUTDIR/logo.svg"
print_style > "$OUTDIR/style.css"
@@ -725,25 +1211,41 @@ for flag in "$@"; do
esac
done
+VERBOSE=true
INDEX=false
TITLE="$MSG_DEFAULT_TITLE"
OUTDIR=
CLONE_URL=
CACHE_DIR=
-while getopts 'o:t:u:ihV' flag; do
+MAX_JOBS=1
+MAIN_BRANCH='main'
+MAIN_BRANCH_SET=false
+REPO=
+DESCRIPTION=
+while getopts 'b:o:t:u:j:iqhV' flag; do
case "$flag" in
- i)
- INDEX=true
- ;;
- t)
- TITLE="$OPTARG"
+ b)
+ MAIN_BRANCH="$OPTARG"
+ MAIN_BRANCH_SET=true
;;
o)
OUTDIR="$(realpath "$OPTARG")"
;;
+ t)
+ TITLE="$OPTARG"
+ ;;
u)
CLONE_URL="$OPTARG"
;;
+ j)
+ MAX_JOBS="$OPTARG"
+ ;;
+ i)
+ INDEX=true
+ ;;
+ q)
+ VERBOSE=false
+ ;;
h)
usage
help
@@ -763,7 +1265,8 @@ shift $((OPTIND - 1))
assert_arg() {
if [ -z "$1" ]; then
- printf "$MSG_MISSING_CLIARG" "$2" >&2
+ printf "$MSG_MISSING_CLIARG\n\n" "$2" >&2
+ usage >&2
exit 2
fi
}
@@ -772,16 +1275,25 @@ assert_arg "$OUTDIR" '-o DIRECTORY'
if [ "$INDEX" = false ]; then
assert_arg "$CLONE_URL" '-u CLONE_URL'
-elif [ -n "$CLONE_URL" ]; then
- printf '%s\n' "$MSG_INCOMPATIBLE_OPTIONS" >&2
- exit 2
-fi
+elif [ -n "$CLONE_URL" ] || [ "$MAIN_BRANCH_SET" = true ]; then
+ {
+ printf '%s' "$MSG_INCOMPATIBLE_OPTIONS"
+ printf -- '-i'
+ if [ -n "$CLONE_URL" ]; then
+ printf -- ' -u'
+ fi
+ if [ "$MAIN_BRANCH_SET" = true ]; then
+ printf -- ' -b'
+ fi
+ printf '\n\n'
-if [ -z "${1:-}" ]; then
- printf '%s\n' "$MSG_MISSING_ARGS" >&2
+ usage
+ } >&2
exit 2
fi
+assert_arg "${1:-}" "$MSG_MISSING_ARGS"
+
mkdir -p "$OUTDIR"
if [ "$INDEX" = true ]; then
diff --git a/src/lib.c b/src/lib.c
deleted file mode 100644
index 018cabe..0000000
--- a/src/lib.c
+++ /dev/null
@@ -1,2424 +0,0 @@
-#include "config.h"
-#include "logerr.h"
-#include "tar.h"
-#include "gistatic.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <libgen.h>
-#include <string.h>
-#include <assert.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <nl_types.h>
-#include <locale.h>
-
-#include <git2.h>
-
-
-#ifdef TEST
-#include "tests-lib.h"
-#endif
-
-
-static const int
-EXIT_ERROR = 1;
-
-static const int
-EXIT_USAGE = 2;
-
-
-static const char *const
-CATALOG_NAME = PROGNAME;
-
-static nl_catd
-catalog_descriptor = NULL;
-
-
-#define MSG_DEFAULT_TITLE 1
-#define MSG_LOGO_ALT_INDEX 2
-#define MSG_LOGO_ALT_REPOSITORY 3
-#define MSG_FOOTER_TEMPLATE 4
-#define MSG_LANG 5
-#define MSG_NAME 6
-#define MSG_DESCRIPTION 7
-#define MSG_LAST_COMMIT 8
-#define MSG_USAGE 9
-#define MSG_HELP 10
-#define MSG_COMMIT_FEED 11
-#define MSG_TAGS_FEED 12
-#define MSG_NAV_FILES 13
-#define MSG_NAV_LOG 14
-#define MSG_NAV_REFS 15
-#define MSG_THEAD_BRANCH 16
-#define MSG_THEAD_COMMITMSG 17
-#define MSG_THEAD_AUTHOR 18
-#define MSG_THEAD_DATE 19
-#define MSG_THEAD_TAG 20
-#define MSG_MISSING_URL 21
-#define MSG_MISSING_ARGS 22
-#define MSG_INCOMPATIBLE_OPTIONS 23
-#define MSG_INDEX_DESCRIPTION 24
-
-static const char *const
-MSGS[] = {
- "",
- [MSG_DEFAULT_TITLE]="Repositories",
- [MSG_LOGO_ALT_INDEX]="Logo image of the repository list",
- [MSG_LOGO_ALT_REPOSITORY]="Logo image of the repository",
- [MSG_FOOTER_TEMPLATE]="Generated with %s",
- [MSG_LANG]="en",
- [MSG_NAME]="Name",
- [MSG_DESCRIPTION]="Description",
- [MSG_LAST_COMMIT]="Last commit",
- [MSG_USAGE]=""
- "Usage:\n"
- " " PROGNAME " -i -o DIRECTORY REPOSITORY...\n"
- " " PROGNAME " -o DIRECTORY -u CLONE_URL REPOSITORY\n"
- " " PROGNAME " [-hV]\n"
- "",
- [MSG_HELP]=""
- "\n"
- "Options:\n"
- " -i build the index page of the repositories\n"
- " -u CLONE_URL address to be shown alongside \"git clone\"\n"
- " -o DIRECTORY output where to place the generated files\n"
- " -h, --help show this help\n"
- " -V, --version print the version number\n"
- "\n"
- "See \"man gistatic\" for more information.\n"
- "",
- [MSG_COMMIT_FEED]="commit feed",
- [MSG_TAGS_FEED]="tags feed",
- [MSG_NAV_FILES]="files",
- [MSG_NAV_LOG]="log",
- [MSG_NAV_REFS]="refs",
- [MSG_THEAD_BRANCH]="Branch",
- [MSG_THEAD_COMMITMSG]="Commit message",
- [MSG_THEAD_AUTHOR]="Author",
- [MSG_THEAD_DATE]="Date",
- [MSG_THEAD_TAG]="Tag",
- [MSG_MISSING_URL]="Missing '-u CLONE_URL'",
- [MSG_MISSING_ARGS]="Missing [PATH | [PATHS]]",
- [MSG_INCOMPATIBLE_OPTIONS]="Incompatible options -u and -i",
- [MSG_INDEX_DESCRIPTION]="Index of repositories",
- NULL
-};
-
-#ifdef TEST
-static void
-dump_translatable_strings(void) {
- const size_t size =
- strlen(__FILE__) - strlen(".c") + strlen(".msg") + sizeof('\0');
- char *const catalog_path = malloc(size);
- assert(catalog_path);
-
- strcpy(catalog_path, __FILE__);
- catalog_path[strlen(__FILE__) - strlen(".c")] = '\0';
- strcat(catalog_path, ".msg");
-
- FILE *const f = fopen(catalog_path, "w");
- assert(f);
- for (size_t i = 1; MSGS[i] != NULL; i++) {
- assert(fprintf(f, "%ld %s\n\n", i, MSGS[i]) > 0);
- }
-
- assert(fclose(f) == 0);
- free(catalog_path);
-}
-#endif
-
-static const char *const
-LOGO_STR = ""
- "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
- "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">\n"
- " <path d=\"M 0 8 L 1 8 L 1 9 L 0 9 L 0 8 Z\" />\n"
- " <path d=\"M 0 13 L 1 13 L 1 14 L 0 14 L 0 13 Z\" />\n"
- " <path d=\"M 1 8 L 2 8 L 2 9 L 1 9 L 1 8 Z\" />\n"
- " <path d=\"M 1 13 L 2 13 L 2 14 L 1 14 L 1 13 Z\" />\n"
- " <path d=\"M 2 8 L 3 8 L 3 9 L 2 9 L 2 8 Z\" />\n"
- " <path d=\"M 2 13 L 3 13 L 3 14 L 2 14 L 2 13 Z\" />\n"
- " <path d=\"M 3 8 L 4 8 L 4 9 L 3 9 L 3 8 Z\" />\n"
- " <path d=\"M 3 13 L 4 13 L 4 14 L 3 14 L 3 13 Z\" />\n"
- " <path d=\"M 4 7 L 5 7 L 5 8 L 4 8 L 4 7 Z\" />\n"
- " <path d=\"M 4 8 L 5 8 L 5 9 L 4 9 L 4 8 Z\" />\n"
- " <path d=\"M 4 13 L 5 13 L 5 14 L 4 14 L 4 13 Z\" />\n"
- " <path d=\"M 5 6 L 6 6 L 6 7 L 5 7 L 5 6 Z\" />\n"
- " <path d=\"M 5 7 L 6 7 L 6 8 L 5 8 L 5 7 Z\" />\n"
- " <path d=\"M 5 13 L 6 13 L 6 14 L 5 14 L 5 13 Z\" />\n"
- " <path d=\"M 6 5 L 7 5 L 7 6 L 6 6 L 6 5 Z\" />\n"
- " <path d=\"M 6 6 L 7 6 L 7 7 L 6 7 L 6 6 Z\" />\n"
- " <path d=\"M 6 14 L 7 14 L 7 15 L 6 15 L 6 14 Z\" />\n"
- " <path d=\"M 7 1 L 8 1 L 8 2 L 7 2 L 7 1 Z\" />\n"
- " <path d=\"M 7 14 L 8 14 L 8 15 L 7 15 L 7 14 Z\" />\n"
- " <path d=\"M 7 15 L 8 15 L 8 16 L 7 16 L 7 15 Z\" />\n"
- " <path d=\"M 7 2 L 8 2 L 8 3 L 7 3 L 7 2 Z\" />\n"
- " <path d=\"M 7 3 L 8 3 L 8 4 L 7 4 L 7 3 Z\" />\n"
- " <path d=\"M 7 4 L 8 4 L 8 5 L 7 5 L 7 4 Z\" />\n"
- " <path d=\"M 7 5 L 8 5 L 8 6 L 7 6 L 7 5 Z\" />\n"
- " <path d=\"M 8 1 L 9 1 L 9 2 L 8 2 L 8 1 Z\" />\n"
- " <path d=\"M 8 15 L 9 15 L 9 16 L 8 16 L 8 15 Z\" />\n"
- " <path d=\"M 9 1 L 10 1 L 10 2 L 9 2 L 9 1 Z\" />\n"
- " <path d=\"M 9 2 L 10 2 L 10 3 L 9 3 L 9 2 Z\" />\n"
- " <path d=\"M 9 6 L 10 6 L 10 7 L 9 7 L 9 6 Z\" />\n"
- " <path d=\"M 9 15 L 10 15 L 10 16 L 9 16 L 9 15 Z\" />\n"
- " <path d=\"M 10 2 L 11 2 L 11 3 L 10 3 L 10 2 Z\" />\n"
- " <path d=\"M 10 3 L 11 3 L 11 4 L 10 4 L 10 3 Z\" />\n"
- " <path d=\"M 10 4 L 11 4 L 11 5 L 10 5 L 10 4 Z\" />\n"
- " <path d=\"M 10 5 L 11 5 L 11 6 L 10 6 L 10 5 Z\" />\n"
- " <path d=\"M 10 6 L 11 6 L 11 7 L 10 7 L 10 6 Z\" />\n"
- " <path d=\"M 11 6 L 12 6 L 12 7 L 11 7 L 11 6 Z\" />\n"
- " <path d=\"M 11 8 L 12 8 L 12 9 L 11 9 L 11 8 Z\" />\n"
- " <path d=\"M 10 15 L 11 15 L 11 16 L 10 16 L 10 15 Z\" />\n"
- " <path d=\"M 11 10 L 12 10 L 12 11 L 11 11 L 11 10 Z\" />\n"
- " <path d=\"M 11 12 L 12 12 L 12 13 L 11 13 L 11 12 Z\" />\n"
- " <path d=\"M 11 14 L 12 14 L 12 15 L 11 15 L 11 14 Z\" />\n"
- " <path d=\"M 11 15 L 12 15 L 12 16 L 11 16 L 11 15 Z\" />\n"
- " <path d=\"M 12 6 L 13 6 L 13 7 L 12 7 L 12 6 Z\" />\n"
- " <path d=\"M 12 8 L 13 8 L 13 9 L 12 9 L 12 8 Z\" />\n"
- " <path d=\"M 12 10 L 13 10 L 13 11 L 12 11 L 12 10 Z\" />\n"
- " <path d=\"M 12 12 L 13 12 L 13 13 L 12 13 L 12 12 Z\" />\n"
- " <path d=\"M 12 14 L 13 14 L 13 15 L 12 15 L 12 14 Z\" />\n"
- " <path d=\"M 13 6 L 14 6 L 14 7 L 13 7 L 13 6 Z\" />\n"
- " <path d=\"M 13 8 L 14 8 L 14 9 L 13 9 L 13 8 Z\" />\n"
- " <path d=\"M 13 10 L 14 10 L 14 11 L 13 11 L 13 10 Z\" />\n"
- " <path d=\"M 13 12 L 14 12 L 14 13 L 13 13 L 13 12 Z\" />\n"
- " <path d=\"M 13 13 L 14 13 L 14 14 L 13 14 L 13 13 Z\" />\n"
- " <path d=\"M 13 14 L 14 14 L 14 15 L 13 15 L 13 14 Z\" />\n"
- " <path d=\"M 14 7 L 15 7 L 15 8 L 14 8 L 14 7 Z\" />\n"
- " <path d=\"M 14 8 L 15 8 L 15 9 L 14 9 L 14 8 Z\" />\n"
- " <path d=\"M 14 9 L 15 9 L 15 10 L 14 10 L 14 9 Z\" />\n"
- " <path d=\"M 14 10 L 15 10 L 15 11 L 14 11 L 14 10 Z\" />\n"
- " <path d=\"M 14 11 L 15 11 L 15 12 L 14 12 L 14 11 Z\" />\n"
- " <path d=\"M 14 12 L 15 12 L 15 13 L 14 13 L 14 12 Z\" />\n"
- "</svg>\n";
-
-static const char *const
-STYLE_STR = ""
- ":root {\n"
- " --color: black;\n"
- " --background-color: white;\n"
- " --hover-color: hsl(0, 0%, 93%);\n"
- " --nav-color: hsl(0, 0%, 87%);\n"
- "}\n"
- "\n"
- "@media(prefers-color-scheme: dark) {\n"
- " :root {\n"
- " --color: white;\n"
- " --background-color: black;\n"
- " --hover-color: hsl(0, 0%, 7%);\n"
- " --nav-color: hsl(0, 0%, 13%);\n"
- " }\n"
- "\n"
- " body {\n"
- " color: var(--color);\n"
- " background-color: var(--background-color);\n"
- " }\n"
- "\n"
- " a {\n"
- " color: hsl(211, 100%, 60%);\n"
- " }\n"
- "\n"
- " a:visited {\n"
- " color: hsl(242, 100%, 80%);\n"
- " }\n"
- "}\n"
- "\n"
- "body {\n"
- " font-family: monospace;\n"
- " max-width: 1100px;\n"
- " margin: 0 auto 0 auto;\n"
- "}\n"
- "\n"
- ".logo {\n"
- " height: 6em;\n"
- " width: 6em;\n"
- "}\n"
- "\n"
- ".header-horizontal-grouping {\n"
- " display: flex;\n"
- " align-items: center;\n"
- " margin-top: 1em;\n"
- " margin-bottom: 1em;\n"
- "}\n"
- "\n"
- ".header-description {\n"
- " margin-left: 2em;\n"
- "}\n"
- "\n"
- "nav {\n"
- " margin-top: 2em;\n"
- "}\n"
- "\n"
- "nav ul {\n"
- " display: flex;\n"
- " list-style-type: none;\n"
- " margin-bottom: 0;\n"
- "}\n"
- "\n"
- "nav li {\n"
- " margin-left: 10px;\n"
- "}\n"
- "\n"
- "nav a, nav a:visited {\n"
- " padding: 2px 8px 0px 8px;\n"
- " color: var(--color);\n"
- "}\n"
- "\n"
- ".selected-nav-item {\n"
- " background-color: var(--nav-color);\n"
- "}\n"
- "\n"
- "hr {\n"
- " margin-top: 0;\n"
- " border: 0;\n"
- " border-top: 3px solid var(--nav-color);\n"
- "}\n"
- "\n"
- "table {\n"
- " margin: 2em auto;\n"
- "}\n"
- "\n"
- "th {\n"
- " padding-bottom: 1em;\n"
- "}\n"
- "\n"
- "tbody tr:hover {\n"
- " background-color: var(--hover-color);\n"
- "}\n"
- "\n"
- "td {\n"
- " padding-left: 1em;\n"
- " padding-right: 1em;\n"
- "}\n"
- "\n"
- "footer {\n"
- " text-align: center;\n"
- "}\n"
- "";
-
-
-#define DESCRIPTION_MAXLENGTH 81
-
-static bool
-verbose = false;
-
-static const char *const
-GIT_SUFFIX = ".git";
-
-static const char *const
-PROJECT_HOMEPAGE_LINK =
- "<a href=\"https://euandreh.xyz/" PROGNAME "\">" PROGNAME "</a>.";
-
-
-static void
-logerr(
- const char *const s,
- const char *const msg,
- const int lineno
-) {
- logerr_file(s, msg, __FILE__, lineno);
-}
-
-static void
-logerrs(
- const char *const pre,
- const char *const mid,
- const char *const post,
- const char *const msg,
- const int lineno
-) {
- logerrs_file(pre, mid, post, msg, __FILE__, lineno);
-}
-
-static void
-logerrl(
- const char *const pre,
- const size_t mid,
- const char *const post,
- const char *const msg,
- const int lineno
-) {
- logerrl_file(pre, mid, post, msg, __FILE__, lineno);
-}
-
-
-static const char *
-_(int msg_id) {
- if (!catalog_descriptor || catalog_descriptor == (nl_catd)-1) {
- return MSGS[msg_id];
- }
- errno = 0;
- const char *const ret =
- catgets(catalog_descriptor, NL_SETD, msg_id, MSGS[msg_id]);
- if (errno && verbose) {
- logerrl("catgets(", msg_id, ")", strerror(errno), __LINE__);
- }
- return ret;
-}
-
-#ifdef TEST
-static void
-test_underscore(void) {
- test_start("test_underscore");
- const char *const original_locale = setlocale(LC_ALL, NULL);
- setlocale(LC_ALL, "");
- setenv("NLSPATH", "", 1);
- {
- testing("a NULL value for catalog_descriptor");
- test_ok();
- }
- setlocale(LC_ALL, original_locale);
-}
-#endif
-
-static int
-print_msg(FILE *const fd, const char *const msg) {
- if (fprintf(fd, "%s", msg) < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- return -1;
- }
- return 0;
-}
-
-static int
-print_help(FILE *const fd) {
- return print_msg(fd, _(MSG_HELP));
-}
-
-static int
-print_version(FILE *const fd) {
- return print_msg(fd, PROGNAME "-" VERSION " " DATE "\n");
-}
-
-static int
-print_usage(FILE *const fd) {
- return print_msg(fd, _(MSG_USAGE));
-}
-
-
-static char *
-remove_suffix(char *const s, const char *const suffix) {
- if (!s || !suffix) {
- return NULL;
- }
-
- char *const name = strdup(s);
- if (!name) {
- logerrs("strdup(\"", s, "\")", strerror(errno), __LINE__);
- return NULL;
- }
-
- const char *const match_suffix = strrchr(name, suffix[0]);
- if (match_suffix && (strcmp(match_suffix, suffix) == 0)) {
- const size_t suffix_idx = strlen(name) - strlen(suffix);
- name[suffix_idx] = '\0';
- }
- return name;
-}
-
-#ifdef TEST
-static void
-test_remove_suffix(void) {
- test_start("test_remove_suffix");
- {
- testing("empty string");
- char *const s = remove_suffix("", "suffix");
- assert(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("empty suffix");
- char *const s = remove_suffix("a string", "");
- assert(s);
- assert(strcmp(s, "a string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("with no suffix match");
- char *const s = remove_suffix("another string", "NO MATCH!");
- assert(s);
- assert(strcmp(s, "another string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("with suffix bigger than string");
- char *const s = remove_suffix("str", "a string");
- assert(s);
- assert(strcmp(s, "str") == 0);
- free(s);
- test_ok();
- }
- {
- testing("with a NULL string");
- char *const s = remove_suffix(NULL, "a non-null suffix");
- assert(s == NULL);
- test_ok();
- }
- {
- testing("with a NULL suffix");
- char *const s = remove_suffix("not null", NULL);
- assert(s == NULL);;
- test_ok();
- }
- {
- testing("both are NULL");
- char *const s = remove_suffix(NULL, NULL);
- assert(s == NULL);
- test_ok();
- }
- {
- testing("matching prefix");
- char *const s = remove_suffix("aaa bbb ccc", "aaa");
- assert(s);
- assert(strcmp(s, "aaa bbb ccc") == 0);
- free(s);
- test_ok();
- }
- {
- testing("matching infix");
- char *const s = remove_suffix("aaa bbb ccc", "bbb");
- assert(s);
- assert(strcmp(s, "aaa bbb ccc") == 0);
- free(s);
- test_ok();
- }
- {
- testing("with suffix equal to string");
- char *const s = remove_suffix("string", "string");
- assert(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("with matching suffix");
- char *const s = remove_suffix("string", "ing");
- assert(s);
- assert(strcmp(s, "str") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: with .git suffix");
- char *const s = remove_suffix("reponame.git", GIT_SUFFIX);
- assert(s);
- assert(strcmp(s, "reponame") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: with full path");
- char *const s = remove_suffix("an/example/path", "ath");
- assert(s);
- assert(strcmp(s, "an/example/p") == 0);
- free(s);
- test_ok();
- }
-}
-#endif
-
-static char *
-strjoin(const char *const s1, const char *const s2) {
- if (!s1 || !s2) {
- return NULL;
- }
-
- const size_t size1 = strnlen(s1, SIZE_MAX);
- const size_t size2 = strnlen(s2, SIZE_MAX - sizeof('\0'))
- + sizeof('\0');
- if (SIZE_MAX - size1 < size2) {
- errno = EOVERFLOW;
- logerr("strjoin()", strerror(errno), __LINE__);
- return NULL;
- }
- const size_t size = size1 + size2;
- char *const s = malloc(size);
- if (!s) {
- logerrl("malloc(", size, ")", strerror(errno), __LINE__);
- return NULL;
- }
-
- strcpy(s, s1);
- strcat(s, s2);
- return s;
-}
-
-#ifdef TEST
-static void
-test_strjoin(void) {
- test_start("test_strjoin");
- {
- testing("joining empty strings");
- char *const s = strjoin("", "");
- assert(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("joining NULL strings");
- assert(strjoin(NULL, NULL) == NULL);
- assert(strjoin("", NULL) == NULL);
- assert(strjoin(NULL, "") == NULL);
- test_ok();
- }
- {
- testing("first string is empty");
- char *const s = strjoin("", "second not empty");
- assert(s);
- assert(strcmp(s, "second not empty") == 0);
- free(s);
- test_ok();
- }
- {
- testing("second string is empty");
- char *const s = strjoin("first not empty", "");
- assert(s);
- assert(strcmp(s, "first not empty") == 0);
- free(s);
- test_ok();
- }
- {
- testing("two non-empty strings");
- char *const s = strjoin("abc", "def");
- assert(s);
- assert(strcmp(s, "abcdef") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: with file names");
- char *const s = strjoin("../repository.git", "/description");
- assert(s);
- assert(strcmp(s, "../repository.git/description") == 0);
- free(s);
- test_ok();
- }
-}
-#endif
-
-static char *
-strsjoin(const char *const strs[]) {
- size_t size = sizeof('\0');
- for (size_t i = 0; strs[i]; i++) {
- const size_t curr_len = strnlen(strs[i], SIZE_MAX);
- if (SIZE_MAX - size < curr_len) {
- errno = EOVERFLOW;
- logerr("strsjoin()", strerror(errno), __LINE__);
- return NULL;
- }
- if (SIZE_MAX == i) {
- errno = EOVERFLOW;
- logerr("strsjoin()", strerror(errno), __LINE__);
- return NULL;
- }
- size += curr_len;
- }
-
- char *const s = malloc(size);
- if (!s) {
- logerrl("malloc(", size, ")", strerror(errno), __LINE__);
- return NULL;
- }
- *s = '\0';
-
- // "i" can't overflow now, as it was already checked above
- for (size_t i = 0; strs[i]; i++) {
- strcat(s, strs[i]);
- }
-
- return s;
-}
-
-#ifdef TEST
-static void
-test_strsjoin(void) {
- test_start("test_strsjoin");
- {
- testing("joining empty strings");
- char *const s = strsjoin((const char *const [])
- { "", "", NULL });
- assert(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("joining empty strs array");
- char *const s = strsjoin((const char *const [])
- { NULL });
- assert(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("first string is empty");
- char *const s = strsjoin((const char *const [])
- { "", "second not empty", NULL });
- assert(s);
- assert(strcmp(s, "second not empty") == 0);
- free(s);
- test_ok();
- }
- {
- testing("third string is empty");
- char *const s = strsjoin((const char *const [])
- { "first not empty", "second not empty", "", NULL });
- assert(s);
- assert(strcmp(s, "first not emptysecond not empty") == 0);
- free(s);
- test_ok();
- }
- {
- testing("four non-empty strings");
- char *const s = strsjoin((const char *const [])
- { "abc", "def", "ghi", "jkl", NULL });
- assert(s);
- assert(strcmp(s, "abcdefghijkl") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: with file names");
- char *const s = strsjoin((const char *const [])
- { "../repository.git", "/description", NULL });
- assert(s);
- assert(strcmp(s, "../repository.git/description") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: with file name parts");
- char *const s = strsjoin((const char *const []){
- "/tmp/gistatic.ABCDEF", "/", "project",
- "-", "main", NULL
- });
- assert(s);
- assert(strcmp(s, "/tmp/gistatic.ABCDEF/project-main") == 0);
- free(s);
- test_ok();
- }
-}
-#endif
-
-static char *
-formatted_date(const time_t time_sec) {
- const struct tm *const time_utc = gmtime(&time_sec);
- if (!time_utc) {
- logerrl("gmtime(", time_sec, ")", strerror(errno), __LINE__);
- return NULL;
- }
-
- /* Expected size, plus breathing room for date format variations */
- const size_t size = (strlen("XXX-XX-XX XX:XX") + sizeof('\0')) * 2;
- char *const formatted = malloc(size);
- if (!formatted) {
- logerrl("malloc(", size, ")", strerror(errno), __LINE__);
- return NULL;
- }
-
- strftime(formatted, size, "%Y-%m-%d %H:%M", time_utc);
- return formatted;
-}
-
-#ifdef TEST
-static void
-test_formatted_date(void) {
- test_start("test_formatted_date");
- {
- testing("when given 0");
- char *const s = formatted_date(0);
- assert(s);
- assert(strcmp(s, "1970-01-01 00:00") == 0);
- free(s);
- test_ok();
- }
- {
- testing("when given negative values");
- char *s;
-
- s = formatted_date(-1);
- assert(s);
- assert(strcmp(s, "1969-12-31 23:59") == 0);
- free(s);
-
- s = formatted_date(-123456789);
- assert(s);
- assert(strcmp(s, "1966-02-02 02:26") == 0);
- free(s);
-
- s = formatted_date(-999999999);
- assert(s);
- assert(strcmp(s, "1938-04-24 22:13") == 0);
- free(s);
-
- test_ok();
- }
- {
- testing("when given recent date values");
- char *s;
-
- s = formatted_date(1627645522);
- assert(s);
- assert(strcmp(s, "2021-07-30 11:45") == 0);
- free(s);
-
- s = formatted_date(1500000000);
- assert(s);
- assert(strcmp(s, "2017-07-14 02:40") == 0);
- free(s);
-
- s = formatted_date(1000000000);
- assert(s);
- assert(strcmp(s, "2001-09-09 01:46") == 0);
- free(s);
-
- test_ok();
- }
-}
-#endif
-
-static size_t
-max(const size_t size1, const size_t size2) {
- return size1 > size2 ? size1 : size2;
-}
-
-#ifdef TEST
-static void
-test_max(void) {
- test_start("max");
- {
- testing("equal values");
- assert(max(1, 1) == 1);
- assert(max(3, 3) == 3);
- assert(max(0, 0) == 0);
- assert(max(999, 999) == 999);
- assert(max(SIZE_MAX, SIZE_MAX) == SIZE_MAX);
- test_ok();
- }
- {
- testing("first is bigger");
- assert(max(SIZE_MAX, SIZE_MAX - 1) == SIZE_MAX);
- assert(max(SIZE_MAX - 1, SIZE_MAX - 2) == SIZE_MAX - 1);
- assert(max(1, 0) == 1);
- assert(max(999, 3) == 999);
- test_ok();
- }
- {
- testing("second is bigger");
- assert(max(123, 321) == 321);
- assert(max(1, 999) == 999);
- test_ok();
- }
-}
-#endif
-
-static char *
-escape_html(const char *s) {
- if (!s) {
- return NULL;
- }
-
- #define AMPCHAR '&'
- #define LTCHAR '<'
- #define GTCHAR '>'
- #define DQUOTCHAR '"'
- #define SQUOTCHAR '\''
-
- static const char *const AMP = "&amp;";
- static const char *const LT = "&lt;";
- static const char *const GT = "&gt;";
- static const char *const DQUOT = "&quot;";
- static const char *const SQUOT = "&#39;";
-
- const size_t AMP_SIZE = strlen(AMP);
- const size_t LT_SIZE = strlen(LT);
- const size_t GT_SIZE = strlen(GT);
- const size_t DQUOT_SIZE = strlen(DQUOT);
- const size_t SQUOT_SIZE = strlen(SQUOT);
-
- const size_t BIGGEST = max(AMP_SIZE, max(LT_SIZE, max(GT_SIZE,
- max(DQUOT_SIZE, SQUOT_SIZE))));
-
- const size_t input_size = strnlen(s, SIZE_MAX);
- size_t escaped_size = 0;
- for (size_t i = 0; i < input_size; i++) {
- if (SIZE_MAX - escaped_size < BIGGEST) {
- errno = EOVERFLOW;
- logerr("escape_html()", strerror(errno), __LINE__);
- return NULL;
- }
- switch (s[i]) {
- case AMPCHAR:
- escaped_size += AMP_SIZE;
- break;
- case LTCHAR:
- escaped_size += LT_SIZE;
- break;
- case GTCHAR:
- escaped_size += GT_SIZE;
- break;
- case DQUOTCHAR:
- escaped_size += DQUOT_SIZE;
- break;
- case SQUOTCHAR:
- escaped_size += SQUOT_SIZE;
- break;
- default:
- escaped_size += sizeof(s[i]);
- break;
- }
- }
-
- const size_t size = escaped_size + sizeof('\0');
- char *const escaped = malloc(size);
- if (!escaped) {
- logerrl("malloc(", size, ")", strerror(errno), __LINE__);
- return NULL;
- }
- escaped[0] = '\0';
-
- char c;
- size_t escaped_idx = 0;
- while ((c = *s++)) {
- switch (c) {
- case AMPCHAR:
- strcat(escaped, AMP);
- escaped_idx += AMP_SIZE;
- break;
- case LTCHAR:
- strcat(escaped, LT);
- escaped_idx += LT_SIZE;
- break;
- case GTCHAR:
- strcat(escaped, GT);
- escaped_idx += GT_SIZE;
- break;
- case DQUOTCHAR:
- strcat(escaped, DQUOT);
- escaped_idx += DQUOT_SIZE;
- break;
- case SQUOTCHAR:
- strcat(escaped, SQUOT);
- escaped_idx += SQUOT_SIZE;
- break;
- default:
- escaped[escaped_idx] = c;
- escaped[escaped_idx + 1] = '\0';
- escaped_idx += sizeof(c);
- break;
- }
- }
-
- return escaped;
-}
-
-#ifdef TEST
-static void
-test_escape_html(void) {
- test_start("test_escape_html");
- {
- testing("string with no escapable chars");
- char *const s = escape_html("a plain string");
- assert(s);
- assert(strcmp(s, "a plain string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("an empty string");
- char *const s = escape_html("");
- assert(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("a NULL value");
- char *const s = escape_html(NULL);
- assert(s == NULL);
- test_ok();
- }
- {
- testing("string with a single & character");
- char *const s = escape_html("&");
- assert(s);
- assert(strcmp(s, "&amp;") == 0);
- free(s);
- test_ok();
- }
- {
- testing("a string with many escapable characters in sequence");
- char *const s = escape_html("&& >> text <<");
- assert(s);
- assert(strcmp(s, "&amp;&amp; &gt;&gt; text &lt;&lt;") == 0);
- free(s);
- test_ok();
- }
- {
- testing("all escapable characters");
- char *const s = escape_html("&<>\"'");
- assert(s);
- assert(strcmp(s, "&amp;&lt;&gt;&quot;&#39;") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: /path/edn->json.git/");
- char *const s = escape_html("/path/edn->json.git/");
- assert(s);
- assert(strcmp(s, "/path/edn-&gt;json.git/") == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: Description"
- " with \"quotes\" && 'quotes'");
- char *const s = escape_html(
- "Description with \"quotes\" && 'quotes'"
- );
- assert(s);
- const char *const expected = "Description with"
- " &quot;quotes&quot; &amp;&amp; &#39;quotes&#39;";
- assert(strcmp(s, expected) == 0);
- free(s);
- test_ok();
- }
- {
- testing("example usage: plain HTML");
- char *const s = escape_html("<a href=\"#\">link</a>");
- assert(s);
- const char *const expected = "&lt;a"
- " href=&quot;#&quot;&gt;link&lt;/a&gt;";
- assert(strcmp(s, expected) == 0);
- free(s);
- test_ok();
- }
-}
-#endif
-
-static bool
-should_trim(const char c) {
- return c == '\n' || c == '\t' || c == '\r' || c == ' ';
-}
-
-#ifdef TEST
-static void
-test_should_trim(void) {
- test_start("test_should_trim");
- {
- testing("the \\0 null character");
- assert(should_trim('\0') == false);
- test_ok();
- }
- {
- testing("the \\b character");
- assert(should_trim('\b') == false);
- test_ok();
- }
- {
- testing("the space character");
- assert(should_trim(' ') == true);
- test_ok();
- }
-}
-#endif
-
-static void
-strtrim(char *const s) {
- if (s == NULL) {
- return;
- }
-
- if (s[0] == '\0') {
- return;
- }
-
- size_t len = strnlen(s, SIZE_MAX);
- while (len != 0 && should_trim(s[len - 1])) {
- s[len - 1] = '\0';
- len--;
- }
-
- size_t offset_count = 0;
- while (should_trim(s[offset_count])) {
- offset_count++;
- };
-
- if (offset_count == 0) {
- return;
- }
-
- for (size_t i = 0; s[i]; i++) {
- s[i] = s[i + offset_count];
- }
-}
-
-#ifdef TEST
-static void
-test_strtrim(void) {
- test_start("test_strtrim");
- {
- testing("empty string");
- char *const s = "";
- strtrim(s);
- assert(strcmp(s, "") == 0);
- test_ok();
- }
- {
- testing("NULL string");
- char *const s = NULL;
- strtrim(s);
- assert(s == NULL);
- test_ok();
- }
- {
- testing("string without a newline");
- char *const s = "a string without a newline";
- strtrim(s);
- assert(strcmp(s, "a string without a newline") == 0);
- test_ok();
- }
- {
- testing("string with a newline");
- char *const s = strdup("a string with an ending newline\n");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "a string with an ending newline") == 0);
- free(s);
- test_ok();
- }
- {
- testing("a single newline");
- char *const s = strdup("\n");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "") == 0);
- free(s);
- test_ok();
- }
- {
- testing("multiple newlines at the end");
- char *const s = strdup("a string\n\n\n\n\n");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "a string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("a single space at the end");
- char *const s = strdup("a string ");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "a string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("a single space at the beginning");
- char *const s = strdup(" a string");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "a string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("multiple newlines");
- char *const s = strdup("\n\na string\n\n");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "a string") == 0);
- free(s);
- test_ok();
- }
- {
- testing("newline on the middle of the string");
- char *const s = "a\nstring";
- strtrim(s);
- assert(strcmp(s, "a\nstring") == 0);
- test_ok();
- }
- {
- testing("multiple trimmable characters");
- char *const s = strdup(" \t \n \r a string \n \t \r ");
- assert(s);
- strtrim(s);
- assert(strcmp(s, "a string") == 0);
- free(s);
- test_ok();
- }
-}
-#endif
-
-static int
-index_header_write(FILE *const fd, const char *const idx_title) {
- const int e = fprintf(
- fd,
- ""
- "<!DOCTYPE html>\n"
- "<html lang=\"%s\">\n"
- " <head>\n"
- " <meta charset=\"UTF-8\" />\n"
- " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
- " <meta name=\"description\" content=\"%s\" />\n"
- " <link rel=\"icon\" type=\"image/svg+xml\" href=\"logo.svg\" />\n"
- " <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n"
- " <title>%s</title>\n"
- " </head>\n"
- " <body>\n"
- " <header>\n"
- " <div class=\"header-horizontal-grouping\">\n"
- " <img alt=\"%s\" class=\"logo\" src=\"logo.svg\" />\n"
- " <h1 class=\"header-description\">\n"
- " %s\n"
- " </h1>\n"
- " </div>\n"
- " <hr />\n"
- " </header>\n"
- " <main>\n"
- " <table>\n"
- " <thead>\n"
- " <tr>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " </tr>\n"
- " </thead>\n"
- " <tbody>\n",
- _(MSG_LANG),
- _(MSG_INDEX_DESCRIPTION),
- idx_title,
- _(MSG_LOGO_ALT_INDEX),
- idx_title,
- _(MSG_NAME),
- _(MSG_DESCRIPTION),
- _(MSG_LAST_COMMIT)
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- return -1;
- }
- return 0;
-}
-
-static char *
-footer_signature_string(void) {
- const size_t template_size = strnlen(
- _(MSG_FOOTER_TEMPLATE),
- SIZE_MAX
- ) - strlen("%s");
- const size_t link_size = strnlen(
- PROJECT_HOMEPAGE_LINK,
- SIZE_MAX - sizeof('\0')
- ) + sizeof('\0');
- if (SIZE_MAX - template_size < link_size) {
- errno = EOVERFLOW;
- logerr("footer_signature_string()", strerror(errno), __LINE__);
- return NULL;
- }
- const size_t signature_size = template_size + link_size;
-
- char *const signature_text = malloc(signature_size);
- if (!signature_text) {
- logerrl("malloc(", signature_size, ")", strerror(errno),
- __LINE__);
- return NULL;
- }
-
- snprintf(
- signature_text,
- signature_size,
- _(MSG_FOOTER_TEMPLATE),
- PROJECT_HOMEPAGE_LINK
- );
- return signature_text;
-}
-
-static int
-index_footer_write(FILE *const fd) {
- char *const signature_text = footer_signature_string();
- if (!signature_text) {
- return -1;
- }
-
- const int e = fprintf(
- fd,
- ""
- " </tbody>\n"
- " </table>\n"
- " </main>\n"
- " <footer>\n"
- " <hr />\n"
- " <p>\n"
- " %s\n"
- " </p>\n"
- " </footer>\n"
- " </body>\n"
- "</html>\n",
- signature_text
- );
- free(signature_text);
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- return -1;
- }
-
- return 0;
-}
-
-static char *
-last_commit_date(struct git_repository *const repo) {
- struct git_commit *commit = NULL;
- struct git_revwalk *walker = NULL;
- struct git_oid oid;
- char *ret;
-
- if (
- git_revwalk_new(&walker, repo)
- || git_revwalk_push_head(walker)
- || git_revwalk_next(&oid, walker)
- || git_commit_lookup(&commit, repo, &oid)
- ) {
- char *const default_return = strdup("");
- if (!default_return) {
- logerr("strdup(\"\")", strerror(errno), __LINE__);
- }
- ret = default_return;
- goto cleanup;
- }
-
- const struct git_signature *const author = git_commit_author(commit);
- assert(author);
- ret = formatted_date(author->when.time);
-
-cleanup:
- git_commit_free(commit);
- git_revwalk_free(walker);
- return ret;
-}
-
-#ifdef TEST
-static void
-test_last_commit_date(void) {
- test_start("test_last_commit_date");
- {
- testing("embedded Git repository"
- " tests/resources/repositories/repo-1");
- struct git_repository *repo;
- const int e = git_repository_open_ext(
- &repo,
- "tests/resources/repositories/repo-1",
- GIT_REPOSITORY_OPEN_NO_SEARCH,
- NULL
- );
- assert(e == 0);
- char *const date = last_commit_date(repo);
- assert(date);
- assert(strcmp(date, "2021-07-31 19:29") == 0);
- free(date);
- git_repository_free(repo);
- test_ok();
- }
- {
- testing("embedded Git repository"
- " tests/resources/repositories/repo-2");
- struct git_repository *repo;
- const int e = git_repository_open_ext(
- &repo,
- "tests/resources/repositories/repo-2",
- GIT_REPOSITORY_OPEN_NO_SEARCH,
- NULL
- );
- assert(e == 0);
- char *const date = last_commit_date(repo);
- assert(date);
- assert(strcmp(date, "2021-07-31 19:27") == 0);
- free(date);
- git_repository_free(repo);
- test_ok();
- }
-}
-#endif
-
-static int
-logo_write(FILE *const fd) {
- if (fprintf(fd, "%s", LOGO_STR) < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- return -1;
- }
- return 0;
-}
-
-static int
-style_write(FILE *const fd) {
- if (fprintf(fd, "%s", STYLE_STR) < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- return -1;
- }
- return 0;
-}
-
-static int
-index_row_write(FILE *const fd, char *const repopath) {
- int ret = 0;
- int e;
-
- struct git_repository *repo = NULL;
- char *name = NULL;
- char *description_path = NULL;
- char *date = NULL;
- char *encoded_name = NULL;
- char *encoded_description = NULL;
- char *encoded_date = NULL;
-
- e = git_repository_open_ext(
- &repo,
- repopath,
- GIT_REPOSITORY_OPEN_NO_SEARCH,
- NULL
- );
- if (e) {
- fprintf(stderr, "%s: cannot open repository\n", repopath);
- ret = 1;
- goto cleanup;
- }
-
- if (
- !(name = remove_suffix(basename(repopath), GIT_SUFFIX))
- || !(description_path = strjoin(repopath, "/description"))
- || !(date = last_commit_date(repo))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- char description[DESCRIPTION_MAXLENGTH] = "";
- FILE *const f = fopen(description_path, "r");
- if (f) {
- if (!fgets(description, sizeof(description), f)) {
- logerrs("fgets(\"", description_path, "\")",
- strerror(errno), __LINE__);
- } else {
- strtrim(description);
- }
- if (fclose(f)) {
- logerrs("fclose(\"", description_path, "\")",
- strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
- }
-
- if (
- !(encoded_name = escape_html(name))
- || !(encoded_description = escape_html(description))
- || !(encoded_date = escape_html(date))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- e = fprintf(
- fd,
- ""
- " <tr>\n"
- " <td>\n"
- " <a href=\"%s/\">\n"
- " %s\n"
- " </a>\n"
- " </td>\n"
- " <td>\n"
- " %s\n"
- " </td>\n"
- " <td>\n"
- " %s\n"
- " </td>\n"
- " </tr>\n",
- encoded_name,
- encoded_name,
- encoded_description,
- encoded_date
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- free(encoded_date);
- free(encoded_description);
- free(encoded_name);
- free(date);
- free(description_path);
- free(name);
- git_repository_free(repo);
- return ret;
-}
-
-static int
-index_write(
- const char *const outdir,
- const char *const title,
- const int repoc,
- char *const repov[]
-) {
- int ret = 0;
- char *index_path = NULL;
- char *logo_path = NULL;
- char *style_path = NULL;
- FILE *index_fd = NULL;
- FILE *logo_fd = NULL;
- FILE *style_fd = NULL;
-
- if (
- !(index_path = strjoin(outdir, "/index.html"))
- || !(logo_path = strjoin(outdir, "/logo.svg"))
- || !(style_path = strjoin(outdir, "/style.css"))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- if (!(index_fd = fopen(index_path, "w"))) {
- logerrs("fopen(\"", index_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
- if (!(logo_fd = fopen(logo_path, "w"))) {
- logerrs("fopen(\"", logo_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
- if (!(style_fd = fopen(style_path, "w"))) {
- logerrs("fopen(\"", style_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- if (index_header_write(index_fd, title)) {
- ret = -1;
- goto cleanup;
- }
- for (int i = 0; i < repoc; i++) {
- const int e = index_row_write(index_fd, repov[i]);
- if (e == -1) {
- ret = -1;
- goto cleanup;
- }
- if (e) {
- ret = 1;
- }
- }
-
- if (
- index_footer_write(index_fd)
- || logo_write(logo_fd)
- || style_write(style_fd)
- ) {
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- if (style_fd && fclose(style_fd)) {
- logerrs("fclose(\"", style_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- if (logo_fd && fclose(logo_fd)) {
- logerrs("fclose(\"", logo_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- if (index_fd && fclose(index_fd)) {
- logerrs("fclose(\"", index_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- free(style_path);
- free(logo_path);
- free(index_path);
- return ret;
-}
-
-static int
-repo_refs_branches_each(
- FILE *const refs_fd,
- struct git_repository *const repo,
- struct git_reference *const ref
-) {
- int ret = 0;
- struct git_commit *commit = NULL;
- char *encoded_name = NULL;
- char *encoded_summary = NULL;
- char *encoded_author = NULL;
- char *date = NULL;
- int e;
-
- const char *const name = git_reference_shorthand(ref);
- assert(name);
- const struct git_oid *const oid = git_reference_target(ref);
- assert(oid);
-
- if (git_commit_lookup(&commit, repo, oid)) {
- const git_error *const error = git_error_last();
- assert(error);
- logerrs("git_commit_lookup(", name, ")", error->message,
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- const char *const sha = git_oid_tostr_s(oid);
- assert(sha);
- const char *const summary = git_commit_summary(commit);
- assert(summary);
- const struct git_signature *const author = git_commit_author(commit);
- assert(author);
-
- if (
- !(encoded_name = escape_html(name))
- || !(encoded_summary = escape_html(summary))
- || !(encoded_author = escape_html(author->name))
- || !(date = formatted_date(author->when.time))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- e = fprintf(
- refs_fd,
- ""
- " <tr>\n"
- " <td>\n"
- " <a href=\"log/%s.html\">\n"
- " %s\n"
- " </a>\n"
- " </td>\n"
- " <td>\n"
- " <a href=\"commit/%s.html\">\n"
- " %s\n"
- " </a>\n"
- " </td>\n"
- " <td>\n"
- " %s\n"
- " </td>\n"
- " <td>\n"
- " %s\n"
- " </td>\n"
- " </tr>\n"
- "",
- encoded_name,
- encoded_name,
- sha,
- encoded_summary,
- encoded_author,
- date
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- free(date);
- free(encoded_author);
- free(encoded_summary);
- free(encoded_name);
- git_commit_free(commit);
- return ret;
-}
-
-static int
-repo_refs_tags_each(
- FILE *const refs_fd,
- struct git_repository *const repo,
- struct git_reference *const ref,
- const char *const project_name
-) {
- int ret = 0;
- struct git_commit *commit = NULL;
- char *encoded_name = NULL;
- char *encoded_author = NULL;
- char *date = NULL;
- int e;
-
- if (!git_reference_is_tag(ref)) {
- goto cleanup;
- }
-
- const char *const name = git_reference_shorthand(ref);
- assert(name);
- const struct git_oid *const oid = git_reference_target(ref);
- assert(oid);
-
- if (git_commit_lookup(&commit, repo, oid)) {
- const git_error *const error = git_error_last();
- assert(error);
- logerrs("git_commit_lookup(", name, ")", error->message,
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- const char *const summary = git_commit_summary(commit);
- assert(summary);
- const struct git_signature *const author = git_commit_author(commit);
- assert(author);
-
- if (
- !(encoded_name = escape_html(name))
- || !(encoded_author = escape_html(author->name))
- || !(date = formatted_date(author->when.time))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- e = fprintf(
- refs_fd,
- ""
- " <tr>\n"
- " <td>\n"
- " <a href=\"tag/%s.html\">\n"
- " %s\n"
- " </a>\n"
- " (<a href=\"tarballs/%s-%s.tar.gz\">tarball</a>, <a href=\"tarballs/%s-%s.tar.gz.asc\">sig</a>)\n"
- " </td>\n"
- " <td>\n"
- " %s\n"
- " </td>\n"
- " <td>\n"
- " %s\n"
- " </td>\n"
- " </tr>\n"
- "",
- encoded_name,
- encoded_name,
- project_name,
- encoded_name,
- project_name,
- encoded_name,
- encoded_author,
- date
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- free(date);
- free(encoded_author);
- free(encoded_name);
- git_commit_free(commit);
- return ret;
-}
-
-static int
-repo_refs_write(
- const char *const outdir,
- struct git_repository *const repo,
- const char *const project_name,
- const char *const description,
- const char *const clone_url
-) {
- int ret = 0;
- char *signature_text = NULL;
- char *refs_path = NULL;
- FILE *refs_fd = NULL;
- struct git_branch_iterator *branch_iter = NULL;
- struct git_reference_iterator *ref_iter = NULL;
- int e;
-
-
- if (
- !(signature_text = footer_signature_string())
- || !(refs_path = strjoin(outdir, "/refs.html"))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- if (!(refs_fd = fopen(refs_path, "w"))) {
- logerrs("fopen(\"", refs_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- if (
- git_branch_iterator_new(&branch_iter, repo, GIT_BRANCH_LOCAL)
- || git_reference_iterator_new(&ref_iter, repo)
- ) {
- const git_error *const error = git_error_last();
- assert(error);
- const char *const fn = branch_iter == NULL
- ? "git_branch_iterator_new()"
- : "git_reference_iterator_new()";
- logerr(fn, error->message, __LINE__);
- ret = -1;
- goto cleanup;
- }
-
-
- e = fprintf(
- refs_fd,
- ""
- "<!DOCTYPE html>\n"
- "<html lang=\"%s\">\n"
- " <head>\n"
- " <meta charset=\"UTF-8\" />\n"
- " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
- " <meta name=\"description\" content=\"%s\" />\n"
- " <link rel=\"icon\" type=\"image/svg+xml\" href=\"logo.svg\" />\n"
- " <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n"
- " <link rel=\"alternate\" type=\"application/atom+xml\" href=\"commits.xml\" title=\"%s - %s\" hreflang=\"%s\" />\n"
- " <link rel=\"alternate\" type=\"application/atom+xml\" href=\"tags.xml\" title=\"%s - %s\" hreflang=\"%s\" />\n"
- " <title>%s</title>\n"
- " </head>\n"
- " <body>\n"
- " <header>\n"
- " <div class=\"header-horizontal-grouping\">\n"
- " <a href=\"../\">\n"
- " <img alt=\"%s\" class=\"logo\" src=\"logo.svg\" />\n"
- " </a>\n"
- " <div class=\"header-description\">\n"
- " <h1>\n"
- " %s\n"
- " </h1>\n"
- " <h2>\n"
- " %s\n"
- " </h2>\n"
- " <code>\n"
- " git clone %s\n"
- " </code>\n"
- " </div>\n"
- " </div>\n"
- " <nav>\n"
- " <ul>\n"
- " <li>\n"
- " <a href=\"files.html\">\n"
- " %s\n"
- " </a>\n"
- " </li>\n"
- " <li>\n"
- " <a href=\"log.html\">\n"
- " %s\n"
- " </a>\n"
- " </li>\n"
- " <li class=\"selected-nav-item\">\n"
- " <a href=\"refs.html\">\n"
- " %s\n"
- " </a>\n"
- " </li>\n"
- " </ul>\n"
- " </nav>\n"
- " <hr />\n"
- " </header>\n"
- " <main>\n"
- " <table>\n"
- " <thead>\n"
- " <tr>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " </tr>\n"
- " </thead>\n"
- " <tbody>\n",
- _(MSG_LANG),
- description,
- project_name,
- _(MSG_COMMIT_FEED),
- _(MSG_LANG),
- project_name,
- _(MSG_TAGS_FEED),
- _(MSG_LANG),
- project_name,
- _(MSG_LOGO_ALT_REPOSITORY),
- project_name,
- description,
- clone_url,
- _(MSG_NAV_FILES),
- _(MSG_NAV_LOG),
- _(MSG_NAV_REFS),
- _(MSG_THEAD_BRANCH),
- _(MSG_THEAD_COMMITMSG),
- _(MSG_THEAD_AUTHOR),
- _(MSG_THEAD_DATE)
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- struct git_reference *ref;
- git_branch_t _btype;
- while (!(e = git_branch_next(&ref, &_btype, branch_iter))) {
- e = repo_refs_branches_each(refs_fd, repo, ref);
- git_reference_free(ref);
- if (e == -1) {
- ret = -1;
- goto cleanup;
- }
- if (e) {
- ret = 1;
- }
- }
- if (e != GIT_ITEROVER) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_branch_next()", error->message, __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- e = fprintf(
- refs_fd,
- ""
- " </tbody>\n"
- " </table>\n"
- " <table>\n"
- " <thead>\n"
- " <tr>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " <th>\n"
- " %s\n"
- " </th>\n"
- " </tr>\n"
- " </thead>\n"
- " <tbody>\n",
- _(MSG_THEAD_TAG),
- _(MSG_THEAD_AUTHOR),
- _(MSG_THEAD_DATE)
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- while (!(e = git_reference_next(&ref, ref_iter))) {
- e = repo_refs_tags_each(refs_fd, repo, ref, project_name);
- git_reference_free(ref);
- if (e == -1) {
- ret = -1;
- goto cleanup;
- }
- if (e) {
- ret = 1;
- }
- }
- if (e != GIT_ITEROVER) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_reference_next()", error->message, __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- e = fprintf(
- refs_fd,
- ""
- " </tbody>\n"
- " </table>\n"
- " </main>\n"
- " <footer>\n"
- " <hr />\n"
- " <p>\n"
- " %s\n"
- " </p>\n"
- " </footer>\n"
- " </body>\n"
- "</html>\n",
- signature_text
- );
- if (e < 0) {
- logerr("fprintf()", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- git_branch_iterator_free(branch_iter);
- git_reference_iterator_free(ref_iter);
- if (refs_fd && fclose(refs_fd)) {
- logerrs("fclose(\"", refs_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- free(refs_path);
- free(signature_text);
- return ret;
-}
-
-static int
-repo_tarballs_refs_each(
- struct git_repository *const repo,
- struct git_reference *const ref,
- const char *const tarballs_dir,
- const char *const project_name
-) {
- int ret = 0;
- char *out = NULL;
- struct git_commit *commit = NULL;
- struct git_tree *tree = NULL;
- char *tar_path = NULL;
- FILE *tar_fd = NULL;
-
- const bool is_tag = git_reference_is_tag(ref);
- const bool is_branch = git_reference_is_branch(ref);
-
- if (!is_branch && !is_tag) {
- goto cleanup;
- }
-
- const char *const name = git_reference_shorthand(ref);
- assert(name);
- const struct git_oid *const oid = git_reference_target(ref);
- assert(oid != NULL);
-
- char template[] = "/tmp/gistatic.XXXXXX";
- const char *const tmpdir = mkdtemp(template);
- if (!tmpdir) {
- logerrs("mkdtemp(", tmpdir, ")", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
- out = strsjoin((const char *const [])
- { tmpdir, "/", project_name, "-", name, NULL });
- if (!out) {
- ret = -1;
- goto cleanup;
- }
-
- git_checkout_options options;
- if (git_checkout_options_init(&options, GIT_CHECKOUT_OPTIONS_VERSION)) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_checkout_options_init()", error->message, __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- options.target_directory = out;
- options.checkout_strategy =
- GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
-
- if (git_commit_lookup(&commit, repo, oid)) {
- const git_error *const error = git_error_last();
- assert(error);
- logerrs("git_commit_lookup(", name, ")", error->message,
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- const struct git_object *const treeish =
- (const git_object *const)commit;
- if (git_checkout_tree(repo, treeish, &options)) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_reference_iterator_new()", error->message,
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- tar_path = strsjoin((const char *const [])
- { tarballs_dir, "/", project_name, "-", name, ".tar", NULL });
- if (!tar_path) {
- ret = -1;
- goto cleanup;
- }
-
- if (!(tar_fd = fopen(tar_path, "w"))) {
- logerrs("fopen(\"", tar_path, "\")", strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- if (tarball_write_from_directory(tar_fd, out)) {
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- if (tar_fd && fclose(tar_fd)) {
- logerrs("fclose(\"", tar_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- free(tar_path);
- git_tree_free(tree);
- git_commit_free(commit);
- free(out);
- return ret;
-}
-
-static int
-repo_tarballs_write(
- const char *const outdir,
- struct git_repository *const repo,
- const char *const project_name
-) {
- int ret = 0;
- int e;
- char *tarballs_dir = NULL;
- struct git_reference_iterator *ref_iter = NULL;
-
- if (!(tarballs_dir = strjoin(outdir, "/tarballs"))) {
- ret = -1;
- goto cleanup;
- }
-
- errno = 0;
- if (mkdir(tarballs_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
- && errno != EEXIST) {
- logerrs("mkdir(\"", tarballs_dir, "\")", strerror(errno),
- __LINE__);
- ret = EXIT_ERROR;
- goto cleanup;
- }
-
- if (git_reference_iterator_new(&ref_iter, repo)) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_reference_iterator_new()", error->message,
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- struct git_reference *ref;
- while (!(e = git_reference_next(&ref, ref_iter))) {
- e = repo_tarballs_refs_each(repo, ref,
- tarballs_dir, project_name);
- git_reference_free(ref);
- if (e == -1) {
- ret = -1;
- goto cleanup;
- }
- if (e) {
- ret = 1;
- }
- }
- if (e != GIT_ITEROVER) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_reference_next()", error->message, __LINE__);
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- git_reference_iterator_free(ref_iter);
- free(tarballs_dir);
- return ret;
-}
-
-static int
-repo_write(
- const char *const outdir,
- char *const reporelpath,
- const char *const clone_url
-) {
- int ret = 0;
- int e;
-
- char *repopath = NULL;
- char *description_path = NULL;
- char *logo_path = NULL;
- char *style_path = NULL;
- char *name = NULL;
- char *encoded_name = NULL;
- char *encoded_description = NULL;
- FILE *logo_fd = NULL;
- FILE *style_fd = NULL;
- struct git_repository *repo = NULL;
-
- if (!(repopath = realpath(reporelpath, NULL))) {
- logerrs("realpath(\"", reporelpath, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- if (
- !(description_path = strjoin(repopath, "/description"))
- || !(logo_path = strjoin(outdir, "/logo.svg"))
- || !(style_path = strjoin(outdir, "/style.css"))
- || !(name = remove_suffix(basename(repopath), GIT_SUFFIX))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- if (!(logo_fd = fopen(logo_path, "w"))) {
- logerrs("fopen(\"", logo_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
- if (!(style_fd = fopen(style_path, "w"))) {
- logerrs("fopen(\"", style_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- if (logo_write(logo_fd) || style_write(style_fd)) {
- ret = -1;
- goto cleanup;
- }
-
- e = git_repository_open_ext(
- &repo,
- repopath,
- GIT_REPOSITORY_OPEN_NO_SEARCH,
- NULL
- );
- if (e) {
- const git_error *const error = git_error_last();
- assert(error);
- logerr("git_repository_open_ext()", error->message, __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- char description[DESCRIPTION_MAXLENGTH] = "";
- FILE *const f = fopen(description_path, "r");
- if (f) {
- if (!fgets(description, sizeof(description), f)) {
- logerrs("fget(\"", description_path, "\")",
- strerror(errno), __LINE__);
- } else {
- strtrim(description);
- }
- if (fclose(f)) {
- logerrs("fclose(\"", description_path, "\")",
- strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
- }
-
- if (
- !(encoded_name = escape_html(name))
- || !(encoded_description = escape_html(description))
- ) {
- ret = -1;
- goto cleanup;
- }
-
- if (
- repo_refs_write(outdir, repo, encoded_name,
- encoded_description, clone_url)
- || repo_tarballs_write(outdir, repo, encoded_name)
- ) {
- ret = -1;
- goto cleanup;
- }
-
-cleanup:
- free(encoded_description);
- free(encoded_name);
- if (repo) {
- git_repository_free(repo);
- }
- if (style_fd && fclose(style_fd)) {
- logerrs("fclose(\"", style_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- if (logo_fd && fclose(logo_fd)) {
- logerrs("fclose(\"", style_path, "\")", strerror(errno),
- __LINE__);
- ret = -1;
- }
- free(name);
- free(style_path);
- free(logo_path);
- free(description_path);
- free(repopath);
- return ret;
-}
-
-int
-gistatic_main(const int argc, char *const argv[]) {
- int ret = EXIT_SUCCESS;
- bool cleanup_libgit = false;
- catalog_descriptor = catopen(CATALOG_NAME, NL_CAT_LOCALE);
-
- if (argc < 2) {
- if (print_usage(stderr)) {
- ret = EXIT_ERROR;
- goto cleanup;
- }
- ret = EXIT_USAGE;
- goto cleanup;
- }
-
- int flag;
- bool index = false;
- const char *idx_title = _(MSG_DEFAULT_TITLE);
- const char *outdir = ".";
- const char *clone_url = NULL;
- while ((flag = getopt(argc, argv, "o:t:u:ivhV")) != -1) {
- switch (flag) {
- case 'i':
- index = true;
- break;
- case 't':
- idx_title = optarg;
- break;
- case 'o':
- outdir = optarg;
- break;
- case 'v':
- verbose = true;
- break;
- case 'u':
- clone_url = optarg;
- break;
- case 'h':
- if (print_usage(stdout) || print_help(stdout)) {
- ret = EXIT_ERROR;
- }
- goto cleanup;
- case 'V':
- if (print_version(stdout)) {
- ret = EXIT_ERROR;
- }
- goto cleanup;
- default:
- if (print_usage(stderr)) {
- ret = EXIT_ERROR;
- }
- goto cleanup;
- }
- }
-
- if (!index && !clone_url) {
- if (fprintf(stderr, "%s\n", _(MSG_MISSING_URL)) < 0
- || print_usage(stderr)) {
- ret = EXIT_ERROR;
- goto cleanup;
- }
- ret = EXIT_USAGE;
- goto cleanup;
- }
- if (index && clone_url) {
- if (fprintf(stderr, "%s\n", _(MSG_INCOMPATIBLE_OPTIONS)) < 0
- || print_usage(stderr)) {
- ret = EXIT_ERROR;
- goto cleanup;
- }
- ret = EXIT_USAGE;
- goto cleanup;
- }
- if (optind == argc) {
- if (fprintf(stderr, "%s\n", _(MSG_MISSING_ARGS)) < 0
- || print_usage(stderr)) {
- ret = EXIT_ERROR;
- goto cleanup;
- }
- ret = EXIT_USAGE;
- goto cleanup;
- }
-
-
- errno = 0;
- if (mkdir(outdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
- && errno != EEXIST) {
- logerrs("mkdir(\"", outdir, "\")", strerror(errno), __LINE__);
- ret = EXIT_ERROR;
- goto cleanup;
- }
-
- git_libgit2_init();
- cleanup_libgit = true;
-
- if (index) {
- if (index_write(outdir, idx_title,
- argc - optind, argv + optind)) {
- ret = EXIT_ERROR;
- goto cleanup;
- }
- } else {
- if (repo_write(outdir, argv[optind], clone_url)) {
- ret = EXIT_ERROR;
- goto cleanup;
- }
- }
-
-cleanup:
- if (cleanup_libgit) {
- git_libgit2_shutdown();
- }
- if (catalog_descriptor && catalog_descriptor != (nl_catd)-1) {
- if (catclose(catalog_descriptor)) {
- logerr("catclose()", strerror(errno), __LINE__);
- ret = EXIT_ERROR;
- }
- }
- return ret;
-}
-
-
-#ifdef TEST
-void
-unit_tests_gistatic(void) {
- dump_translatable_strings();
-
- git_libgit2_init();
- test_underscore();
- test_remove_suffix();
- test_strjoin();
- test_strsjoin();
- test_formatted_date();
- test_max();
- test_escape_html();
- test_should_trim();
- test_strtrim();
- test_last_commit_date();
- git_libgit2_shutdown();
-}
-#endif
diff --git a/src/lib.h b/src/lib.h
deleted file mode 100644
index 07e900b..0000000
--- a/src/lib.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef GISTATIC_GISTATIC_H
-#define GISTATIC_GISTATIC_H
-
-int
-gistatic_main(const int argc, char *const argv[]);
-
-#ifdef TEST
-void
-unit_tests_gistatic(void);
-#endif
-
-#endif
diff --git a/src/lib.msg b/src/lib.msg
deleted file mode 100644
index 84fc327..0000000
--- a/src/lib.msg
+++ /dev/null
@@ -1,61 +0,0 @@
-1 Repositories
-
-2 Logo image of the repository list
-
-3 Logo image of the repository
-
-4 Generated with %s
-
-5 en
-
-6 Name
-
-7 Description
-
-8 Last commit
-
-9 Usage:
- gistatic -i -o DIRECTORY REPOSITORY...
- gistatic -o DIRECTORY -u CLONE_URL REPOSITORY
- gistatic [-hV]
-
-
-10
-Options:
- -i build the index page of the repositories
- -u CLONE_URL address to be shown alongside "git clone"
- -o DIRECTORY output where to place the generated files
- -h, --help show this help
- -V, --version print the version number
-
-See "man gistatic" for more information.
-
-
-11 commit feed
-
-12 tags feed
-
-13 files
-
-14 log
-
-15 refs
-
-16 Branch
-
-17 Commit message
-
-18 Author
-
-19 Date
-
-20 Tag
-
-21 Missing '-u CLONE_URL'
-
-22 Missing [PATH | [PATHS]]
-
-23 Incompatible options -u and -i
-
-24 Index of repositories
-
diff --git a/src/logerr.c b/src/logerr.c
deleted file mode 100644
index 029ed7f..0000000
--- a/src/logerr.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#include "config.h"
-#include "logerr.h"
-
-#include <stdio.h>
-
-
-void
-logerr_file(
- const char *const s,
- const char *const msg,
- const char *const file,
- const int lineno
-) {
- fprintf(
- stderr,
- "%s:%s:%d: %s: %s\n",
- PROGNAME,
- file,
- lineno,
- s,
- msg
- );
-}
-
-void
-logerrs_file(
- const char *const pre,
- const char *const mid,
- const char *const post,
- const char *const msg,
- const char *const file,
- const int lineno
-) {
- fprintf(
- stderr,
- "%s:%s:%d: %s%s%s: %s\n",
- PROGNAME,
- file,
- lineno,
- pre,
- mid,
- post,
- msg
- );
-}
-
-void
-logerrl_file(
- const char *const pre,
- const size_t mid,
- const char *const post,
- const char *const msg,
- const char *const file,
- int lineno
-) {
- fprintf(
- stderr,
- "%s:%s:%d: %s%ld%s: %s\n",
- PROGNAME,
- file,
- lineno,
- pre,
- mid,
- post,
- msg
- );
-}
diff --git a/src/logerr.h b/src/logerr.h
deleted file mode 100644
index 47228ab..0000000
--- a/src/logerr.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef GISTATIC_LOGERR_H
-#define GISTATIC_LOGERR_H
-
-#include <stddef.h>
-
-void
-logerr_file(
- const char *const s,
- const char *const msg,
- const char *const file,
- const int lineno
-);
-
-void
-logerrs_file(
- const char *const pre,
- const char *const mid,
- const char *const post,
- const char *const msg,
- const char *const file,
- const int lineno
-);
-
-void
-logerrl_file(
- const char *const pre,
- const size_t mid,
- const char *const post,
- const char *const msg,
- const char *const file,
- const int lineno
-);
-
-#endif
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index a8bab28..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "tar.h"
-#include "gistatic.h"
-
-#include <stdlib.h>
-
-#ifdef TEST
-static void
-unit_tests(void) {
- unit_tests_tar();
- unit_tests_gistatic();
-}
-#endif
-
-int
-main(int argc, char *argv[]) {
-#ifdef TEST
- unit_tests();
- return EXIT_SUCCESS;
-#endif
-
- return gistatic_main(argc, argv);
-}
diff --git a/src/static/style.css b/src/static/style.css
new file mode 100644
index 0000000..d1fc04d
--- /dev/null
+++ b/src/static/style.css
@@ -0,0 +1,133 @@
+:root {
+ --row-light-gray: #eee;
+ --row-background-light-gray: #ccc;
+ --background-light-gray: #999;
+ font-family: sans-serif;
+}
+
+a {
+ color: black;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+#horizontal-aligner-of-logo-and-header-items {
+ display: flex;
+}
+
+#vertical-aligner-of-header-items {
+ display: flex;
+ flex-direction: row;
+}
+
+div#vertical-aligner-of-header-items > h2 {
+ font-weigth: normal;
+ color: red;
+}
+
+#repository-logo {
+ width: 5em;
+ margin: 2em;
+}
+
+hr {
+ border: none;
+ height: 5px;
+ background-color: var(--background-light-gray);
+}
+
+nav ul {
+ display: flex;
+ list-style-type: none;
+ border-bottom: 5px solid var(--background-light-gray);
+}
+
+nav li {
+ margin-left: 10px;
+}
+
+nav a {
+ padding-top: 2px;
+ padding-left: 8px;
+ padding-right: 8px;
+}
+
+nav .selected-nav-item a {
+ background-color: var(--background-light-gray);
+}
+
+main {
+ padding: 2em;
+}
+
+table {
+ text-align: left;
+ border: none;
+ border-collapse: collapse;
+ width: 100%;
+}
+
+table tr.table-no-highlight-row {
+ background-color: white;
+}
+
+table tr.table-no-highlight-row:hover {
+ background-color: white;
+}
+
+table tr:hover {
+ background-color: var(--row-background-light-gray);
+}
+
+tr:nth-child(odd) {
+ background-color: white;
+}
+
+tr:nth-child(even) {
+ background-color: var(--row-light-gray);
+}
+
+footer {
+ text-align: center;
+}
+
+/* Page log.html */
+
+.log-commit-box {
+ padding: 1em;
+ background-color: var(--row-light-gray);
+}
+
+.log-head-highlight {
+ padding: 2px;
+ background-color: #ff8888;
+ border: 1px solid;
+}
+
+.log-branch-highlight {
+ padding: 2px;
+ background-color: #88ff88;
+ border: 1px solid;
+}
+
+.log-tag-highlight {
+ padding: 2px;
+ background-color: #ffff88;
+ border: 1px solid;
+}
+
+
+/* Page tree.html */
+
+table#files-table td:nth-child(1) {
+ width: 10%;
+ padding-right: 2em;
+}
+
+.files-row-item-directory {
+ color: blue;
+ font-weight: bold;
+}
diff --git a/src/static/styles.css b/src/static/styles.css
deleted file mode 100644
index a1ed48d..0000000
--- a/src/static/styles.css
+++ /dev/null
@@ -1,133 +0,0 @@
-:root {
- --row-light-gray: #eee;
- --row-background-light-gray: #ccc;
- --background-light-gray: #999;
- font-family: sans-serif;
-}
-
-a {
- color: black;
- text-decoration: none;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-#horizontal-aligner-of-logo-and-header-items {
- display: flex;
-}
-
-#vertical-aligner-of-header-items {
- display: flex;
- flex-direction: row;
-}
-
-div#vertical-aligner-of-header-items > h2 {
- font-weigth: normal;
- color: red;
-}
-
-#repository-logo {
- width: 5em;
- margin: 2em;
-}
-
-hr {
- border: none;
- height: 5px;
- background-color: var(--background-light-gray);
-}
-
-nav ul {
- display: flex;
- list-style-type: none;
- border-bottom: 5px solid var(--background-light-gray);
-}
-
-nav li {
- margin-left: 10px;
-}
-
-nav a {
- padding-top: 2px;
- padding-left: 8px;
- padding-right: 8px;
-}
-
-nav .selected-nav-item a {
- background-color: var(--background-light-gray);
-}
-
-main {
- padding: 2em;
-}
-
-table {
- text-align: left;
- border: none;
- border-collapse: collapse;
- width: 100%;
-}
-
-table tr.table-no-highlight-row {
- background-color: white;
-}
-
-table tr.table-no-highlight-row:hover {
- background-color: white;
-}
-
-table tr:hover {
- background-color: var(--row-background-light-gray);
-}
-
-tr:nth-child(odd) {
- background-color: white;
-}
-
-tr:nth-child(even) {
- background-color: var(--row-light-gray);
-}
-
-footer {
- text-align: center;
-}
-
-/* Page log.html */
-
-.log-commit-box {
- padding: 1em;
- background-color: var(--row-light-gray);
-}
-
-.log-head-highlight {
- padding: 2px;
- background-color: #ff8888;
- border: 1px solid;
-}
-
-.log-branch-highlight {
- padding: 2px;
- background-color: #88ff88;
- border: 1px solid;
-}
-
-.log-tag-highlight {
- padding: 2px;
- background-color: #ffff88;
- border: 1px solid;
-}
-
-
-/* Page tree.html */
-
-table#files-table td:nth-child(1) {
- width: 10%;
- padding-right: 2em;
-}
-
-.files-row-item-directory {
- color: blue;
- font-weight: bold;
-}
diff --git a/src/tar.c b/src/tar.c
deleted file mode 100644
index 8272eb4..0000000
--- a/src/tar.c
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "config.h"
-#include "tar.h"
-#include "logerr.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <stdbool.h>
-
-/*
-
-About the ustar format:
-- name[100] can only contain 100 bytes, while PATH_MAX's minimum value is 256
- for `_POSIX_PATH_MAX`, and 1024 for _XOPEN_PATH_MAX. Wait! Actually there is
- a "prefix" field, which is probably what I'm looking for;
-- characters are encoded in ISO/IEC 646:1991 [0] instead of UTF-8, even though
- it requires the file names to be conformant to the POSIX filename
- restrictions;
-
-[0]: https://en.wikipedia.org/wiki/ISO/IEC_646
-
-*/
-
-/*
- * Implementation of the "ustar archive tape" (tar) format, conformant to the
- * specification [0].
- *
- * [0]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
- */
-
-
-static void
-logerr(
- const char *const s,
- const char *const msg,
- const int lineno
-) {
- logerr_file(s, msg, __FILE__, lineno);
-}
-
-static void
-logerrs(
- const char *const pre,
- const char *const mid,
- const char *const post,
- const char *const msg,
- const int lineno
-) {
- logerrs_file(pre, mid, post, msg, __FILE__, lineno);
-}
-
-static void
-logerrl(
- const char *const pre,
- const size_t mid,
- const char *const post,
- const char *const msg,
- const int lineno
-) {
- logerrl_file(pre, mid, post, msg, __FILE__, lineno);
-}
-
-
-struct TarEntry {
- // What size the spec says?
- unsigned int begin;
-};
-
-// static int tar_write(FILE *const fd, 
-
-int
-tarball_write_from_directory(
- const FILE *const fd,
- const char *const directory_path
-) {
- int ret = 0;
- struct TarEntry *entry = NULL;
- (void)directory_path;
- (void)fd;
-
- if (!(entry = malloc(sizeof(struct TarEntry)))) {
- logerrl("malloc(", sizeof(struct TarEntry), ")",
- strerror(errno), __LINE__);
- ret = -1;
- goto cleanup;
- }
-
- if (false) {
- logerrs("", "", "", "", 0);
- logerr("", "", 0);
- }
-
-
-cleanup:
- free(entry);
- return ret;
-}
-
-#ifdef TEST
-void
-unit_tests_tar(void) {}
-#endif
diff --git a/src/tar.h b/src/tar.h
deleted file mode 100644
index ce1b1fb..0000000
--- a/src/tar.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef GISTATIC_TAR_H
-#define GISTATIC_TAR_H
-
-#include <stdio.h>
-
-int
-tarball_write_from_directory(
- const FILE *const fd,
- const char *const directory_path
-);
-
-#ifdef TEST
-void
-unit_tests_tar(void);
-#endif
-
-#endif
diff --git a/src/templates/files.html b/src/templates/files.html
index b2fd2af..fb556be 100644
--- a/src/templates/files.html
+++ b/src/templates/files.html
@@ -1,311 +1,311 @@
<!DOCTYPE html>
<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="icon" type="image/svg+xml" href="../static/logo.svg" />
- <link rel="stylesheet" type="text/css" href="../static/styles.css" />
- <link rel="alternate" type="application/atom+xml" href="$PREFIX/commits.xml" title="$PROJECT_NAME - commit feed" hreflang="en" />
- <link rel="alternate" type="application/atom+xml" href="$PREFIX/tags.xml" title="$PROJECT_NAME - tags feed" hreflang="en" />
+ <link rel="icon" type="image/svg+xml" href="../static/logo.svg" />
+ <link rel="stylesheet" type="text/css" href="../static/styles.css" />
+ <link rel="alternate" type="application/atom+xml" href="$PREFIX/commits.xml" title="$PROJECT_NAME - commit feed" hreflang="en" />
+ <link rel="alternate" type="application/atom+xml" href="$PREFIX/tags.xml" title="$PROJECT_NAME - tags feed" hreflang="en" />
- <title>$PROJECT_NAME - $PROJECT_DESCRIPTION</title>
+ <title>$PROJECT_NAME - $PROJECT_DESCRIPTION</title>
- <meta property="og:site_name" content="Repository for $PROJECT_NAME" />
- <meta property="og:locale" content="en" />
- <meta property="og:title" content="$PROJECT_NAME" />
- <meta property="og:description" content="$PROJECT_DESCRIPTION" />
- <meta name="description" content="$PROJECT_DESCRIPTION" />
- </head>
- <body>
- <header>
- <div id="horizontal-aligner-of-logo-and-header-items">
- <img id="repository-logo" src="../static/logo.svg" alt="Logo image of the repository" />
- <div id="vertical-aligner-of-project-name-and-description">
- <h1>
- $PROJECT_NAME
- </h1>
- <h2>
- $PROJECT_DESCRIPTION
- </h2>
- <code>
- git clone $PROJECT_CLONE_URL
- </code>
- </div>
- </div>
- <nav>
- <ul>
- <li class="selected-nav-item">
- <a href="$PREFIX/files.html">
- files
- </a>
- </li>
- <li>
- <a href="$PREFIX/files.html">
- log
- </a>
- </li>
- <li>
- <a href="$PREFIX/files.html">
- refs
- </a>
- </li>
- </ul>
- </nav>
- </header>
- <main>
- <table id="files-table">
- <tr class="table-no-highlight-row">
- <th>
- Mode
- </th>
- <th>
- Name
- </th>
- <th>
- Size
- </th>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- .gitignore
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- CHAGELOG.md
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- COPYING
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- Makefile
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- README.md
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- TODOs.md
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- d---------
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
- <code>
- aux/
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- description
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- d---------
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
- <code>
- doc/
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- -rw-r--r--
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html">
- <code>
- long-description
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- d---------
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
- <code>
- src/
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- <tr>
- <td>
- <code>
- d---------
- </code>
- </td>
- <td>
- <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
- <code>
- tests/
- </code>
- </a>
- </td>
- <td>
- <code>
- 123B
- </code>
- </td>
- </tr>
- </table>
- <hr />
- <section>
- <h1>README</h1>
- <p>This does something.</p>
- </section>
- </main>
- <footer>
- <hr />
- <p>
- Generated by gistatic $VERSION at $NOW
- </p>
- </footer>
- </body>
+ <meta property="og:site_name" content="Repository for $PROJECT_NAME" />
+ <meta property="og:locale" content="en" />
+ <meta property="og:title" content="$PROJECT_NAME" />
+ <meta property="og:description" content="$PROJECT_DESCRIPTION" />
+ <meta name="description" content="$PROJECT_DESCRIPTION" />
+ </head>
+ <body>
+ <header>
+ <div id="horizontal-aligner-of-logo-and-header-items">
+ <img id="repository-logo" src="../static/logo.svg" alt="Logo image of the repository" />
+ <div id="vertical-aligner-of-project-name-and-description">
+ <h1>
+ $PROJECT_NAME
+ </h1>
+ <h2>
+ $PROJECT_DESCRIPTION
+ </h2>
+ <code>
+ git clone $PROJECT_CLONE_URL
+ </code>
+ </div>
+ </div>
+ <nav>
+ <ul>
+ <li class="selected-nav-item">
+ <a href="$PREFIX/files.html">
+ files
+ </a>
+ </li>
+ <li>
+ <a href="$PREFIX/files.html">
+ log
+ </a>
+ </li>
+ <li>
+ <a href="$PREFIX/files.html">
+ refs
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </header>
+ <main>
+ <table id="files-table">
+ <tr class="table-no-highlight-row">
+ <th>
+ Mode
+ </th>
+ <th>
+ Name
+ </th>
+ <th>
+ Size
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ .gitignore
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ CHAGELOG.md
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ COPYING
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ Makefile
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ README.md
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ TODOs.md
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ d---------
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
+ <code>
+ aux/
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ description
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ d---------
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
+ <code>
+ doc/
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ -rw-r--r--
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html">
+ <code>
+ long-description
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ d---------
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
+ <code>
+ src/
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>
+ d---------
+ </code>
+ </td>
+ <td>
+ <a href="$PREFIX/tree/main/$FILE.html" class="files-row-item-directory">
+ <code>
+ tests/
+ </code>
+ </a>
+ </td>
+ <td>
+ <code>
+ 123B
+ </code>
+ </td>
+ </tr>
+ </table>
+ <hr />
+ <section>
+ <h1>README</h1>
+ <p>This does something.</p>
+ </section>
+ </main>
+ <footer>
+ <hr />
+ <p>
+ Generated by gistatic $VERSION at $NOW
+ </p>
+ </footer>
+ </body>
</html>
diff --git a/src/templates/log.html b/src/templates/log.html
index 5052c24..9f85189 100644
--- a/src/templates/log.html
+++ b/src/templates/log.html
@@ -1,83 +1,83 @@
<!DOCTYPE html>
<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="icon" type="image/svg+xml" href="../static/logo.svg" />
- <link rel="stylesheet" type="text/css" href="../static/styles.css" />
- <link rel="alternate" type="application/atom+xml" href="$PREFIX/commits.xml" title="$PROJECT_NAME - commit feed" hreflang="en" />
- <link rel="alternate" type="application/atom+xml" href="$PREFIX/tags.xml" title="$PROJECT_NAME - tags feed" hreflang="en" />
+ <link rel="icon" type="image/svg+xml" href="../static/logo.svg" />
+ <link rel="stylesheet" type="text/css" href="../static/styles.css" />
+ <link rel="alternate" type="application/atom+xml" href="$PREFIX/commits.xml" title="$PROJECT_NAME - commit feed" hreflang="en" />
+ <link rel="alternate" type="application/atom+xml" href="$PREFIX/tags.xml" title="$PROJECT_NAME - tags feed" hreflang="en" />
- <title>$PROJECT_NAME - $PROJECT_DESCRIPTION</title>
+ <title>$PROJECT_NAME - $PROJECT_DESCRIPTION</title>
- <meta property="og:site_name" content="Repository for $PROJECT_NAME" />
- <meta property="og:locale" content="en" />
- <meta property="og:title" content="$PROJECT_NAME" />
- <meta property="og:description" content="$PROJECT_DESCRIPTION" />
- <meta name="description" content="$PROJECT_DESCRIPTION" />
- </head>
- <body>
- <header>
- <div id="horizontal-aligner-of-logo-and-header-items">
- <img id="repository-logo" src="../static/logo.svg" alt="Logo image of the repository" />
- <div id="vertical-aligner-of-project-name-and-description">
- <h1>
- $PROJECT_NAME
- </h1>
- <h2>
- $PROJECT_DESCRIPTION
- </h2>
- <code>
- git clone $PROJECT_CLONE_URL
- </code>
- </div>
- </div>
- <nav>
- <ul>
- <li>
- <a href="$PREFIX/">
- files
- </a>
- </li>
- <li class="selected-nav-item">
- <a href="$PREFIX/log">
- log
- </a>
- </li>
- <li>
- <a href="$PREFIX/refs/">
- refs
- </a>
- </li>
- </ul>
- </nav>
- </header>
- <main>
- <div class="log-commit-box">
- <p>
- <a href="$PREFIX/commits/$SHA.html">739a235c</a>
- |
- EuAndreh
- <a href="$PREFIX/commits/SHA.html" class="log-head-highlight">HEAD</a>
- <a href="$PREFIX/tree/main.html" class="log-branch-highlight">main</a>
- <a href="$PREFIX/tags/v0.2.1.html" class="log-tag-highlight">v0.2.1</a>
- </p>
- <pre>Only display filename if it's set
+ <meta property="og:site_name" content="Repository for $PROJECT_NAME" />
+ <meta property="og:locale" content="en" />
+ <meta property="og:title" content="$PROJECT_NAME" />
+ <meta property="og:description" content="$PROJECT_DESCRIPTION" />
+ <meta name="description" content="$PROJECT_DESCRIPTION" />
+ </head>
+ <body>
+ <header>
+ <div id="horizontal-aligner-of-logo-and-header-items">
+ <img id="repository-logo" src="../static/logo.svg" alt="Logo image of the repository" />
+ <div id="vertical-aligner-of-project-name-and-description">
+ <h1>
+ $PROJECT_NAME
+ </h1>
+ <h2>
+ $PROJECT_DESCRIPTION
+ </h2>
+ <code>
+ git clone $PROJECT_CLONE_URL
+ </code>
+ </div>
+ </div>
+ <nav>
+ <ul>
+ <li>
+ <a href="$PREFIX/">
+ files
+ </a>
+ </li>
+ <li class="selected-nav-item">
+ <a href="$PREFIX/log">
+ log
+ </a>
+ </li>
+ <li>
+ <a href="$PREFIX/refs/">
+ refs
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </header>
+ <main>
+ <div class="log-commit-box">
+ <p>
+ <a href="$PREFIX/commits/$SHA.html">739a235c</a>
+ |
+ EuAndreh
+ <a href="$PREFIX/commits/SHA.html" class="log-head-highlight">HEAD</a>
+ <a href="$PREFIX/tree/main.html" class="log-branch-highlight">main</a>
+ <a href="$PREFIX/tags/v0.2.1.html" class="log-tag-highlight">v0.2.1</a>
+ </p>
+ <pre>Only display filename if it's set
This prevents None from showing up in different places when pastes are
submitted through the API.</pre>
- <pre>Notes:
+ <pre>Notes:
See CI logs with:
git notes --ref=refs/notes/ci-logs show 92bfa3c5f06558cebcfa06bf78159e76254e07fd
git notes --ref=refs/notes/ci-data show 92bfa3c5f06558cebcfa06bf78159e76254e07fd</pre>
- </div>
- </main>
- <footer>
- <hr />
- <p>
- Generated by gistatic $VERSION at $NOW
- </p>
- </footer>
- </body>
+ </div>
+ </main>
+ <footer>
+ <hr />
+ <p>
+ Generated by gistatic $VERSION at $NOW
+ </p>
+ </footer>
+ </body>
</html>
diff --git a/src/templates/refs.html b/src/templates/refs.html
index 7d1e1c1..8af5803 100644
--- a/src/templates/refs.html
+++ b/src/templates/refs.html
@@ -1,213 +1,213 @@
<!DOCTYPE html>
<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="icon" type="image/svg+xml" href="../static/logo.svg" />
- <link rel="stylesheet" type="text/css" href="../static/styles.css" />
- <link rel="alternate" type="application/atom+xml" href="$PREFIX/commits.xml" title="$PROJECT_NAME - commit feed" hreflang="en" />
- <link rel="alternate" type="application/atom+xml" href="$PREFIX/tags.xml" title="$PROJECT_NAME - tags feed" hreflang="en" />
+ <link rel="icon" type="image/svg+xml" href="../static/logo.svg" />
+ <link rel="stylesheet" type="text/css" href="../static/styles.css" />
+ <link rel="alternate" type="application/atom+xml" href="$PREFIX/commits.xml" title="$PROJECT_NAME - commit feed" hreflang="en" />
+ <link rel="alternate" type="application/atom+xml" href="$PREFIX/tags.xml" title="$PROJECT_NAME - tags feed" hreflang="en" />
- <title>$PROJECT_NAME - $PROJECT_DESCRIPTION</title>
+ <title>$PROJECT_NAME - $PROJECT_DESCRIPTION</title>
- <meta property="og:site_name" content="Repository for $PROJECT_NAME" />
- <meta property="og:locale" content="en" />
- <meta property="og:title" content="$PROJECT_NAME" />
- <meta property="og:description" content="$PROJECT_DESCRIPTION" />
- <meta name="description" content="$PROJECT_DESCRIPTION" />
- </head>
- <body>
- <header>
- <div id="horizontal-aligner-of-logo-and-header-items">
- <img id="repository-logo" src="../static/logo.svg" alt="Logo image of the repository" />
- <div id="vertical-aligner-of-project-name-and-description">
- <h1>
- $PROJECT_NAME
- </h1>
- <h2>
- $PROJECT_DESCRIPTION
- </h2>
- <code>
- git clone $PROJECT_CLONE_URL
- </code>
- </div>
- </div>
- <nav>
- <ul>
- <li>
- <a href="$PREFIX/">
- files
- </a>
- </li>
- <li>
- <a href="$PREFIX/log/">
- log
- </a>
- </li>
- <li class="selected-nav-item">
- <a href="$PREFIX/refs/">
- refs
- </a>
- </li>
- </ul>
- </nav>
- </header>
- <main>
- <table>
- <tr class="table-no-highlight-row">
- <th>
- Branch
- </th>
- <th>
- Commit message
- </th>
- <th>
- Author
- </th>
- <th>
- Date
- </th>
- </tr>
- <tr>
- <td>
- <a href="$PREFIX/log/main.html">
- <code>
- main
- </code>
- </a>
- </td>
- <td>
- <a href="$PREFIX/commits/$SHA.html">
- aux/workflow/public.sh: Mark as executable
- </a>
- </td>
- <td>
- EuAndreh
- </td>
- <td>
- 2021-01-02 15:26 -3
- </td>
- </tr>
- <tr class="table-no-highlight-row">
- <td colspan="4">&nbsp;</td>
- </tr>
- <tr class="table-no-highlight-row">
- <th>
- Tag
- </th>
- <th>
- Download
- </th>
- <th>
- Author
- </th>
- <th>
- Date
- </th>
- </tr>
- <tr>
- <td>
- <a href="$PREFIX/tags/v0.2.1.html">
- v0.2.1
- </a>
- </td>
- <td>
- <a href="$PREFIX/snapshots/remembering-0.2.1.tar.xz">
- remembering-0.2.1.tar.xz
- </a>
- (<a href="$PREFIX/snapshots/remembering-0.2.1.tar.xz.asc">sig</a>)
- </td>
- <td>
- EuAndreh
- </td>
- <td>
- 2021-01-05 15:26 -3
- </td>
- </tr>
- <tr>
- <td>
- <a href="$PREFIX/tags/v0.2.0.html">
- v0.2.0
- </a>
- </td>
- <td>
- <a href="$PREFIX/snapshots/remembering-0.2.0.tar.xz">
- remembering-0.2.0.tar.xz
- </a>
- (<a href="$PREFIX/snapshots/remembering-0.2.0.tar.xz.asc">sig</a>)
- </td>
- <td>
- EuAndreh
- </td>
- <td>
- 2021-01-04 15:26 -3
- </td>
- </tr>
- <tr>
- <td>
- <a href="$PREFIX/tags/v0.1.2.html">
- v0.1.2
- </a>
- </td>
- <td>
- <a href="$PREFIX/snapshots/remembering-0.1.2.tar.xz">
- remembering-0.2.1.tar.xz
- </a>
- (<a href="$PREFIX/snapshots/remembering-0.1.2.tar.xz.asc">sig</a>)
- </td>
- <td>
- EuAndreh
- </td>
- <td>
- 2021-01-03 15:26 -3
- </td>
- </tr>
- <tr>
- <td>
- <a href="$PREFIX/tags/v0.1.1.html">
- v0.1.1
- </a>
- </td>
- <td>
- <a href="$PREFIX/snapshots/remembering-0.1.1.tar.xz">
- remembering-0.2.1.tar.xz
- </a>
- (<a href="$PREFIX/snapshots/remembering-0.1.1.tar.xz.asc">sig</a>)
- </td>
- <td>
- EuAndreh
- </td>
- <td>
- 2021-01-02 15:26 -3
- </td>
- </tr>
- <tr>
- <td>
- <a href="$PREFIX/tags/v0.1.0.html">
- v0.1.0
- </a>
- </td>
- <td>
- <a href="$PREFIX/snapshots/remembering-0.1.0.tar.xz">
- remembering-0.2.1.tar.xz
- </a>
- (<a href="$PREFIX/snapshots/remembering-0.1.0.tar.xz.asc">sig</a>)
- </td>
- <td>
- EuAndreh
- </td>
- <td>
- 2021-01-01 15:26 -3
- </td>
- </tr>
- </table>
- </main>
- <footer>
- <hr />
- <p>
- Generated by gistatic $VERSION at $NOW
- </p>
- </footer>
- </body>
+ <meta property="og:site_name" content="Repository for $PROJECT_NAME" />
+ <meta property="og:locale" content="en" />
+ <meta property="og:title" content="$PROJECT_NAME" />
+ <meta property="og:description" content="$PROJECT_DESCRIPTION" />
+ <meta name="description" content="$PROJECT_DESCRIPTION" />
+ </head>
+ <body>
+ <header>
+ <div id="horizontal-aligner-of-logo-and-header-items">
+ <img id="repository-logo" src="../static/logo.svg" alt="Logo image of the repository" />
+ <div id="vertical-aligner-of-project-name-and-description">
+ <h1>
+ $PROJECT_NAME
+ </h1>
+ <h2>
+ $PROJECT_DESCRIPTION
+ </h2>
+ <code>
+ git clone $PROJECT_CLONE_URL
+ </code>
+ </div>
+ </div>
+ <nav>
+ <ul>
+ <li>
+ <a href="$PREFIX/">
+ files
+ </a>
+ </li>
+ <li>
+ <a href="$PREFIX/log/">
+ log
+ </a>
+ </li>
+ <li class="selected-nav-item">
+ <a href="$PREFIX/refs/">
+ refs
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </header>
+ <main>
+ <table>
+ <tr class="table-no-highlight-row">
+ <th>
+ Branch
+ </th>
+ <th>
+ Commit message
+ </th>
+ <th>
+ Author
+ </th>
+ <th>
+ Date
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <a href="$PREFIX/log/main.html">
+ <code>
+ main
+ </code>
+ </a>
+ </td>
+ <td>
+ <a href="$PREFIX/commits/$SHA.html">
+ aux/workflow/public.sh: Mark as executable
+ </a>
+ </td>
+ <td>
+ EuAndreh
+ </td>
+ <td>
+ 2021-01-02 15:26 -3
+ </td>
+ </tr>
+ <tr class="table-no-highlight-row">
+ <td colspan="4">&nbsp;</td>
+ </tr>
+ <tr class="table-no-highlight-row">
+ <th>
+ Tag
+ </th>
+ <th>
+ Download
+ </th>
+ <th>
+ Author
+ </th>
+ <th>
+ Date
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <a href="$PREFIX/tags/v0.2.1.html">
+ v0.2.1
+ </a>
+ </td>
+ <td>
+ <a href="$PREFIX/snapshots/remembering-0.2.1.tar.xz">
+ remembering-0.2.1.tar.xz
+ </a>
+ (<a href="$PREFIX/snapshots/remembering-0.2.1.tar.xz.asc">sig</a>)
+ </td>
+ <td>
+ EuAndreh
+ </td>
+ <td>
+ 2021-01-05 15:26 -3
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="$PREFIX/tags/v0.2.0.html">
+ v0.2.0
+ </a>
+ </td>
+ <td>
+ <a href="$PREFIX/snapshots/remembering-0.2.0.tar.xz">
+ remembering-0.2.0.tar.xz
+ </a>
+ (<a href="$PREFIX/snapshots/remembering-0.2.0.tar.xz.asc">sig</a>)
+ </td>
+ <td>
+ EuAndreh
+ </td>
+ <td>
+ 2021-01-04 15:26 -3
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="$PREFIX/tags/v0.1.2.html">
+ v0.1.2
+ </a>
+ </td>
+ <td>
+ <a href="$PREFIX/snapshots/remembering-0.1.2.tar.xz">
+ remembering-0.2.1.tar.xz
+ </a>
+ (<a href="$PREFIX/snapshots/remembering-0.1.2.tar.xz.asc">sig</a>)
+ </td>
+ <td>
+ EuAndreh
+ </td>
+ <td>
+ 2021-01-03 15:26 -3
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="$PREFIX/tags/v0.1.1.html">
+ v0.1.1
+ </a>
+ </td>
+ <td>
+ <a href="$PREFIX/snapshots/remembering-0.1.1.tar.xz">
+ remembering-0.2.1.tar.xz
+ </a>
+ (<a href="$PREFIX/snapshots/remembering-0.1.1.tar.xz.asc">sig</a>)
+ </td>
+ <td>
+ EuAndreh
+ </td>
+ <td>
+ 2021-01-02 15:26 -3
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="$PREFIX/tags/v0.1.0.html">
+ v0.1.0
+ </a>
+ </td>
+ <td>
+ <a href="$PREFIX/snapshots/remembering-0.1.0.tar.xz">
+ remembering-0.2.1.tar.xz
+ </a>
+ (<a href="$PREFIX/snapshots/remembering-0.1.0.tar.xz.asc">sig</a>)
+ </td>
+ <td>
+ EuAndreh
+ </td>
+ <td>
+ 2021-01-01 15:26 -3
+ </td>
+ </tr>
+ </table>
+ </main>
+ <footer>
+ <hr />
+ <p>
+ Generated by gistatic $VERSION at $NOW
+ </p>
+ </footer>
+ </body>
</html>
diff --git a/src/tests-lib.c b/src/tests-lib.c
deleted file mode 100644
index a9d229a..0000000
--- a/src/tests-lib.c
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "config.h"
-#include "tests-lib.h"
-#include <stdio.h>
-#include <assert.h>
-
-#define COLOUR_RESET "\033[0m"
-#define COLOUR_GREEN "\033[0;32m"
-#define COLOUR_YELLOW "\033[0;33m"
-
-void
-test_start(const char *const name) {
- assert(fprintf(stderr, "%s():\n", name) > 0);
-}
-
-void
-testing(const char *const message) {
- assert(
- fprintf(
- stderr,
- COLOUR_YELLOW "testing" COLOUR_RESET ": %s...",
- message
- ) > 0
- );
-}
-
-void
-test_ok(void) {
- assert(fprintf(stderr, " " COLOUR_GREEN "OK" COLOUR_RESET ".\n") > 0);
-}
diff --git a/src/tests-lib.h b/src/tests-lib.h
deleted file mode 100644
index 1ceab27..0000000
--- a/src/tests-lib.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef GISTATIC_TESTS_LIB_H
-#define GISTATIC_TESTS_LIB_H
-
-void
-test_start(const char *const name);
-
-void
-testing(const char *const message);
-
-void
-test_ok(void);
-
-#endif
diff --git a/tests/assert-catgets.sh b/tests/assert-catgets.sh
deleted file mode 100755
index 885e186..0000000
--- a/tests/assert-catgets.sh
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/bin/sh
-set -eu
-
-assert_sequential_ids() {
- awk '
- BEGIN {
- n = 0
- }
-
- /^#define MSG_/ {
- if (++n != $3) {
- print "Bad sequential ID:"
- printf "%s:%s:%s\n", FILENAME, NR, $0
- printf "expected: %s\ngot: %s\n", n, $3
- exit 1
- }
- }
- ' "$1"
-}
-
-assert_consistent_msg_definitions() {
- awk '
- BEGIN {
- i = 0
- j = 0
- }
-
- /^#define MSG_/ {
- defines[i++] = $2
- }
-
- /^\t\[MSG_/ {
- msgs[j++] = substr($0, 3, index($0, "]") - 3)
- }
-
- END {
- for (n in defines) {
- if (defines[n] != msgs[n]) {
- printf "Order mismatch between #define"
- printf " and usage in MSGS[]:\n"
- printf "#define: %s\nMSGS[]: %s\n",
- defines[n], msgs[n]
- exit 1
- }
- }
- }
- ' "$1"
-}
-
-assert_no_unused_msgs() {
- DEFINES="$(mktemp)"
- USAGES="$(mktemp)"
- awk '/^#define MSG_/ { print $2 }' "$f" | sort > "$DEFINES"
- awk -F'_\\(' '
- /_\(MSG_/ { print substr($2, 0, index($2, ")") - 1) }
- ' "$f" | sort | uniq > "$USAGES"
- if ! diff "$DEFINES" "$USAGES"; then
- echo 'Some defined MSG_ items not being used' >&2
- exit 1
- fi
-}
-
-for f in "$@"; do
- assert_sequential_ids "$f"
- assert_consistent_msg_definitions "$f"
- assert_no_unused_msgs "$f"
-done
diff --git a/tests/c-lint.sh b/tests/c-lint.sh
deleted file mode 100755
index 0fa0b01..0000000
--- a/tests/c-lint.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/sh
-set -eu
-
-awk '
-BEGIN {
- ret = 0
- msg = "function not on the start of the line:"
-}
-
-/^[a-zA-Z0-9_]+ .+\(/ {
- if (ret == 0) {
- print msg
- }
- printf "%s:%s:%s\n", FILENAME, FNR, $0
- ret = 1
-}
-
-END {
- exit ret
-}
-' "$@"
-
-
-awk '
-BEGIN {
- ret = 0
- static = 1
- msg = "non-static function is not declared in a header:"
-}
-
-/^[a-zA-Z0-9_]+\(.*$/ && static == 0 {
- split($0, line, /\(/)
- fn_name = line[1]
- if (fn_name != "main") {
- header = substr(FILENAME, 0, length(FILENAME) - 2) ".h"
- if (system("grep -q ^\"" fn_name "\" \"" header "\"")) {
- if (ret == 0) {
- print msg
- }
- printf "%s:%s:%s\n", FILENAME, FNR, $0
- ret = 1
- }
- }
-}
-
-/^static / {
- static = 1
-}
-
-!/^static / {
- static = 0
-}
-
-END {
- exit ret
-}
-' "$@"
-
-
-RE='[a-z]+\(\) {'
-if grep -Eq "$RE" "$@"; then
- echo 'Functions with no argument without explicit "void" parameter:' >&2
- grep -En "$RE" "$@"
- exit 1
-fi
diff --git a/tests/dev-integration.sh b/tests/dev-integration.sh
deleted file mode 100755
index c920a2d..0000000
--- a/tests/dev-integration.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-# shellcheck disable=2086
-set -eu
-
-. tests/lib.sh
-
-VALGRIND_FLAGS='
---show-error-list=yes
---show-leak-kinds=all
---leak-check=full
---track-origins=yes
---error-exitcode=1
-'
-
-valgrind $VALGRIND_FLAGS ./gistatic-tests
-valgrind $VALGRIND_FLAGS ./gistatic -o "$(mkdtemp)" \
- -u 'https://example.com' tests/resources/repositories/repo-1
-valgrind $VALGRIND_FLAGS ./gistatic -o "$(mkdtemp)" \
- -i tests/resources/repositories/*
diff --git a/tests/lib.sh b/tests/lib.sh
index 527e645..750cb68 100755..100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -122,12 +122,12 @@ uuid() {
# Taken from:
# https://serverfault.com/a/799198
od -xN20 /dev/urandom |
- head -1 |
+ head -n1 |
awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}'
}
tmpname() {
- echo 'mkstemp(template)' | m4 -D template="${TMPDIR:-/tmp}/m4-tmpname."
+ echo "${TMPDIR:-/tmp}/uuid-tmpname with spaces.$(uuid)"
}
mkstemp() {