184 lines
5.4 KiB
Bash
184 lines
5.4 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="
|
|
"
|
|
|
|
# process an html formatted file either given or from stdin and output to stdout
|
|
# if a file is given, stdin is free to be used by any shp files it encounters
|
|
__process_shp() {
|
|
__SHLVL=$((__SHLVL + 1))
|
|
eval '__SH_FLAG'$__SHLVL'=""
|
|
__LINE_EXEC_'$__SHLVL'=""
|
|
while IFS= read -r __LINE'$__SHLVL'; do
|
|
if [ -z "$__SH_FLAG'$__SHLVL'" ]; then
|
|
case "$__LINE'$__SHLVL'" in
|
|
*"<?shp"*)
|
|
printf "%s" "${__LINE'$__SHLVL'%%<\?shp*}"
|
|
__LINE'$__SHLVL'="${__LINE'$__SHLVL'#*<\?shp}"
|
|
__SH_FLAG'$__SHLVL'=1
|
|
;;
|
|
*) echo "$__LINE'$__SHLVL'" ;;
|
|
esac
|
|
fi
|
|
if [ -n "$__SH_FLAG'$__SHLVL'" ]; then
|
|
__LINE_EXEC_'$__SHLVL'="$__LINE_EXEC_'$__SHLVL'$_NL$__LINE'$__SHLVL'"
|
|
case "$__LINE'$__SHLVL'" in
|
|
*"?>"*)
|
|
eval "${__LINE_EXEC_'$__SHLVL'%%\?>*}"
|
|
printf "%s" "${__LINE'$__SHLVL'#*\?>}"
|
|
__SH_FLAG'$__SHLVL'=""
|
|
__LINE_EXEC_'$__SHLVL'=""
|
|
;;
|
|
*) ;;
|
|
esac
|
|
fi
|
|
done '"${1:+"< \"\$1\""}"'
|
|
[ $__SH_FLAG'$__SHLVL' ] && eval "$__LINE_EXEC_'$__SHLVL'"'
|
|
__SHLVL=$((__SHLVL - 1))
|
|
}
|
|
__SHLVL=0
|
|
|
|
# lowdown wrapper function
|
|
__lowdown() {
|
|
lowdown --html-no-owasp --html-no-skiphtml --html-no-escapehtml --html-no-num-ent "$@" | \
|
|
sed -E '
|
|
s/&[lr]squo;/\'/g
|
|
s/–/--/g
|
|
s/&[lr]dquo;/\"/g
|
|
'
|
|
}
|
|
|
|
# process a pure markdown file
|
|
# uses a special file in _INCDIR, `markdown-template.sh` to create the html
|
|
__process_md() {
|
|
eval ' {
|
|
set_md_metadata
|
|
__process_shp "$_INCDIR"/_markdown-template.shp
|
|
}'"${1:+"< \"\$1\""}"
|
|
}
|
|
|
|
# to call when including a markdown file because we don't want to *process* the whole thing
|
|
__include_md() {
|
|
eval ' {
|
|
set_md_metadata
|
|
__lowdown
|
|
}'"${1:+"< \"\$1\""}"
|
|
}
|
|
|
|
# standard library
|
|
include() {
|
|
# if the path contains a slash, then treat it as-is
|
|
# else, try getting it from $_INCDIR
|
|
case "$1" in
|
|
*/*.md) __include_md "$1" ;;
|
|
*.md) __include_md "$_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
|
|
# for maximum portability, I guess. even though every relevant unix has /dev/stdin
|
|
# I guess it's paranoia for the accidentally swallowing stdin thing as well. whatever
|
|
set_md_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() {
|
|
__ESC_STR=$*
|
|
# do this first to not create an infinite loop
|
|
while :; do
|
|
case "$__ESC_STR" in
|
|
*\&*) __ESC_STR_INT="${__ESC_STR%%&*}&"; __ESC_STR="${__ESC_STR#*&}" ;;
|
|
*) __ESC_STR_INT="$__ESC_STR_INT$__ESC_STR"; break ;;
|
|
esac
|
|
done
|
|
__ESC_STR=$__ESC_STR_INT
|
|
while :; do
|
|
case "$__ESC_STR" in
|
|
*\<*) __ESC_STR="${__ESC_STR%<*}<${__ESC_STR##*<}" ;;
|
|
*\>*) __ESC_STR="${__ESC_STR%>*}>${__ESC_STR##*>}" ;;
|
|
*\"*) __ESC_STR="${__ESC_STR%\"*}"${__ESC_STR##*\"}" ;;
|
|
*\'*) __ESC_STR="${__ESC_STR%\'*}'${__ESC_STR##*\'}" ;;
|
|
*) break;
|
|
esac
|
|
done
|
|
printf '%s\n' "$__ESC_STR"
|
|
}
|
|
|
|
# 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 -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g' -e 's/"/\"/g' -e "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)"
|
|
[ $? -ne 0 ] \
|
|
&& echo 'unable to find number of cores! defaulting to 1 thread unless otherwise specified...' >&2 \
|
|
&& __THREADS=1
|
|
|
|
# make a parallel fifo/file descriptor we need
|
|
__fifo_para="$(mktemp -u -t "shpbuild.XXXXXX")"
|
|
mkfifo "$__fifo_para"
|
|
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" ;;
|
|
*.[mM][dD]) ( __run_in_parallel __process_md "$_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"
|