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

%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_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#\*\([^*]*\)\*#\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 "$@"