#!/bin/sh ### Initialize # init buffers buff="$(mktemp)" lbuf="$(mktemp)" meta="$(mktemp)" cleanup() { rm "$buff" "$lbuff" "$meta" 2>/dev/null; } trap cleanup EXIT INT KILL # init state prev= # previous linetype curr= # current linetype tmpl= # template (optional) verbatimp=false # in verbatim block? metap=true # in metadata block? IPATH="$PWD" # inclusion path # (tuneables) : "${nl:=::NL::}" # newline : "${sp:=::SP::}" # space : "${te:=::END::}" # template end : "${to:=html}" # output format ### Formats ## HTML and GMI are given here. Other formats can be defined in their ## own files and they'll be sourced. ## NOTES # should we allow modifying variables from the environment ? html() { : "${fmtbuff_hd_1:=
$nl%s$nl}" : "${fmtline_quot:=%s$nl}" : "${fmtbuff_list:=
%s
$nl}" : "${fmtline_para:=%s$nl}" : "${fmtline_plnk:=%s$nl}" : "${fmtbuff_link:=%s
$nl}"
: "${fmtline_verb:=%s$nl}"
: "${fmtbuff_blank:=$nl}"
: "${fmtline_blank:=$nl}"
}
gmi() {
: "${fmtbuff_hd_1:=# %s$nl}"
: "${fmtline_hd_1:=%s}"
: "${fmtbuff_hd_2:=## %s$nl}"
: "${fmtline_hd_2:=%s}"
: "${fmtbuff_hd_3:=### %s$nl}"
: "${fmtline_hd_3:=%s}"
: "${fmtbuff_quot:=> %s$nl}"
: "${fmtline_quot:=%s$sp}"
: "${fmtbuff_list:=%s$nl}"
: "${fmtline_list:=* %s$nl}"
: "${fmtbuff_para:=%s$nl}"
: "${fmtline_para:=%s$sp}"
: "${fmtline_plnk:=$nl=> %s %s$nl}"
: "${fmtbuff_link:=%s$nl}"
: "${fmtline_link:==> %s %s}"
: "${fmtbuff_verb:=\`\`\`$nl%s\`\`\`$nl}"
: "${fmtline_verb:=%s$nl}"
: "${fmtbuff_blank:=$nl}"
: "${fmtline_blank:=$nl}"
}
### Filters
filter_buff() {
f="filter_buff_$to"
if type "$f" 2>/dev/null | grep -q function
then "$f"
else cat
fi
}
filter_line() {
f="filter_line_$to"
if type "$f" 2>/dev/null | grep -q function
then printf '%s\n' "$*" | "$f"
else printf '%s\n' "$*"
fi
}
filter_line_html() {
# s/// : escape <, >, & from html
# s### : *bold*, _italic_, `code`
# s@@@ : smart versions of things
sed \
-e 's/&/\&/g' \
-e 's/\</g' \
-e 's/>/\>/g' \
-e 's#\*\([^*]*\)\*#\1#g' \
-e 's#_\([^_]*\)_#\1#g' \
-e 's#`\([^`]*\)`#\1
#' \
-e 's@---@\—@g' \
-e 's@--@\–@g'
}
### Processing
## Utility functions
pushline() {
tag="$1"; shift
printf "$(eval echo "\$fmtline_$tag")" "$@" >> "$buff"
}
bufprint() {
b="$(filter_buff<"$buff")"
printf "$(eval echo "\$fmtbuff_$1")" "$b" |
sed -e "s/$nl/\n/g" -e "s/$sp/ /g" # fix whitespace
: > "$buff"
}
### Where the magic happens
process() {
set -f
while read -r sigil line
do
if $verbatimp && test "$sigil" != '```'
then
pushline verb "$(filter_line "$sigil $line")"
continue
fi
case "$sigil" in
(*':') # metadata
if $metap
then printf 'export %s="%s"\n' \
"${sigil%:}" "$line" >>"$meta"
fi
;;
('```') # verbatim
# CONSIDER: "types" of verbatim
# designated by extra fields after the
# sigil
## ``` class_of_content
# ^--- change the class of the content,
# eg. in html do # other formats might do other things ## ``` | some_program # ^--- pipe the buffer to some_program metap=false if $verbatimp then bufprint verb verbatimp=false prev= else bufprint "$prev" verbatimp=true fi continue ;; ('=>') # link metap=false printf '%s\n' "$line" > "$lbuf" read -r url title < "$lbuf" if test "$curr" = para then pushline plnk "$url" "$title" continue else curr=link fi ;; ('#'*) # header metap=false curr=hd_${#sigil} ;; ('>') # quote metap=false curr=quot ;; ('*') # list metap=false curr=list ;; ('') # blank line metap=false curr=blank ;; (*) # paragraph metap=false curr=para line="$sigil $line" ;; esac test "$curr" = "$prev" || bufprint "$prev" prev="$curr" if test "$curr" = verb then pushline "$curr" "$line" continue fi if test "$curr" = link then pushline "$curr" "$url" "$(filter_line "$title")" else pushline "$curr" "$(filter_line "$line")" fi done bufprint "$curr" } templatize() { eval "cat<<$te $(cat $@) $te" } ### Entry point usage() { cat <&2 jimmy: convert gmi to other formats usage: jimmy [-h] [-t FORMAT] [-I DIRECTORY] [-T FILE] [FILE...] If no FILE is given on the command line, jimmy reads standard input. options: -h show this help and exit -x enable xtrace (set -x) -t FORMAT convert gmi to FORMAT. html is default, gmi is built-in. you can also pass the name of a file that will be sourced. -I DIRECTORY add DIRECTORY to the include path for -t. the current directory is always in the include path. -T FILE use FILE as a template for the output text. EOF exit $1 } main() { while getopts hxI:t:T: OPT do case "$OPT" in (h) usage 0 ;; (x) set -x ;; (I) IPATH="$OPTARG:$IPATH" ;; (t) to="$OPTARG" ;; (T) tmpl="$OPTARG" ;; (*) usage 1 ;; esac done shift $((OPTIND - 1)) case "$to" in (html|gmi) "$to" ;; (*) found=false for p in $(echo "$IPATH"|tr : ' ') do if test -f "$p/$to" then . "$p/$to"; found=true elif test -f "$p/$to.sh" then . "$p/$to.sh"; found=true fi done if ! $found then echo >&2 "Can't find file: '$to'" echo >&2 "Looked in $IPATH" exit 2 fi ;; esac # while read requires a final newline (cat "${@:--}"; echo) | process | if test -n "$tmpl" then # use eval cat instead of source for pipe sequencing # reasons eval "$(cat "$meta")" templatize "$tmpl" else cat fi } main "$@"