diff options
author | EuAndreh <eu@euandre.org> | 2023-04-14 14:07:43 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2023-04-14 14:08:18 -0300 |
commit | 7afce21a30355068a4bdd186b44d79dd37d94937 (patch) | |
tree | 13dbb2350efc192a64ee7e738d5d4dd343fa789b | |
parent | rm -rf aux/ (diff) | |
download | gistatic-7afce21a30355068a4bdd186b44d79dd37d94937.tar.gz gistatic-7afce21a30355068a4bdd186b44d79dd37d94937.tar.xz |
Cleanup repo, reinit in sh
34 files changed, 1350 insertions, 4418 deletions
@@ -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: @@ -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|&|\&|g;s|<|\<|g;s|>|\>|g;s|"|\"|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 = "&"; - static const char *const LT = "<"; - static const char *const GT = ">"; - static const char *const DQUOT = """; - static const char *const SQUOT = "'"; - - 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, "&") == 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, "&& >> text <<") == 0); - free(s); - test_ok(); - } - { - testing("all escapable characters"); - char *const s = escape_html("&<>\"'"); - assert(s); - assert(strcmp(s, "&<>"'") == 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->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" - " "quotes" && 'quotes'"; - 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 = "<a" - " href="#">link</a>"; - 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"> </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"> </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() { |