From 7afce21a30355068a4bdd186b44d79dd37d94937 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Fri, 14 Apr 2023 14:07:43 -0300 Subject: Cleanup repo, reinit in sh --- Makefile | 44 +- TODOs.md | 21 +- doc/manpages.sh | 24 +- po/LC_MESSAGES/CHANGELOG.en.md/eo.po | 6 - po/LC_MESSAGES/CHANGELOG.en.md/fr.po | 6 - po/LC_MESSAGES/CHANGELOG.en.md/pt.po | 6 - po/LC_MESSAGES/README.en.md/eo.po | 176 --- po/LC_MESSAGES/README.en.md/fr.po | 176 --- po/LC_MESSAGES/README.en.md/pt.po | 176 --- po/LC_MESSAGES/doc/gistatic.en.1.in/eo.po | 18 - po/LC_MESSAGES/doc/gistatic.en.1.in/fr.po | 18 - po/LC_MESSAGES/doc/gistatic.en.1.in/pt.po | 18 - src/config.h.in | 9 - src/gistatic.h | 1 - src/gistatic.in | 700 +++++++-- src/lib.c | 2424 ----------------------------- src/lib.h | 12 - src/lib.msg | 61 - src/logerr.c | 67 - src/logerr.h | 34 - src/main.c | 22 - src/static/style.css | 133 ++ src/static/styles.css | 133 -- src/tar.c | 103 -- src/tar.h | 17 - src/templates/files.html | 610 ++++---- src/templates/log.html | 142 +- src/templates/refs.html | 414 ++--- src/tests-lib.c | 29 - src/tests-lib.h | 13 - tests/assert-catgets.sh | 67 - tests/c-lint.sh | 65 - tests/dev-integration.sh | 19 - tests/lib.sh | 4 +- 34 files changed, 1350 insertions(+), 4418 deletions(-) delete mode 100644 po/LC_MESSAGES/CHANGELOG.en.md/eo.po delete mode 100644 po/LC_MESSAGES/CHANGELOG.en.md/fr.po delete mode 100644 po/LC_MESSAGES/CHANGELOG.en.md/pt.po delete mode 100644 po/LC_MESSAGES/README.en.md/eo.po delete mode 100644 po/LC_MESSAGES/README.en.md/fr.po delete mode 100644 po/LC_MESSAGES/README.en.md/pt.po delete mode 100644 po/LC_MESSAGES/doc/gistatic.en.1.in/eo.po delete mode 100644 po/LC_MESSAGES/doc/gistatic.en.1.in/fr.po delete mode 100644 po/LC_MESSAGES/doc/gistatic.en.1.in/pt.po delete mode 100644 src/config.h.in delete mode 120000 src/gistatic.h delete mode 100644 src/lib.c delete mode 100644 src/lib.h delete mode 100644 src/lib.msg delete mode 100644 src/logerr.c delete mode 100644 src/logerr.h delete mode 100644 src/main.c create mode 100644 src/static/style.css delete mode 100644 src/static/styles.css delete mode 100644 src/tar.c delete mode 100644 src/tar.h delete mode 100644 src/tests-lib.c delete mode 100644 src/tests-lib.h delete mode 100755 tests/assert-catgets.sh delete mode 100755 tests/c-lint.sh delete mode 100755 tests/dev-integration.sh mode change 100755 => 100644 tests/lib.sh diff --git a/Makefile b/Makefile index 48a52ef..96d228f 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,12 @@ .POSIX: -PREFIX = /usr/local -MANPREFIX = '$(PREFIX)/share/man' +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man DATE = 1970-01-01 VERSION = 0.1.0 NAME = gistatic MAILING_LIST = public-inbox TRANSLATIONS = pt fr eo CONTRIBLANGS = -LDLIBS = -lgit2 .SUFFIXES: @@ -21,12 +20,6 @@ LDLIBS = -lgit2 < $< > $@ if [ -x $< ]; then chmod +x $@; fi -.c.o: - $(CC) $(CFLAGS) -o $@ -c $< - -.c.to: - $(CC) $(CFLAGS) -DTEST -o $@ -c $< - manpages.en.in = \ doc/gistatic.en.1.in @@ -36,37 +29,9 @@ manpages.in = $(manpages.en.in) \ doc/gistatic.eo.1.in manpages = $(manpages.in:.in=) -sources = \ - src/logerr.c \ - src/tar.c \ - src/lib.c -lib-objects = $(sources:.c=.o) -all-objects = $(lib-objects) src/main.o -t-objects = $(sources:.c=.to) src/tests-lib.to src/main.to - - -all: libgistatic.a gistatic src/gistatic $(manpages) - -libgistatic.a: $(lib-objects) - $(AR) $(ARFLAGS) $@ $(lib-objects) - -gistatic: libgistatic.a src/main.o - $(CC) $(LDFLAGS) -o $@ src/main.o libgistatic.a $(LDLIBS) -gistatic-tests: $(t-objects) - $(CC) $(LDFLAGS) -o $@ $(t-objects) $(LDLIBS) -$(all-objects) $(t-objects): src/config.h -src/tests-lib.to: src/tests-lib.h -src/logerr.o src/logerr.to: src/logerr.h -src/tar.o src/tar.to: src/tar.h -src/tar.o: src/logerr.o -src/tar.to: src/logerr.to src/tests-lib.to -src/lib.o src/lib.to: src/lib.h -src/lib.o: src/logerr.o src/tar.o -src/lib.to: src/logerr.to src/tar.to src/tests-lib.to -src/main.o: src/logerr.o src/tar.o src/lib.o -src/main.to: src/logerr.to src/tar.to src/lib.to src/tests-lib.to +all: src/gistatic $(manpages) tests/resources/repositories/repo-1/.git \ @@ -77,9 +42,6 @@ check: all gistatic-tests \ tests/resources/repositories/repo-1/.git \ tests/resources/repositories/repo-2/.git sh tests/build-sample.sh - ./gistatic-tests - sh tests/assert-catgets.sh src/*.c - sh tests/c-lint.sh src/*.c sh tests/integration.sh clean: diff --git a/TODOs.md b/TODOs.md index 435c6ce..61e566f 100644 --- a/TODOs.md +++ b/TODOs.md @@ -128,6 +128,8 @@ Add both to the `README.md` and to the manpages. Current items: - can't compare commits: this requires `O(n!)` code and HTML to be generated; +- can't display human-friendly date formats like "3 days ago", only timestamps; +- can't handle big repositories, only small and medium ones; ## TODO Integrate `fallible` or other form of fault injection into the test suite {#task-ca8d80ae-9d31-5dfa-03de-b3cfccf2a8b2} - TODO in 2021-07-31 @@ -144,9 +146,9 @@ Current items: --- -- `README.md` -- `CHANGELOG.md` -- `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 , 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 \n" -"Language-Team: LANGUAGE \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 , 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 \n" -"Language-Team: LANGUAGE \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 , 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 \n" -"Language-Team: LANGUAGE \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 @NAME@.' 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 gistatic.' 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() {

- $(escape "$MSG_FOOTER_TEMPLATE") + $MSG_ESCAPED_FOOTER_TEMPLATE

@@ -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() {
- +

@@ -428,12 +574,12 @@ print_repo_refs() {

-$(print_formatted_diff "$sha") +$(print_formatted_diff "$sha" "$previous_sha")

- $(escape "$MSG_FOOTER_TEMPLATE") + $MSG_ESCAPED_FOOTER_TEMPLATE

@@ -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=" newer FIXME i18n"' +' + 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=" older FIXME i18n"' +' + 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='
+' + escaped_pagination_close='
+' + 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 < + + + + + + + + + + $(escape "$repo") - $(escape "$title") + + +
+
+ + + +
+

+ + $(escape "$repo") + +

+

+ $(escape "$description") +

+ + git clone $(escape "$CLONE_URL") + +
+
+ +
+
+
+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=" +
$(escape "$notes_txt")
" + fi + + # FIXME: show if commit is in branch, tag or HEAD + # HEAD + # main + # v0.2.1 + + cat < +

+ $(escape "$commit") + | + $(escape "$(git log -1 --format=%an "$commit")") +

+
$(escape "$(git log -1 --format=%s "$commit")")$(escape "$body")
$escaped_notes + +EOF done + + cat < +
+
+

+ $MSG_ESCAPED_FOOTER_TEMPLATE +

+
+ + +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 < + + + + + + + + + + $(escape "$repo") - FIXME + + +
+
+ + + +
+

+ + $(escape "$repo") + +

+

+ $(escape "$description") +

+ + git clone $(escape "$CLONE_URL") + +
+
+ +
+
+
+
+ Os arquivos vão aqui. +
+
+ O README vai aqui. +
+
+
+
+

+ $MSG_ESCAPED_FOOTER_TEMPLATE +

+
+ + +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#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 = "" - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\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 = - "" PROGNAME "."; - - -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("link"); - 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, - "" - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - "
\n" - "
\n" - " \"%s\"\n" - "

\n" - " %s\n" - "

\n" - "
\n" - "
\n" - "
\n" - "
\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \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, - "" - " \n" - "
\n" - " %s\n" - " \n" - " %s\n" - " \n" - " %s\n" - "
\n" - "
\n" - "
\n" - "
\n" - "

\n" - " %s\n" - "

\n" - "
\n" - " \n" - "\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, - "" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - " %s\n" - " \n" - " \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, - "" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - " %s\n" - " \n" - " \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, - "" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " (tarball, sig)\n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - " %s\n" - " \n" - " \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, - "" - "\n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " %s\n" - " \n" - " \n" - "
\n" - "
\n" - " \n" - " \"%s\"\n" - " \n" - "
\n" - "

\n" - " %s\n" - "

\n" - "

\n" - " %s\n" - "

\n" - " \n" - " git clone %s\n" - " \n" - "
\n" - "
\n" - " \n" - "
\n" - "
\n" - "
\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \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, - "" - " \n" - "
\n" - " %s\n" - " \n" - " %s\n" - " \n" - " %s\n" - " \n" - " %s\n" - "
\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \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, - "" - " \n" - "
\n" - " %s\n" - " \n" - " %s\n" - " \n" - " %s\n" - "
\n" - "
\n" - "
\n" - "
\n" - "

\n" - " %s\n" - "

\n" - "
\n" - " \n" - "\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 - - -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 - -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 - -#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 -#include -#include -#include -#include - -/* - -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 - -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 @@ - - - + + + - - - - + + + + - $PROJECT_NAME - $PROJECT_DESCRIPTION + $PROJECT_NAME - $PROJECT_DESCRIPTION - - - - - - - -
-
- -
-

- $PROJECT_NAME -

-

- $PROJECT_DESCRIPTION -

- - git clone $PROJECT_CLONE_URL - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Mode - - Name - - Size -
- - -rw-r--r-- - - - - - .gitignore - - - - - 123B - -
- - -rw-r--r-- - - - - - CHAGELOG.md - - - - - 123B - -
- - -rw-r--r-- - - - - - COPYING - - - - - 123B - -
- - -rw-r--r-- - - - - - Makefile - - - - - 123B - -
- - -rw-r--r-- - - - - - README.md - - - - - 123B - -
- - -rw-r--r-- - - - - - TODOs.md - - - - - 123B - -
- - d--------- - - - - - aux/ - - - - - 123B - -
- - -rw-r--r-- - - - - - description - - - - - 123B - -
- - d--------- - - - - - doc/ - - - - - 123B - -
- - -rw-r--r-- - - - - - long-description - - - - - 123B - -
- - d--------- - - - - - src/ - - - - - 123B - -
- - d--------- - - - - - tests/ - - - - - 123B - -
-
-
-

README

-

This does something.

-
-
-
-
-

- Generated by gistatic $VERSION at $NOW -

-
- + + + + + + + +
+
+ +
+

+ $PROJECT_NAME +

+

+ $PROJECT_DESCRIPTION +

+ + git clone $PROJECT_CLONE_URL + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Mode + + Name + + Size +
+ + -rw-r--r-- + + + + + .gitignore + + + + + 123B + +
+ + -rw-r--r-- + + + + + CHAGELOG.md + + + + + 123B + +
+ + -rw-r--r-- + + + + + COPYING + + + + + 123B + +
+ + -rw-r--r-- + + + + + Makefile + + + + + 123B + +
+ + -rw-r--r-- + + + + + README.md + + + + + 123B + +
+ + -rw-r--r-- + + + + + TODOs.md + + + + + 123B + +
+ + d--------- + + + + + aux/ + + + + + 123B + +
+ + -rw-r--r-- + + + + + description + + + + + 123B + +
+ + d--------- + + + + + doc/ + + + + + 123B + +
+ + -rw-r--r-- + + + + + long-description + + + + + 123B + +
+ + d--------- + + + + + src/ + + + + + 123B + +
+ + d--------- + + + + + tests/ + + + + + 123B + +
+
+
+

README

+

This does something.

+
+
+
+
+

+ Generated by gistatic $VERSION at $NOW +

+
+ 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 @@ - - - + + + - - - - + + + + - $PROJECT_NAME - $PROJECT_DESCRIPTION + $PROJECT_NAME - $PROJECT_DESCRIPTION - - - - - - - -
-
- -
-

- $PROJECT_NAME -

-

- $PROJECT_DESCRIPTION -

- - git clone $PROJECT_CLONE_URL - -
-
- -
-
-
-

- 739a235c - | - EuAndreh - HEAD - main - v0.2.1 -

-
Only display filename if it's set
+    
+    
+    
+    
+    
+  
+  
+    
+
+ +
+

+ $PROJECT_NAME +

+

+ $PROJECT_DESCRIPTION +

+ + git clone $PROJECT_CLONE_URL + +
+
+ +
+
+
+

+ 739a235c + | + EuAndreh + HEAD + main + v0.2.1 +

+
Only display filename if it's set
 
 This prevents None from showing up in different places when pastes are
 submitted through the API.
-
Notes:
+        
Notes:
   See CI logs with:
     git notes --ref=refs/notes/ci-logs show 92bfa3c5f06558cebcfa06bf78159e76254e07fd
     git notes --ref=refs/notes/ci-data show 92bfa3c5f06558cebcfa06bf78159e76254e07fd
-
-
-
-
-

- Generated by gistatic $VERSION at $NOW -

-
- +
+
+
+
+

+ Generated by gistatic $VERSION at $NOW +

+
+ 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 @@ - - - + + + - - - - + + + + - $PROJECT_NAME - $PROJECT_DESCRIPTION + $PROJECT_NAME - $PROJECT_DESCRIPTION - - - - - - - -
-
- -
-

- $PROJECT_NAME -

-

- $PROJECT_DESCRIPTION -

- - git clone $PROJECT_CLONE_URL - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Branch - - Commit message - - Author - - Date -
- - - main - - - - - aux/workflow/public.sh: Mark as executable - - - EuAndreh - - 2021-01-02 15:26 -3 -
 
- Tag - - Download - - Author - - Date -
- - v0.2.1 - - - - remembering-0.2.1.tar.xz - - (sig) - - EuAndreh - - 2021-01-05 15:26 -3 -
- - v0.2.0 - - - - remembering-0.2.0.tar.xz - - (sig) - - EuAndreh - - 2021-01-04 15:26 -3 -
- - v0.1.2 - - - - remembering-0.2.1.tar.xz - - (sig) - - EuAndreh - - 2021-01-03 15:26 -3 -
- - v0.1.1 - - - - remembering-0.2.1.tar.xz - - (sig) - - EuAndreh - - 2021-01-02 15:26 -3 -
- - v0.1.0 - - - - remembering-0.2.1.tar.xz - - (sig) - - EuAndreh - - 2021-01-01 15:26 -3 -
-
-
-
-

- Generated by gistatic $VERSION at $NOW -

-
- + + + + + + + +
+
+ +
+

+ $PROJECT_NAME +

+

+ $PROJECT_DESCRIPTION +

+ + git clone $PROJECT_CLONE_URL + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Branch + + Commit message + + Author + + Date +
+ + + main + + + + + aux/workflow/public.sh: Mark as executable + + + EuAndreh + + 2021-01-02 15:26 -3 +
 
+ Tag + + Download + + Author + + Date +
+ + v0.2.1 + + + + remembering-0.2.1.tar.xz + + (sig) + + EuAndreh + + 2021-01-05 15:26 -3 +
+ + v0.2.0 + + + + remembering-0.2.0.tar.xz + + (sig) + + EuAndreh + + 2021-01-04 15:26 -3 +
+ + v0.1.2 + + + + remembering-0.2.1.tar.xz + + (sig) + + EuAndreh + + 2021-01-03 15:26 -3 +
+ + v0.1.1 + + + + remembering-0.2.1.tar.xz + + (sig) + + EuAndreh + + 2021-01-02 15:26 -3 +
+ + v0.1.0 + + + + remembering-0.2.1.tar.xz + + (sig) + + EuAndreh + + 2021-01-01 15:26 -3 +
+
+
+
+

+ Generated by gistatic $VERSION at $NOW +

+
+ 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 -#include - -#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 old mode 100755 new mode 100644 index 527e645..750cb68 --- 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() { -- cgit v1.2.3