375 lines
10 KiB
Bash
Executable File
375 lines
10 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
die() {
|
|
echo "Error: $1" >&2
|
|
exit 1
|
|
}
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $0 [--from <src>] [--to <out>]
|
|
$0 [src] [out]
|
|
$0 --new [title]
|
|
$0 --help
|
|
|
|
Options:
|
|
--help Show this help message.
|
|
--new [title] Create a new site directory (default: site)
|
|
--from <src> Source directory (default: site)
|
|
--to <out> Output directory (default: out)
|
|
EOF
|
|
}
|
|
|
|
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
awk_dir="$script_dir/awk"
|
|
|
|
ensure_root_defaults() {
|
|
if [ ! -f "./site.conf" ]; then
|
|
cat > "./site.conf" <<'EOF'
|
|
title = "kewt"
|
|
style = "kewt"
|
|
dir_indexes = true
|
|
single_file_index = true
|
|
flatten = false
|
|
order = ""
|
|
footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
|
|
logo = ""
|
|
display_logo = false
|
|
display_title = true
|
|
logo_as_favicon = true
|
|
favicon = ""
|
|
EOF
|
|
fi
|
|
|
|
if [ ! -f "./template.html" ]; then
|
|
cat > "./template.html" <<'EOF'
|
|
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>{{TITLE}}</title>
|
|
|
|
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
|
{{HEAD_EXTRA}}
|
|
</head>
|
|
|
|
<body>
|
|
<header>
|
|
<h1>{{HEADER_BRAND}}</h1>
|
|
</header>
|
|
|
|
<nav id="side-bar">{{NAV}}</nav>
|
|
|
|
<article>{{CONTENT}}</article>
|
|
<footer>{{FOOTER}}</footer>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
create_new_site() {
|
|
new_title="$1"
|
|
new_dir="site"
|
|
[ -n "$new_title" ] && new_dir="$new_title"
|
|
|
|
[ -e "$new_dir" ] && die "Target '$new_dir' already exists."
|
|
|
|
ensure_root_defaults
|
|
|
|
mkdir -p "$new_dir"
|
|
cp "./site.conf" "$new_dir/site.conf"
|
|
printf "# _kewt_ website\n" > "$new_dir/index.md"
|
|
|
|
if [ -n "$new_title" ]; then
|
|
awk -v new_title="$new_title" '
|
|
BEGIN { done = 0 }
|
|
/^title[[:space:]]*=/ {
|
|
print "title = \"" new_title "\""
|
|
done = 1
|
|
next
|
|
}
|
|
{ print }
|
|
END {
|
|
if (!done) print "title = \"" new_title "\""
|
|
}
|
|
' "$new_dir/site.conf" > "$new_dir/site.conf.tmp" && mv "$new_dir/site.conf.tmp" "$new_dir/site.conf"
|
|
fi
|
|
|
|
echo "Created new site at '$new_dir'."
|
|
exit 0
|
|
}
|
|
|
|
generate_nav() {
|
|
dinfo=$(find "$1" -not -path '*/.*' | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
|
|
find "$1" -name "*.md" | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
|
|
}
|
|
|
|
src=""
|
|
out=""
|
|
new_mode="false"
|
|
new_title=""
|
|
positional_count=0
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--help|-h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--new)
|
|
new_mode="true"
|
|
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
|
new_title="$2"
|
|
shift
|
|
fi
|
|
;;
|
|
--from)
|
|
[ $# -lt 2 ] && die "--from requires a value."
|
|
src="$2"
|
|
shift
|
|
;;
|
|
--to)
|
|
[ $# -lt 2 ] && die "--to requires a value."
|
|
out="$2"
|
|
shift
|
|
;;
|
|
--*)
|
|
die "Unknown option: $1"
|
|
;;
|
|
*)
|
|
positional_count=$((positional_count + 1))
|
|
if [ "$positional_count" -eq 1 ]; then
|
|
[ -z "$src" ] && src="$1" || die "Source already set (use either positional or --from)."
|
|
elif [ "$positional_count" -eq 2 ]; then
|
|
[ -z "$out" ] && out="$1" || die "Output already set (use either positional or --to)."
|
|
else
|
|
die "Too many positional arguments."
|
|
fi
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[ "$new_mode" = "true" ] && create_new_site "$new_title"
|
|
|
|
ensure_root_defaults
|
|
|
|
[ -z "$src" ] && src="site"
|
|
[ -z "$out" ] && out="out"
|
|
|
|
[ -d "$src" ] || die "Source directory '$src' does not exist."
|
|
|
|
title="kewt"
|
|
style="kewt"
|
|
footer="made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
|
dir_indexes="true"
|
|
single_file_index="true"
|
|
flatten="false"
|
|
order=""
|
|
logo=""
|
|
display_logo="false"
|
|
display_title="true"
|
|
logo_as_favicon="true"
|
|
favicon=""
|
|
|
|
load_config() {
|
|
[ -f "$1" ] || return
|
|
while IFS= read -r line; do
|
|
case "$line" in
|
|
''|'#'*) continue ;;
|
|
*=*) ;;
|
|
*) continue ;;
|
|
esac
|
|
|
|
key=${line%%=*}
|
|
val=${line#*=}
|
|
|
|
key=$(printf '%s' "$key" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
|
val=$(printf '%s' "$val" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
|
case "$val" in
|
|
\"*\") val=${val#\"}; val=${val%\"} ;;
|
|
\'*\') val=${val#\'}; val=${val%\'} ;;
|
|
esac
|
|
|
|
case "$key" in
|
|
title) title="$val" ;;
|
|
style) style="$val" ;;
|
|
dir_indexes) dir_indexes="$val" ;;
|
|
single_file_index) single_file_index="$val" ;;
|
|
flatten) flatten="$val" ;;
|
|
order) order="$val" ;;
|
|
footer) footer="$val" ;;
|
|
logo) logo="$val" ;;
|
|
display_logo) display_logo="$val" ;;
|
|
display_title) display_title="$val" ;;
|
|
logo_as_favicon) logo_as_favicon="$val" ;;
|
|
favicon) favicon="$val" ;;
|
|
esac
|
|
done < "$1"
|
|
}
|
|
|
|
load_config "./site.conf"
|
|
load_config "$src/site.conf"
|
|
|
|
template="$src/template.html"
|
|
[ -f "$template" ] || template="./template.html"
|
|
[ -f "$template" ] || die "Template '$template' not found."
|
|
|
|
[ -d "$out" ] && rm -rf "$out"
|
|
mkdir -p "$out"
|
|
|
|
nav=$(generate_nav "$src")
|
|
|
|
find_closest() {
|
|
target="$1"
|
|
start_dir="$2"
|
|
curr="$start_dir"
|
|
while [ "$curr" != "$src" ] && [ "$curr" != "." ] && [ "$curr" != "/" ]; do
|
|
if [ -f "$curr/$target" ]; then
|
|
echo "$curr/$target"
|
|
return
|
|
fi
|
|
curr=$(dirname "$curr")
|
|
done
|
|
if [ -f "$src/$target" ]; then
|
|
echo "$src/$target"
|
|
fi
|
|
}
|
|
|
|
copy_style_with_resolved_vars() {
|
|
src_style="$1"
|
|
out_style="$2"
|
|
awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style"
|
|
}
|
|
|
|
render_markdown() {
|
|
file="$1"
|
|
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
|
[ -z "$local_template" ] && local_template="$template"
|
|
|
|
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
|
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
|
if [ -n "$closest_style_src" ]; then
|
|
style_rel_to_src="${closest_style_src#$src/}"
|
|
case "$closest_style_src" in
|
|
"$src/styles.css") style_rel_to_src="styles.css" ;;
|
|
"$src/style.css") style_rel_to_src="style.css" ;;
|
|
esac
|
|
style_path="/${style_rel_to_src%styles.css}"
|
|
style_path="${style_path%style.css}styles.css"
|
|
else
|
|
style_path="/styles.css"
|
|
fi
|
|
|
|
logo_html=""
|
|
if [ "$display_logo" = "true" ] && [ -n "$logo" ]; then
|
|
logo_html="<img class=\"site-logo\" src=\"$logo\" alt=\"$title\" />"
|
|
fi
|
|
|
|
brand_text=""
|
|
if [ "$display_title" = "true" ]; then
|
|
brand_text="$title"
|
|
fi
|
|
|
|
if [ -n "$logo_html" ] && [ -n "$brand_text" ]; then
|
|
header_brand="<a href=\"/index.html\">$logo_html $brand_text</a>"
|
|
elif [ -n "$logo_html" ]; then
|
|
header_brand="<a href=\"/index.html\">$logo_html</a>"
|
|
elif [ -n "$brand_text" ]; then
|
|
header_brand="<a href=\"/index.html\">$brand_text</a>"
|
|
else
|
|
header_brand="<a href=\"/index.html\">$title</a>"
|
|
fi
|
|
|
|
favicon_src=""
|
|
if [ "$logo_as_favicon" = "true" ] && [ -n "$logo" ]; then
|
|
favicon_src="$logo"
|
|
elif [ -n "$favicon" ]; then
|
|
favicon_src="$favicon"
|
|
fi
|
|
head_extra=""
|
|
if [ -n "$favicon_src" ]; then
|
|
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
|
fi
|
|
|
|
MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="styles/$style.css" sh "$script_dir/markdown.sh" "$file" | awk -v title="$title" -v nav="$nav" -v footer="$footer" -v style_path="$style_path" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
|
|
}
|
|
|
|
echo "Building site from '$src' to '$out'..."
|
|
|
|
find "$src" -type d | sort | while read -r dir; do
|
|
rel_dir="${dir#$src/}"
|
|
[ "$dir" = "$src" ] && rel_dir="."
|
|
out_dir="$out/$rel_dir"
|
|
mkdir -p "$out_dir"
|
|
|
|
if [ -f "$dir/styles.css" ]; then
|
|
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
|
elif [ -f "$dir/style.css" ]; then
|
|
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
|
fi
|
|
|
|
[ "$dir_indexes" != "true" ] && continue
|
|
|
|
if [ ! -f "$dir/index.md" ]; then
|
|
if [ "$single_file_index" = "true" ]; then
|
|
md_count=$(find "$dir" -maxdepth 1 -name "*.md" | wc -l)
|
|
if [ "$md_count" -eq 1 ]; then
|
|
md_file=$(find "$dir" -maxdepth 1 -name "*.md")
|
|
render_markdown "$md_file" > "$out_dir/index.html"
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
temp_index="/tmp/kewt_index_$$.md"
|
|
display_dir="${rel_dir#.}"
|
|
[ -z "$display_dir" ] && display_dir="/"
|
|
echo "# Index of $display_dir" > "$temp_index"
|
|
echo "" >> "$temp_index"
|
|
find "$dir" -maxdepth 1 -not -path '*/.*' -not -path "$dir" | sort | while read -r entry; do
|
|
name="${entry##*/}"
|
|
case "$name" in
|
|
template.html|site.conf|style.css|index.md) continue ;;
|
|
esac
|
|
if [ -d "$entry" ]; then
|
|
echo "- [${name}/](${name}/index.html)" >> "$temp_index"
|
|
elif [ "${entry%.md}" != "$entry" ]; then
|
|
echo "- [${name%.md}](${name%.md}.html)" >> "$temp_index"
|
|
else
|
|
echo "- [$name]($name)" >> "$temp_index"
|
|
fi
|
|
done
|
|
render_markdown "$temp_index" > "$out_dir/index.html"
|
|
rm "$temp_index"
|
|
fi
|
|
done
|
|
|
|
if [ ! -f "$out/styles.css" ] && [ -f "styles/$style.css" ]; then
|
|
copy_style_with_resolved_vars "styles/$style.css" "$out/styles.css"
|
|
fi
|
|
|
|
find "$src" -type f | sort | while IFS= read -r file; do
|
|
rel_path="${file#$src/}"
|
|
dir_rel=$(dirname "$rel_path")
|
|
out_dir="$out/$dir_rel"
|
|
|
|
case "${file##*/}" in
|
|
template.html|site.conf|style.css|styles.css) continue ;;
|
|
esac
|
|
|
|
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ ! -f "$(dirname "$file")/index.md" ]; then
|
|
md_count=$(find "$(dirname "$file")" -maxdepth 1 -name "*.md" | wc -l)
|
|
[ "$md_count" -eq 1 ] && continue
|
|
fi
|
|
|
|
if [ "${file%.md}" != "$file" ]; then
|
|
out_file="$out/${rel_path%.md}.html"
|
|
render_markdown "$file" > "$out_file"
|
|
else
|
|
cp "$file" "$out/$rel_path"
|
|
fi
|
|
done
|
|
|
|
echo "Build complete."
|