about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--README.gmi4
-rwxr-xr-xjimmy240
-rw-r--r--test.gmi6
3 files changed, 160 insertions, 90 deletions
diff --git a/README.gmi b/README.gmi new file mode 100644 index 0000000..3abdd2a --- /dev/null +++ b/README.gmi
@@ -0,0 +1,4 @@
1# jimmy --- convert .gmi to .html and other formats
2
3[to be documented]
4
diff --git a/jimmy b/jimmy index ed6c2b7..f5dfa0f 100755 --- a/jimmy +++ b/jimmy
@@ -4,16 +4,22 @@
4# init buffers 4# init buffers
5buff="$(mktemp)" 5buff="$(mktemp)"
6lbuf="$(mktemp)" 6lbuf="$(mktemp)"
7trap 'rm "$buff" "$lbuf"' EXIT INT KILL 7meta="$(mktemp)"
8cleanup() { rm "$buff" "$lbuff" "$meta" 2>/dev/null; }
9trap cleanup EXIT INT KILL
8 10
9# init state 11# init state
10prev= 12prev= # previous linetype
11curr= 13curr= # current linetype
12verbatim=false 14tmpl= # template (optional)
15verbatimp=false # in verbatim block?
16metap=true # in metadata block?
17IPATH="$PWD" # inclusion path
13# (tuneables) 18# (tuneables)
14nl='::NL::' 19: "${nl:=::NL::}" # newline
15sp='::SP::' 20: "${sp:=::SP::}" # space
16to=html 21: "${te:=::END::}" # template end
22: "${to:=html}" # output format
17 23
18### Formats 24### Formats
19## HTML and GMI are given here. Other formats can be defined in their 25## HTML and GMI are given here. Other formats can be defined in their
@@ -23,64 +29,62 @@ to=html
23# should we allow modifying variables from the environment ? 29# should we allow modifying variables from the environment ?
24 30
25html() { 31html() {
26 fmtbuff_hd_1="<h1>%s</h1>$nl" 32 : "${fmtbuff_hd_1:=<h1>%s</h1>$nl}"
27 fmtline_hd_1="%s" 33 : "${fmtline_hd_1:=%s}"
28 fmtbuff_hd_2="<h2>%s</h2>$nl" 34 : "${fmtbuff_hd_2:=<h2>%s</h2>$nl}"
29 fmtline_hd_2="%s" 35 : "${fmtline_hd_2:=%s}"
30 fmtbuff_hd_3="<h3>%s</h3>$nl" 36 : "${fmtbuff_hd_3:=<h3>%s</h3>$nl}"
31 fmtline_hd_3="%s" 37 : "${fmtline_hd_3:=%s}"
32 fmtbuff_quot="<blockquote>$nl%s</blockquote>$nl" 38 : "${fmtbuff_quot:=<blockquote>$nl%s</blockquote>$nl}"
33 fmtline_quot="%s$nl" 39 : "${fmtline_quot:=%s$nl}"
34 fmtbuff_list="<ul>$nl%s</ul>$nl" 40 : "${fmtbuff_list:=<ul>$nl%s</ul>$nl}"
35 fmtline_list="<li>%s</li>$nl" 41 : "${fmtline_list:=<li>%s</li>$nl}"
36 fmtbuff_para="<p>%s</p>$nl" 42 : "${fmtbuff_para:=<p>%s</p>$nl}"
37 fmtline_para="%s$nl" 43 : "${fmtline_para:=%s$nl}"
38 fmtline_plnk="<a href=\"%s\">%s</a>$nl" 44 : "${fmtline_plnk:=<a href=\"%s\">%s</a>$nl}"
39 fmtbuff_link="<ul class=\"links\">$nl%s</ul>$nl" 45 : "${fmtbuff_link:=<ul class=\"links\">$nl%s</ul>$nl}"
40 fmtline_link="<li><a href=\"%s\">%s</li>$nl" 46 : "${fmtline_link:=<li><a href=\"%s\">%s</li>$nl}"
41 fmtbuff_verb="<pre><code>%s</code></pre>$nl" 47 : "${fmtbuff_verb:=<pre><code>%s</code></pre>$nl}"
42 fmtline_verb="%s$nl" 48 : "${fmtline_verb:=%s$nl}"
43 fmtbuff_blank="$nl" 49 : "${fmtbuff_blank:=$nl}"
44 fmtline_blank="$nl" 50 : "${fmtline_blank:=$nl}"
45} 51}
46 52
47gmi() { 53gmi() {
48 fmtbuff_hd_1="# %s$nl" 54 : "${fmtbuff_hd_1:=# %s$nl}"
49 fmtline_hd_1="%s" 55 : "${fmtline_hd_1:=%s}"
50 fmtbuff_hd_2="## %s$nl" 56 : "${fmtbuff_hd_2:=## %s$nl}"
51 fmtline_hd_2="%s" 57 : "${fmtline_hd_2:=%s}"
52 fmtbuff_hd_3="### %s$nl" 58 : "${fmtbuff_hd_3:=### %s$nl}"
53 fmtline_hd_3="%s" 59 : "${fmtline_hd_3:=%s}"
54 fmtbuff_quot="> %s$nl" 60 : "${fmtbuff_quot:=> %s$nl}"
55 fmtline_quot="%s$sp" 61 : "${fmtline_quot:=%s$sp}"
56 fmtbuff_list="%s$nl" 62 : "${fmtbuff_list:=%s$nl}"
57 fmtline_list="* %s$nl" 63 : "${fmtline_list:=* %s$nl}"
58 fmtbuff_para="%s$nl" 64 : "${fmtbuff_para:=%s$nl}"
59 fmtline_para="%s$sp" 65 : "${fmtline_para:=%s$sp}"
60 fmtline_plnk="$nl=> %s %s$nl" 66 : "${fmtline_plnk:=$nl=> %s %s$nl}"
61 fmtbuff_link="%s$nl" 67 : "${fmtbuff_link:=%s$nl}"
62 fmtline_link="=> %s %s" 68 : "${fmtline_link:==> %s %s}"
63 fmtbuff_verb="\`\`\`$nl%s\`\`\`$nl" 69 : "${fmtbuff_verb:=\`\`\`$nl%s\`\`\`$nl}"
64 fmtline_verb="%s$nl" 70 : "${fmtline_verb:=%s$nl}"
65 fmtbuff_blank="$nl" 71 : "${fmtbuff_blank:=$nl}"
66 fmtline_blank="$nl" 72 : "${fmtline_blank:=$nl}"
67} 73}
68 74
69### Filters 75### Filters
70 76
71filter_buff_html(){ cat; }
72
73filter_buff() { 77filter_buff() {
74 f="filter_buff_$to" 78 f="filter_buff_$to"
75 if type "$f" | grep -q function 79 if type "$f" 2>/dev/null | grep -q function
76 then "$f" 80 then "$f"
77 else cat 81 else cat
78 fi | sed -e "s/$nl/\n/g" -e "s/$sp/ /g" # fix whitespace 82 fi
79} 83}
80 84
81filter_line() { 85filter_line() {
82 f="filter_line_$to" 86 f="filter_line_$to"
83 if type "$f" | grep -q function 87 if type "$f" 2>/dev/null | grep -q function
84 then printf '%s\n' "$*" | "$f" 88 then printf '%s\n' "$*" | "$f"
85 else printf '%s\n' "$*" 89 else printf '%s\n' "$*"
86 fi 90 fi
@@ -89,12 +93,16 @@ filter_line() {
89filter_line_html() { 93filter_line_html() {
90 # s/// : escape <, >, & from html 94 # s/// : escape <, >, & from html
91 # s### : *bold*, _italic_, `code` 95 # s### : *bold*, _italic_, `code`
92 sed -e 's/&/\&amp;/g' \ 96 # s@@@ : smart versions of things
93 -e 's/</\&lt;/g' \ 97 sed \
94 -e 's/>/\&gt;/g' \ 98 -e 's/&/\&amp;/g' \
95 -e 's#\*\([^*]*\)\*#<b>\1</b>#g' \ 99 -e 's/</\&lt;/g' \
96 -e 's#_\([^_]*\)_#<i>\1</i>#g' \ 100 -e 's/>/\&gt;/g' \
97 -e 's#`\([^`]*\)`#<code>\1</code>#' 101 -e 's#\*\([^*]*\)\*#<b>\1</b>#g' \
102 -e 's#_\([^_]*\)_#<i>\1</i>#g' \
103 -e 's#`\([^`]*\)`#<code>\1</code>#' \
104 -e 's@---@\&mdash;@g' \
105 -e 's@--@\&ndash;@g'
98 106
99} 107}
100 108
@@ -108,46 +116,55 @@ pushline() {
108} 116}
109 117
110bufprint() { 118bufprint() {
111 b="$(cat<"$buff")" 119 b="$(filter_buff<"$buff")"
112 printf "$(eval echo "\$fmtbuff_$1")" "$b" | filter_buff 120 printf "$(eval echo "\$fmtbuff_$1")" "$b" |
121 sed -e "s/$nl/\n/g" -e "s/$sp/ /g" # fix whitespace
113 : > "$buff" 122 : > "$buff"
114} 123}
115 124
116## Where the magic happens 125### Where the magic happens
117process() { 126process() {
118 set -f 127 set -f
119 while read -r sigil line 128 while read -r sigil line
120 do 129 do
121 if $verbatim && test "$sigil" != '```' 130 if $verbatimp && test "$sigil" != '```'
122 then 131 then
123 pushline verb "$(filter_line "$sigil $line")" 132 pushline verb "$(filter_line "$sigil $line")"
124 continue 133 continue
125 fi 134 fi
126 135
127 case "$sigil" in 136 case "$sigil" in
128 ('```') 137 (*':') # metadata
129 if $verbatim 138 if $metap
139 then printf 'export %s="%s"\n' \
140 "${sigil%:}" "$line" >>"$meta"
141 fi
142 ;;
143 ('```') # verbatim
144 # CONSIDER: "types" of verbatim
145 # designated by extra fields after the
146 # sigil
147 ## ``` class_of_content
148 # ^--- change the class of the content,
149 # eg. in html do <pre class="type">
150 # other formats might do other things
151 ## ``` | some_program
152 # ^--- pipe the buffer to some_program
153 metap=false
154 if $verbatimp
130 then 155 then
131 bufprint verb 156 bufprint verb
132 verbatim=false 157 verbatimp=false
133 prev= 158 prev=
134 else 159 else
135 # CONSIDER: "types" of verbatim
136 # designated by extra fields after the
137 # sigil
138 ## ``` class_of_content
139 # ^- change the class of the content,
140 # eg. in html do <pre class="type">
141 # other formats might do other things
142 ## ``` | some_program
143 # ^- pipe the buffer to some_program
144 ## others?
145 bufprint "$prev" 160 bufprint "$prev"
146 verbatim=true 161 verbatimp=true
147 fi 162 fi
148 continue 163 continue
149 ;; 164 ;;
150 ('=>') 165 ('=>') # link
166 metap=false
167
151 printf '%s\n' "$line" > "$lbuf" 168 printf '%s\n' "$line" > "$lbuf"
152 read -r url title < "$lbuf" 169 read -r url title < "$lbuf"
153 if test "$curr" = para 170 if test "$curr" = para
@@ -157,11 +174,20 @@ process() {
157 else curr=link 174 else curr=link
158 fi 175 fi
159 ;; 176 ;;
160 ('#'*) curr=hd_${#sigil} ;; 177 ('#'*) # header
161 ('>') curr=quot ;; 178 metap=false
162 ('*') curr=list ;; 179 curr=hd_${#sigil} ;;
163 ('') curr=blank ;; 180 ('>') # quote
164 (*) 181 metap=false
182 curr=quot ;;
183 ('*') # list
184 metap=false
185 curr=list ;;
186 ('') # blank line
187 metap=false
188 curr=blank ;;
189 (*) # paragraph
190 metap=false
165 curr=para 191 curr=para
166 line="$sigil $line" 192 line="$sigil $line"
167 ;; 193 ;;
@@ -185,28 +211,43 @@ process() {
185 bufprint "$curr" 211 bufprint "$curr"
186} 212}
187 213
214templatize() {
215 eval "cat<<$te
216$(cat $@)
217$te"
218}
219
188### Entry point 220### Entry point
189 221
190usage() { 222usage() {
191 cat <<EOF >&2 223 cat <<EOF >&2
192jimmy: convert gmi to other formats 224jimmy: convert gmi to other formats
193usage: jimmy [-h] [-t FORMAT] [FILE...] 225usage: jimmy [-h] [-t FORMAT] [-I DIRECTORY] [-T FILE] [FILE...]
194If no FILE is given on the command line, jimmy reads standard input. 226If no FILE is given on the command line, jimmy reads standard input.
195options: 227options:
196 -h show this help and exit 228 -h show this help and exit
229 -x enable xtrace (set -x)
197 -t FORMAT 230 -t FORMAT
198 convert gmi to FORMAT. html is default, gmi is built-in. 231 convert gmi to FORMAT. html is default, gmi is built-in.
199 you can also pass the name of a file that will be sourced. 232 you can also pass the name of a file that will be sourced.
233 -I DIRECTORY
234 add DIRECTORY to the include path for -t. the current
235 directory is always in the include path.
236 -T FILE
237 use FILE as a template for the output text.
200EOF 238EOF
239 exit $1
201} 240}
202 241
203main() { 242main() {
204 while getopts ht:x OPT 243 while getopts hxI:t:T: OPT
205 do 244 do
206 case "$OPT" in 245 case "$OPT" in
207 (h) usage 0 ;; 246 (h) usage 0 ;;
208 (t) to="$OPTARG" ;;
209 (x) set -x ;; 247 (x) set -x ;;
248 (I) IPATH="$OPTARG:$IPATH" ;;
249 (t) to="$OPTARG" ;;
250 (T) tmpl="$OPTARG" ;;
210 (*) usage 1 ;; 251 (*) usage 1 ;;
211 esac 252 esac
212 done 253 done
@@ -214,15 +255,36 @@ main() {
214 255
215 case "$to" in 256 case "$to" in
216 (html|gmi) "$to" ;; 257 (html|gmi) "$to" ;;
217 (*) . "$to" || { 258 (*)
218 echo >&2 "Can't find file: '$to'" 259 found=false
219 exit 2 260 for p in $(echo "$IPATH"|tr : ' ')
220 } 261 do
221 ;; 262 if test -f "$p/$to"
263 then . "$p/$to"; found=true
264 elif test -f "$p/$to.sh"
265 then . "$p/$to.sh"; found=true
266 fi
267 done
268 if ! $found
269 then
270 echo >&2 "Can't find file: '$to'"
271 echo >&2 "Looked in $IPATH"
272 exit 2
273 fi
274 ;;
222 esac 275 esac
223 276
224 # while read requires a final newline 277 # while read requires a final newline
225 (cat "${@:--}"; echo) | process 278 (cat "${@:--}"; echo) |
279 process |
280 if test -n "$tmpl"
281 then
282 # use eval cat instead of source for pipe sequencing
283 # reasons
284 eval "$(cat "$meta")"
285 templatize "$tmpl"
286 else cat
287 fi
226} 288}
227 289
228main "$@" 290main "$@"
diff --git a/test.gmi b/test.gmi index f2dc0dc..62bed2e 100644 --- a/test.gmi +++ b/test.gmi
@@ -1,3 +1,7 @@
1title: a test document
2date: 2024-05-13T03:02:45Z
3uuid: b3daebf1-440b-4828-a4d9-9089c7bd7c61
4
1# a test document of some kind 5# a test document of some kind
2 6
3here is a test document. 7here is a test document.
@@ -30,4 +34,4 @@ as well as `code`, _emph_ and such?
30what if *i _nest_ them* 34what if *i _nest_ them*
31what if *i _nest them* wrong_ ? 35what if *i _nest them* wrong_ ?
32what about *breaking them 36what about *breaking them
33over two lines?* \ No newline at end of file 37over two lines?*