main mite / bin / helper
  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(/&lt;/, "<", s)
 35			gsub(/&gt;/, ">", s)
 36			gsub(/&quot;/, "\"", s)
 37			gsub(/&amp;/, "&", 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:]]*&lt;.*&gt;[[: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\">&rsaquo; %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