From b4f9be57d3afbe19970bb268ecb6723cd80e703e Mon Sep 17 00:00:00 2001
From: Joe Carstairs
Date: Tue, 9 Jun 2026 16:11:06 +0100
Subject: [PATCH] http: builds blogs from gemlogs
---
http/.gitignore | 2 +
http/Makefile | 14 +-
http/bin/blog | 88 +++++++++
http/bin/gmi2upphtml | 176 ++++++++++++++++++
http/bin/mkws | 44 +++--
http/blogs/blog | 1 +
http/blogs/microlog | 1 +
http/share/components/formatted_date.upphtml | 4 +
http/share/layouts/blog.layout.upphtml | 50 +++++
.../default.layout.upphtml} | 0
http/share/layouts/microlog.layout.upphtml | 50 +++++
11 files changed, 414 insertions(+), 16 deletions(-)
create mode 100755 http/bin/blog
create mode 100755 http/bin/gmi2upphtml
create mode 120000 http/blogs/blog
create mode 120000 http/blogs/microlog
create mode 100644 http/share/components/formatted_date.upphtml
create mode 100644 http/share/layouts/blog.layout.upphtml
rename http/share/{l.upphtml => layouts/default.layout.upphtml} (100%)
create mode 100644 http/share/layouts/microlog.layout.upphtml
diff --git a/http/.gitignore b/http/.gitignore
index 82e4abd..f6b18c2 100644
--- a/http/.gitignore
+++ b/http/.gitignore
@@ -1,2 +1,4 @@
out
vendor
+src/microlog
+src/blog
diff --git a/http/Makefile b/http/Makefile
index 9e468f7..eb8e440 100644
--- a/http/Makefile
+++ b/http/Makefile
@@ -1,8 +1,18 @@
all: clean out
-out: $(shell find src -type f) $(shell find share -type f) $(shell find bin)
+out: microlog blog $(shell find src -type f) $(shell find share -type f) $(shell find bin)
./bin/mkws https://joeac.net
+microlog: src/microlog
+
+src/microlog: $(shell find -L blogs/microlog -type f) bin/blog
+ ./bin/blog -i microlog -l microlog -t
+
+blog: src/blog
+
+src/blog: $(shell find -L blogs/blog -type f) bin/blog
+ ./bin/blog -i blog -l blog
+
.PHONY: clean
clean:
- rm -rf out
+ rm -rf out src/microlog src/blog
diff --git a/http/bin/blog b/http/bin/blog
new file mode 100755
index 0000000..a16a692
--- /dev/null
+++ b/http/bin/blog
@@ -0,0 +1,88 @@
+#!/bin/sh -e
+export PATH="$(dirname $(realpath $0)):$PATH"
+export BLOGS="$(realpath blogs)"
+export SRC="$(realpath src)"
+export LAYOUTS="$(realpath share/layouts)"
+
+usage()
+{
+ >&2 cat << USAGE
+usage: blog -i LOG [-l LAYOUT] [-t]
+
+options:
+
+ -i LOG
+ The name of the source directory under \$BLOGS where log posts live.
+
+ -l LAYOUT
+ If provided, the layout under \$LAYOUTS to use for the generated
+ upphtml files. If not provided, uses the default layout.
+
+ -t
+ If enabled, use filenames as post titles
+USAGE
+}
+
+log_name=""
+layout="default"
+use_filenames_for_titles=0
+
+while getopts "i:l:t" opt
+do
+ case $opt in
+ i)
+ log_name="$OPTARG"
+ ;;
+ l)
+ layout_name="$OPTARG"
+ ;;
+ t)
+ use_filenames_for_titles=1
+ ;;
+ esac
+done
+
+if [ -z "${log_name}" ]
+then
+ usage
+ exit 1
+fi
+
+if ! [ -d "${BLOGS}/${log_name}" ]
+then
+ >&2 echo "there is no directory '${input_log}' under ${BLOGS}. options: $(ls common)"
+ usage
+ exit 1
+fi
+
+layout="${LAYOUTS}/${layout_name}.layout.upphtml"
+if [ "${layout_name}" = "default" ]
+then
+ layout_ext=""
+else
+ layout_ext=".${layout_name}"
+fi
+if ! [ -f "${layout}" ]
+then
+ echo >&2 "Warning: no layout found at $(basename "${LAYOUTS}")/${layout_name}.layout.upphtml"
+fi
+
+rm -rf "${SRC}/${log_name}/"
+mkdir -p "${SRC}/${log_name}"
+
+for entry in $(find -L "${BLOGS}/${log_name}" -type f)
+do
+ basename="$(basename "$entry")"
+ name="${basename%%\.*}"
+ dest="${SRC}/${log_name}/${name}${layout_ext}.upphtml"
+ if [ $use_filenames_for_titles -eq 1 ]
+ then
+ title="$name"
+ cat "$entry" | gmi2upphtml "$title" > "$dest"
+ else
+ title="$(grep --regexp "^\# " --max-count 1 "$entry" | cut -c3-)"
+ cat "$entry" | grep --regexp "^\# " --invert-match | gmi2upphtml "$title" > "$dest"
+ fi
+ echo "${title}" > "${dest}.args"
+ echo "Processed ${entry#$(dirname ${BLOGS})/} to ${dest#$(dirname ${SRC})/}"
+done
diff --git a/http/bin/gmi2upphtml b/http/bin/gmi2upphtml
new file mode 100755
index 0000000..608f4e8
--- /dev/null
+++ b/http/bin/gmi2upphtml
@@ -0,0 +1,176 @@
+#!/bin/sh -e
+
+post_title="${1:-}"
+
+is_in_list=0
+is_in_link_block=0
+is_in_preformatted_block=0
+is_in_blockquote=0
+
+if_in_list_then_close()
+{
+ if [ $is_in_list -ne 0 ]
+ then
+ echo ""
+ is_in_list=0
+ fi
+}
+
+if_in_link_block_then_close()
+{
+ if [ $is_in_link_block -ne 0 ]
+ then
+ echo "
"
+ is_in_link_block=0
+ fi
+}
+
+if_in_preformatted_block_then_close()
+{
+ if [ $is_in_preformatted_block -ne 0 ]
+ then
+ echo ""
+ is_in_preformatted_block=0
+ fi
+}
+
+if_in_blockquote_then_close()
+{
+ if [ $is_in_blockquote -ne 0 ]
+ then
+ echo ""
+ is_in_blockquote=0
+ fi
+}
+
+close_all_open_blocks()
+{
+ if_in_list_then_close
+ if_in_link_block_then_close
+ if_in_preformatted_block_then_close
+ if_in_blockquote_then_close
+}
+
+escape_upphtml()
+{
+ echo "$1" | sed "s/\`/\\\\\`/g" | sed "s/\"/\\\\\"/g" | sed "s/\\\$/\\\\\$/g"
+}
+
+close_all_open_blocks_except()
+{
+ except="$1"
+ if [ "$except" != "list" ]
+ then
+ if_in_list_then_close
+ fi
+ if [ "$except" != "link" ]
+ then
+ if_in_link_block_then_close
+ fi
+ if [ "$except" != "pre" ]
+ then
+ if_in_preformatted_block_then_close
+ fi
+ if [ "$except" != "blockquote" ]
+ then
+ if_in_blockquote_then_close
+ fi
+}
+
+if [ -z "$post_title" ]
+then
+ read first_line
+ if [ "$(echo "$first_line" | cut -c1-2)" != "# " ]
+ then
+ echo >&2 "Error: no [post_title] argument provided and no top-level heading at top of the document."
+ exit 1
+ fi
+ post_title="${first_line### }"
+fi
+
+echo "#!
+export TITLE=\"${post_title} | joeac's blog\"
+export DESCRIPTION=\"${post_title} | joeac's blog\"
+pp \"\${SHARE}\"/components/meta.upphtml
+#!"
+
+while read line
+do
+ if [ "$(echo $line | sed "s/[[:space:]]//g")" = "" ]
+ then
+ continue
+ fi
+
+ if [ "$(echo $line | cut -c1)" = "#" ]
+ then
+ title="$(escape_upphtml "${line#*# }")"
+ heading="${line%%$title}"
+ heading="${heading%% }"
+ heading_level="${#heading}"
+ if [ "$(echo "$line" | cut -c$(( $heading_level + 1 )) )" = " " ]
+ then
+ close_all_open_blocks
+ echo "${title}"
+ continue
+ fi
+ fi
+
+ if [ "$(echo "$line" | cut -c1-2)" = "=>" ]
+ then
+ close_all_open_blocks_except link
+ href="$(escape_upphtml "$(echo "$line" | sed "s/^=>[[:space:]]*\([[:alnum:][:punct:]]\+\).*$/\1/")")"
+ title="$(escape_upphtml "$(echo "$line" | sed "s/^=>[[:space:]]*[[:alnum:][:punct:]]\+[[:space:]]\(.*\)$/\1/")")"
+ if [ $is_in_link_block -eq 0 ]
+ then
+ echo ""
+ else
+ echo "
"
+ fi
+ is_in_link_block=1
+ echo "=> ${title}"
+ continue
+ fi
+
+ if [ "$( echo "$line" | cut -c1-2)" = "* " ]
+ then
+ close_all_open_blocks_except list
+ if [ $is_in_list -eq 0 ]
+ then
+ echo "
"
+ fi
+ is_in_list=1
+ echo "- $(escape_upphtml "$(echo "$line" | cut -c3-)")
"
+ continue
+ fi
+
+ if [ "$(echo "$line" | cut -c1)" = ">" ]
+ then
+ close_all_open_blocks_except blockquote
+ if [ $is_in_blockquote -eq 0 ]
+ then
+ echo ""
+ fi
+ is_in_blockquote=1
+ echo "$(escape_upphtml "$(echo "$line" | cut -c2-)")
"
+ continue
+ fi
+
+ if [ "$(echo "$line" | cut -c1-3)" = "\`\`\`" ]
+ then
+ close_all_open_blocks_except pre
+ if [ $is_in_preformatted_block -eq 0 ]
+ then
+ echo ""
+ else
+ echo -e "\n"
+ fi
+ is_in_preformatted_block=1
+ echo "$(escape_upphtml "$(echo "$line" | cut -c4-)")"
+ continue
+ fi
+
+ close_all_open_blocks
+ echo "$(escape_upphtml "${line}")
"
+done
+
+close_all_open_blocks
diff --git a/http/bin/mkws b/http/bin/mkws
index d706905..9cd2bee 100755
--- a/http/bin/mkws
+++ b/http/bin/mkws
@@ -11,6 +11,8 @@ test $# -lt 1 && usage
export SRC="$(realpath src)"
export PUBLIC="$(realpath public)"
export SHARE="$(realpath share)"
+export LAYOUTS="$(realpath share/layouts)"
+export DEFAULT_LAYOUT="${LAYOUTS}/default.layout.upphtml"
export OUT="$(realpath out)"
if [ -d "${PUBLIC}" ]
@@ -28,22 +30,36 @@ then
exit 1
fi
-for t in "${SRC}"/*.upphtml
+for t in $(find -L "${SRC}/" -type f -and -name "*\.upp*" -and -not -name "*\.upp*\.*")
do
- echo "Making $(basename "${t%.upphtml}".html)"
- pp "${SHARE}"/l.upphtml "${t}" "$1" > \
- "${OUT}/$(basename "${t%.upphtml}".html)"
-done
+ basename="$(basename "$t")"
+ ext="${basename##*upp}"
-for t in "${SRC}"/**/*.uppcss
-do
- t_out="${t%.uppcss}".css
- t_out="${t_out#${SRC}/}"
- echo "Making ${t_out}"
- t_out="${OUT}/${t_out}"
- mkdir -p "$(dirname "${t_out}")"
- pp "${t}" "$1" > \
- "${t_out}"
+ basename_without_ext="${basename%\.upp${ext}}"
+ penultimate_segment="${basename_without_ext##*\.}"
+ if [ "$basename_without_ext" != "$penultimate_segment" ] && [ -f "${LAYOUTS}/${penultimate_segment}.layout.upphtml" ]
+ then
+ layout="${LAYOUTS}/${penultimate_segment}.layout.upphtml"
+ name="${basename%\.${penultimate_segment}\.upp${ext}}"
+ else
+ layout="${DEFAULT_LAYOUT}"
+ name="${basename%\.upp${ext}}"
+ fi
+
+ in_dir="$(dirname "$t")/"
+ rel_dir="${in_dir#${SRC}/}"
+ mkdir -p "${OUT}/${rel_dir}/"
+
+ args="$( [ -f "${t}.args" ] && cat "${t}.args" || echo "" )"
+ if [ "$ext" = "html" ]
+ then
+ pp "${layout}" "${t}" "$1" "$args" > \
+ "${OUT}/${rel_dir}/${name}.${ext}"
+ else
+ pp "${t}" "$1" "$args" > \
+ "${OUT}/${rel_dir}/${name}.${ext}"
+ fi
+ echo "Written ${OUT}/${rel_dir}${name}.${ext}"
done
echo "Making sitemap.xml"
diff --git a/http/blogs/blog b/http/blogs/blog
new file mode 120000
index 0000000..908e98e
--- /dev/null
+++ b/http/blogs/blog
@@ -0,0 +1 @@
+../../common/longlog/
\ No newline at end of file
diff --git a/http/blogs/microlog b/http/blogs/microlog
new file mode 120000
index 0000000..4f95064
--- /dev/null
+++ b/http/blogs/microlog
@@ -0,0 +1 @@
+../../common/microlog/
\ No newline at end of file
diff --git a/http/share/components/formatted_date.upphtml b/http/share/components/formatted_date.upphtml
new file mode 100644
index 0000000..ba86181
--- /dev/null
+++ b/http/share/components/formatted_date.upphtml
@@ -0,0 +1,4 @@
+#!
+FORMATTED_DATE="$(date --date "${DATE}" +'%A %-d %B %Y')"
+echo ""
+#!
diff --git a/http/share/layouts/blog.layout.upphtml b/http/share/layouts/blog.layout.upphtml
new file mode 100644
index 0000000..55a790d
--- /dev/null
+++ b/http/share/layouts/blog.layout.upphtml
@@ -0,0 +1,50 @@
+#!
+SRC="$1"
+SITE_URL="$2"
+POST_TITLE="$3"
+DATE_PUBLISHED="$(basename "${SRC}" | cut -c1-10)"
+CANONICAL_URL="${SITE_URL}/blog/$(basename "${SRC}")"
+#!
+
+
+
+
+
+#!
+pp "${SHARE}"/components/head.upphtml
+#!
+
+
+
+
+#!
+pp "${SHARE}"/components/navbar.upphtml
+#!
+
+
+
+
+
+
+
+#!
+echo "${POST_TITLE}"
+#!
+
+
+
+
+
+
diff --git a/http/share/l.upphtml b/http/share/layouts/default.layout.upphtml
similarity index 100%
rename from http/share/l.upphtml
rename to http/share/layouts/default.layout.upphtml
diff --git a/http/share/layouts/microlog.layout.upphtml b/http/share/layouts/microlog.layout.upphtml
new file mode 100644
index 0000000..84b59c4
--- /dev/null
+++ b/http/share/layouts/microlog.layout.upphtml
@@ -0,0 +1,50 @@
+#!
+SRC="$1"
+SITE_URL="$2"
+POST_TITLE="$3"
+DATE_PUBLISHED="$(basename "${SRC}" | cut -c1-10)"
+CANONICAL_URL="${SITE_URL}/microlog/$(basename "${SRC}")"
+#!
+
+
+
+
+
+#!
+pp "${SHARE}"/components/head.upphtml
+#!
+
+
+
+
+#!
+pp "${SHARE}"/components/navbar.upphtml
+#!
+
+
+
+
+
+
+
+#!
+echo "${POST_TITLE}"
+#!
+
+
+
+
+
+