summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2022-08-07 00:49:11 -0500
committerCase Duckworth2022-08-07 00:49:11 -0500
commite94aa713d4ce61666b521b174b9981b957b48ec7 (patch)
treea428cfe4d3754da96c54bec5eb9419b7f9dbf40d
parentIgnore meta.sh (diff)
downloadht-e94aa713d4ce61666b521b174b9981b957b48ec7.tar.gz
ht-e94aa713d4ce61666b521b174b9981b957b48ec7.zip
"Real" initial commit
-rw-r--r--feed.tmpl.xml12
-rw-r--r--footer.htm1
-rw-r--r--header.htm2
-rwxr-xr-xht287
-rwxr-xr-xht.sh60
-rw-r--r--index.tmpl.htm12
-rw-r--r--page.tmpl.htm10
-rw-r--r--test.ht32
8 files changed, 321 insertions, 95 deletions
diff --git a/feed.tmpl.xml b/feed.tmpl.xml new file mode 100644 index 0000000..13182b3 --- /dev/null +++ b/feed.tmpl.xml
@@ -0,0 +1,12 @@
1<?xml version="1.0" encoding="utf-8"?>
2<feed xmlns="http://www.w3.org/2005/Atom">
3 <title>acdw</title>
4 <subtitle>a home on the web</subtitle>
5 <link href="$BASEURL/$FEEDNAME" rel="self" />
6 <link href="$BASEURL" />
7 <id>$BASEURL</id>
8 <generator uri="https://git.acdw.net/ht" version="infinite">ht</generator>
9 <rights>https://acdw.casa/gcl/</rights>
10 <updated>$(date -R)</updated>
11 $(cat)
12</feed>
diff --git a/footer.htm b/footer.htm deleted file mode 100644 index 0ceac0f..0000000 --- a/footer.htm +++ /dev/null
@@ -1 +0,0 @@
1<!-- so ends ${file} -->
diff --git a/header.htm b/header.htm deleted file mode 100644 index 14eeb22..0000000 --- a/header.htm +++ /dev/null
@@ -1,2 +0,0 @@
1<!DOCTYPE html>
2<title>${title}</title>
diff --git a/ht b/ht new file mode 100755 index 0000000..c8cef5c --- /dev/null +++ b/ht
@@ -0,0 +1,287 @@
1#!/bin/sh
2
3### Code:
4
5usage() {
6 cat <<\EOF
7ht: Build a website
8Usage: ht -h
9 ht [OPTIONS...] FILE...
10
11FLAGS:
12 -h Show this help and exit.
13 -B Treat all targets as "new" (like in `make').
14
15OPTIONS:
16 -u BASEURL The base URL of the built site. Default: https://example.com
17 -o OUTD Output built files to OUTD. Default: ./out/
18 -w WORKD Use WORKD as the working directory. Default: /tmp/ht/
19 -p PROC Use PROC to process each input file. Default: ./ht.awk
20 -P TEMPLATE Use TEMPLATE as the page template. Default: ./page.tmpl.htm
21 -i ITEMFMT Format individual items with ITEMFMT in the site index.
22 -I TEMPLATE Use TEMPLATE as the index template. Default: ./index.tmpl.htm
23 -f ITEMFMT Format individual items with ITEMFMT in the site feed.
24 -F TEMPLATE Use TEMPLATE as the feed template. Default: ./feed.tmpl.xml
25 -s DIRECTORY The DIRECTORY holding static files (images, css, etc.).
26 Default: ./static/
27 -S DIRECTORY The DIRECTORY to copy static files to. Default: ./out/static.
28
29PARAMETERS:
30 FILE... The list of files to include in the website.
31EOF
32 exit "${1:-0}"
33}
34
35main() {
36 configure "$@"
37 shift $((OPTIND - 1))
38 prepare
39 static_copy
40
41 for input; do
42 page_write "$PAGE_TEMPLATE" "$input"
43 done
44
45 if [ -n "$INDEXEACH" ]; then
46 index_write "$INDEX_TEMPLATE" "$INDEXEACH" "$OUTD/$INDEXNAME"
47 fi
48 if [ -n "$FEEDEACH" ]; then
49 index_write "$FEED_TEMPLATE" "$FEEDEACH" "$OUTD/$FEEDNAME"
50 fi
51 printf 'Done. ' >&2
52 if test -f "$WORKD/ok"; then
53 eprint "No errors reported."
54 else
55 eprint "There were errors."
56 fi
57}
58
59configure() {
60 OUTD="${HT_OUT_DIR:-./out}"
61 WORKD="${HT_WORKD:-/tmp/ht}"
62 PROC="${HT_PROC:-./ht.awk}" # XXX: Needs better resolution
63 BASEURL="${HT_BASEURL:-https://example.com}"
64 PAGE_TEMPLATE="${HT_PAGE_TEMPLATE:-./page.tmpl.htm}"
65 INDEX_TEMPLATE="${HT_INDEX_TEMPLATE:-./index.tmpl.htm}"
66 FEED_TEMPLATE="${HT_FEED_TEMPLATE:-./feed.tmpl.xml}"
67 ALLNEW=false
68 STATICD="${HT_STATIC_DIR:-./static}"
69 STATICOUT="${HT_STATIC_OUTPUT_DIR:-${OUTD}/static}"
70
71 INDEXNAME="${HT_INDEX_NAME:-index.html}"
72 if [ -n "$HT_INDEX_ITEM_FORMAT" ]; then
73 INDEXEACH="$HT_INDEX_ITEM_FORMAT"
74 else
75 # shellcheck disable=2016
76 INDEXEACH='<li><a href=\"$BASEURL/${file}/\">${title}</a></li>'
77 fi
78
79 FEEDNAME="${HT_FEED_NAME:-feed.xml}"
80 if [ -n "$HT_FEED_ITEM_FORMAT" ]; then
81 FEEDEACH="$HT_FEED_ITEM_FORMAT"
82 else
83 # shellcheck disable=2016
84 FEEDEACH='<entry>
85 <id>$BASEURL/$file</id>
86 <link rel=\"alternate\" href=\"$BASEURL/$file/\" />
87 <title>${title}</title>
88 <updated>$(date -u -d "@${time}" -R)</updated>
89 <author>$AUTHOR</author>
90 <content type=\"text/html\">
91 <![CDATA[$(cat "$WORKD/${file}.body")]]>
92 </content>
93 </entry>'
94 fi
95
96 # shellcheck disable=2034 # BASEURL is used in templates
97 while getopts hBo:w:p:i:I:f:F:u:s:S: opt; do
98 case "$opt" in
99 h) usage ;;
100 o) OUTD="$OPTARG" ;;
101 w) WORKD="$OPTARG" ;;
102 p) PROC="$OPTARG" ;;
103 P) PAGE_TEMPLATE="$OPTARG" ;;
104 i) INDEXEACH="$OPTARG" ;;
105 I) INDEX_TEMPLATE="$OPTARG" ;;
106 f) FEEDEACH="$OPTARG" ;;
107 F) FEED_TEMPLATE="$OPTARG" ;;
108 u) BASEURL="$OPTARG" ;;
109 B) ALLNEW=true ;;
110 s) STATICD="$OPTARG" ;;
111 S) STATICOUT="$OPTARG" ;;
112 *) usage 1 ;;
113 esac
114 done
115
116 INDEX="$WORKD/index.txt"
117}
118
119prepare() {
120 test -n "$WORKD" && rm -rf "$WORKD"
121 mkdir -p "$OUTD" "$WORKD"
122 test -x "$PROC" || {
123 eprint "Can't find processor: $PROC"
124 exit 2
125 }
126 touch "$WORKD/ok"
127}
128
129### Utilities
130
131print() {
132 printf '%s\n' "$*"
133}
134
135eprint() {
136 print "$@" >&2
137}
138
139olderp() { # olderp REF OTHER
140 # Is REF older than OTHER ? Necessary b/c test -ot is not POSIX.
141 a="$1"
142 b="$2"
143
144 if a_age="$(stat -c%Y "$a" 2>/dev/null)"; then
145 a_exist=true
146 else
147 a_exist=false
148 fi
149 if b_age="$(stat -c%Y "$b" 2>/dev/null)"; then
150 b_exist=true
151 else
152 b_exist=false
153 fi
154
155 if $a_exist && ! $b_exist; then
156 # B doesn't exist -- so B is newer
157 # eprint "$a is older than $b (doesn't exist)"
158 return 0
159 elif ! $a_exist && $b_exist; then
160 # A doesn't exist -- so A is newer
161 # eprint "$a (doesn't exist) is newer than $b"
162 return 1
163 else
164 # A's age > B's age ?
165 # eprint "$a ($a_age) <= $b ($b_age)?"
166 test "$a_age" -le "$b_age"
167 fi
168}
169
170uptodate() { # uptodate FILE DEPENDENCIES... # && return
171 # Check if FILE is up-to-date with DEPENDENCIES.
172 $ALLNEW && return 1
173 uptodate=1
174 file="$1"
175 shift
176 for dep in "$@"; do
177 olderp "$dep" "$file" && uptodate=0
178 done
179 return $uptodate
180}
181
182### File conversion
183
184template_expand() { # template_expand TEMPLATES...
185 end="tmpl_$(date +%s)_${count:=0}"
186 eval "$(
187 print "cat <<$end"
188 cat "$@"
189 print
190 print "$end"
191 )"
192 count=$((count + 1))
193}
194
195meta_save() { # meta_save [-c COMMENTCH] [-m METACH] META_FILE < INPUT
196 COMMENTCH=';'
197 METACH='@'
198 while getopts c:m: opt; do
199 case "$opt" in
200 c) COMMENTCH="$OPTARG" ;;
201 m) METACH="$OPTARG" ;;
202 *) ;;
203 esac
204 done
205 shift $((OPTIND - 1))
206 sed -n "s/^${COMMENTCH}${COMMENTCH}${METACH}//p" 2>/dev/null | tee "$1"
207}
208
209meta_clear() { # meta_clear META_FILE
210 while read -r line; do
211 case "$line" in
212 *'()'*) unset -f "${line%()*}" ;;
213 *=*) unset -v "${line%=*}" ;;
214 *) ;;
215 esac
216 done <"$1" 2>/dev/null
217}
218
219page_write() { # html_write TEMPLATE INPUT
220 template="$1"
221 file="$2"
222
223 fn="${file##*/}"
224 fn="${fn%%.*}"
225 out="${OUTD}/${fn}/index.html"
226 mkdir -p "${out%/*}"
227 meta="$WORKD/${fn}.meta"
228
229 eval "$(meta_save "$meta" <"$file")"
230 stat -c "%Y ${fn} $meta" "$file" >>"$INDEX"
231
232 uptodate "$out" "$template" "$file" && return
233
234 eprint "Page: $file -> $out"
235 if
236 ! "$PROC" "$file" |
237 tee "$WORKD/${fn}.body" |
238 template_expand "$template" >"$out"
239 then
240 eprint "$file -> $out ... ERROR!"
241 rm "$WORKD/ok"
242 return
243 fi
244 meta_clear "$meta"
245}
246
247index_write() { # index_write TEMPLATE EACH OUTFILE
248 template="$1"
249 each="$2"
250 out="$3"
251
252 test -f "$INDEX" || return 1
253 uptodate "$out" "$template" "$INDEX" && return
254
255 eprint "Index: $out"
256 # shellcheck disable=2034 # file and time can be used in `each'
257 sort -k1,1 -nr "$INDEX" |
258 while IFS=' ' read -r time file meta; do
259 # shellcheck disable=1090
260 . "$meta"
261 if ! item="$(eval print "\"$each\"")"; then
262 eprint "ERROR: couldn't add '$file' to $out"
263 rm "$WORKD/ok"
264 fi
265 print "$item"
266 meta_clear "$meta"
267 done |
268 template_expand "$template" >"$out"
269}
270
271### Static files
272
273static_copy() { # static_copy
274 test -d "$STATICD" || return 1
275 if command -v rsync 2>/dev/null; then
276 eprint rsync -avz "$STATICD/" "$STATICOUT/"
277 rsync -avz "$STATICD/" "$STATICOUT/"
278 else
279 eprint cp -r "$STATICD"/* "$STATICOUT/"
280 cp -r "$STATICD"/* "$STATICOUT/"
281 fi
282}
283
284### Do the thing
285
286test "$DEBUG" && set -x
287test "$SOURCE" || main "$@"
diff --git a/ht.sh b/ht.sh deleted file mode 100755 index 7f20255..0000000 --- a/ht.sh +++ /dev/null
@@ -1,60 +0,0 @@
1#!/bin/sh
2# ht.sh
3# *.ht -> *html
4
5# config
6header_file=header.htm
7footer_file=footer.htm
8meta_file=meta.sh
9
10print() {
11 printf '%s\n' "$*"
12}
13
14htt() { # htt FILE
15 # Like `cat`, but with templating.
16 : "${HT_TMPL_COUNT:=0}"
17 ht_end="ht_main_$(date +%s)_${HT_TMPL_COUNT}" # be extra double sure
18 eval "$(
19 print "cat <<$ht_end"
20 cat "$@"
21 print
22 print "$ht_end"
23 )"
24 HT_TMPL_COUNT=$((HT_TMPL_COUNT + 1))
25}
26
27htmeta_clear() {
28 # Generate metadata-clearing commands from $meta_file.
29 while read -r line; do
30 case "$line" in
31 *'()'*) # function
32 unset -f "${line%()*}"
33 ;;
34 *=*) # variable assignment
35 unset -v "${line%=*}"
36 ;;
37 *) # other -- XXX: Don't know what to do
38 ;;
39 esac
40 done <"$meta_file" 2>/dev/null
41}
42
43htmeta() { # htmeta FILE
44 # Collect metadata from FILE.
45 # Metadata looks like this: `;;@<SHELL_EXPRESSION>`
46 sed -n 's/^;;@//p' "$1" 2>/dev/null | tee "$meta_file"
47}
48
49main() {
50 # Make two passes over each input file, collecting metadata and content.
51 # Of course, this isn't safe, but you trust yourself, right?
52 for file; do
53 eval "$(htmeta_clear)"
54 eval "$(htmeta "$file")"
55 ./ht.awk <"$file" | htt "$header_file" - "$footer_file"
56 done
57}
58
59test "$DEBUG" && set -x
60test "$SOURCE" || main "$@"
diff --git a/index.tmpl.htm b/index.tmpl.htm new file mode 100644 index 0000000..5884af5 --- /dev/null +++ b/index.tmpl.htm
@@ -0,0 +1,12 @@
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>acdw!</title>
5 </head>
6 <body>
7 <ul>
8 $(cat)
9 </ul>
10 <footer>cool B)</footer>
11 </body>
12</html>
diff --git a/page.tmpl.htm b/page.tmpl.htm new file mode 100644 index 0000000..6655494 --- /dev/null +++ b/page.tmpl.htm
@@ -0,0 +1,10 @@
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>${title}</title>
5 </head>
6 <body>
7 $(cat)
8 <!-- so ends ${file} -->
9 </body>
10</html>
diff --git a/test.ht b/test.ht deleted file mode 100644 index 58125a8..0000000 --- a/test.ht +++ /dev/null
@@ -1,32 +0,0 @@
1# ht: a bespoke document preparation system
2;;@title="ht: a bespoke document preparation system"
3;; comments are like this.
4;; they're a good time.
5
6`ht
7is a quasi-line-based markup language that takes inspiration from
8@https://gemini.circumlunar.space/docs/gemtext.gmi gemtext\
9,
10@https://daringfireball.net/projects/markdown/ markdown\
11, and others.
12Its aim is to be somewhat easy to read while being fairly easy to parse.
13
14In fact,
15`ht
16is a simple awk script.
17
18## Usage
19
20- one
21- two
22- three
23
24ordered list:
25
26% one
27% two
28% three
29
30```
31./ht.awk source.ht
32```