#!/bin/sh start_time=$(date +%s) domain="https://btxx.org" timestamp_file="timestamps.tsv" # Check the operating system and set aliases case $(uname -s) in OpenBSD) alias sed=gsed alias date=gdate alias rsync=openrsync ;; Darwin) alias sed=gsed alias date=gdate ;; esac set -eu MARKDOWN=lowdown IFS=$'\t' # Precompute values used in multiple places header_title=$(sed -n '/^# /{s/# //p; q}' index.md) git_user_name=$(git config user.name) first_commit_date=$(git log --pretty='format:%ai' . | cut -d ' ' -f1 | tail -1) now=$(date +%FT%TZ) now_rss=$(date -u +"%a, %d %b %Y %H:%M:%S %z") # Update the timestamp for a markdown file update_timestamp() { local filename=$1 local timestamp=$(date -r "$filename" +%s) # Remove old timestamp and add new awk -v file="$filename" -v ts="$timestamp" '$1 != file {print $0}' "$timestamp_file" > "${timestamp_file}.tmp" echo "$filename $timestamp" >> "${timestamp_file}.tmp" mv "${timestamp_file}.tmp" "$timestamp_file" } # Check if the markdown file needs rebuilding needs_rebuilding() { local filename=$1 local last_mod=$(date -r "$filename" +%s) local last_build=$(awk -v file="$filename" '$1 == file {print $2}' "$timestamp_file") if [ "$last_mod" -gt "${last_build:-0}" ]; then return 0 # needs rebuilding else return 1 # does not need rebuilding fi } # Define write_page function write_page() { local filename=$1 local title=$2 local category=$3 # Category determines if it's a post or a page if needs_rebuilding "$filename"; then local directory if [ "$category" = "posts" ]; then directory="build/posts/$(basename "$filename" .md)" else directory="build/$(basename "$filename" .md)" fi mkdir -p "$directory" local target="${directory}/index.html" local created=$(head -3 "$filename" | tail -1) { sed "s|{{TITLE}}|$title|" header.html $MARKDOWN "$filename" cat footer.html } > "$target" update_timestamp "$filename" echo "Rebuilt: $filename" fi } # Create tab separated file with filename, title index_tsv() { for f in "$1"/*.md; do title=$(sed -n '/^# /{s/# //p; q}' "$f") printf '%s\t%s\n' "$f" "${title:="No Title"}" done } # Generate index.html index_html() { sed "s/{{TITLE}}/$header_title/" header.html $MARKDOWN index.md echo "
" while read -r f title; do link="${f%.md}/" created=$(head -3 "$f" | tail -1) echo "$created · $title
" done < "$1" | sort -r echo "
" cat footer.html } # Generate atom.xml atom_xml() { echo " $header_title $now $git_user_name $domain,$first_commit_date:default-atom-feed" while read -r f title; do content=$($MARKDOWN "$f" | sed 's/&/\&/g; s//\>/g; s/"/\"/g; s/'"'"'/\'/g') post_link="${f%.md}" basic_date=$(head -3 "$f" | tail -1) published_date=$(date -d "$basic_date" -u +%Y-%m-%dT10:%M:%SZ) echo " $title $content $domain/$post_link $published_date $published_date " done < "$1" echo '' } # Generate RSS rss_xml() { uri=$(sed -rn '/rss.xml/ s/.*href="([^"]*)".*/\1/ p' header.html) first_commit_date=$(git log --pretty='format:%ai' . | cut -d ' ' -f1 | tail -1) cat < $(sed -n '/^# /{s/# //p; q}' index.md) $domain/rss.xml Feed description here $(date -u +"%a, %d %b %Y %H:%M:%S %z") $(date -u +"%a, %d %b %Y %H:%M:%S %z") Custom RSS Generator 1800 EOF while read -r f title created; do content=$($MARKDOWN "$f" | sed 's/&/\&/g; s//\>/g; s/"/\"/g; s/'"'"'/\'/g') post_link=$(echo "$f" | sed -E 's|posts/(.*).md|\1|') basic_date=$(echo $(head -3 "$f" | tail -1)) published_date=$(date -d "$basic_date" -u +"%a, %d %b %Y %H:%M:%S %z") cat < $title $content $domain/$post_link $domain/$post_link $published_date EOF done < "$1" echo '' echo '' } # Initialize timestamps for all Markdown files if not already done initialize_timestamps() { if [ ! -f "$timestamp_file" ] || [ ! -s "$timestamp_file" ]; then echo "Initializing timestamps..." for f in index.md posts/*.md pages/*.md; do update_timestamp "$f" done echo "Timestamps initialized for all Markdown files." fi } # Main operations mkdir -p build/posts build/pages initialize_timestamps index_tsv posts > build/posts.tsv index_tsv pages > build/pages.tsv while read -r f title; do write_page "$f" "$title" "posts" done < build/posts.tsv while read -r f title; do write_page "$f" "$title" "pages" done < build/pages.tsv if needs_rebuilding "index.md"; then index_html build/posts.tsv > build/index.html echo "Rebuilt index.html due to changes in index.md" update_timestamp "index.md" fi atom_xml build/posts.tsv > build/atom.xml rss_xml build/posts.tsv > build/rss.xml end_time=$(date +%s) elapsed_time=$((end_time - start_time)) echo "Total time: ${elapsed_time}s"