178 lines
4.9 KiB
Bash
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' "&" ;;
|
|
\<) printf '%s' "<" ;;
|
|
\>) printf '%s' ">" ;;
|
|
\") printf '%s' """ ;;
|
|
\') printf '%s' "'" ;;
|
|
*) 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/&/\&/g
|
|
s/</\</g
|
|
s/>/\>/g
|
|
s/"/\"/g
|
|
s/'\''/\\'/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"
|