#!/bin/sh set -e # usage: # ./md.sh # ./md.sh # # input: /src.md ; ./md-header ; ./md-footer # output: /index.html # dependencies: lowdown, fzf, ./urlencode # Copyright 2019-2020 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 . # i/o variables IN="$1/src.md" OUT="$1/index.html" if [ "$2" ] ; then IN="$1" ; OUT="$2" ; fi # confirm source exists before continuing [ -f $IN ] || { echo "---- [FATAL] $IN not found ----" ; exit 1 ; } ### initial write cat md-header > $OUT lowdown -Thtml $IN >> $OUT cat md-footer >> $OUT echo '[ok] initial assembly' ### useful functions sedesc() { sed -e 's;&;\\\&;g' -e 's/;/\\\;/g' } unesc() { sed -e 's;\\\&;\&;g' -e 's/\\\;/;/g' } encode() ( ./urlencode ) decode() { /bin/printf '%b' "$(echo "$1" | sed 's;%;\\x;g')" } ### header variables extracted from metadata section TITLE=$(grep -e '^title: ' -m 1 $IN | cut -f 2- -d ' ' | sedesc) sed "s/#/<title>${TITLE} \&mdash\; DistressNetwork°/1" -i'' $OUT printf '[ok] title: %s\n' "$(echo $TITLE | unesc)" BOMBER=$(grep -e '^bomber: ' -m 1 $IN | cut -f 2- -d ' ' | sedesc) sed "s/<div class=\"bomber\">#/<div class=\"bomber\">${BOMBER}/1" -i'' $OUT printf '[ok] bomber: %s\n' "$(echo $BOMBER | unesc)" MDATE=$(stat -c '%y' $IN | sed -e 's/\.[0-9]* //' -e 's/\(..\)\(..\)$/\1:\2/') # format: "yyyy-mm-dd hh:mm:ss±zz:zz" (ISO 8601) DATE=$(echo $MDATE | cut -f 1 -d ' ' | sed -e 's/-//g' -e 's/^..//') # format: "yymmdd" (for humans) sed "s;<div class=\"ident\"><time>#;<div class=\"ident\"><time datetime=\"${MDATE}\">${DATE};1" -i'' $OUT sed "/^<\/head>/i <meta http-equiv=\"last-modified\" content=\"${MDATE}\" />" -i'' $OUT printf '[ok] date: %s (%s)\n' "$DATE" "$MDATE" LEADING=$(grep -e '^leading: ' -m 1 $IN | cut -f 2- -d ' ' | sedesc) sed "s;<div class=\"leading\">#;<div class=\"leading\">${LEADING};1" -i'' $OUT printf '[ok] leading: "%s"\n' "$(echo $LEADING | unesc)" METADESC="\«\;${BOMBER}\»\; \&mdash\; ${LEADING}" sed "s;<meta name=\"description\" content=\"#;<meta name=\"description\" content=\"${METADESC};1" -i'' $OUT printf '[ok] meta description\n' ### image handling grep -n '<p><img' $OUT | \ 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 [ "$CAPT" = "#tex" ] ; then # for tex figures: remove caption and containers sed "${LINE}s;.*;<figure><img src=\"${FILE}\" alt=\"\"></figure>;" -i'' $OUT ; printf '[ok] tex figure at line %s\n' "$LINE" ; else # use new image format sed "${LINE}s;.*;<figure><a href=\"${FILE}\"><img src=\"${FILE}\" alt=\"\"></a><figcaption><p>${CAPT}</p></figcaption></figure>;" -i'' $OUT ; printf '[ok] image at line %s\n' "$LINE" ; fi done ### table of contents generation tocgen() ( tmp=$(mktemp -p /tmp) # using a tempfile, more convenient for storage and operations TOCLIST=$(grep -e '^#' $IN | sed -e 's; ;- ;' -e 's;^#;;' -e 's;#; ;g') # headers from source as md list echo "$TOCLIST" | while IFS='' read -r data ; do # encode headers as urls HEADER=$(echo "$data" | sed 's/^[[:space:]-]*//' | encode) ; echo "$data" | sed "s/^\([[:space:]-]*\)\(.*\)$/\1${HEADER}/" >> $tmp ; done buffer=$(cat $tmp) ; echo "$buffer" | lowdown -Thtml > $tmp # convert to html, write to tempfile (reset) sed -e '1i <nav class="toc">' -e '/^$/d' -i'' $tmp # prepend starting tag, remove empty lines cat $tmp | while IFS='' read -r data ; do # get header (both encoded and raw), write as link ID=$(echo $data | sed "s;^<li>\([^</>]*\)\(</li>\)*$;\1;") ; HEADER=$(decode "$ID" | sedesc) ; sed -e "s;^<li>\(${ID}\);<li><a href=\"#\1\">${HEADER};" -e "s;\([[:alnum:]]\)</li>$;\1</a></li>;" -i'' $tmp ; # find old id in output, replace with new id IDLINE=$(grep -ne "^<h[[:digit:]] id=" $OUT | fzf -f "$(echo $HEADER | unesc)" | head -n 1 | cut -f 1 -d :) ; if [ "$IDLINE" ] ; then sed -e "${IDLINE}s;id=\".*\">.*</h;id=\"${ID}\">${HEADER}</h;" -i'' $OUT ; printf ' assembled header "%s"\n' "$(echo $HEADER | unesc)" ; # else # printf '[warning] could not match header id: "%s"\n' "$HEADER" ; # echo "---- [FATAL] could not find header id (possible escapement issue) ----" ; # rm $tmp ; # exit 1 ; fi ; 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=$(grep -n 'end of header' $OUT | cut -f 1 -d :) sed "${TOCLINE}r $tmp" -i'' $OUT rm $tmp # cleanup echo '[ok] table of contents' ) ### metadata option handling opts=$(grep -e '^opts: ' -m 1 $IN | cut -f 2- -d ' ') while [ "$opts" ]; do case "$opts" in (*[Cc]*) tocgen opts=$(echo $opts | sed 's/[Cc]//g') ;; (*[Ff]*) sed "/^<footer>/,/^<\/footer>/d" -i'' $OUT echo "[ok] footerless" opts=$(echo $opts | sed 's/[Ff]//g') ;; (*[Hh]*) sed "/^<header>/,/^<\/header>/d" -i'' $OUT sed "/bg.js/d" -i'' $OUT echo "[ok] headerless" opts=$(echo $opts | sed 's/[Hh]//g') ;; (*) echo "[warning] metadata: unrecognized option(s)" ;; esac done echo "---- build complete: ${OUT} ----"