#!/bin/sh # # ISC License (ISC) # # Copyright (c) 2021-2024 Paul W. Rankin # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # Configuration: # SITE_URL your URL # SITE_AUTHOR your name # SITE_EMAIL your email # SITE_TITLE title for html pages # BLOG_TITLE title of generated blog page # BLOG_DATE_FMT date format for blog index as per strftime(3) # SITE_TZ site timezone offset with separator as per ISO 8601 # TITLE_SEP separator between SITE_TITLE and page titles # SRC_DIR directory of source files to convert # STATIC_DIR directory of static source files # HTML_TMPL location of m4 template for html files # FEED_TMPL location of m4 template for atom file # HTML_DIR output directory # BLOG_INDEX output location of generated blog page, relative to HTML_DIR # FEED output location of generated atom file, relative to HTML_DIR prog=$(basename "$0") site_url="${SITE_URL:-https://rnkn.xyz}" site_author="${SITE_AUTHOR:-Paul W. Rankin}" site_email="${SITE_EMAIL:-rnkn@rnkn.xyz}" site_title="${SITE_TITLE:-Paul W. Rankin}" blog_title="${BLOG_TITLE:-Blog}" blog_date_fmt="${BLOG_DATE_FMT:-+%d %b %Y}" site_tz="${SITE_TZ:-+10:00}" title_sep="${TITLE_SEP:- – }" src_dir="${SRC_DIR:-src}" static_dir="${STATIC_DIR:-static}" html_tmpl="${HTML_TMPL:-index.html.in}" feed_tmpl="${FEED_TMPL:-feed.xml.in}" db="${DB:-$(mktemp)}" html_dir="${HTML_DIR:-html}" blog_dir="${BLOG_DIR:-words}" blog_index="${BLOG_INDEX:-${blog_dir}/index.html}" feed="${FEED:-feed.xml}" sitemap="${SITEMAP:-${html_dir}/sitemap.txt}" usage() { cat </dev/null || echo NULL) title=$(lowdown -X title "$file" 2>/dev/null || echo NULL) build="${file%.md}.html" path="${build#$src_dir}"; path="${path#/}" # requires Unix date *not* GNUdate fmt_date=$(date -jf %F "$date" "$blog_date_fmt" 2>/dev/null || echo NULL) printf '%s\t%s\t%s\t%s\n' "$date" "$fmt_date" "$path" "$title" done | sort -r } # make_index(database) # returns: formatted HTML table make_index() { rows=$( awk -F \\t -v dir="$blog_dir" '$1 != "NULL" && $3 ~ "^"dir \ { printf "%s%s\n", $2, $3, $4 }' < "$1") printf '\n\n%s\n\n
\n' "$rows" } # convert_md(markdownfile) # returns: formatted HTML convert_md() { src="$1" dst="${src%.md}.html" [ "$verbose" -eq 1 ] && echo "converting $src > $dst" lowdown -Thtml --html-no-skiphtml --html-no-escapehtml --html-no-owasp \ < "$src" > "$dst" } # make_htmlpage(htmlfile) # returns: formatted standalone HTML make_htmlpage() { src="$1" dst="${src#$src_dir}"; dst="${dst#/}" mkdir -p $(dirname "${html_dir}/${dst}") if [ "$src" == "${src_dir}/${blog_index}" ]; then title="$blog_title" else title=$(awk -F \\t -v dst="$dst" '$3 == dst { print $4 }' < "$db") date=$(awk -F \\t -v dst="$dst" '$3 == dst { print $2 }' < "$db") fi if [ "$title" == NULL ] || [ -z "$title" ]; then title="$site_title" else title="${site_title}${title_sep}${title}" fi [ "$date" == NULL ] && unset date [ "$verbose" -eq 1 ] && echo "generating ${html_dir}/${dst}" m4 -D__TITLE__="$title" \ -D__DATE__="$date" \ -D__CONTENT__="$src" \ -P < "$html_tmpl" > "${html_dir}/${dst}" unset title unset date } # make_feed_entries(database) # returns: formatted XML make_feed_entries() { < "$1" awk -F \\t -v dir="$blog_dir" '$1 != "NULL" && $3 ~ "^"dir' | while read -r line; do date=$(echo "$line" | awk -F \\t '{ print $1 }') path=$(echo "$line" | awk -F \\t '{ print $3 }') title=$(echo "$line" | awk -F \\t '{ print $4 }') cat < ${title} ${site_url}/${path} ${date}T00:00:00${site_tz} EOF done } # make_feed(feedcontent) # returns: formatted XML Atom feed make_feed() { src="$1" m4 -D__TITLE__="$site_title" \ -D__AUTHOR__="$site_author" \ -D__EMAIL__="$site_email" \ -D__SITE_URL__="$site_url" \ -D__PATH__="$feed" \ -D__DATE__=$(date "+%FT%R:%S${site_tz}") \ -D__CONTENT__="$src" \ -P < "$feed_tmpl" } main() { verbose=0; force=0; debug=0 while getopts 'dfv' opt do case "$opt" in (d) debug=1 ;; (f) force=1 ;; (v) verbose=1 ;; (*) usage ;; esac done [ "$verbose" -eq 1 ] && echo "-> generating blog db..." get_src "md" | make_db > "$db" [ "$verbose" -eq 1 ] && echo "-> converting Markdown to HTML..." get_src "md" | while read -r file; do convert_md "$file" done [ "$verbose" -eq 1 ] && echo "-> generating blog index ${src_dir}/${blog_index}" mkdir -p "${src_dir}/${blog_dir}" make_index "$db"> "${src_dir}/${blog_index}" mkdir -p "$html_dir" if [ "$force" -eq 1 ]; then [ "$verbose" -eq 1 ] && echo "-> cleaning destination $html_dir" rm -rf "$html_dir/"* fi [ "$verbose" -eq 1 ] && echo "-> generating HTML pages..." get_src "html" | while read -r file; do make_htmlpage "$file" done [ "$verbose" -eq 1 ] && echo "-> generating Atom feed ${html_dir}/${feed}" feed_tmp=$(mktemp) make_feed_entries "$db" > "$feed_tmp" make_feed "$feed_tmp" > "${html_dir}/${feed}" [ "$verbose" -eq 1 ] && echo "-> copying static files into $html_dir" cp -Rp "$static_dir"/* "$html_dir" [ "$verbose" -eq 1 ] && echo "-> generating $sitemap" find "$html_dir" -type f -name '*.html' | while read -r file; do path="${file#$html_dir}"; path="${path#/}" echo "${site_url}/${path}" done > "$sitemap" if [ ! "$debug" -eq 1 ]; then [ "$verbose" -eq 1 ] && echo "-> removing temporary files..." rm -rf "${src_dir}/${blog_index}" "$db" "$feed_tmp" get_src "md" | while read -r file; do target="${file%.md}.html" if [ -e "$target" ]; then [ "$verbose" -eq 1 ] && echo "removing $target" rm -rf "$target" fi done fi } main "$@" # Local Variables: # indent-tabs-mode: t # End: