diff options
Diffstat (limited to 'ht.awk')
-rwxr-xr-x | ht.awk | 385 |
1 files changed, 187 insertions, 198 deletions
diff --git a/ht.awk b/ht.awk index 60e042b..b9ae377 100755 --- a/ht.awk +++ b/ht.awk | |||
@@ -1,246 +1,235 @@ | |||
1 | #!/usr/bin/awk -f | 1 | #!/bin/awk -f |
2 | # -*- indent-tabs-mode: t; -*- | ||
3 | # HAT TRICK | 2 | # HAT TRICK |
4 | # (C) 2022 C. Duckworth | 3 | # Copyright (C) 2022 Case Duckworth <acdw@acdw.net> |
5 | 4 | # | |
6 | ### Commentary: | ||
7 | |||
8 | # OLDIFS=$IFS; IFS=$'\n'; | ||
9 | # for line in `cat testfile`; do | ||
10 | # test=`echo "$line" | grep -E '[\]$'`; | ||
11 | # if [ $test ]; then | ||
12 | # newline=`echo $line | rev | cut -c 2- | rev`; | ||
13 | # echo -n "$newline"; else echo "$line"; | ||
14 | # fi; done; | ||
15 | # IFS=$OLDIFS | ||
16 | |||
17 | ### Code: | ||
18 | BEGIN { | 5 | BEGIN { |
19 | width = 72 | 6 | # Configuration |
20 | default_htag = "p" | 7 | DEFAULT_CONFIG_MODE = "config" |
21 | default_gtag = "" | 8 | config_initialize() |
22 | default_ftag = "" | 9 | config_parse(ENVIRON["HT_CONFIG"] ? ENVIRON["HT_CONFIG"] : "ht.conf") |
23 | } | 10 | # State |
24 | 11 | DEFTAG = CONFIG["default_tag"] | |
25 | ### Raw formatting | 12 | DEFATTR = CONFIG["default_attr"] |
26 | /^>>>/ { | 13 | TAG = DEFTAG |
27 | getline first_raw | 14 | ATTR = DEFATTR |
28 | if (raw_fmt_p("html")) { | 15 | } |
29 | raw_html = 1 | 16 | |
30 | html[++hpar] = "<pre><code>" html_escape(first_raw) | 17 | # Mutliple-file awareness |
31 | } | 18 | FNR == 1 { |
32 | if (raw_fmt_p("gemini")) { | 19 | fileflush() |
33 | raw_gemini = 1 | 20 | } |
34 | gemini[++gpar] = "```" | 21 | |
35 | gemini[++gpar] = first_raw | 22 | # Handle raw sections |
36 | } | 23 | $0 ~ CONFIG["raw_delim"] { |
37 | if (raw_fmt_p("gopher")) { | 24 | RAW = ! RAW |
38 | raw_gopher = 1 | 25 | if (RAW) { |
39 | gopher[++fpar] = first_raw | 26 | buflush() |
27 | bufpush(CONFIG["raw_beg"], -1) | ||
28 | } else { | ||
29 | bufpush(CONFIG["raw_end"], -1) | ||
30 | print BUFFER | ||
31 | BUFFER = "" | ||
40 | } | 32 | } |
41 | raw = 1 | ||
42 | next | 33 | next |
43 | } | 34 | } |
44 | 35 | ||
45 | /^<<</ { | 36 | RAW { |
46 | if (raw_html) { | 37 | bufpush($0) |
47 | html[hpar] = html[hpar] "</code></pre>" | ||
48 | } | ||
49 | if (raw_gemini) { | ||
50 | gemini[++gpar] = "```" | ||
51 | gemini[++gpar] = "" | ||
52 | } | ||
53 | if (raw_gopher) { | ||
54 | gopher[++fpar] = "" | ||
55 | } | ||
56 | raw_html = 0 | ||
57 | raw_gemini = 0 | ||
58 | raw_gopher = 0 | ||
59 | raw = 0 | ||
60 | next | 38 | next |
61 | } | 39 | } |
62 | 40 | ||
63 | raw { | 41 | # Comments |
64 | if (raw_html) { | 42 | $0 ~ ("^" COMMENT_DELIM) { |
65 | html_empty = 0 | ||
66 | html[++hpar] = html_escape($0) | ||
67 | } | ||
68 | if (raw_gemini) { | ||
69 | gemini_empty = 0 | ||
70 | gemini[++gpar] = $0 | ||
71 | } | ||
72 | if (raw_gopher) { | ||
73 | gopher_empty = 0 | ||
74 | gopher[++fpar] = $0 | ||
75 | } | ||
76 | next | 43 | next |
77 | } | 44 | } |
78 | 45 | ||
79 | # Block types | 46 | # HTML escape hatch |
80 | /^#/ { | 47 | /^</ { |
81 | match($0, /#+/) | 48 | HTML = 1 |
82 | htag = "h" (RLENGTH > 6 ? 6 : RLENGTH) | 49 | bufpush($0) |
83 | gtag = substr($0, RSTART, (RLENGTH > 3 ? 3 : RLENGTH)) " " | 50 | next |
84 | ftag = substr($0, RSTART, RLENGTH) " " | ||
85 | sub(/^#+[ \t]*/, "", $0) | ||
86 | } | 51 | } |
87 | 52 | ||
88 | # Line types | 53 | # Sure, let's do templating! This makes it less... weird. |
89 | /^=>/ { | 54 | /\$/ { |
90 | title = "" | 55 | # XXX: This is probably the dumbest way to do it. |
91 | for (i = 3; i <= NF; i++) { | 56 | gsub(/\$\$/, "$\a", $0) |
92 | title = title (title ? " " : "") $i | 57 | gsub(/\$[^\a]/, "\\\\&", $0) |
93 | } | 58 | gsub(/\$\a/, "$", $0) |
94 | hbuf[++hline] = "<a href=\"" $2 "\">" title "</a>" | ||
95 | gbuf[++gline] = "\ngemini\t" $0 | ||
96 | # TODO: gopher | ||
97 | next | ||
98 | } | 59 | } |
99 | 60 | ||
100 | ### Everything else | 61 | # Blocks of text |
101 | /./ { | 62 | /./ { |
102 | html_empty = 0 | 63 | # EOL escape |
103 | gemini_empty = 0 | 64 | if (match($0, /\\$/)) { |
104 | gopher_empty = 0 | 65 | sep = -1 |
105 | hbuf[++hline] = $0 | 66 | $0 = substr($0, 1, RSTART - 1) |
106 | gbuf[++gline] = $0 | 67 | } else { |
107 | fbuf[++fline] = $0 | 68 | sep = "\n" |
69 | } | ||
70 | # Loop through BLOCK_TYPES | ||
71 | for (bt in BLOCK_TYPES) { | ||
72 | if (match($0, "^" bt "[ \t]*")) { | ||
73 | $0 = substr($0, RSTART + RLENGTH) | ||
74 | if (match(BLOCK_TYPES[bt], "[ \t]*>[ \t]*")) { | ||
75 | parent = substr(BLOCK_TYPES[bt], 1, RSTART - 1) | ||
76 | child = substr(BLOCK_TYPES[bt], RSTART + RLENGTH) | ||
77 | } | ||
78 | if (parent) { | ||
79 | split(parent, pa, FS) | ||
80 | split(child, bl, FS) | ||
81 | if (! IN_PARENT) { | ||
82 | IN_PARENT = pa[1] | ||
83 | } | ||
84 | TAG = IN_PARENT | ||
85 | ATTR = "" | ||
86 | for (i = 2; i <= length(pa); i++) { | ||
87 | ATTR = ATTR (ATTR ? " " : "") pa[i] | ||
88 | } | ||
89 | bufpush("<" child ">" $0 "</" bl[1] ">") | ||
90 | next # XXX: This is messy. | ||
91 | } else { | ||
92 | split(BLOCK_TYPES[bt], bl, FS) | ||
93 | if (IN_PARENT) { | ||
94 | bufpush("</" IN_PARENT ">") | ||
95 | IN_PARENT = "" | ||
96 | } | ||
97 | if (! BUFFER) { | ||
98 | TAG = bl[1] | ||
99 | for (b = 2; b <= length(bl); b++) { | ||
100 | ATTR = ATTR (ATTR ? " " : "") bl[b] | ||
101 | } | ||
102 | } else { | ||
103 | $0 = "<" BLOCK_TYPES[bt] ">" $0 "</" bl[1] ">" | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | # Loop through LINE_TYPES | ||
109 | for (lt in LINE_TYPES) { | ||
110 | if (match($0, "^" lt "[ \t]*")) { | ||
111 | $0 = substr($0, RSTART + RLENGTH) | ||
112 | templ = LINE_TYPES[lt] | ||
113 | while (match(templ, /\$[0-9]+/)) { | ||
114 | sub(/\$[0-9]+/, $(substr(templ, RSTART + 1, RLENGTH - 1)), templ) | ||
115 | } | ||
116 | $0 = templ | ||
117 | } | ||
118 | } | ||
119 | # Push to buffer | ||
120 | bufpush($0, sep) | ||
108 | } | 121 | } |
109 | 122 | ||
123 | # Blank lines end blocks | ||
110 | /^$/ { | 124 | /^$/ { |
111 | bufput() | 125 | if (HTML) { |
126 | html_end() | ||
127 | } | ||
128 | if (! RAW) { | ||
129 | buflush() | ||
130 | } | ||
112 | } | 131 | } |
113 | 132 | ||
133 | # Clean up | ||
114 | END { | 134 | END { |
115 | bufput() | 135 | if (HTML) { |
116 | printarr(html, "html") | 136 | html_end() |
117 | printarr(gemini, "gemini") | 137 | } else if (RAW) { |
118 | printarr(gopher, "gopher") | 138 | bufpush(CONFIG["raw_end"], -1) |
119 | } | 139 | print BUFFER |
120 | 140 | } else { | |
121 | 141 | buflush() | |
122 | function bufput() | ||
123 | { | ||
124 | hbufput() | ||
125 | gbufput() | ||
126 | fbufput() | ||
127 | } | ||
128 | |||
129 | function clear(arr) | ||
130 | { | ||
131 | for (x in arr) { | ||
132 | delete arr[x] | ||
133 | } | 142 | } |
134 | } | 143 | } |
135 | 144 | ||
136 | function fbufput() | ||
137 | { | ||
138 | if (! length(fbuf)) { | ||
139 | next | ||
140 | } | ||
141 | for (ln in fbuf) { # XXX: gopher line types | ||
142 | paragraph = paragraph (paragraph ? " " : "") fbuf[ln] | ||
143 | } | ||
144 | fill(paragraph) | ||
145 | for (ln in fp) { | ||
146 | gopher[++fpar] = ((ln == 1) ? ftag : "") fp[ln] | ||
147 | } | ||
148 | gopher[++fpar] = "" | ||
149 | paragraph = "" | ||
150 | ftag = default_ftag | ||
151 | clear(fp) | ||
152 | clear(fbuf) | ||
153 | } | ||
154 | 145 | ||
155 | function fill(paragraph) | 146 | ### Buffer-y functions |
147 | function buflush() | ||
156 | { | 148 | { |
157 | char = 0 | 149 | buftrim() |
158 | ln = 1 | 150 | if (BUFFER) { |
159 | split(paragraph, words, FS) | 151 | if (TAG) { |
160 | for (word in words) { | 152 | TAG_BEG = "<" TAG (ATTR ? " " ATTR : "") ">" |
161 | char += length(words[word]) | 153 | TAG_END = "</" TAG ">" |
162 | if (char <= width) { | ||
163 | fp[ln] = fp[ln] (fp[ln] ? " " : "") words[word] | ||
164 | } else { | ||
165 | fp[++ln] = words[word] | ||
166 | char = length(words[word]) | ||
167 | } | 154 | } |
155 | print TAG_BEG BUFFER TAG_END | ||
156 | BUFFER = "" | ||
157 | TAG = DEFTAG | ||
158 | ATTR = DEFATTR | ||
159 | IN_PARENT = "" | ||
168 | } | 160 | } |
169 | } | 161 | } |
170 | 162 | ||
171 | function gbufput() | 163 | function bufpush(text, separator) |
172 | { | 164 | { |
173 | if (! length(gbuf)) { | 165 | if (! separator) { |
174 | next | 166 | separator = "\n" |
175 | } | 167 | } |
176 | for (ln in gbuf) { | 168 | if (separator == -1) { |
177 | paragraph = paragraph (paragraph ? " " : "") gbuf[ln] | 169 | separator = "" |
178 | } | 170 | } |
179 | gemini[++gpar] = gtag paragraph | 171 | BUFFER = BUFFER text (separator ? separator : "") |
180 | gemini[++gpar] = "" | ||
181 | gtag = default_gtag | ||
182 | paragraph = "" | ||
183 | clear(gbuf) | ||
184 | } | 172 | } |
185 | 173 | ||
186 | function gopher_line(type, display, selector, hostname, port) | 174 | function buftrim() |
187 | { | 175 | { |
188 | return (type display "\t" selector "\t" hostname "\t" port) | 176 | if (match(BUFFER, "\n+$")) { |
189 | } | 177 | BUFFER = substr(BUFFER, 1, RSTART - 1) |
190 | |||
191 | function hbufput() | ||
192 | { | ||
193 | if (! length(hbuf)) { | ||
194 | next | ||
195 | } | ||
196 | for (ln in hbuf) { | ||
197 | paragraph = paragraph (paragraph ? " " : "") hbuf[ln] | ||
198 | } | ||
199 | fill(paragraph) | ||
200 | for (ln in fp) { | ||
201 | html[++hpar] = ((ln == 1) ? "<" (htag ? htag : default_htag) ">" : "") fp[ln] | ||
202 | } | 178 | } |
203 | html[hpar] = html[hpar] (htag_end ? htag_end : "</" (htag ? htag : default_htag) ">") | ||
204 | paragraph = "" | ||
205 | htag = default_htag | ||
206 | clear(fp) | ||
207 | clear(hbuf) | ||
208 | } | 179 | } |
209 | 180 | ||
210 | function html_escape(text) | 181 | ### Config functions |
182 | function config_initialize() | ||
211 | { | 183 | { |
212 | gsub(/&/, "\\&", text) | 184 | COMMENT_DELIM = ";" |
213 | gsub(/</, "\\<", text) | 185 | CONFIG["raw_delim"] = "```" |
214 | gsub(/>/, "\\>", text) | 186 | CONFIG["raw_beg"] = "<pre><code>" |
215 | return text | 187 | CONFIG["raw_end"] = "</code></pre>" |
216 | } | 188 | CONFIG["default_tag"] = "p" |
217 | 189 | CONFIG["default_attr"] = "" | |
218 | function printarr(arr, prefix) | 190 | LINE_TYPES["@"] = "<a href=\"$1\">$2</a>" |
191 | LINE_TYPES["`"] = "<code>$0</code>" | ||
192 | BLOCK_TYPES["#"] = "h1" | ||
193 | BLOCK_TYPES["##"] = "h2" | ||
194 | BLOCK_TYPES["###"] = "h3" | ||
195 | BLOCK_TYPES["-"] = "ul>li" | ||
196 | } | ||
197 | |||
198 | function config_parse(file) | ||
219 | { | 199 | { |
220 | if (prefix) { | 200 | mode = DEFAULT_CONFIG_MODE |
221 | fmt = "%s\t%s\n" | 201 | while ((getline < file) > 0) { |
222 | } else { | 202 | if (match($0, /^#/) || ! $0) { |
223 | fmt = "%s%s\n" | 203 | continue |
224 | } | 204 | } |
225 | for (x in arr) { | 205 | if (match($0, /^\\/)) { |
226 | printf fmt, prefix, arr[x] | 206 | $0 = substr($0, 2) |
207 | } | ||
208 | if (match($0, /\[[^\]]+\]/)) { | ||
209 | mode = substr($0, RSTART + 1, RLENGTH - 2) | ||
210 | continue | ||
211 | } else { | ||
212 | var = $1 | ||
213 | val = "" | ||
214 | for (i = 2; i <= NF; i++) { | ||
215 | val = val (val ? " " : "") $i | ||
216 | } | ||
217 | if (mode == "config") { | ||
218 | CONFIG[var] = val | ||
219 | } else if (mode == "block") { | ||
220 | BLOCK_TYPES[var] = val | ||
221 | } else if (mode == "line") { | ||
222 | LINE_TYPES[var] = val | ||
223 | } | ||
224 | } | ||
227 | } | 225 | } |
228 | } | 226 | } |
229 | 227 | ||
230 | function raw_fmt_p(format) | 228 | ### Other functions |
229 | function html_end() | ||
231 | { | 230 | { |
232 | if (NF < 2) { | 231 | buftrim() |
233 | return 1 | 232 | print BUFFER |
234 | } | 233 | BUFFER = "" |
235 | if ($2 ~ /-/) { | 234 | HTML = 0 |
236 | if ($2 ~ ("-" format)) { | ||
237 | return 0 | ||
238 | } else { | ||
239 | return 1 | ||
240 | } | ||
241 | } | ||
242 | if ($2 ~ format) { | ||
243 | return 1 | ||
244 | } | ||
245 | return 0 | ||
246 | } | 235 | } |