dots

Personal dotfiles
git clone git://git.gormless.xyz/dots.git
Log | Files | Refs

ssg6 (5725B)


      1 #!/bin/sh -e
      2 #
      3 # https://rgz.ee/bin/ssg6
      4 # Copyright 2018-2019 Roman Zolotarev <hi@romanzolotarev.com>
      5 #
      6 # Permission to use, copy, modify, and/or distribute this software for any
      7 # purpose with or without fee is hereby granted, provided that the above
      8 # copyright notice and this permission notice appear in all copies.
      9 #
     10 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17 #
     18 
     19 main() {
     20 	test -n "$1" || usage
     21 	test -n "$2" || usage
     22 	test -n "$3" || usage
     23 	test -n "$4" || usage
     24  	test -d "$1" || no_dir "$1"
     25  	test -d "$2" || no_dir "$2"
     26 
     27 	src=$(readlink_f "$1")
     28 	dst=$(readlink_f "$2")
     29 
     30 	IGNORE=$(
     31 		if ! test -f "$src/.ssgignore"
     32 		then
     33 			printf ' ! -path "*/.*"'
     34 			return
     35 		fi
     36 		while read -r x
     37 		do
     38 			test -n "$x" || continue
     39 			printf ' ! -path "*/%s*"' "$x"
     40 		done < "$src/.ssgignore"
     41 	)
     42 
     43 	# files
     44 
     45 	title="$3"
     46 
     47 	h_file="$src/_header.html"
     48 	f_file="$src/_footer.html"
     49 	test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER
     50 	test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER
     51 
     52 	list_dirs "$src" |
     53 	(cd "$src" && cpio -pdu "$dst")
     54 
     55 	fs=$(
     56  	if test -f "$dst/.files"
     57 	then list_affected_files "$src" "$dst/.files"
     58 	else list_files "$1"
     59 	fi
     60 	)
     61 
     62 	if test -n "$fs"
     63 	then
     64 		echo "$fs" | tee "$dst/.files"
     65 
     66 		if echo "$fs" | grep -q '\.md$'
     67 		then
     68 			if test -x "$(which lowdown 2> /dev/null)"
     69 			then
     70 				echo "$fs" | grep '\.md$' |
     71 				render_md_files_lowdown "$src" "$dst" "$title"
     72 			else
     73 				if test -x "$(which Markdown.pl 2> /dev/null)"
     74 				then
     75 					echo "$fs" | grep '\.md$' |
     76 					render_md_files_Markdown_pl "$src" "$dst" "$title"
     77 				else
     78 					echo "couldn't find lowdown nor Markdown.pl"
     79 					exit 3
     80 				fi
     81 			fi
     82 		fi
     83 
     84 		echo "$fs" | grep '\.html$' |
     85 		render_html_files "$src" "$dst" "$title"
     86 
     87 		echo "$fs" | grep -Ev '\.md$|\.html$' |
     88 		(cd "$src" && cpio -pu "$dst")
     89 	fi
     90 
     91 	printf '[ssg] ' >&2
     92 	print_status 'file, ' 'files, ' "$fs" >&2
     93 
     94 
     95 	# sitemap
     96 
     97 	base_url="$4"
     98 	date=$(date +%Y-%m-%d)
     99  	urls=$(list_pages "$src")
    100 
    101 	test -n "$urls" &&
    102 	render_sitemap "$urls" "$base_url" "$date" > "$dst/sitemap.xml"
    103 
    104 	print_status 'url' 'urls' "$urls" >&2
    105 	echo >&2
    106 }
    107 
    108 
    109 readlink_f() {
    110 	file="$1"
    111 	cd "$(dirname "$file")"
    112 	file=$(basename "$file")
    113 	while test -L "$file"
    114 	do
    115 		file=$(readlink "$file")
    116 		cd "$(dirname "$file")"
    117 		file=$(basename "$file")
    118 	done
    119 	dir=$(pwd -P)
    120 	echo "$dir/$file"
    121 }
    122 
    123 
    124 print_status() {
    125 	test -z "$3" && printf 'no %s' "$2" && return
    126 
    127 	echo "$3" | awk -v singular="$1" -v plural="$2" '
    128 	END {
    129 		if (NR==1) printf NR " " singular
    130 		if (NR>1) printf NR " " plural
    131 	}'
    132 }
    133 
    134 
    135 usage() {
    136 	echo "usage: ${0##*/} src dst title base_url" >&2
    137 	exit 1
    138 }
    139 
    140 
    141 no_dir() {
    142 	echo "${0##*/}: $1: No such directory" >&2
    143 	exit 2
    144 }
    145 
    146 list_dirs() {
    147 	cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE"
    148 }
    149 
    150 
    151 list_files() {
    152 	cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE"
    153 }
    154 
    155 
    156 list_dependant_files () {
    157  	e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)"
    158 	cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e"
    159 }
    160 
    161 list_newer_files() {
    162 	cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2"
    163 }
    164 
    165 
    166 has_partials() {
    167 	grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
    168 }
    169 
    170 
    171 list_affected_files() {
    172 	fs=$(list_newer_files "$1" "$2")
    173 
    174 	if echo "$fs" | has_partials
    175 	then list_dependant_files "$1"
    176 	else echo "$fs"
    177 	fi
    178 }
    179 
    180 
    181 render_html_files() {
    182 	while read -r f
    183 	do render_html_file "$3" < "$1/$f" > "$2/$f"
    184 	done
    185 }
    186 
    187 
    188 render_md_files_lowdown() {
    189 	while read -r f
    190 	do
    191 		lowdown \
    192 		--html-no-escapehtml \
    193 		--html-no-skiphtml \
    194 		--parse-no-metadata \
    195 		--parse-no-autolink < "$1/$f" |
    196 		render_html_file "$3" \
    197 		> "$2/${f%\.md}.html"
    198 	done
    199 }
    200 
    201 
    202 render_md_files_Markdown_pl() {
    203 	while read -r f
    204 	do
    205 		Markdown.pl < "$1/$f" |
    206 		render_html_file "$3" \
    207 		> "$2/${f%\.md}.html"
    208 	done
    209 }
    210 
    211 
    212 render_html_file() {
    213 	# h/t Devin Teske
    214 	awk -v title="$1" '
    215 	{ body = body "\n" $0 }
    216 	END {
    217 		body = substr(body, 2)
    218 		if (body ~ /<\/?[Hh][Tt][Mm][Ll]/) {
    219 			print body
    220 			exit
    221 		}
    222 		if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) {
    223 			t = substr(body, RSTART + RLENGTH)
    224 			sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t)
    225 			gsub(/^[[:space:]]*|[[:space:]]$/, "", t)
    226 			if (t) title = t " &mdash; " title
    227 		}
    228 		n = split(ENVIRON["HEADER"], header, /\n/)
    229 		for (i = 1; i <= n; i++) {
    230 			if (match(tolower(header[i]), "<title></title>")) {
    231 				head = substr(header[i], 1, RSTART - 1)
    232 				tail = substr(header[i], RSTART + RLENGTH)
    233 				print head "<title>" title "</title>" tail
    234 			} else print header[i]
    235 		}
    236 		print body
    237 		print ENVIRON["FOOTER"]
    238 	}'
    239 }
    240 
    241 
    242 list_pages() {
    243 	e="\\( -name '*.html' -o -name '*.md' \\)"
    244 	cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
    245 	sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
    246 }
    247 
    248 
    249 render_sitemap() {
    250 	urls="$1"
    251 	base_url="$2"
    252 	date="$3"
    253 
    254 	echo '<?xml version="1.0" encoding="UTF-8"?>'
    255 	echo '<urlset'
    256 	echo 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
    257 	echo 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'
    258 	echo 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"'
    259 	echo 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
    260 	echo "$urls" |
    261 	sed -E 's#^(.*)$#<url><loc>'"$base_url"'/\1</loc><lastmod>'\
    262 "$date"'</lastmod><priority>1.0</priority></url>#'
    263 	echo '</urlset>'
    264 }
    265 
    266 main "$@"