about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2023-01-14 20:03:24 -0600
committerCase Duckworth2023-01-14 20:03:24 -0600
commit7e8ba932f0b4f71a016ef42ac8fdbe2cc4856353 (patch)
treedad59d096460883e1dbdc5d62a73dc1c0849e846
parentTypo (diff)
downloadvienna-7e8ba932f0b4f71a016ef42ac8fdbe2cc4856353.tar.gz
vienna-7e8ba932f0b4f71a016ef42ac8fdbe2cc4856353.zip
Whitespace
-rwxr-xr-xvienna566
1 files changed, 284 insertions, 282 deletions
diff --git a/vienna b/vienna index 54f5297..3d6de65 100755 --- a/vienna +++ b/vienna
@@ -6,7 +6,7 @@
6### Entry point 6### Entry point
7 7
8usage() { 8usage() {
9 cat <<EOF 9 cat <<EOF
10VIENNA: a tiny, tasty ssg 10VIENNA: a tiny, tasty ssg
11by C. Duckworth <acdw@acdw.net> 11by C. Duckworth <acdw@acdw.net>
12 12
@@ -38,194 +38,196 @@ which by default is ./.vienna.sh. vienna uses heredoc-inspired templating, so
38you can include shell snippets and variables by doubling the dollar signs. 38you can include shell snippets and variables by doubling the dollar signs.
39 39
40EOF 40EOF
41 exit "${1:-0}" 41 exit "${1:-0}"
42} 42}
43 43
44configure() { 44configure() {
45 ## Set up environment 45 ## Set up environment
46 URL_ROOT="${VIENNA_URL_ROOT:-https://www.example.com}" 46 URL_ROOT="${VIENNA_URL_ROOT:-https://www.example.com}"
47 TEMPDIR="${VIENNA_TEMPDIR:-/tmp/vienna}" 47 TEMPDIR="${VIENNA_TEMPDIR:-/tmp/vienna}"
48 WORKDIR="${VIENNA_WORKDIR:-$PWD}" 48 WORKDIR="${VIENNA_WORKDIR:-$PWD}"
49 OUTDIR="${VIENNA_OUTDIR:-out}" 49 OUTDIR="${VIENNA_OUTDIR:-out}"
50 PLUGINDIR="${VIENNA_PLUGINDIR:-.plugins}" 50 PLUGINDIR="${VIENNA_PLUGINDIR:-.plugins}"
51 CONFIG="${VIENNA_CONFIG:-./.vienna.sh}" 51 CONFIG="${VIENNA_CONFIG:-./.vienna.sh}"
52 # Templates 52 # Templates
53 PAGE_TEMPLATE="${VIENNA_PAGE_TEMPLATE:-.page.tmpl.html}" 53 PAGE_TEMPLATE="${VIENNA_PAGE_TEMPLATE:-.page.tmpl.html}"
54 INDEX_TEMPLATE="${VIENNA_INDEX_TEMPLATE:-.index.tmpl.html}" 54 INDEX_TEMPLATE="${VIENNA_INDEX_TEMPLATE:-.index.tmpl.html}"
55 FEED_TEMPLATE="${VIENNA_FEED_TEMPLATE:-.feed.tmpl.xml}" 55 FEED_TEMPLATE="${VIENNA_FEED_TEMPLATE:-.feed.tmpl.xml}"
56 # Options 56 # Options
57 PHTML_OPTIONS="${VIENNA_PHTML_OPTIONS:-expand entities}" 57 PHTML_OPTIONS="${VIENNA_PHTML_OPTIONS:-expand entities}"
58 # File extensions 58 # File extensions
59 RAW_PAGE_EXTENSION="${VIENNA_RAW_PAGE_EXTENSION:-htm}" 59 RAW_PAGE_EXTENSION="${VIENNA_RAW_PAGE_EXTENSION:-htm}"
60 # Logging 60 # Logging
61 LOG=true 61 LOG=true
62 ## Parse command line arguments 62 ## Parse command line arguments
63 while getopts hqC:c:o:r: opt; do 63 while getopts hqC:c:o:r: opt; do
64 case "$opt" in 64 case "$opt" in
65 h) usage 0 ;; 65 h) usage 0 ;;
66 q) LOG=false ;; 66 q) LOG=false ;;
67 C) WORKDIR="$OPTARG" ;; 67 C) WORKDIR="$OPTARG" ;;
68 c) 68 c)
69 CONFIG="$OPTARG" 69 CONFIG="$OPTARG"
70 # To error later if a config is specified on the command 70 # To error later if a config is specified on the command
71 # line but doesn't exist. 71 # line but doesn't exist.
72 CONFIG_ARG=1 72 CONFIG_ARG=1
73 ;; 73 ;;
74 o) OUTDIR="$OPTARG" ;; 74 o) OUTDIR="$OPTARG" ;;
75 r) URL_ROOT="$OPTARG" ;; 75 r) URL_ROOT="$OPTARG" ;;
76 *) exit 1 ;; 76 *) exit 1 ;;
77 esac 77 esac
78 done 78 done
79 ## Initialize state 79 ## Initialize state
80 FILE= 80 FILE=
81 ## Cleanup after we're done 81 ## Cleanup after we're done
82 trap cleanup INT QUIT 82 trap cleanup INT QUIT
83} 83}
84 84
85main() { 85main() {
86 # State predicates 86 # State predicates
87 alias pagep=false indexp=false feedp=false 87 alias pagep=false indexp=false feedp=false
88 # Convenience aliases 88 # Convenience aliases
89 alias body=cat title='meta title' pubdate='meta date' 89 alias body=cat title='meta title' pubdate='meta date'
90 # Configure 90 # Configure
91 configure "$@" 91 configure "$@"
92 shift "$((OPTIND - 1))" 92 shift "$((OPTIND - 1))"
93 # Prepare 93 # Prepare
94 cd "$WORKDIR" || exit 2 94 cd "$WORKDIR" || exit 2
95 # Source config 95 # Source config
96 if test -f "$CONFIG"; then 96 if test -f "$CONFIG"; then
97 # Source ./.vienna.sh, if it exists. 97 # Source ./.vienna.sh, if it exists.
98 . "$CONFIG" 98 . "$CONFIG"
99 elif test -n "$CONFIG_ARG"; then 99 elif test -n "$CONFIG_ARG"; then
100 # If a -c option was passed on the command line but the file 100 # If a -c option was passed on the command line but the file
101 # doesn't exist, that's an error. If we're just looking for the 101 # doesn't exist, that's an error. If we're just looking for the
102 # default file, however, there is no error---the user might want 102 # default file, however, there is no error---the user might want
103 # to use the default configuration. 103 # to use the default configuration.
104 log error "Can't find configuration \`$CONFIG'." 104 log error "Can't find configuration \`$CONFIG'."
105 exit 2 105 exit 2
106 else
107 print >&2 "I'm not sure this is a \`vienna' site directory."
108 if yornp "Initialize? (y/N)"; then
109 initialize
110 else 106 else
111 yornp "Continue building? (y/N)" || exit 2 107 print >&2 "I'm not sure this is a \`vienna' site directory."
108 if yornp "Initialize? (y/N)"; then
109 initialize
110 else
111 yornp "Continue building? (y/N)" || exit 2
112 fi
112 fi 113 fi
113 fi 114 # Further argument processing --- pre-build
114 # Further argument processing --- pre-build 115 preprocess "$@" || shift
115 preprocess "$@" || shift 116 log vienna config
116 log vienna config 117 # Log configuration variables
117 # Log configuration variables 118 log config 'base url': "$URL_ROOT"
118 log config 'base url': "$URL_ROOT" 119 log config 'work dir': "$WORKDIR"
119 log config 'work dir': "$WORKDIR" 120 log config output: "$OUTDIR"
120 log config output: "$OUTDIR" 121 log template page: "$PAGE_TEMPLATE"
121 log template page: "$PAGE_TEMPLATE" 122 log template index: "$INDEX_TEMPLATE"
122 log template index: "$INDEX_TEMPLATE" 123 log template feed: "$FEED_TEMPLATE"
123 log template feed: "$FEED_TEMPLATE" 124 # Plugins
124 # Plugins 125 for plugin in "$PLUGINDIR"/*.sh; do
125 for plugin in "$PLUGINDIR"/*.sh; do 126 test -f "$plugin" || continue
126 test -f "$plugin" || continue 127 log plugin "$plugin"
127 log plugin "$plugin" 128 . "$plugin"
128 . "$plugin" 129 done
129 done 130 # Prepare output directories
130 # Prepare output directories 131 mkdir -p "$OUTDIR" || exit 2
131 mkdir -p "$OUTDIR" || exit 2 132 mkdir -p "$TEMPDIR" || exit 2
132 mkdir -p "$TEMPDIR" || exit 2 133 log vienna build
133 log vienna build 134 # Build pages
134 # Build pages 135 alias pagep=true
135 alias pagep=true 136 genpage *."$RAW_PAGE_EXTENSION" || exit 2
136 genpage *."$RAW_PAGE_EXTENSION" || exit 2 137 alias pagep=false
137 alias pagep=false 138 # Build index
138 # Build index 139 alias indexp=true
139 alias indexp=true 140 genlist index_item "$INDEX_TEMPLATE" *."$RAW_PAGE_EXTENSION" >"$OUTDIR/index.html" || exit 2
140 genlist index_item "$INDEX_TEMPLATE" *."$RAW_PAGE_EXTENSION" >"$OUTDIR/index.html" || exit 2 141 alias indexp=false
141 alias indexp=false 142 # Build feed
142 # Build feed 143 alias feedp=true
143 alias feedp=true 144 genlist feed_item "$FEED_TEMPLATE" *."$RAW_PAGE_EXTENSION" >"$OUTDIR/feed.xml" || exit 2
144 genlist feed_item "$FEED_TEMPLATE" *."$RAW_PAGE_EXTENSION" >"$OUTDIR/feed.xml" || exit 2 145 alias feedp=false
145 alias feedp=false 146 # Copy static files
146 # Copy static files 147 static * || exit 2
147 static * || exit 2 148 # Further argument processing --- post-build
148 # Further argument processing --- post-build 149 postprocess "$@"
149 postprocess "$@"
150} 150}
151 151
152preprocess() { 152preprocess() {
153 case "${1:-ok}" in 153 case "${1:-ok}" in
154 ok) ;; 154 ok) ;;
155 init) 155 init)
156 shift 156 shift
157 initialize "$@" # exit 157 initialize "$@" # exit
158 ;; 158 ;;
159 clean) 159 clean)
160 log vienna clean 160 log vienna clean
161 rm -r "$OUTDIR" 161 rm -r "$OUTDIR"
162 cleanup 162 cleanup
163 if [ $# -eq 0 ]; then 163 if [ $# -eq 0 ]; then
164 exit # Quit when only cleaning 164 exit # Quit when only cleaning
165 else 165 else
166 return 1 # Otherwise, continue processing 166 return 1 # Otherwise, continue processing
167 fi 167 fi
168 ;; 168 ;;
169 esac 169 esac
170} 170}
171 171
172postprocess() { 172postprocess() {
173 case "${1:-ok}" in 173 case "${1:-ok}" in
174 ok) ;; 174 ok) ;;
175 publish) 175 publish)
176 log vienna publish 176 log vienna publish
177 publish "$OUTDIR" 177 publish "$OUTDIR"
178 ;; 178 ;;
179 preview) 179 preview)
180 log vienna preview 180 log vienna preview
181 preview "$OUTDIR" 181 preview "$OUTDIR"
182 ;; 182 ;;
183 *) 183 *)
184 log error "Don't know command \`$1'." 184 log error "Don't know command \`$1'."
185 exit 1 185 exit 1
186 ;; 186 ;;
187 esac 187 esac
188} 188}
189 189
190cleanup() { 190cleanup() {
191 test -z "$DEBUG" && 191 test -z "$DEBUG" &&
192 test -z "$NODEP_RM" && 192 test -z "$NODEP_RM" &&
193 rm -r "$TEMPDIR" 193 rm -r "$TEMPDIR"
194} 194}
195 195
196_publish() { 196_publish() {
197 cat <<EOF >&2 197 cat <<EOF >&2
198 198
199I want to publish your website but I don't know how. 199I want to publish your website but I don't know how.
200$(if test -f "$CONFIG"; 200$(if test -f "$CONFIG"; then
201then echo "Edit the"; 201 echo "Edit the"
202else echo "Write a"; 202 else
203fi) \`publish' function in the \`$CONFIG' file in this 203 echo "Write a"
204 fi) \`publish' function in the \`$CONFIG' file in this
204directory that tells me what to do. 205directory that tells me what to do.
205 206
206EOF 207EOF
207 exit 3 208 exit 3
208} 209}
209publish() { _publish; } 210publish() { _publish; }
210 211
211_preview() { 212_preview() {
212 cat <<EOF >&2 213 cat <<EOF >&2
213 214
214I want to show you a preview of your website but I don't 215I want to show you a preview of your website but I don't
215know how. $(if test -f "$CONFIG"; 216know how. $(if test -f "$CONFIG"; then
216then echo "Edit the"; 217 echo "Edit the"
217else echo "Write a"; 218 else
218fi) \`preview' function in the \`$CONFIG' file 219 echo "Write a"
220 fi) \`preview' function in the \`$CONFIG' file
219in this directory that tells me what to do. 221in this directory that tells me what to do.
220 222
221EOF 223EOF
222 exit 3 224 exit 3
223} 225}
224preview() { _preview; } 226preview() { _preview; }
225 227
226initialize() { # initialize 228initialize() { # initialize
227 log init "$CONFIG" 229 log init "$CONFIG"
228 cat >"$CONFIG" <<EOF 230 cat >"$CONFIG" <<EOF
229# .vienna.sh 231# .vienna.sh
230 232
231# Basic configuration variables 233# Basic configuration variables
@@ -268,21 +270,21 @@ SERVER_ROOT=
268# fi 270# fi
269# } 271# }
270EOF 272EOF
271 log init "$PAGE_TEMPLATE" 273 log init "$PAGE_TEMPLATE"
272 cat >"$PAGE_TEMPLATE" <<\EOF 274 cat >"$PAGE_TEMPLATE" <<\EOF
273<title>$$(title)</title> 275<title>$$(title)</title>
274$$(body) 276$$(body)
275EOF 277EOF
276 log init "$INDEX_TEMPLATE" 278 log init "$INDEX_TEMPLATE"
277 cat >"$INDEX_TEMPLATE" <<\EOF 279 cat >"$INDEX_TEMPLATE" <<\EOF
278<title>a home page!</title> 280<title>a home page!</title>
279<h1>hey! it's a home page of some sort!</h1> 281<h1>hey! it's a home page of some sort!</h1>
280<ul> 282<ul>
281$$(body) 283$$(body)
282</ul> 284</ul>
283EOF 285EOF
284 log init "$FEED_TEMPLATE" 286 log init "$FEED_TEMPLATE"
285 cat >"$FEED_TEMPLATE" <<\EOF 287 cat >"$FEED_TEMPLATE" <<\EOF
286<rss version="2.0"> 288<rss version="2.0">
287 <channel> 289 <channel>
288 <title>a feed!</title> 290 <title>a feed!</title>
@@ -291,32 +293,32 @@ EOF
291 </channel> 293 </channel>
292</rss> 294</rss>
293EOF 295EOF
294 exit 296 exit
295} 297}
296 298
297### Utility 299### Utility
298 300
299log() { 301log() {
300 if "$LOG"; then 302 if "$LOG"; then
301 printf >&2 '[%s] ' "$1" 303 printf >&2 '[%s] ' "$1"
302 shift 304 shift
303 printf >&2 "%s\t" "$@" 305 printf >&2 "%s\t" "$@"
304 echo >&2 306 echo >&2
305 fi 307 fi
306} 308}
307 309
308print() { 310print() {
309 printf '%s\n' "$*" 311 printf '%s\n' "$*"
310} 312}
311 313
312yornp() { # yornp PROMPT 314yornp() { # yornp PROMPT
313 printf >&2 '%s \n' "$@" 315 printf >&2 '%s \n' "$@"
314 read -r yn 316 read -r yn
315 case "$yn" in 317 case "$yn" in
316 [Nn]*) return 1 ;; 318 [Nn]*) return 1 ;;
317 [Yy]*) return 0 ;; 319 [Yy]*) return 0 ;;
318 *) return 2 ;; 320 *) return 2 ;;
319 esac 321 esac
320} 322}
321 323
322### File processing 324### File processing
@@ -324,43 +326,43 @@ yornp() { # yornp PROMPT
324## Building block functions 326## Building block functions
325 327
326shellfix() { # shellfix FILE... 328shellfix() { # shellfix FILE...
327 ## Replace ` with \`, $ with \$, and $$ with $ 329 ## Replace ` with \`, $ with \$, and $$ with $
328 # shellcheck disable=2016 330 # shellcheck disable=2016
329 sed -E \ 331 sed -E \
330 -e 's/`/\\`/g' \ 332 -e 's/`/\\`/g' \
331 -e 's/\$\$\$/\\&/g' \ 333 -e 's/\$\$\$/\\&/g' \
332 -e 's/(^|[^\$])\$([^\$]|$)/\1\\$\2/g' \ 334 -e 's/(^|[^\$])\$([^\$]|$)/\1\\$\2/g' \
333 -e 's/\$\$/$/g' \ 335 -e 's/\$\$/$/g' \
334 "$@" 336 "$@"
335} 337}
336 338
337expand() { # expand TEMPLATE... < INPUT 339expand() { # expand TEMPLATE... < INPUT
338 ## Print TEMPLATE to stdout, expanding shell constructs. 340 ## Print TEMPLATE to stdout, expanding shell constructs.
339 end="expand_:_${count:=0}_:_end" 341 end="expand_:_${count:=0}_:_end"
340 eval "$( 342 eval "$(
341 echo "cat<<$end" 343 echo "cat<<$end"
342 shellfix "$@" 344 shellfix "$@"
343 echo 345 echo
344 echo "$end" 346 echo "$end"
345 )" && count=$((count + 1)) 347 )" && count=$((count + 1))
346} 348}
347 349
348phtml() { # phtml < INPUT 350phtml() { # phtml < INPUT
349 ## Output HTML, pretty much. 351 ## Output HTML, pretty much.
350 # Paragraphs unadorned with html tags will be wrapped in <p> tags, and 352 # Paragraphs unadorned with html tags will be wrapped in <p> tags, and
351 # &, <, > will be escaped unless prepended with \. Paragraphs where the 353 # &, <, > will be escaped unless prepended with \. Paragraphs where the
352 # first character is < will be left as-is, excepting indentation on the 354 # first character is < will be left as-is, excepting indentation on the
353 # first line (an implementation detail). 355 # first line (an implementation detail).
354 case "$PHTML_OPTIONS" in 356 case "$PHTML_OPTIONS" in
355 *entities*) 357 *entities*)
356 _entities='s#([^\\])&#\1\&amp;#g; 358 _entities='s#([^\\])&#\1\&amp;#g;
357 s#([^\\])<#\1\&lt;#g; 359 s#([^\\])<#\1\&lt;#g;
358 s#([^\\])>#\1\&gt;#g; 360 s#([^\\])>#\1\&gt;#g;
359 s#\\([&<>])#\1#g;' 361 s#\\([&<>])#\1#g;'
360 ;; 362 ;;
361 *) _entities= 363 *) _entities= ;;
362 esac 364 esac
363 sed -E ' 365 sed -E '
364 /./ {H;$!d}; x 366 /./ {H;$!d}; x
365 s#^[ \n\t]+([^<].*)#\1# 367 s#^[ \n\t]+([^<].*)#\1#
366 t par; b end 368 t par; b end
@@ -374,123 +376,123 @@ phtml() { # phtml < INPUT
374} 376}
375 377
376meta_init() { # meta_init FILE 378meta_init() { # meta_init FILE
377 ## Extract metadata from FILE for later processing. 379 ## Extract metadata from FILE for later processing.
378 # Metadata should exist as colon-separated data in HTML comments in the 380 # Metadata should exist as colon-separated data in HTML comments in the
379 # input file. 381 # input file.
380 m=false 382 m=false
381 t=false 383 t=false
382 while read -r line; do 384 while read -r line; do
383 case "$line" in 385 case "$line" in
384 '<!--') m=true ;; 386 '<!--') m=true ;;
385 '-->') m=false ;; 387 '-->') m=false ;;
386 *title:*) t=true && print "$line" ;; 388 *title:*) t=true && print "$line" ;;
387 *) "$m" && print "$line" ;; 389 *) "$m" && print "$line" ;;
388 esac 390 esac
389 done <"$1" 391 done <"$1"
390 if ! "$t"; then 392 if ! "$t"; then
391 title="${1##*/}" 393 title="${1##*/}"
392 title="${title%.*}" 394 title="${title%.*}"
393 print "title: ${title%.*}" 395 print "title: ${title%.*}"
394 fi 396 fi
395} 397}
396 398
397meta() { # meta FIELD [FILE] 399meta() { # meta FIELD [FILE]
398 ## Extract metadata FIELDS from INPUT. 400 ## Extract metadata FIELDS from INPUT.
399 # FILE gives the filename to save metadata to in the $WORKDIR. It 401 # FILE gives the filename to save metadata to in the $WORKDIR. It
400 # defaults to the current value for $FILE. 402 # defaults to the current value for $FILE.
401 # 403 #
402 # Metadata should exist as colon-separated data in an HTML comment at 404 # Metadata should exist as colon-separated data in an HTML comment at
403 # the beginning of an input file. 405 # the beginning of an input file.
404 sed -n "s/^[ \t]*$1:[ \t]*//p" <"${2:-$META}" 406 sed -n "s/^[ \t]*$1:[ \t]*//p" <"${2:-$META}"
405} 407}
406 408
407## Customizable bits 409## Customizable bits
408 410
409filters() { # filters < INPUT 411filters() { # filters < INPUT
410 ## The filters to run input through. 412 ## The filters to run input through.
411 # This is a good candidate for customization in .vienna.sh. 413 # This is a good candidate for customization in .vienna.sh.
412 phtml | 414 phtml |
413 case "$PHTML_OPTIONS" in 415 case "$PHTML_OPTIONS" in
414 *expand*) expand ;; 416 *expand*) expand ;;
415 *) cat ;; 417 *) cat ;;
416 esac 418 esac
417} 419}
418 420
419### Site building 421### Site building
420 422
421genpage() { # genpage PAGE... 423genpage() { # genpage PAGE...
422 ## Compile PAGE(s) into $OUTDIR for publication. 424 ## Compile PAGE(s) into $OUTDIR for publication.
423 # Outputs a file of the format $OUTDIR/<PAGE>/index.html. 425 # Outputs a file of the format $OUTDIR/<PAGE>/index.html.
424 test -f "$PAGE_TEMPLATE" || return 1 426 test -f "$PAGE_TEMPLATE" || return 1
425 for FILE; do 427 for FILE; do
426 test -f "$FILE" || continue 428 test -f "$FILE" || continue
427 log genpage "$FILE" 429 log genpage "$FILE"
428 outd="$OUTDIR/${FILE%.$RAW_PAGE_EXTENSION}" 430 outd="$OUTDIR/${FILE%.$RAW_PAGE_EXTENSION}"
429 outf="$outd/index.html" 431 outf="$outd/index.html"
430 tmpf="$TEMPDIR/$FILE.tmp" 432 tmpf="$TEMPDIR/$FILE.tmp"
431 META="$TEMPDIR/$FILE.meta" 433 META="$TEMPDIR/$FILE.meta"
432 mkdir -p "$outd" 434 mkdir -p "$outd"
433 meta_init "$FILE" >"$META" 435 meta_init "$FILE" >"$META"
434 filters <"$FILE" >"$tmpf" 436 filters <"$FILE" >"$tmpf"
435 expand "$PAGE_TEMPLATE" <"$tmpf" >"$outf" 437 expand "$PAGE_TEMPLATE" <"$tmpf" >"$outf"
436 done 438 done
437} 439}
438 440
439genlist() { # genlist PERITEM_FUNC TEMPLATE_FILE PAGE... 441genlist() { # genlist PERITEM_FUNC TEMPLATE_FILE PAGE...
440 ## Generate a list. 442 ## Generate a list.
441 peritem_func="$1" 443 peritem_func="$1"
442 template_file="$2" 444 template_file="$2"
443 tmpf="$TEMPDIR/$1" 445 tmpf="$TEMPDIR/$1"
444 shift 2 || return 2 446 shift 2 || return 2
445 test -f "$template_file" || return 1 447 test -f "$template_file" || return 1
446 printf '%s\n' "$@" | sort_items | 448 printf '%s\n' "$@" | sort_items |
447 while read -r FILE; do 449 while read -r FILE; do
448 test -f "$FILE" || continue 450 test -f "$FILE" || continue
449 log genlist "$peritem_func:" "$FILE" 451 log genlist "$peritem_func:" "$FILE"
450 LINK="$URL_ROOT${URL_ROOT:+/}${FILE%.$RAW_PAGE_EXTENSION}" 452 LINK="$URL_ROOT${URL_ROOT:+/}${FILE%.$RAW_PAGE_EXTENSION}"
451 META="$TEMPDIR/$FILE.meta" 453 META="$TEMPDIR/$FILE.meta"
452 "$peritem_func" "$FILE" 454 "$peritem_func" "$FILE"
453 done | expand "$template_file" 455 done | expand "$template_file"
454} 456}
455 457
456sort_items() { # sort_items < ITEMS 458sort_items() { # sort_items < ITEMS
457 ## Sort ITEMS separated by newlines. 459 ## Sort ITEMS separated by newlines.
458 # This function assumes that no ITEM contains a newline. 460 # This function assumes that no ITEM contains a newline.
459 cat 461 cat
460} 462}
461 463
462index_item() { # index_item PAGE 464index_item() { # index_item PAGE
463 ## Construct a single item in an index.html. 465 ## Construct a single item in an index.html.
464 print "<li><a href=\"$LINK\">$(meta title "$1")</a></li>" 466 print "<li><a href=\"$LINK\">$(meta title "$1")</a></li>"
465} 467}
466 468
467feed_item() { # feed_item PAGE 469feed_item() { # feed_item PAGE
468 ## Construct a single item in an RSS feed. 470 ## Construct a single item in an RSS feed.
469 date="$(meta date "$1")" 471 date="$(meta date "$1")"
470 cat <<EOF 472 cat <<EOF
471<item> 473<item>
472 <title>$(meta title "$1")</title> 474 <title>$(meta title "$1")</title>
473 <link>$LINK</link> 475 <link>$LINK</link>
474 <guid>$LINK</guid> 476 <guid>$LINK</guid>
475 $(test -n "$date" && print "<pubDate>$date</pubDate>") 477 $(test -n "$date" && print "<pubDate>$date</pubDate>")
476 <description><![CDATA[$(filters<"$1")]]></description> 478 <description><![CDATA[$(filters <"$1")]]></description>
477</item> 479</item>
478EOF 480EOF
479} 481}
480 482
481static() { # static FILE... 483static() { # static FILE...
482 ## Copy static FILE(s) to $OUTDIR as-is. 484 ## Copy static FILE(s) to $OUTDIR as-is.
483 # Performs a simple heuristic to determine whether to copy a file or 485 # Performs a simple heuristic to determine whether to copy a file or
484 # not. 486 # not.
485 for FILE; do 487 for FILE; do
486 test -f "$FILE" || continue 488 test -f "$FILE" || continue
487 case "$FILE" in 489 case "$FILE" in
488 .*) continue ;; 490 .*) continue ;;
489 *.htm) continue ;; 491 *.htm) continue ;;
490 "$OUTDIR") continue ;; 492 "$OUTDIR") continue ;;
491 *) cp -r "$FILE" "$OUTDIR/" ;; 493 *) cp -r "$FILE" "$OUTDIR/" ;;
492 esac 494 esac
493 done 495 done
494} 496}
495 497
496### Do the thing! 498### Do the thing!