1#!/bin/sh
2set -eu
3#helper script for mite.
4cmd="${1:-}"
5shift || true
6
7case "$cmd" in
8raw)
9 src_dir="$1"
10 rel="$2"
11 out="$3"
12 src=""
13 for sec in 1 2 3 3p 4 5 6 7 8 9 mdoc; do
14 cand="$src_dir/$rel.$sec"
15 if [ -f "$cand" ]; then
16 src="$cand"
17 break
18 fi
19 done
20 if [ -z "$src" ]; then
21 echo "helper raw: no source for $rel under $src_dir" >&2
22 exit 1
23 fi
24 mandoc -T html -O style=mandoc.css "$src" > "$out"
25 ;;
26body)
27 in="$1"
28 out="$2"
29 # extract and normalize only the rendered body from mandocs output
30 # this strips mandocs header and footer tables because we do our own
31 awk '
32 function lower(s) { return tolower(s) }
33 function decode_html_entities(s) {
34 gsub(/</, "<", s)
35 gsub(/>/, ">", s)
36 gsub(/"/, "\"", s)
37 gsub(/&/, "&", s)
38 return s
39 }
40 function resetblock() {
41 in_literal_div = 0
42 in_pre = 0
43 lit_n = 0
44 pre_n = 0
45 }
46 function push_litline(s) { lit[++lit_n] = s }
47 function push_preline(s) { pre[++pre_n] = s }
48 function flushblock( i, line, non_empty, all_htmlish) {
49 # if it looks like an html and quacks like an html...
50 non_empty = 0
51 all_htmlish = 1
52 for (i = 1; i <= pre_n; i++) {
53 line = pre[i]
54 if (line ~ /^[[:space:]]*$/) continue
55 non_empty++
56 if (line !~ /^[[:space:]]*<.*>[[:space:]]*$/) all_htmlish = 0
57 }
58 if (non_empty > 0 && all_htmlish) {
59 for (i = 1; i <= pre_n; i++) print decode_html_entities(pre[i])
60 } else {
61 for (i = 1; i <= lit_n; i++) print lit[i]
62 }
63 resetblock()
64 }
65 BEGIN {
66 in_body = 0
67 skip_table = 0
68 resetblock()
69 }
70 {
71 line = $0
72 lc = lower(line)
73
74 if (!in_body) {
75 if (lc ~ /<body[^>]*>/) {
76 in_body = 1
77 sub(/^.*<body[^>]*>/, "", line)
78 if (length(line)) $0 = line
79 else next
80 } else next
81 }
82
83 if (in_body && lc ~ /<\/body>/) {
84 sub(/<\/body>.*/, "", line)
85 if (in_literal_div) {
86 if (length(line)) push_litline(line)
87 flushblock()
88 } else if (length(line)) print line
89 in_body = 0
90 next
91 }
92
93 if (in_literal_div) {
94 push_litline(line)
95 if (!in_pre && lc ~ /<pre>/) {
96 in_pre = 1
97 line_after_pre = line
98 sub(/^.*<pre>/, "", line_after_pre)
99 if (length(line_after_pre)) push_preline(line_after_pre)
100 next
101 }
102 if (in_pre && lc ~ /<\/pre>/) {
103 line_before_pre_end = line
104 sub(/<\/pre>.*/, "", line_before_pre_end)
105 if (length(line_before_pre_end)) push_preline(line_before_pre_end)
106 in_pre = 0
107 } else if (in_pre) push_preline(line)
108 if (lc ~ /<\/div>/) flushblock()
109 next
110 }
111
112 if (lc ~ /<table class="head">/ || lc ~ /<table class="foot">/) {
113 skip_table = 1
114 next
115 }
116 if (skip_table) {
117 if (lc ~ /<\/table>/) skip_table = 0
118 next
119 }
120
121 if (lc ~ /<div class="bd[^"]*li[^"]*">/) {
122 in_literal_div = 1
123 push_litline(line)
124 if (lc ~ /<pre>/) in_pre = 1
125 next
126 }
127
128 print line
129 }
130 END {
131 if (in_literal_div) flushblock()
132 }
133 ' "$in" > "$out"
134 ;;
135sidebar)
136 out="$1"
137 src_dir="$2"
138 {
139 echo '<ul class="side-nav side-tree">'
140 # parse the page metadata from mk/*.mk and emit rows
141 # path|url|title-label
142 awk '
143 function flush() {
144 if (page != "" && title != "" && url != "") {
145 if (page == "index") return
146 u = url
147 gsub(/\$\(PAGE\)/, page, u)
148 printf "%s|%s|%s\n", page, u, title
149 }
150 }
151 FNR==1 {
152 if (NR > 1) flush()
153 page=""; title=""; url=""
154 }
155 /^PAGE := / { page=substr($0, 9) }
156 /^TITLE\.\$\(PAGE\) = / { title=substr($0, 17) }
157 /^URL\.\$\(PAGE\) := / { url=substr($0, 16) }
158 END { flush() }
159 ' "$src_dir"/mk/*.mk \
160 | sort \
161 | awk -F'|' '
162 # build the dir tree
163 function close_to(n, i) {
164 for (i = level; i > n; i--) {
165 print "</ul></details></li>"
166 }
167 level = n
168 }
169 BEGIN { level = 0 }
170 {
171 path=$1; url=$2; title=$3
172 n = split(path, p, "/")
173 dirs = n - 1
174 common = 0
175 for (i = 1; i <= dirs && i <= level; i++) {
176 if (p[i] == dstack[i]) common = i
177 else break
178 }
179 close_to(common)
180 for (i = common + 1; i <= dirs; i++) {
181 dstack[i] = p[i]
182 dirpath = p[1]
183 for (j = 2; j <= i; j++) dirpath = dirpath "/" p[j]
184 printf "<li class=\"dir\"><details data-dir=\"%s\"><summary>%s/</summary><ul>\n", dirpath, p[i]
185 }
186 level = dirs
187 printf "<li class=\"page\"><a href=\"%s\">› %s</a></li>\n", url, title
188 }
189 END { close_to(0) }
190 '
191 echo '</ul>'
192 } > "$out"
193 ;;
194page)
195 out="$1"
196 rel="$2"
197 title="$3"
198 os="$4"
199 url="$5"
200 label="$6"
201 out_dir="$7"
202 site_title="$8"
203 site_subtitle="${9:-}"
204 home_url="${10:-}"
205 src_dir="${11:-}"
206 [ -n "$home_url" ] || home_url="/index.html"
207 raw="$out_dir/$rel.raw.html"
208 site_id="$(basename "$out_dir")"
209 style_href="/pub/style.css?site=$site_id"
210 mandoc_href="/mandoc.css?site=$site_id"
211 # get label from source filename extension, example.1 -> example(1)
212 label="$title"
213 if [ -n "$src_dir" ]; then
214 src=""
215 for sec in 1 2 3 3p 4 5 6 7 8 9 mdoc; do
216 cand="$src_dir/$rel.$sec"
217 if [ -f "$cand" ]; then
218 src="$cand"
219 break
220 fi
221 done
222 if [ -n "$src" ]; then
223 base="$(basename "$src")"
224 name="${base%.*}"
225 sec="${base##*.}"
226 label="${name}(${sec})"
227 fi
228 fi
229 date_dd="$(awk 'match($0, /<td class="foot-date">[^<]*<\/td>/){v=substr($0, RSTART, RLENGTH); gsub(/<[^>]*>/, "", v); print v; exit}' "$raw")"
230 if [ -z "$date_dd" ]; then
231 # do current date if none
232 date_dd="$(LC_ALL=C date -u '+%B %e, %Y' | awk '{$1=$1; print}')"
233 fi
234
235 {
236 if [ -f "$src_dir/_mite/header.html" ]; then
237 awk -v t="$title" -v s="$style_href" -v m="$mandoc_href" '{
238 gsub(/@TITLE@/, t)
239 gsub(/href="\/pub\/style\.css"/, "href=\"" s "\"")
240 gsub(/href="\/mandoc\.css"/, "href=\"" m "\"")
241 print
242 }' "$src_dir/_mite/header.html"
243 else
244 printf '<!doctype html>\n<html>\n<head>\n<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1">\n<title>%s</title>\n<link rel="stylesheet" href="%s">\n<link rel="stylesheet" href="%s">\n</head>\n<body>\n' "$title" "$mandoc_href" "$style_href"
245 fi
246
247 echo '<div class="layout">'
248 echo '<aside class="sidebar">'
249 printf '<div class="sidebar-title"><a href="%s">%s</a></div>\n' "$home_url" "$site_title"
250 if [ -n "$site_subtitle" ]; then
251 printf '<div class="sidebar-subtitle">%s</div>\n' "$site_subtitle"
252 fi
253 # for some current page link open only the ancestor <details>
254 # before i had some js to do this and it had one or two extra things it could do,
255 # like opening multiple dirs at once and keeping the open ones open, but this
256 # is probably better, because js bad or smth. (thanks sewn)
257 awk -v cur="$url" '
258 {
259 line = $0
260 if (index(line, "href=\"" cur "\"") > 0) sub(/<a /, "<a class=\"thisPage\" ", line)
261 lines[++n] = line
262 if (line ~ /<details[^>]*data-dir=/) stack[++sp] = n
263 if (line ~ /class="thisPage"/) {
264 for (i = 1; i <= sp; i++) keep_open[stack[i]] = 1
265 }
266 if (line ~ /<\/ul><\/details><\/li>/ && sp > 0) sp--
267 }
268 END {
269 for (i = 1; i <= n; i++) {
270 line = lines[i]
271 if (keep_open[i] && line ~ /<details/ && line !~ /<details[^>]* open([[:space:]]|>)/) {
272 sub(/<details/, "<details open", line)
273 }
274 print line
275 }
276 }
277 ' "$out_dir/sidebar.html"
278 echo '<div class="sidebar-powered"><a href="https://git.sr.ht/~shrub900/mite">powered by mite</a></div>'
279 echo '</aside>'
280 echo '<main class="content">'
281 printf '<div class="head" role="doc-pageheader" aria-label="Manual header line"><span class="head-ltitle">%s</span> <span class="head-vol">%s</span> <span class="head-rtitle">%s</span></div>\n' "$label" "$title" "$label"
282 cat "$out_dir/$rel.body.html"
283 printf '<div class="foot" role="doc-pagefooter" aria-label="Manual footer line"><span class="foot-left"></span><span class="foot-date">%s</span> <span class="foot-os foot-right">%s</span></div>\n' "$date_dd" "$os"
284 echo '</main>'
285 echo '</div>'
286
287 if [ -f "$src_dir/_mite/footer.html" ]; then
288 cat "$src_dir/_mite/footer.html"
289 else
290 printf '</body>\n</html>\n'
291 fi
292 } > "$out"
293 ;;
294serve)
295 out_dir="$1"
296 port="${2:-8000}"
297 cd "$out_dir"
298 echo "serve: http://127.0.0.1:$port/"
299 #you need python, duh. this is really just for quick local preview
300 python3 -m http.server "$port"
301 ;;
302*)
303 echo "usage: $0 {raw|body|sidebar|page|serve} ..." >&2
304 exit 1
305 ;;
306esac