diff options
-rwxr-xr-x | ht | 127 | ||||
-rwxr-xr-x | ht.awk | 26 |
2 files changed, 104 insertions, 49 deletions
diff --git a/ht b/ht index c8cef5c..39f86d0 100755 --- a/ht +++ b/ht | |||
@@ -35,31 +35,43 @@ EOF | |||
35 | main() { | 35 | main() { |
36 | configure "$@" | 36 | configure "$@" |
37 | shift $((OPTIND - 1)) | 37 | shift $((OPTIND - 1)) |
38 | test $# -eq 0 && usage 1 | ||
39 | |||
38 | prepare | 40 | prepare |
39 | static_copy | 41 | static_copy |
40 | 42 | ||
41 | for input; do | 43 | for input; do |
42 | page_write "$PAGE_TEMPLATE" "$input" | 44 | case "$input" in |
45 | _* | */_*) continue ;; | ||
46 | *) page_write "$PAGE_TEMPLATE" "$input" ;; | ||
47 | esac | ||
43 | done | 48 | done |
44 | 49 | ||
45 | if [ -n "$INDEXEACH" ]; then | 50 | if okp && [ -n "$INDEXEACH" ]; then |
46 | index_write "$INDEX_TEMPLATE" "$INDEXEACH" "$OUTD/$INDEXNAME" | 51 | index_write "$INDEX_TEMPLATE" "$INDEXEACH" "$OUTD/$INDEXNAME" |
47 | fi | 52 | fi |
48 | if [ -n "$FEEDEACH" ]; then | 53 | if okp && [ -n "$FEEDEACH" ]; then |
49 | index_write "$FEED_TEMPLATE" "$FEEDEACH" "$OUTD/$FEEDNAME" | 54 | index_write "$FEED_TEMPLATE" "$FEEDEACH" "$OUTD/$FEEDNAME" |
50 | fi | 55 | fi |
56 | |||
51 | printf 'Done. ' >&2 | 57 | printf 'Done. ' >&2 |
52 | if test -f "$WORKD/ok"; then | 58 | if okp; then |
53 | eprint "No errors reported." | 59 | eprint "No errors reported." |
54 | else | 60 | else |
55 | eprint "There were errors." | 61 | eprint "There were errors." |
56 | fi | 62 | fi |
57 | } | 63 | } |
58 | 64 | ||
65 | htawk_resolve() { | ||
66 | if ! command -v ht.awk 2>/dev/null; then | ||
67 | print ./ht.awk | ||
68 | fi | ||
69 | } | ||
70 | |||
59 | configure() { | 71 | configure() { |
60 | OUTD="${HT_OUT_DIR:-./out}" | 72 | OUTD="${HT_OUT_DIR:-./out}" |
61 | WORKD="${HT_WORKD:-/tmp/ht}" | 73 | WORKD="${HT_WORKD:-/tmp/ht}" |
62 | PROC="${HT_PROC:-./ht.awk}" # XXX: Needs better resolution | 74 | PROC="${HT_PROC:-$(htawk_resolve)}" |
63 | BASEURL="${HT_BASEURL:-https://example.com}" | 75 | BASEURL="${HT_BASEURL:-https://example.com}" |
64 | PAGE_TEMPLATE="${HT_PAGE_TEMPLATE:-./page.tmpl.htm}" | 76 | PAGE_TEMPLATE="${HT_PAGE_TEMPLATE:-./page.tmpl.htm}" |
65 | INDEX_TEMPLATE="${HT_INDEX_TEMPLATE:-./index.tmpl.htm}" | 77 | INDEX_TEMPLATE="${HT_INDEX_TEMPLATE:-./index.tmpl.htm}" |
@@ -94,7 +106,7 @@ configure() { | |||
94 | fi | 106 | fi |
95 | 107 | ||
96 | # shellcheck disable=2034 # BASEURL is used in templates | 108 | # shellcheck disable=2034 # BASEURL is used in templates |
97 | while getopts hBo:w:p:i:I:f:F:u:s:S: opt; do | 109 | while getopts ho:w:p:P:i:I:f:F:u:Bs:S: opt; do |
98 | case "$opt" in | 110 | case "$opt" in |
99 | h) usage ;; | 111 | h) usage ;; |
100 | o) OUTD="$OPTARG" ;; | 112 | o) OUTD="$OPTARG" ;; |
@@ -113,16 +125,17 @@ configure() { | |||
113 | esac | 125 | esac |
114 | done | 126 | done |
115 | 127 | ||
128 | STATICOUT="${HT_STATIC_OUTPUT_DIR:-${OUTD}/static}" | ||
116 | INDEX="$WORKD/index.txt" | 129 | INDEX="$WORKD/index.txt" |
117 | } | 130 | } |
118 | 131 | ||
119 | prepare() { | 132 | prepare() { |
120 | test -n "$WORKD" && rm -rf "$WORKD" | 133 | test -n "$WORKD" && rm -rf "$WORKD" |
121 | mkdir -p "$OUTD" "$WORKD" | 134 | mkdir -p "$OUTD" "$WORKD" |
122 | test -x "$PROC" || { | 135 | if ! test -x "$PROC"; then |
123 | eprint "Can't find processor: $PROC" | 136 | eprint "Can't find processor: $PROC" |
124 | exit 2 | 137 | exit 2 |
125 | } | 138 | fi |
126 | touch "$WORKD/ok" | 139 | touch "$WORKD/ok" |
127 | } | 140 | } |
128 | 141 | ||
@@ -136,6 +149,14 @@ eprint() { | |||
136 | print "$@" >&2 | 149 | print "$@" >&2 |
137 | } | 150 | } |
138 | 151 | ||
152 | okp() { | ||
153 | test -f "$WORKD/ok" | ||
154 | } | ||
155 | |||
156 | notok() { | ||
157 | rm "$WORKD/ok" 2>/dev/null | ||
158 | } | ||
159 | |||
139 | olderp() { # olderp REF OTHER | 160 | olderp() { # olderp REF OTHER |
140 | # Is REF older than OTHER ? Necessary b/c test -ot is not POSIX. | 161 | # Is REF older than OTHER ? Necessary b/c test -ot is not POSIX. |
141 | a="$1" | 162 | a="$1" |
@@ -174,6 +195,7 @@ uptodate() { # uptodate FILE DEPENDENCIES... # && return | |||
174 | file="$1" | 195 | file="$1" |
175 | shift | 196 | shift |
176 | for dep in "$@"; do | 197 | for dep in "$@"; do |
198 | |||
177 | olderp "$dep" "$file" && uptodate=0 | 199 | olderp "$dep" "$file" && uptodate=0 |
178 | done | 200 | done |
179 | return $uptodate | 201 | return $uptodate |
@@ -182,28 +204,21 @@ uptodate() { # uptodate FILE DEPENDENCIES... # && return | |||
182 | ### File conversion | 204 | ### File conversion |
183 | 205 | ||
184 | template_expand() { # template_expand TEMPLATES... | 206 | template_expand() { # template_expand TEMPLATES... |
185 | end="tmpl_$(date +%s)_${count:=0}" | 207 | if test "$noproc"; then |
186 | eval "$( | 208 | cat - |
187 | print "cat <<$end" | 209 | else |
188 | cat "$@" | 210 | end="tmpl_$(date +%s)_${count:=0}" |
189 | 211 | eval "$( | |
190 | print "$end" | 212 | print "cat <<$end" |
191 | )" | 213 | cat "$@" |
192 | count=$((count + 1)) | 214 | |
215 | print "$end" | ||
216 | )" && count=$((count + 1)) | ||
217 | fi | ||
193 | } | 218 | } |
194 | 219 | ||
195 | meta_save() { # meta_save [-c COMMENTCH] [-m METACH] META_FILE < INPUT | 220 | meta_save() { # meta_save META_FILE < INPUT |
196 | COMMENTCH=';' | 221 | sed -n "s/^;;@//p" 2>/dev/null | tee "$1" |
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 | } | 222 | } |
208 | 223 | ||
209 | meta_clear() { # meta_clear META_FILE | 224 | meta_clear() { # meta_clear META_FILE |
@@ -219,27 +234,38 @@ meta_clear() { # meta_clear META_FILE | |||
219 | page_write() { # html_write TEMPLATE INPUT | 234 | page_write() { # html_write TEMPLATE INPUT |
220 | template="$1" | 235 | template="$1" |
221 | file="$2" | 236 | file="$2" |
237 | noproc= # File can set `noproc' in their metadata to not be processed. | ||
238 | |||
239 | if ! test -f "$file"; then | ||
240 | eprint "ERROR: File doesn't exist: $file" | ||
241 | notok | ||
242 | return 1 | ||
243 | fi | ||
222 | 244 | ||
223 | fn="${file##*/}" | 245 | fn="${file##*/}" |
224 | fn="${fn%%.*}" | 246 | fn="${fn%%.*}" |
225 | out="${OUTD}/${fn}/index.html" | 247 | out="${OUTD}/${fn}/index.html" |
226 | mkdir -p "${out%/*}" | 248 | mkdir -p "${out%/*}" |
227 | meta="$WORKD/${fn}.meta" | 249 | meta="$WORKD/${fn}.meta" |
250 | body="$WORKD/${fn}.body" | ||
228 | 251 | ||
229 | eval "$(meta_save "$meta" <"$file")" | 252 | eval "$(meta_save "$meta" <"$file")" |
230 | stat -c "%Y ${fn} $meta" "$file" >>"$INDEX" | 253 | stat -c "%Y ${fn} $meta" "$file" >>"$INDEX" |
231 | 254 | ||
232 | uptodate "$out" "$template" "$file" && return | 255 | # uptodate "$out" "$template" "$file" && return |
233 | 256 | ||
234 | eprint "Page: $file -> $out" | 257 | eprint "Page: $file -> $out" |
235 | if | 258 | test "$noproc" || "$PROC" "$file" >"$body" |
236 | ! "$PROC" "$file" | | 259 | if ! [ -f "$body" ]; then |
237 | tee "$WORKD/${fn}.body" | | 260 | eprint "ERROR: Conversion: $file" |
238 | template_expand "$template" >"$out" | 261 | notok |
239 | then | 262 | return 2 |
240 | eprint "$file -> $out ... ERROR!" | 263 | fi |
241 | rm "$WORKD/ok" | 264 | test "$noproc" || body="$(template_expand "${body}")" |
242 | return | 265 | if ! template_expand "$template" >"$out"; then |
266 | eprint "ERROR: Expansion: $file" | ||
267 | notok | ||
268 | return 3 | ||
243 | fi | 269 | fi |
244 | meta_clear "$meta" | 270 | meta_clear "$meta" |
245 | } | 271 | } |
@@ -249,8 +275,10 @@ index_write() { # index_write TEMPLATE EACH OUTFILE | |||
249 | each="$2" | 275 | each="$2" |
250 | out="$3" | 276 | out="$3" |
251 | 277 | ||
252 | test -f "$INDEX" || return 1 | 278 | if ! test -f "$INDEX"; then |
253 | uptodate "$out" "$template" "$INDEX" && return | 279 | : >"$INDEX" |
280 | fi | ||
281 | # uptodate "$out" "$template" "$INDEX" && return | ||
254 | 282 | ||
255 | eprint "Index: $out" | 283 | eprint "Index: $out" |
256 | # shellcheck disable=2034 # file and time can be used in `each' | 284 | # shellcheck disable=2034 # file and time can be used in `each' |
@@ -258,9 +286,11 @@ index_write() { # index_write TEMPLATE EACH OUTFILE | |||
258 | while IFS=' ' read -r time file meta; do | 286 | while IFS=' ' read -r time file meta; do |
259 | # shellcheck disable=1090 | 287 | # shellcheck disable=1090 |
260 | . "$meta" | 288 | . "$meta" |
289 | # shellcheck disable=2154 # noindex is a file var | ||
290 | [ "$noindex" ] && continue | ||
261 | if ! item="$(eval print "\"$each\"")"; then | 291 | if ! item="$(eval print "\"$each\"")"; then |
262 | eprint "ERROR: couldn't add '$file' to $out" | 292 | eprint "ERROR: couldn't add '$file' to $out" |
263 | rm "$WORKD/ok" | 293 | notok |
264 | fi | 294 | fi |
265 | print "$item" | 295 | print "$item" |
266 | meta_clear "$meta" | 296 | meta_clear "$meta" |
@@ -271,14 +301,17 @@ index_write() { # index_write TEMPLATE EACH OUTFILE | |||
271 | ### Static files | 301 | ### Static files |
272 | 302 | ||
273 | static_copy() { # static_copy | 303 | static_copy() { # static_copy |
274 | test -d "$STATICD" || return 1 | 304 | if ! test -d "$STATICD"; then |
275 | if command -v rsync 2>/dev/null; then | 305 | return 1 |
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 | 306 | fi |
307 | mkdir -p "$STATICOUT" | ||
308 | for f in "$STATICD"/*; do | ||
309 | if [ -d "$f" ]; then | ||
310 | cp -r "$f" "$STATICOUT/" | ||
311 | else | ||
312 | cp "$f" "$STATICOUT/" | ||
313 | fi | ||
314 | done | ||
282 | } | 315 | } |
283 | 316 | ||
284 | ### Do the thing | 317 | ### Do the thing |
diff --git a/ht.awk b/ht.awk index 7581cdb..c15ccc0 100755 --- a/ht.awk +++ b/ht.awk | |||
@@ -62,6 +62,10 @@ $0 ~ ("^" COMMENT_DELIM) { | |||
62 | } else { | 62 | } else { |
63 | sep = "\n" | 63 | sep = "\n" |
64 | } | 64 | } |
65 | # Sanitize HTML | ||
66 | gsub(/&/, "\\\\\\&", $0) | ||
67 | gsub(/</, "\\\\\\<", $0) | ||
68 | gsub(/>/, "\\\\\\>", $0) | ||
65 | # Loop through BLOCK_TYPES | 69 | # Loop through BLOCK_TYPES |
66 | for (bt in BLOCK_TYPES) { | 70 | for (bt in BLOCK_TYPES) { |
67 | if (match($0, "^" bt "[ \t]*")) { | 71 | if (match($0, "^" bt "[ \t]*")) { |
@@ -105,8 +109,26 @@ $0 ~ ("^" COMMENT_DELIM) { | |||
105 | if (match($0, "^" lt "[ \t]*")) { | 109 | if (match($0, "^" lt "[ \t]*")) { |
106 | $0 = substr($0, RSTART + RLENGTH) | 110 | $0 = substr($0, RSTART + RLENGTH) |
107 | templ = LINE_TYPES[lt] | 111 | templ = LINE_TYPES[lt] |
108 | while (match(templ, /\$[0-9]+/)) { | 112 | while (match(templ, /\$[0-9-]+/)) { |
109 | sub(/\$[0-9]+/, $(substr(templ, RSTART + 1, RLENGTH - 1)), templ) | 113 | if (substr(templ, RSTART + 1, 1) == "-") { |
114 | # Up to $field | ||
115 | f = "" | ||
116 | n = substr(templ, RSTART + 2, RLENGTH - 2) | ||
117 | for (i = 1; i <= n; i++) { | ||
118 | f = f (f ? " " : "") $i | ||
119 | } | ||
120 | sub(/\$[0-9-]+/, f, templ) | ||
121 | } else if (substr(templ, RSTART + RLENGTH - 1, 1) == "-") { | ||
122 | # $Field to end | ||
123 | f = "" | ||
124 | n = substr(templ, RSTART + 1, RLENGTH - 2) | ||
125 | for (i = n; i <= NF; i++) { | ||
126 | f = f (f ? " " : "") $i | ||
127 | } | ||
128 | sub(/\$[0-9-]+/, f, templ) | ||
129 | } else { | ||
130 | sub(/\$[0-9]+/, $(substr(templ, RSTART + 1, RLENGTH - 1)), templ) | ||
131 | } | ||
110 | } | 132 | } |
111 | $0 = templ | 133 | $0 = templ |
112 | } | 134 | } |