website/build.sh
2024-09-17 02:28:48 -04:00

178 lines
4.9 KiB
Bash

#!/bin/sh
__BASE="${SHP_BASE:-"$PWD"}"
export _INCDIR="${SHP_INCDIR:-"${__BASE}/include"}"
export _BUILDDIR="${SHP_BUILDDIR:-"${__BASE}/build"}"
export _SRCDIR="${SHP_SRCDIR:-"${__BASE}/src"}"
export _NL="
"
export _DJOT="jotdown"
__cleanup() {
ec=$?
trap - INT HUP QUIT TERM EXIT
[ -p "$__fifo_para" ] && rm "$__fifo_para"
exit "$ec"
}
trap __cleanup INT HUP QUIT TERM EXIT
# process an shp/html formatted file given and output to stdout
# $1 is the file to process, $2... are passed to the file as arguments
__SHLVL=0
__process_shp() {
__SHLVL=$((__SHLVL + 1))
eval '
shift
IFS= read -r __LINE'$__SHLVL'
while :; do
case $__LINE'$__SHLVL' in
*"<?shp"*)
__LINE_EXEC'$__SHLVL'=
printf "%s" "${__LINE'$__SHLVL'%%<\?shp*}"
__LINE'$__SHLVL'=${__LINE'$__SHLVL'#*<\?shp}
while :; do
__LINE_EXEC'$__SHLVL'=$__LINE_EXEC'$__SHLVL'$_NL$__LINE'$__SHLVL'
case $__LINE'$__SHLVL' in
*"?>"*)
eval "${__LINE_EXEC'$__SHLVL'%%\?>*}"
__LINE'$__SHLVL'=${__LINE'$__SHLVL'#*\?>}
break
;;
esac
IFS= read -r __LINE'$__SHLVL' || { eval "$__LINE_EXEC'$__SHLVL'" && break 2; }
done
[ -n "$__LINE'$__SHLVL'" ] && continue
;;
*) printf "%s\n" "$__LINE'$__SHLVL'" ;;
esac
IFS= read -r __LINE'$__SHLVL' || break
done' < "$1"
__SHLVL=$((__SHLVL - 1))
}
# process a pure djot file
# uses a special file in _INCDIR, `_djot-template.shp` to create the html
__process_djot() {
__process_shp "$_INCDIR"/_djot-template.shp "$1"
}
# to call when including a markdown file because we don't want to *process* the whole thing
__include_djot() {
{
set_djot_metadata
jotdown
} < "$1"
}
# standard library
include() {
# if the path contains a slash, then treat it as-is
# else, try getting it from $_INCDIR
case "$1" in
*/*.djot) __include_djot "$1" ;;
*.djot) __include_djot "$_INCDIR"/"$1" ;;
*/*) __process_shp "$1" ;;
*) __process_shp "$_INCDIR"/"$1" ;;
esac
}
# set markdown metadata either given or from stdin
# why do we have this big eval here--why not use /dev/stdin?
# using /dev/stdin makes the input behave Very Weirdly, I can't really explain
# what is going on but it just doesn't work how one would expect
# this eval prevents a lengthy if-else statement
# I adapted this method to all other stuff that either accepts stdin or an argument
# I guess it's paranoia for the accidentally swallowing stdin thing as well. whatever
set_djot_metadata() {
eval ' {
# if 1st line isnt all -, skip setting metadata
IFS= read -r __first
case "$__first" in
*[!-]*) return ;;
esac
while IFS=" =" read -r __key __val; do
case "$__key" in
*[!-]*) eval "$__key=\$__val" ;;
*) return ;;
esac
done
} '"${1:+"< \"\$1\""}"
}
# good for inputs without many chars to escape
# so most titles, strings, etc.
# this is why it only accepts an argument and not stdin
escape() {
# getopts iterates over every character like this
while getopts ":" opt "-$*"; do
# if the opt it detects is a colon OPTARG will be blank so we compensate
case "${OPTARG:=:}" in
\&) printf '%s' "&amp;" ;;
\<) printf '%s' "&lt;" ;;
\>) printf '%s' "&gt;" ;;
\") printf '%s' "&quot;" ;;
\') printf '%s' "&apos;" ;;
*) printf '%s' "$OPTARG" ;;
esac
done
echo
}
# good for large inputs with lots of chars to escape
# this means mostly full files, entire posts for putting in content for a feed
escapepipe() {
sed '
s/&/\&amp;/g
s/</\&lt;/g
s/>/\&gt;/g
s/"/\&quot;/g
s/'\''/\\&apos;/g
'
}
# build folder structure
find "$_SRCDIR" -type d -exec sh -c 'for d; do mkdir -p "$_BUILDDIR"/"${d#"$_SRCDIR"}"; done' sh {} +
# it's parallel time baby
# hopefully portable enough to not be an issue
__THREADS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null)"
if [ $? -ne 0 ]; then
echo 'unable to find number of cores! defaulting to 1 thread unless otherwise specified...' >&2
__THREADS=1
fi
# make a parallel fifo/file descriptor we need
__fifo_para="$(mktemp -u -t "shpbuild.XXXXXX")"
mkfifo "$__fifo_para" || exit 1
exec 9<>"$__fifo_para"
rm -f "$__fifo_para"
while [ "$__THREADS" -gt 0 ]; do
printf '\n' >&9 # start with THREADS amount of lines in fd 9 for later
__THREADS="$((__THREADS - 1))"
done
# read each line from fd 9, launch new program for each line
# print a line after program finished such that another one can take its place
__run_in_parallel() {
read -r __ <&9
{
"$@"
printf '\n' >&9
} &
}
while IFS= read -r _FILE; do
cd "${_FILE%/*}"
export _OUTFILE="$_BUILDDIR/${_FILE#"$_SRCDIR/"}"
case "${_FILE##*/}" in
*.[hH][tT][mM][lL]|*.[sS][hH][pP]) ( __run_in_parallel __process_shp "$_FILE" ) > "${_OUTFILE%.*}.html" ;;
*.[dD][jJ][oO][tT]) ( __run_in_parallel __process_djot "$_FILE" ) > "${_OUTFILE%.*}.html" ;;
*.[sS][hH]) ( __run_in_parallel . "$_FILE" ) ;; # don't autocopy. this is for code :)
*) __run_in_parallel cp -p "$_FILE" "$_OUTFILE" ;;
esac
done <<-EOF
$(find "$_SRCDIR" -type f)
EOF
[ -f "$_INCDIR/_postbuild.sh" ] && . "$_INCDIR/_postbuild.sh"