#!/bin/sh ### Initialize # init buffers buff="$(mktemp)" lbuf="$(mktemp)" trap 'rm "$buff" "$lbuf"' EXIT INT KILL # init state prev= curr= verbatim=false # (tuneables) nl='::NL::' sp='::SP::' to=html ### 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_html(){ cat; }
filter_buff() {
f="filter_buff_$to"
if type "$f" | grep -q function
then "$f"
else cat
fi | sed -e "s/$nl/\n/g" -e "s/$sp/ /g" # fix whitespace
}
filter_line() {
f="filter_line_$to"
if type "$f" | grep -q function
then printf '%s\n' "$*" | "$f"
else printf '%s\n' "$*"
fi
}
filter_line_html() {
# s/// : escape <, >, & from html
# s### : *bold*, _italic_, `code`
sed -e 's/&/\&/g' \
-e 's/\</g' \
-e 's/>/\>/g' \
-e 's#\*\([^*]*\)\*#\1#g' \
-e 's#_\([^_]*\)_#\1#g' \
-e 's#`\([^`]*\)`#\1
#'
}
### Processing
## Utility functions
pushline() {
tag="$1"; shift
printf "$(eval echo "\$fmtline_$tag")" "$@" >> "$buff"
}
bufprint() {
b="$(cat<"$buff")"
printf "$(eval echo "\$fmtbuff_$1")" "$b" | filter_buff
: > "$buff"
}
## Where the magic happens
process() {
set -f
while read -r sigil line
do
if $verbatim && test "$sigil" != '```'
then
pushline verb "$(filter_line "$sigil $line")"
continue
fi
case "$sigil" in
('```')
if $verbatim
then
bufprint verb
verbatim=false
prev=
else
# 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 ## others? bufprint "$prev" verbatim=true fi continue ;; ('=>') printf '%s\n' "$line" > "$lbuf" read -r url title < "$lbuf" if test "$curr" = para then pushline plnk "$url" "$title" continue else curr=link fi ;; ('#'*) curr=hd_${#sigil} ;; ('>') curr=quot ;; ('*') curr=list ;; ('') curr=blank ;; (*) 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" } ### Entry point usage() { cat <&2 jimmy: convert gmi to other formats usage: jimmy [-h] [-t FORMAT] [FILE...] If no FILE is given on the command line, jimmy reads standard input. options: -h show this help and exit -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. EOF } main() { while getopts ht:x OPT do case "$OPT" in (h) usage 0 ;; (t) to="$OPTARG" ;; (x) set -x ;; (*) usage 1 ;; esac done shift $((OPTIND - 1)) case "$to" in (html|gmi) "$to" ;; (*) . "$to" || { echo >&2 "Can't find file: '$to'" exit 2 } ;; esac # while read requires a final newline (cat "${@:--}"; echo) | process } main "$@"