#!/bin/sh set -e usage="usage: ./md.sh ./md.sh " # input: /src.md ./md-header ./md-footer /nav.html # output: /index.html # dependencies: lowdown # Copyright 2019-2022 DistressNetwork° # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . IN="$1/src.md" OUT="$1/index.html" [ "$2" ] && { IN="$1" ; OUT="$2" ; } [ "$1" ] || { echo "$usage" ; exit 1 ; } [ -f "$IN" ] || { printf "[err] \t%s not found\n" "$IN" ; exit 1 ; } [ -f md-header ] || { printf "[err] \tmd-header not found\n" ; exit 1 ; } [ -f md-footer ] || { printf "[err] \tmd-footer not found\n" ; exit 1 ; } [ -e "$(which lowdown)" ] || { printf "[err] \tlowdown not found\n" ; exit 1 ; } cat md-header > "$OUT" lowdown -Thtml --html-no-owasp "$IN" >> "$OUT" cat md-footer >> "$OUT" printf "[ok] \tinitial assembly\n" sedesc() { sed -e 's;&;\\\&;g' -e 's/;/\\\;/g' } unesc() { sed -e 's;\\\&;\&;g' -e 's/\\\;/;/g' } TITLE=$(grep -e '^title: ' -m 1 "$IN" | cut -f 2- -d ' ' | sedesc) sed "s//&${TITLE} \&mdash\; DistressNetwork°/1" -i'' "$OUT" printf "[ok] \ttitle: %s\n" "$(echo "$TITLE" | unesc)" BOMBER=$(grep -e '^bomber: ' -m 1 "$IN" | cut -f 2- -d ' ' | sedesc) sed "s/<div class=\"bomber\">/&${BOMBER}/1" -i'' "$OUT" printf "[ok] \tbomber: %s\n" "$(echo "$BOMBER" | unesc)" # format: "yyyy-mm-dd hh:mm:ss±zz:zz" (ISO 8601) MDATE=$(stat -c '%y' "$IN" | sed -e 's/\.[0-9]* //' -e 's/\(..\)\(..\)$/\1:\2/') # format: "yymmdd" (for humans) DATE=$(echo "$MDATE" | cut -f 1 -d ' ' | sed -e 's/-//g' -e 's/^..//') sed -e "s;<div class=\"ident\"><time datetime=\";&${MDATE};1" -e "s;</time>;${DATE}&;1" -i'' "$OUT" sed "s;<meta http-equiv=\"last-modified\" content=\";&${MDATE};1" -i'' "$OUT" printf "[ok] \tdate: %s (%s)\n" "$DATE" "$MDATE" LEADING=$(grep -e '^leading: ' -m 1 "$IN" | cut -f 2- -d ' ' | sedesc) sed "s;<div class=\"leading\">;&${LEADING};1" -i'' "$OUT" printf "[ok] \tleading: %s\n" "$(echo "$LEADING" | unesc)" METADESC="\«\;${BOMBER}\»\; \&mdash\; ${LEADING}" sed "s;<meta name=\"description\" content=\";&${METADESC};1" -i'' "$OUT" printf "[ok] \tmeta description\n" if [ -f "$(echo $IN | cut -f 1 -d /)/nav.html" ] ; then NAVLINE=$(grep -n '<nav>' "$OUT" | cut -f 1 -d :) ; sed "${NAVLINE}r $(echo "$OUT" | cut -f 1 -d /)/nav.html" -i'' "$OUT" ; sed '/^<!--title:/d' -i'' "$OUT" ; printf "[ok] \tsitemap\n" ; fi grep -q '<p><img' "$OUT" && { # skip if no figures found # escaping backslashes with '@', to be unescaped later: grep -n '<p><img' "$OUT" | sed 's/\\/@/g' | \ while IFS='' read -r data ; do LINE=$(echo "$data" | cut -f 1 -d :) ; # extract line number FILE=$(echo "$data" | cut -f 2 -d \") ; # extract file path CAPT=$(echo "$data" | cut -f 4 -d \") ; # extract image caption if [ "$FILE" = "%tex" ] ; then # for tex figures (to be caught and rendered later) sed "${LINE}s;.*;<figure><%tex \"${CAPT}\" /></figure>;" -i'' "$OUT" ; else sed "${LINE}s;.*;<figure><a href=\"${FILE}\"><img src=\"${FILE}\" alt=\"\"></a><figcaption><p>${CAPT}</p></figcaption></figure>;" -i'' "$OUT" ; printf "[ok] \timage at line %s\n" "$LINE" ; fi done } toc() { tmp=$(mktemp -p /tmp) # format headers from source as md list grep -e '^#' "$IN" | sed -e 's; ;- ;' -e 's;^#;;' -e 's;#; ;g' > "$tmp" # convert to html, write to tempfile (reset) buffer=$(cat "$tmp") ; echo "$buffer" | lowdown -Thtml > "$tmp" # prepend starting tag, remove empty lines sed -e '1i <nav class="toc">' -e '/^$/d' -i'' "$tmp" # get queue of headers from output HEADERS=$(grep -e "^<h[[:digit:]] id=" "$OUT") # for each toc list item: grep -e "^<li>" "$tmp" | while IFS='' read -r data ; do # get next header in queue, extract id HEADER=$(echo "$HEADERS" | head -n 1) ; ID=$(echo "$HEADER" | cut -f 2 -d \" | sedesc) ; # rewrite toc item as link with id ITEM=$(echo "$data" | sed "s;^<li>\([^</>]*\)\(</li>\)*$;\1;" | sedesc) ; sed -e "s;^<li>\(${ITEM}\);<li><a href=\"#${ID}\">\1;" -e "s;\([[:alnum:]]\)</li>$;\1</a></li>;" -i'' "$tmp" ; # remove processed header from queue HEADERS=$(echo "$HEADERS" | sed '1d') ; printf "\tassembled header: %s\n" "$(echo $ITEM | unesc)" ; done # close link tags on open lines, append closing tag sed "s;[[:alnum:]]$;&</a>;" -i'' "$tmp" echo '</nav>' >> "$tmp" # get toc line number in output, insert data from tempfile TOCLINE=$(wc -l md-header | cut -f 1 -d ' ') sed "${TOCLINE}r $tmp" -i'' "$OUT" rm "$tmp" printf "[ok] \ttable of contents\n" } math() { # [ $(npx katex -V 2>&1 | grep '404 Not Found') ] && { printf "[warn] \tkatex not found, skipping math rendering\n" ; return ; } sed '/main.css/a <link rel="stylesheet" href="/css/katex.css">' -i'' "$OUT" # display / "block" style: grep -n '<figure><%tex' "$OUT" | \ while IFS='' read -r data ; do LINE=$(echo "$data" | cut -f 1 -d :) ; EQNBODY=$(echo "$data" | cut -f 2 -d \" ) ; printf "\trendering tex figure at line %s ... " "$LINE" ; tmp=$(mktemp -p /tmp) ; echo "<figure>" > "$tmp" ; echo "$EQNBODY" | sed 's/@/\\/g' | npx katex -d >> "$tmp" 2>&1 ; echo "</figure>" >> "$tmp" ; sed ':a;N;$!ba;s/\n//g' -i'' "$tmp" ; if grep -q "KaTeX parse error" "$tmp" ; then printf "\n[warn] \ttex expression at line %s contained errors, skipping\n" "$LINE" ; else sed "${LINE}d" -i'' "$OUT" ; sed "$(expr $LINE - 1)r $tmp" -i'' "$OUT" ; printf "complete\n" ; fi ; rm "$tmp" ; done # inline / "span" style: sed 's;<img src="%itex" alt=".*" />;\n&\n;g' -i'' "$OUT" grep -n '<img src="%itex"' "$OUT" | sed 's/\\/@/g' | \ while IFS='' read -r data ; do LINE=$(echo "$data" | cut -f 1 -d :) ; EQNBODY=$(echo "$data" | cut -f 4 -d \" ) ; printf "\trendering inline tex figure at line %s ... " "$LINE" ; tmp=$(mktemp -p /tmp) ; echo "$EQNBODY" | sed 's/@/\\/g' | npx katex > "$tmp" 2>&1 ; sed ':a;N;$!ba;s/\n//g' -i'' "$tmp" ; if grep -q "KaTeX parse error" "$tmp" ; then printf "\n[warn] \tinline tex expression at line %s contained errors, skipping\n" "$LINE" ; else sed "${LINE}d" -i'' "$OUT" ; sed "$(expr $LINE - 1)r $tmp" -i'' "$OUT" ; printf "complete\n" ; fi ; rm "$tmp" ; done printf "[ok] \tmath typesetting\n" } opts=$(grep -e '^opts: ' -m 1 $IN | cut -f 2- -d ' ') while [ "$opts" ] ; do case "$opts" in (*[Cc]*) toc opts=$(echo $opts | sed 's/[Cc]//g') ;; (*[Ff]*) sed "/^<footer>/,/^<\/footer>/d" -i'' "$OUT" printf "[ok] \tfooterless\n" opts=$(echo "$opts" | sed 's/[Ff]//g') ;; (*[Hh]*) sed -e "/^<header>/,/^<\/header>/d" -e '/^<meta name="description"/d' -i'' "$OUT" sed "/bg.js/d" -i'' "$OUT" printf "[ok] \theaderless\n" opts=$(echo "$opts" | sed 's/[Hh]//g') ;; (*[Mm]*) math opts=$(echo "$opts" | sed 's/[Mm]//g') ;; (*) printf "[warn] \tmetadata: unrecognized option(s) \"%s\"\n" "${opts}" ;; esac done printf "%s \tbuild complete: %s\n" "[->]" "$OUT"