#!/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 *""*) 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" } # 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"