#!/bin/sh
needs_rebuild() {
src_file="$1"
out_file="$2"
[ ! -f "$out_file" ] && return 0
[ "$src_file" -nt "$out_file" ] && return 0
[ -f "./site.conf" ] && [ "./site.conf" -nt "$out_file" ] && return 0
[ -f "$src/site.conf" ] && [ "$src/site.conf" -nt "$out_file" ] && return 0
[ -f "$template" ] && [ "$template" -nt "$out_file" ] && return 0
[ -f "$script_dir/styles/$style.css" ] && [ "$script_dir/styles/$style.css" -nt "$out_file" ] && return 0
[ -f "$script_dir/styles/$style.root.css" ] && [ "$script_dir/styles/$style.root.css" -nt "$out_file" ] && return 0
[ -f "$src/styles.root.css" ] && [ "$src/styles.root.css" -nt "$out_file" ] && return 0
return 1
}
write_content_warning_outputs() {
_source_file="$1"
_content_out_file="$2"
_content_rel_url="$3"
_target_url="$4"
_landing_out_file="$5"
_is_home="$6"
is_cw_content_page="true"
render_markdown "$_source_file" "$_is_home" "$_target_url" > "$_content_out_file"
is_cw_content_page="false"
generate_content_warning_page "$fm_title" "$fm_content_warning" "$_content_rel_url" "$_target_url" "$_landing_out_file" "false"
}
build_site() {
echo "Building site from '$src' to '$out'..."
build_markdown_manifest
build_full_nav
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
rel_dir="${dir#"$src"}"
rel_dir="${rel_dir#/}"
[ -z "$rel_dir" ] && rel_dir="."
out_dir="$out/$rel_dir"
mkdir -p "$out_dir"
if [ -f "$dir/styles.css" ]; then
if needs_rebuild "$dir/styles.css" "$out_dir/styles.css"; then
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
fi
elif [ -f "$dir/style.css" ]; then
if needs_rebuild "$dir/style.css" "$out_dir/styles.css"; then
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
fi
fi
[ "$dir_indexes" != "true" ] && continue
has_custom_index="false"
has_list="false"
if [ -f "$dir/index.md" ]; then
has_custom_index="true"
if grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$dir/index.md" 2>/dev/null; then
has_list="true"
fi
fi
if [ "$has_custom_index" = "false" ] || [ "$has_list" = "true" ]; then
is_posts_dir="false"
if is_posts_directory_rel "$rel_dir"; then
is_posts_dir="true"
fi
if [ "$single_file_index" = "true" ] && [ "$is_posts_dir" = "false" ] && [ "$has_list" = "false" ]; then
if load_manifest_dir_entry "$rel_dir" && [ "$dir_md_count" -eq 1 ]; then
md_file="$src/$dir_first_md"
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
target_url=$(directory_index_url "$rel_dir")
if needs_rebuild "$md_file" "$out_dir/index.html"; then
parse_frontmatter "$md_file"
if [ -n "$fm_content_warning" ]; then
content_out_file="$out_dir/content.html"
if [ "$rel_dir" = "." ]; then
content_rel_url="/content.html"
else
content_rel_url="/$(encode_url_path "$rel_dir")/content.html"
fi
write_content_warning_outputs "$md_file" "$content_out_file" "$content_rel_url" "$target_url" "$out_dir/index.html" "$is_home"
else
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
fi
fi
continue
fi
fi
temp_index="$KEWT_TMPDIR/index.md"
temp_list="$KEWT_TMPDIR/list.md"
: > "$temp_list"
if [ "$has_custom_index" = "false" ]; then
display_dir="${rel_dir#.}"
[ -z "$display_dir" ] && display_dir="/"
echo "# Index of $display_dir" > "$temp_index"
echo "" >> "$temp_index"
fi
sort_args=""
# If this is the posts dir reverse
if is_posts_directory_rel "$rel_dir"; then
sort_args="-r"
fi
temp_entries="$KEWT_TMPDIR/entries_$$.txt"
: > "$temp_entries"
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | while read -r entry; do
name="${entry##*/}"
case "$name" in
template.html|site.conf|style.css|styles.root.css|index.md) continue ;;
esac
if [ -d "$entry" ]; then
entry_rel_dir="${entry#"$src"/}"
manifest_dir_hidden_by_draft_index "$entry_rel_dir" && continue
dir_url="$(encode_url_path "$name")/index.html"
echo "${name}|- [${name}/](${dir_url})" >> "$temp_entries"
elif [ "${entry%.md}" != "$entry" ]; then
entry_rel_path="${entry#"$src"/}"
load_manifest_entry "$entry_rel_path" || continue
label="${name%.md}"
[ "$manifest_draft" = "true" ] && continue
post_h="$manifest_title"
is_post_entry="false"
if is_posts_directory_rel "$rel_dir"; then
is_post_entry="true"
fi
if [ -n "$post_h" ]; then
if [ "$is_post_entry" = "true" ]; then
if [ -n "$manifest_post_time" ]; then
label="$post_h - $manifest_post_date $manifest_post_time"
else
label="$post_h - $manifest_post_date"
fi
else
label="$post_h"
fi
elif [ "$is_post_entry" = "true" ]; then
if [ -n "$manifest_post_time" ]; then
label="$manifest_post_date $manifest_post_time"
else
label="$manifest_post_date"
fi
fi
if [ "$is_post_entry" = "true" ]; then
sort_key="${manifest_post_date} ${manifest_post_time}"
else
sort_key="$name"
fi
entry_url=$(encode_url_path "${name%.md}.html")
echo "${sort_key}|- [$label](${entry_url})|$name|${entry_url}" >> "$temp_entries"
else
asset_url=$(encode_url_path "$name")
echo "${name}|- [$name]($asset_url)|$name|$asset_url" >> "$temp_entries"
fi
done
if [ "$is_posts_dir" = "true" ]; then
LC_ALL=C sort $sort_args "$temp_entries" > "$KEWT_TMPDIR/sorted_entries_$$.txt"
cut -d'|' -f2 "$KEWT_TMPDIR/sorted_entries_$$.txt" >> "$temp_list"
mkdir -p "$KEWT_TMPDIR/prevnext"
awk -F'|' '
{
name[NR] = $3
url[NR] = $4
}
END {
for(i=1; i<=NR; i++) {
prev_str = ""
next_str = ""
if(i > 1) {
next_str = "[Next >](" url[i-1] ")"
}
if(i < NR) {
prev_str = "[< Previous](" url[i+1] ")"
}
if (prev_str != "" || next_str != "") {
out = "'"$KEWT_TMPDIR"'/prevnext/" name[i]
printf "%s|%s\n", prev_str, next_str > out
}
}
}
' "$KEWT_TMPDIR/sorted_entries_$$.txt"
rm -f "$KEWT_TMPDIR/sorted_entries_$$.txt"
else
LC_ALL=C sort $sort_args "$temp_entries" | cut -d'|' -f2 >> "$temp_list"
fi
rm -f "$temp_entries"
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
target_url=$(directory_index_url "$rel_dir")
num_items=$(wc -l < "$temp_list")
if [ "$is_posts_dir" = "true" ] && [ -n "$posts_per_page" ] && [ "$posts_per_page" -gt 0 ] && [ "$num_items" -gt "$posts_per_page" ]; then
num_pages=$(( (num_items + posts_per_page - 1) / posts_per_page ))
p=1
while [ "$p" -le "$num_pages" ]; do
chunk_list="$KEWT_TMPDIR/chunk.md"
start_line=$(( (p - 1) * posts_per_page + 1 ))
tail -n +$start_line "$temp_list" | head -n "$posts_per_page" > "$chunk_list"
base_url_dir="$(dirname "$target_url")"
[ "$base_url_dir" = "/" ] && base_url_dir=""
nav_html="
"
if [ "$p" -gt 1 ]; then
if [ "$p" -eq 2 ]; then
nav_html="$nav_html « Prev "
else
nav_html="$nav_html « Prev "
fi
fi
nav_html="$nav_html Page $p of $num_pages "
if [ "$p" -lt "$num_pages" ]; then
nav_html="$nav_html Next » "
fi
nav_html="$nav_html
"
echo "" >> "$chunk_list"
echo "$nav_html" >> "$chunk_list"
temp_index_p="$KEWT_TMPDIR/index_p$p.md"
if [ "$has_custom_index" = "false" ]; then
display_dir="${rel_dir#.}"
[ -z "$display_dir" ] && display_dir="/"
echo "# Index of $display_dir" > "$temp_index_p"
echo "" >> "$temp_index_p"
else
: > "$temp_index_p"
fi
if [ "$has_custom_index" = "true" ]; then
awk '
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
while((getline line < "'"$chunk_list"'") > 0) print line
close("'"$chunk_list"'")
next
}
{ print }
' "$dir/index.md" >> "$temp_index_p"
else
cat "$chunk_list" >> "$temp_index_p"
fi
if [ "$p" -eq 1 ]; then
out_file="$out_dir/index.html"
target_url_p="$target_url"
else
out_file="$out_dir/page/$p/index.html"
target_url_p="$base_url_dir/page/$p/index.html"
mkdir -p "$(dirname "$out_file")"
fi
render_markdown "$temp_index_p" "$is_home" "$target_url_p" > "$out_file"
rm -f "$temp_index_p" "$chunk_list"
p=$((p + 1))
done
else
if [ "$has_custom_index" = "true" ]; then
awk '
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
while((getline line < "'"$temp_list"'") > 0) print line
close("'"$temp_list"'")
next
}
{ print }
' "$dir/index.md" > "$temp_index"
else
cat "$temp_list" >> "$temp_index"
fi
do_rebuild="false"
needs_rebuild "$dir" "$out_dir/index.html" && do_rebuild="true"
[ "$has_custom_index" = "true" ] && needs_rebuild "$dir/index.md" "$out_dir/index.html" && do_rebuild="true"
if [ "$do_rebuild" = "false" ] && [ -f "$out_dir/index.html" ]; then
for _child in "$dir"/*; do
[ -e "$_child" ] || continue
if [ "$_child" -nt "$out_dir/index.html" ]; then
do_rebuild="true"
break
fi
done
fi
if [ "$do_rebuild" = "true" ]; then
if [ "$has_custom_index" = "true" ]; then
parse_frontmatter "$dir/index.md"
else
fm_content_warning=""
fi
if [ -n "$fm_content_warning" ]; then
content_out_file="$out_dir/content.html"
if [ "$rel_dir" = "." ]; then
content_rel_url="/content.html"
else
content_rel_url="/$(encode_url_path "$rel_dir")/content.html"
fi
write_content_warning_outputs "$temp_index" "$content_out_file" "$content_rel_url" "$target_url" "$out_dir/index.html" "$is_home"
else
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
fi
fi
fi
rm -f "$temp_index" "$temp_list"
fi
done
if [ ! -f "$src/styles.css" ] && [ ! -f "$src/style.css" ]; then
if [ -f "$src/styles.root.css" ]; then
_base_css="$script_dir/styles/$style.css"
[ ! -f "$_base_css" ] && _base_css="$script_dir/styles/kewt.css"
if [ ! -f "$out/styles.css" ] || [ "$src/styles.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
merge_root_style "$src/styles.root.css" "$_base_css" "$out/styles.css"
fi
elif [ -f "$script_dir/styles/$style.css" ]; then
if needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
fi
elif [ -f "$script_dir/styles/$style.root.css" ]; then
_base_css="$script_dir/styles/kewt.css"
if [ ! -f "$out/styles.css" ] || [ "$script_dir/styles/$style.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
merge_root_style "$script_dir/styles/$style.root.css" "$_base_css" "$out/styles.css"
fi
fi
fi
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
rel_path="${file#"$src"}"
rel_path="${rel_path#/}"
dir_rel=$(dirname "$rel_path")
out_dir="$out/$dir_rel"
case "${file##*/}" in
template.html|site.conf|style.css|styles.css|styles.root.css) continue ;;
esac
if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then
continue
fi
is_preserved=0
if [ -n "$(eval "find \"$file\" \( $PRESERVE_ARGS \) -print")" ]; then
is_preserved=1
fi
is_posts_dir_2="false"
if is_posts_directory_rel "$dir_rel"; then
is_posts_dir_2="true"
fi
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ] && [ "$is_posts_dir_2" = "false" ]; then
load_manifest_dir_entry "$dir_rel" && [ "$dir_md_count" -eq 1 ] && continue
fi
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
load_manifest_entry "$rel_path" || continue
[ "$manifest_draft" = "true" ] && continue
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
out_file="$out/${rel_path%.md}.html"
if needs_rebuild "$file" "$out_file"; then
fm_title="$manifest_title"
fm_content_warning="$manifest_content_warning"
if [ -n "$manifest_content_warning" ]; then
content_out_file="$out/${rel_path%.md}-content.html"
content_rel_url="/$(encode_url_path "${rel_path%.md}")-content.html"
orig_rel_url="$manifest_url"
write_content_warning_outputs "$file" "$content_out_file" "$content_rel_url" "$orig_rel_url" "$out_file" "$is_home"
else
render_markdown "$file" "$is_home" > "$out_file"
fi
fi
else
if needs_rebuild "$file" "$out/$rel_path"; then
cp "$file" "$out/$rel_path"
fi
fi
done
if [ -n "$error_page" ] && [ ! -f "$out/$error_page" ]; then
temp_404="$KEWT_TMPDIR/404_gen.md"
echo "# 404 - Not Found" > "$temp_404"
echo "" >> "$temp_404"
echo "The requested page could not be found." >> "$temp_404"
render_markdown "$temp_404" "false" "/$error_page" > "$out/$error_page"
rm -f "$temp_404"
fi
if [ -n "$base_url" ]; then
sitemap_file="$out/sitemap.xml"
base_url="${base_url%/}"
today=$(date +%Y-%m-%d)
printf '\n' > "$sitemap_file"
printf '\n' >> "$sitemap_file"
find "$out" -type f -name "*.html" -print | sort | while IFS= read -r html_file; do
rel_url="${html_file#"$out"}"
# Don't include 404 in the sitemap (duh)
[ "${rel_url#/}" = "$error_page" ] && continue
{
printf ' \n'
printf ' %s%s\n' "$base_url" "$rel_url"
printf ' %s\n' "$today"
printf ' \n'
} >> "$sitemap_file"
done
printf '\n' >> "$sitemap_file"
fi
if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
feed_path="$out/$feed_file"
base_url_feed="${base_url%/}"
build_date=$(date -u '+%a, %d %b %Y %H:%M:%S +0000')
printf '\n' > "$feed_path"
{
printf '\n'
printf ' \n'
printf ' %s\n' "$title"
printf ' %s\n' "$base_url_feed"
printf ' %s\n' "$title"
printf ' %s\n' "$build_date"
} >> "$feed_path"
temp_feed_files="$KEWT_TMPDIR/feed_files_$$.txt"
: > "$temp_feed_files"
while IFS= read -r manifest_rel_path; do
case "$manifest_rel_path" in
*"${posts_dir:-__no_posts__}"*) ;;
*) continue ;;
esac
load_manifest_entry "$manifest_rel_path" || continue
[ "$manifest_draft" = "true" ] && continue
printf '%s %s|%s\n' "$manifest_post_date" "$manifest_post_time" "$manifest_rel_path" >> "$temp_feed_files"
done < "$manifest_all_list"
LC_ALL=C sort -r "$temp_feed_files" | cut -d'|' -f2- | while IFS= read -r post_rel_path; do
load_manifest_entry "$post_rel_path" || continue
[ "$manifest_draft" = "true" ] && continue
post_date="$manifest_post_date"
post_time="$manifest_post_time"
post_heading="$manifest_title"
post_slug="$manifest_post_slug"
if [ -z "$post_heading" ] && [ -n "$post_slug" ] && ! echo "$post_slug" | grep -q '^[0-9]\+$'; then
post_heading=$(echo "$post_slug" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)}1')
fi
feed_post_title="$post_heading - $post_date $post_time"
post_url="$base_url_feed$manifest_url"
pub_date=$(format_rfc2822_utc "$post_date" "$post_time")
{
printf ' \n'
printf ' %s\n' "$feed_post_title"
printf ' %s\n' "$post_url"
printf ' %s\n' "$post_url"
printf ' %s\n' "$pub_date"
printf ' \n'
} >> "$feed_path"
done
printf ' \n' >> "$feed_path"
printf '\n' >> "$feed_path"
fi
if [ "$generate_search" = "true" ] || [ "$generate_tags" = "true" ]; then
if [ "$generate_search" = "true" ]; then
printf '[\n' > "$out/search.json"
fi
first_search_item="true"
temp_tags="$KEWT_TMPDIR/tags_$$.txt"
: > "$temp_tags"
while IFS= read -r rel_path; do
load_manifest_entry "$rel_path" || continue
if [ "$manifest_is_index" = "true" ]; then
if [ "$rel_path" = "index.md" ]; then
md_url="/index.html"
else
md_url=$(directory_index_url "${rel_path%/index.md}")
fi
else
md_url="$manifest_url"
if [ "$single_file_index" = "true" ]; then
rel_dir_of_file="$manifest_dir_rel"
[ -z "$rel_dir_of_file" ] && rel_dir_of_file="."
if [ "$rel_dir_of_file" = "." ]; then
dir_of_file="$src"
else
dir_of_file="$src/$rel_dir_of_file"
fi
is_posts_dir_search="false"
if [ -n "$posts_dir" ] && { [ "$rel_dir_of_file" = "$posts_dir" ] || [ "./$rel_dir_of_file" = "$posts_dir" ]; }; then
is_posts_dir_search="true"
fi
if [ "$is_posts_dir_search" = "false" ] && [ ! -f "$dir_of_file/index.md" ]; then
if load_manifest_dir_entry "$rel_dir_of_file" && [ "$dir_md_count" -eq 1 ]; then
if [ "$rel_dir_of_file" = "." ]; then
md_url="/index.html"
else
md_url=$(directory_index_url "$rel_dir_of_file")
fi
fi
fi
fi
fi
[ "$manifest_draft" = "true" ] && continue
md_heading="$manifest_title"
if [ "$generate_search" = "true" ]; then
if [ -z "$manifest_content_warning" ] || [ "$include_cw_pages_in_search" = "true" ]; then
md_content="$manifest_search_content"
if [ "$first_search_item" = "false" ]; then
printf ',\n' >> "$out/search.json"
fi
printf ' {"url": "%s", "title": "%s", "content": "%s"}' "$md_url" "$md_heading" "$md_content" >> "$out/search.json"
first_search_item="false"
fi
fi
if [ "$generate_tags" = "true" ] && [ -n "$manifest_tags" ]; then
old_ifs=$IFS
IFS=','
for tag in $manifest_tags; do
tag=$(echo "$tag" | sed 's/^[ \t]*//;s/[ \t]*$//')
[ -z "$tag" ] && continue
printf '%s|%s|%s\n' "$tag" "$md_url" "$md_heading" >> "$temp_tags"
done
IFS=$old_ifs
fi
done < "$manifest_visible_list"
if [ "$generate_search" = "true" ]; then
printf '\n]\n' >> "$out/search.json"
cp "$script_dir/lib/search.js" "$out/search.js"
search_md="$KEWT_TMPDIR/search_$$.md"
printf '%s\n' '# Search' '' \
'' '' \
'