#!/bin/sh ### Code: usage() { cat <<\EOF ht: Build a website Usage: ht -h ht [OPTIONS...] FILE... FLAGS: -h Show this help and exit. -B Treat all targets as "new" (like in `make'). OPTIONS: -u BASEURL The base URL of the built site. Default: https://example.com -o OUTD Output built files to OUTD. Default: ./out/ -w WORKD Use WORKD as the working directory. Default: /tmp/ht/ -p PROC Use PROC to process each input file. Default: ./ht.awk -P TEMPLATE Use TEMPLATE as the page template. Default: ./page.tmpl.htm -i ITEMFMT Format individual items with ITEMFMT in the site index. -I TEMPLATE Use TEMPLATE as the index template. Default: ./index.tmpl.htm -f ITEMFMT Format individual items with ITEMFMT in the site feed. -F TEMPLATE Use TEMPLATE as the feed template. Default: ./feed.tmpl.xml -s DIRECTORY The DIRECTORY holding static files (images, css, etc.). Default: ./static/ -S DIRECTORY The DIRECTORY to copy static files to. Default: ./out PARAMETERS: FILE... The list of files to include in the website. EOF exit "${1:-0}" } main() { configure "$@" shift $((OPTIND - 1)) test $# -eq 0 && usage 1 prepare static_copy for input; do case "$input" in _* | */_*) continue ;; *) page_write "$PAGE_TEMPLATE" "$input" ;; esac done if okp && [ -n "$INDEXEACH" ]; then index_write "$INDEX_TEMPLATE" "$INDEXEACH" "$OUTD/$INDEXNAME" fi if okp && [ -n "$FEEDEACH" ]; then index_write "$FEED_TEMPLATE" "$FEEDEACH" "$OUTD/$FEEDNAME" fi printf 'Done. ' >&2 if okp; then eprint "No errors reported." else eprint "There were errors." fi } htawk_resolve() { if ! command -v ht.awk 2>/dev/null; then print ./ht.awk fi } configure() { OUTD="${HT_OUT_DIR:-./out}" WORKD="${HT_WORKD:-/tmp/ht}" PROC="${HT_PROC:-$(htawk_resolve)}" BASEURL="${HT_BASEURL:-https://example.com}" PAGE_TEMPLATE="${HT_PAGE_TEMPLATE:-./page.tmpl.htm}" INDEX_TEMPLATE="${HT_INDEX_TEMPLATE:-./index.tmpl.htm}" FEED_TEMPLATE="${HT_FEED_TEMPLATE:-./feed.tmpl.xml}" ALLNEW=false STATICD="${HT_STATIC_DIR:-./static}" STATICOUT="${HT_STATIC_OUTPUT_DIR:-${OUTD}}" INDEXNAME="${HT_INDEX_NAME:-index.html}" if [ -n "$HT_INDEX_ITEM_FORMAT" ]; then INDEXEACH="$HT_INDEX_ITEM_FORMAT" else # shellcheck disable=2016 INDEXEACH='
  • ${title}
  • ' fi FEEDNAME="${HT_FEED_NAME:-feed.xml}" if [ -n "$HT_FEED_ITEM_FORMAT" ]; then FEEDEACH="$HT_FEED_ITEM_FORMAT" else # shellcheck disable=2016 FEEDEACH=' $BASEURL/$file ${title} $(date -u -d "@${time}" -R) $AUTHOR ' fi # shellcheck disable=2034 # BASEURL is used in templates while getopts ho:w:p:P:i:I:f:F:u:Bs:S: opt; do case "$opt" in h) usage ;; o) OUTD="$OPTARG" ;; w) WORKD="$OPTARG" ;; p) PROC="$OPTARG" ;; P) PAGE_TEMPLATE="$OPTARG" ;; i) INDEXEACH="$OPTARG" ;; I) INDEX_TEMPLATE="$OPTARG" ;; f) FEEDEACH="$OPTARG" ;; F) FEED_TEMPLATE="$OPTARG" ;; u) BASEURL="$OPTARG" ;; B) ALLNEW=true ;; s) STATICD="$OPTARG" ;; S) STATICOUT="$OPTARG" ;; *) usage 1 ;; esac done STATICOUT="${HT_STATIC_OUTPUT_DIR:-${OUTD}}" INDEX="$WORKD/index.txt" } prepare() { test -n "$WORKD" && rm -rf "$WORKD" mkdir -p "$OUTD" "$WORKD" if ! test -x "$PROC"; then eprint "Can't find processor: $PROC" exit 2 fi touch "$WORKD/ok" } ### Utilities print() { printf '%s\n' "$*" } eprint() { print "$@" >&2 } okp() { test -f "$WORKD/ok" } notok() { rm "$WORKD/ok" 2>/dev/null } olderp() { # olderp REF OTHER # Is REF older than OTHER ? Necessary b/c test -ot is not POSIX. a="$1" b="$2" if a_age="$(stat -c%Y "$a" 2>/dev/null)"; then a_exist=true else a_exist=false fi if b_age="$(stat -c%Y "$b" 2>/dev/null)"; then b_exist=true else b_exist=false fi if $a_exist && ! $b_exist; then # B doesn't exist -- so B is newer # eprint "$a is older than $b (doesn't exist)" return 0 elif ! $a_exist && $b_exist; then # A doesn't exist -- so A is newer # eprint "$a (doesn't exist) is newer than $b" return 1 else # A's age > B's age ? # eprint "$a ($a_age) <= $b ($b_age)?" test "$a_age" -le "$b_age" fi } uptodate() { # uptodate FILE DEPENDENCIES... # && return # Check if FILE is up-to-date with DEPENDENCIES. $ALLNEW && return 1 uptodate=1 file="$1" shift for dep in "$@"; do olderp "$dep" "$file" && uptodate=0 done return $uptodate } ### File conversion template_expand() { # template_expand TEMPLATES... if test "$noproc"; then cat - else end="tmpl_$(date +%s)_${count:=0}" eval "$( print "cat <<$end" cat "$@" print print "$end" )" && count=$((count + 1)) fi } meta_save() { # meta_save META_FILE < INPUT sed -n "s/^;;@//p" 2>/dev/null | tee "$1" } meta_clear() { # meta_clear META_FILE while read -r line; do case "$line" in *'()'*) unset -f "${line%()*}" ;; *=*) unset -v "${line%=*}" ;; *) ;; esac done <"$1" 2>/dev/null } page_write() { # html_write TEMPLATE INPUT template="$1" file="$2" noproc= # File can set `noproc' in their metadata to not be processed. if ! test -f "$file"; then eprint "ERROR: File doesn't exist: $file" notok return 1 fi fn="${file##*/}" fn="${fn%%.*}" out="${OUTD}/${fn}/index.html" mkdir -p "${out%/*}" meta="$WORKD/${fn}.meta" body="$WORKD/${fn}.body" eval "$(meta_save "$meta" <"$file")" stat -c "%Y ${fn} $meta" "$file" >>"$INDEX" # uptodate "$out" "$template" "$file" && return eprint "Page: $file -> $out" test "$noproc" || "$PROC" "$file" >"$body" if ! [ -f "$body" ]; then eprint "ERROR: Conversion: $file" notok return 2 fi test "$noproc" || body="$(template_expand "${body}")" if ! template_expand "$template" >"$out"; then eprint "ERROR: Expansion: $file" notok return 3 fi meta_clear "$meta" } index_write() { # index_write TEMPLATE EACH OUTFILE template="$1" each="$2" out="$3" if ! test -f "$INDEX"; then : >"$INDEX" fi # uptodate "$out" "$template" "$INDEX" && return eprint "Index: $out" # shellcheck disable=2034 # file and time can be used in `each' sort -k1,1 -nr "$INDEX" | while IFS=' ' read -r time file meta; do # shellcheck disable=1090 . "$meta" # shellcheck disable=2154 # noindex is a file var [ "$noindex" ] && continue if ! item="$(eval print "\"$each\"")"; then eprint "ERROR: couldn't add '$file' to $out" notok fi print "$item" meta_clear "$meta" done | template_expand "$template" >"$out" } ### Static files static_copy() { # static_copy if ! test -d "$STATICD"; then return 1 fi mkdir -p "$STATICOUT" for f in "$STATICD"/*; do if [ -d "$f" ]; then cp -r "$f" "$STATICOUT/" else cp "$f" "$STATICOUT/" fi done } ### Do the thing test "$DEBUG" && set -x test "$SOURCE" || main "$@"