diff options
Diffstat (limited to '')
-rw-r--r-- | v2/src/development/config.env.in | 5 | ||||
-rwxr-xr-x | v2/src/development/dynmake.sh | 75 | ||||
-rwxr-xr-x | v2/src/development/genhtml.sh | 332 |
3 files changed, 412 insertions, 0 deletions
diff --git a/v2/src/development/config.env.in b/v2/src/development/config.env.in new file mode 100644 index 0000000..18d5367 --- /dev/null +++ b/v2/src/development/config.env.in @@ -0,0 +1,5 @@ +#!/bin/sh + +FQDN='@FQDN@' +BASE_URL='@BASE_URL@' +DATE_FMT='+%B %-d, %Y' diff --git a/v2/src/development/dynmake.sh b/v2/src/development/dynmake.sh new file mode 100755 index 0000000..a04e70e --- /dev/null +++ b/v2/src/development/dynmake.sh @@ -0,0 +1,75 @@ +#!/bin/sh +set -eu + + +usage() { + cat <<-'EOF' + Usage: + dynmake.sh + dynmake.sh -h + EOF +} + +help() { + cat <<-'EOF' + + Options: + -h, --help show this message + + + Generate make(1) code for later evaluation by make(1). What + this scripts does is fill the gap where make(1) can't handle + globs and dynamic dependencies, and uses some ad-hoc scripts + to generate those. + EOF +} + +for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac +done + +while getopts 'h' flag; do + case "$flag" in + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + + + +varlist() { + sed -e 's/^/ /' \ + -e 's/$/ \\/' +} + + +# +# Pastebins +# + +pastebins() { + find src/content/pastebins/ -name '*.md' +} + +printf 'pastebins.md = \\\n' +pastebins | varlist +printf '\n' diff --git a/v2/src/development/genhtml.sh b/v2/src/development/genhtml.sh new file mode 100755 index 0000000..953a80d --- /dev/null +++ b/v2/src/development/genhtml.sh @@ -0,0 +1,332 @@ +#!/bin/sh +set -eu + +# FIXMEs: +# - feeds +# - link to next and/or previous in <head> +# - translation support +# - validate input variables: regex for date (same as _plugins/linter.rb) +# - `date -d` isn't POSIX +# - parse commonmark and use a custom HTML emitter over <pre><code> regex +# - handle mixture of personal scripts +# - sitemap? How does it even work? +# - dark mode +# - generate security.txt dynamically +# - make url_for a standalone executable +# - config.env should depend on dynamic.mk? + + + +usage() { + cat <<-'EOF' + Usage: + genhtml.sh FILENAME + genhtml.sh -h + EOF +} + +help() { + cat <<-'EOF' + + Options: + -h, --help show this message + + FILENAME the name of the input file, to also be used as + URL + + + Process the FILENAME, and generate a full HTML page. + + The FILENAME is used to infer the output URL, by removing the + `src/content/` prefix, and replacing the trailing `.md` with + `.html`. This URL is used to build the self-referencing + "canonical" link, extracting plaintext snippets, etc. + + + Examples: + + Generate the HTML for a pastebin: + + $ sh genhtml.sh src/content/a-paste.md > src/content/a-paste.html + EOF +} + + +for f in "$@"; do + case "$f" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac +done + +while getopts 'h' flag; do + case "$flag" in + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + + +FILENAME="${1:-}" +eval "$(assert-arg "$FILENAME" 'FILENAME')" + + +# +# Utility functions +# + +url_for() { + printf '%s/%s' "$BASE_URL" "$1" +} + +absolute() { + printf 'https://%s%s' "$FQDN" "$(cat)" +} + +translate() { + printf '%s' "$1" +} + +_() { + translate "$1" | html +} + +SEEN_SLUGS="$(mkstemp)" +slugify_once() { + SLUG="$(printf '%s' "$1" | slugify)${2:+-$2}" + if grep -q "^$SLUG$" "$SEEN_SLUGS"; then + N="${2:-0}" + N=$((N + 1)) + slugify_once "$1" "$N" + else + printf '%s\n' "$SLUG" >> "$SEEN_SLUGS" + printf '%s' "$SLUG" + fi +} + +extract_content() { + awk ' + separator >= 2 + /^---$/ { separator++ } + ' +} + +add_preamble() { + printf '%s\n%s\n' "$PREAMBLE" "$(cat -)" +} + +markdown_to_html() { + md2html +} + +extract_plaintext_snippets() { + F="$(mkstemp)" + cat > "$F" + ( + IFS='' + BLOCK_NUMBER=0 + IN_BLOCK= + while read -r line; do + if [ "$line" = '</code></pre>' ]; then + IN_BLOCK= + fi + + if [ -n "$IN_BLOCK" ]; then + printf '%s\n' "$line" | html -d >> "$OUT" + fi + + if printf '%s' "$line" | grep -q '^<pre><code.*>'; then + IN_BLOCK=1 + OUT="${FILENAME%.md}.html.$BLOCK_NUMBER.txt" + BLOCK_NUMBER=$((BLOCK_NUMBER + 1)) + printf '%s\n' "$line" | + sed 's|^\(<pre><code.*>\)\(.*\)$|\2|' | + html -d > "$OUT" + fi + done < "$F" + + BLOCK_NUMBER=0 + while read -r line; do + printf '%s\n' "$line" + + if [ "$line" = '</code></pre>' ]; then + printf '<p class="plaintext-link"><a href="%s.%s.txt">plaintext</a></p>\n' "$(url_for "$URL")" "$BLOCK_NUMBER" + BLOCK_NUMBER=$((BLOCK_NUMBER + 1)) + fi + done < "$F" + ) + +} + +add_line_numbers() { + awk ' + /^<\/code><\/pre>$/ { + in_block = 0 + printf "</tbody></table>%s\n", $0 + next + } + + match($0, /^(<pre><code.*>)(.*)$/, a) { + printf "%s<table rules=columns class=\"code-block\"><tbody>", a[1] + + n = 1 + block_count++ + printf "<tr><td class=\"line-number\"><a id=\"B%s-L%s\" href=\"#B%s-L%s\">%s</a></td><td class=\"code-line\">%s</td></tr>\n", block_count, n, block_count, n, n, a[2] + in_block = 1 + next + } + + in_block == 1 { + n++ + printf "<tr><td class=\"line-number\"><a id=\"B%s-L%s\" href=\"#B%s-L%s\">%s</a></td><td class=\"code-line\">%s</td></tr>\n", block_count, n, block_count, n, n, $0 + next + } + + { print } + ' +} + +add_headings_anchors() { + ( + IFS='' + while read -r line; do + if ! printf '%s' "$line" | grep -q '^<h[2-6]>'; then + printf '%s\n' "$line" + continue + fi + LVL="$(printf '%s' "$line" | sed 's|^<h\(.\)>.*|\1|')" + HEADING="$(printf '%s' "$line" | sed 's|^<h.>\(.*\)</h.>$|\1|')" + SLUG="$(slugify_once "$HEADING")" + printf '<h%s class="header-anchor" id="%s">%s<a href="#%s" aria-hidden="true"><img class="svg-icon" src="%s" /></a></h%s>\n' \ + "$LVL" \ + "$SLUG" \ + "$HEADING" \ + "$SLUG" \ + "$(url_for 'static/link.svg')" \ + "$LVL" + done + ) +} + +emit_body() { + cat "$FILENAME" | + extract_content | + add_preamble | + markdown_to_html | + extract_plaintext_snippets | + add_line_numbers | + add_headings_anchors +} + + +# +# Environment variables +# + +. src/development/config.env + +eval "$( + awk ' + /^---$/ { if (++separator > 1) exit; else next; } + { print } + ' "$FILENAME" +)" + +SITE_NAME="$(_ "EuAndreh's website")" +TITLE="$(_ "${TITLE:-$SITE_NAME}")" +URI_TITLE="$(printf '%s' "$TITLE" | uri)" +URL="$(printf '%s' "$FILENAME" | sed -e 's|^src/content/||' -e 's|md$|html|')" + +PREAMBLE="$( + cat <<-EOF + # $TITLE + + <p class="timestamp"> + Posted on <time datetime="$DATE">$(LANG="$LANGUAGE" date -d "$DATE" "$DATE_FMT")</time> + </p> + EOF +)" + + + +# +# Main: generate the HTML to STDOUT. +# + +cat <<-EOF + <!DOCTYPE html> + <html lang="$LANGUAGE"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="stylesheet" type="text/css" href="$(url_for 'static/styles.css')" /> + <link rel="icon" type="image/svg+xml" href="$(url_for 'static/favicon.svg')" /> + + <title>$TITLE</title> + + <meta name="author" content="EuAndreh" /> + <meta property="og:site_name" content="$SITE_NAME" /> + <meta property="og:locale" content="$LANGUAGE" /> + <meta property="og:title" content="$TITLE" /> + + <link rel="canonical" href="$(url_for "$URL" | absolute)" /> + <meta property="og:url" content="$(url_for "$URL" | absolute)" /> + </head> + <body> + <header> + <nav> + <ul> + <a href="$(url_for "$LANGUAGE/")">$(_ 'EuAndreh')</a> + <a href="$(url_for "$(_ 'about.html')")">$(_ 'About')</a> + </ul> + </nav> + <hr /> + </header> + <main> + <article> + $(emit_body) + <hr /> + <p class="post-footer"> + <a href="mailto:~euandreh/public-inbox@lists.sr.ht?Subject=Re%3A%20$URI_TITLE">Comment</a> + and see + <a href="https://lists.sr.ht/~euandreh/public-inbox?search=$URI_TITLE">existing discussions</a> + | + <a href="https://euandreh.xyz/euandre.org.git/tree/$FILENAME">view source</a> + </p> + </article> + </main> + <footer> + <hr /> + <ul> + <li> + <img class="svg-icon" src="$(url_for 'static/envelope.svg')" alt="$(_ 'a envelope icon representing an email address')" /> + <a href="mailto:eu@euandre.org">eu@euandre.org</a> + </li> + <li> + <img class="svg-icon" src="$(url_for 'static/lock.svg')" alt="$(_ 'a lock icon representing a GPG public key')" /> + <a href="$(url_for 'static/public.asc.txt')">81F90EC3CD356060</a> + </li> + </ul> + <p> + $(translate 'The content for this site is licensed under <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>. The <a href="https://euandreh.xyz/euandre.org.git">code</a> is <a rel="license" href="https://euandreh.xyz/euandre.org.git/tree/COPYING">AGPLv3 or later</a>. Patches welcome.') + </p> + </footer> + </body> + </html> +EOF |