#!/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:=

%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:=
$nl%s
$nl}" : "${fmtline_quot:=%s$nl}" : "${fmtbuff_list:=$nl}" : "${fmtline_list:=
  • %s
  • $nl}" : "${fmtbuff_para:=

    %s

    $nl}" : "${fmtline_para:=%s$nl}" : "${fmtline_plnk:=%s$nl}" : "${fmtbuff_link:=$nl}" : "${fmtline_link:=
  • %s
  • $nl}" : "${fmtbuff_verb:=
    %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#\*\([^*]*\)\*#\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 "$@"