67dfc4c049202d90e980e5b09768dc471d4d6858 — Charles Daniels 5 months ago
initial commit
6 files changed, 462 insertions(+), 0 deletions(-)

A .gitignore
A LICENSE
A README.md
A bin/build-browser
A bin/generate-md
A bin/ssg3
A  => .gitignore +2 -0
@@ 1,2 @@
+ html/
+ markdown/

A  => LICENSE +29 -0
@@ 1,29 @@
+ Copyright (c) 2019, Charles Daniels
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+ 
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 
+ 3. Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ 

A  => README.md +4 -0
@@ 1,4 @@
+ # epbsd-browser
+ 
+ Unofficial [EPBSD](https://epbsd.org/) repository browser generator, inspired
+ by (but not derived from) [openports.se](http://openports.se/).

A  => bin/build-browser +13 -0
@@ 1,13 @@
+ #!/bin/sh
+ 
+ # Generate the EPBSD ports browser
+ 
+ cd "$(dirname "$0")"
+ 
+ rm -rf ../html
+ rm -rf ../markdown
+ mkdir ../markdown
+ mkdir ../html
+ 
+ ./generate-md 'https://gitlab.com/epbsd/ports.git' ../markdown 'https://gitlab.com/epbsd/ports/blob/master/' 'https://gitlab.com/epbsd/ports/commit/'
+ ./ssg3 ../markdown/ ../html/ "EPBSD Ports Browser" "https://example.com"

A  => bin/generate-md +153 -0
@@ 1,153 @@
+ #!/bin/sh
+ 
+ # Script to generate EPBSD browser site
+ #
+ # This script will generate files under $2/*/* in the same structure as
+ # the input ports tree ($1). Files are generated in markdown format for later
+ # processing by ssg.
+ #
+ # The "tree base" specifies the base URL for linking to files in the master
+ # branch of the EPBSD git repository.
+ #
+ # The "commit base" specifies the base URL for linking to commits in the EPBSD
+ # repository
+ #
+ # Usage:
+ #
+ #	$1 . . . repository URL
+ #	$2 . . . output directory
+ #	$3 . . . tree base
+ #	$4 . . . commit base
+ 
+ set -e
+ set -u
+ 
+ if [ $# -ne 4 ] ; then
+ 	echo "$0 REPO_URL OUTPUT_DIR TREE_BASE COMMIT_BASE"
+ 	exit 1
+ fi
+ 
+ REPO_URL="$1"
+ TREE_BASE="$3"
+ COMMIT_BASE="$4"
+ 
+ # get the fully qualified path to the output directory -- we use pwd instead of
+ # realpath since it's more portable
+ mkdir -p "$2"
+ cd "$2"
+ OUTPUT_DIR="$(pwd)"
+ 
+ # cd to the parent dir of this script
+ cd "$(dirname "$0")"
+ 
+ # pwd will give us the fully-qualified path to where we are installed
+ INSTALL_DIR="$(pwd)"
+ 
+ # dir to clone the EPBSD tree into
+ TEMP_DIR="$(mktemp -d)"
+ 
+ cd "$TEMP_DIR"
+ 
+ # fetch the EPBSD tree
+ git clone "$REPO_URL" ports
+ cd ports
+ 
+ get_make_variable () {
+ 	# get a single variable ($1) from $port/Makefile
+ 
+ 	# TODO: should use make -C show=$1 - how to set environment variables?
+ 	perl -ne 'print if /^'"$1"'/' < "$port/Makefile" | \
+ 		awk '{$1=$1}1' | \
+ 		cut -d '=' -f 2-
+ }
+ 
+ gen_port_page () {
+ 	# generate the output Markdown for a single port, specified by the
+ 	# global $port
+ 	#
+ 	# Note that $port needs to have the format ./category/portname/
+ 
+ 	# extract primary category and name
+ 	category="$(echo "$port" | cut -d '/' -f 2)"
+ 	name="$(echo "$port" | cut -d '/' -f 3)"
+ 
+ 	# extract information from the Makefile
+ 	comment="$(get_make_variable COMMENT)"
+ 	categories="$(get_make_variable CATEGORIES)"
+ 	homepage="$(get_make_variable HOMEPAGE)"
+ 	maintainer="$(get_make_variable MAINTAINER)"
+ 
+ 	# get package description
+ 	if [ ! -e "$port/pkg/DESCR" ] ; then
+ 		description="Missing DESCR file."
+ 	else
+ 		description="$(cat "$port/pkg/DESCR")"
+ 	fi
+ 
+ 	# create target directory
+ 	page_dir="$OUTPUT_DIR/$category/$name"
+ 	mkdir -p "$page_dir"
+ 
+ 	page="$page_dir/index.md"
+ 
+ 	# generate page contents
+ 	echo "# [$category](../index.html)/$name - $comment" > "$page"
+ 	echo "" >> "$page"
+ 	echo "## Quick Links" >> "$page"
+ 	echo "" >> "$page"
+ 
+ 	# handle empty homepage
+ 	if [ "$homepage" = "" ] ; then
+ 		echo "* HOMEPAGE not set" >> "$page"
+ 	else
+ 		echo "* [$name's homepage]($homepage)" >> "$page"
+ 	fi
+ 
+ 	echo "* [ports/$category/$name on GitLab]($TREE_BASE/$category/$name)" >> "$page"
+ 	echo "" >> "$page"
+ 	echo "## Description"  >> "$page"
+ 	echo "" >> "$page"
+ 	echo "$description" >> "$page"
+ 	echo "" >> "$page"
+ 	echo "## Commit History" >> "$page"
+ 	echo "" >> "$page"
+ 	git log --format=format:'%h#%H#%an#%ai#%s' -- "$port" | \
+ 		awk 'BEGIN {FS="#"} {printf("* [`%s`]('"$COMMIT_BASE"'/%s) by %s at %s: %s\n", $1, $2, $3, $4, $5)}' >> "$page"
+ 
+ 	# add the page to the index
+ 	echo "$name" >> "$OUTPUT_DIR/$category/index.list"
+ 
+ 	# make sure the category has been added to the top-level index
+ 	echo "$category" >> "$OUTPUT_DIR/index.list"
+ 
+ }
+ 
+ # clear existing index files
+ rm -f "$OUTPUT_DIR/index.list"
+ rm -f "$OUTPUT_DIR"/*/index.list
+ 
+ # generate pages for each port
+ for port in ./*/*/ ; do
+ 	echo "$0: begin processing port $port"
+ 	gen_port_page
+ 
+ done
+ 
+ # generate the top-level index
+ echo "# Index for ./\n" > "$OUTPUT_DIR/index.md"
+ sort < "$OUTPUT_DIR/index.list" | uniq | while read -r category ; do
+ 	echo "* [$category](./$category/index.html)" >> "$OUTPUT_DIR/index.md"
+ done
+ 
+ # generate the index for each category
+ for category_dir in ./*/ ; do
+ 	category="$(basename "$category_dir")"
+ 	echo "$0: generating index for $category"
+ 	echo "# Index for [.](../index.html)/$category/\n" > "$OUTPUT_DIR/$category/index.md"
+ 	sort < "$OUTPUT_DIR/$category/index.list" | uniq | while read -r name ; do
+ 		echo "* [$name](./$name/index.html)" >> "$OUTPUT_DIR/$category/index.md"
+ 	done
+ done
+ 
+ cd "$OUTPUT_DIR"
+ rm -rf "$TEMP_DIR"

A  => bin/ssg3 +261 -0
@@ 1,261 @@
+ #!/bin/sh
+ #
+ # https://www.romanzolotarev.com/bin/ssg3
+ # Copyright 2018 Roman Zolotarev <hi@romanzolotarev.com>
+ #
+ # Permission to use, copy, modify, and/or distribute this software for any
+ # purpose with or without fee is hereby granted, provided that the above
+ # copyright notice and this permission notice appear in all copies.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ #
+ set -e
+ 
+ main() {
+ 	test -n "$1" || usage
+ 	test -n "$2" || usage
+ 	test -n "$3" || usage
+ 	test -n "$4" || usage
+  	test -d "$1" || no_dir "$1"
+  	test -d "$2" || no_dir "$2"
+ 
+ 	src=$(readlink -f "$1")
+ 	dst=$(readlink -f "$2")
+ 
+ 
+ 	# files
+ 
+ 	title="$3"
+ 
+ 	head=$(
+ 	wrap_file "$src/_rss.html"
+ 	wrap_file "$src/_styles.css" '<style>' '</style>'
+ 	wrap_file "$src/_scripts.js" '<script>' '</script>'
+ 	)
+ 	header=$(wrap_file "$src/_header.html")
+ 	footer=$(wrap_file "$src/_footer.html")
+ 
+ 	list_dirs "$src" |
+ 	(cd "$src" && cpio -pdu "$dst")
+ 
+ 	fs=$(
+  	if test -f "$dst/.files"
+ 	then list_affected_files "$src" "$dst/.files"
+ 	else list_files "$1"
+ 	fi
+ 	)
+ 
+ 	if test -n "$fs"
+ 	then
+ 		echo "$fs" | tee "$dst/.files"
+ 
+ 		if echo "$fs" | grep -q '\.md$'
+ 		then test -x "$(which lowdown)" || exit 3
+ 		fi
+ 		echo "$fs" | grep '\.md$' |
+ 		render_md_files "$src" "$dst" \
+ 		"$title" "$head" "$header" "$footer"
+ 
+ 		echo "$fs" | grep '\.html$' |
+ 		render_html_files "$src" "$dst" \
+ 		"$title" "$head" "$header" "$footer"
+ 
+ 		echo "$fs" | grep -Ev '\.md$|\.html$' |
+ 		(cd "$src" && cpio -pu "$dst")
+ 	fi
+ 
+ 	printf '[ssg] ' >&2
+ 	print_status 'file,' 'files,' "$fs" >&2
+ 
+ 
+ 	# sitemap
+ 
+ 	base_url="$4"
+ 	date=$(date +%Y-%m-%d)
+  	urls=$(list_pages "$src")
+ 
+ 	if test -n "$urls"
+ 	then
+ 		echo "$urls" |
+ 		render_urls "$base_url" "$date" |
+ 		render_sitemap > "$dst/sitemap.xml"
+ 	fi
+ 
+ 	print_status 'url' 'urls' "$urls" >&2
+ 	echo >&2
+ }
+ 
+ 
+ wrap_file() {
+ 	if test -f "$1"
+ 	then echo "$2$(cat "$1")$3"
+ 	fi
+ }
+ 
+ 
+ print_status() {
+ 	if test -n "$3"
+ 	then echo "$3" | line_counter "$1" "$2"
+ 	else printf 'no %s ' "$2"
+ 	fi
+ }
+ 
+ 
+ line_counter() {
+ 	wc -l |
+ 	awk '{printf $1" "}($1=="1"){printf "'"$1"' "}($1>"1"){printf "'"$2"' "}'
+ }
+ 
+ 
+ usage() {
+ 	echo "usage: ${0##*/} src dst title base_url" >&2
+ 	exit 1
+ }
+ 
+ 
+ no_dir() {
+ 	echo "${0##*/}: $1: No such directory" >&2
+ 	exit 2
+ }
+ 
+ 
+ list_dirs() {
+ 	cd "$1" &&
+ 	find . -type d \
+ 	! -name '.' ! -path '*/.*' ! -path '*/CVS/*' ! -path '*/_*'
+ }
+ 
+ 
+ list_files() {
+ 	cd "$1" &&
+ 	find . -type f \
+ 	! -name '.' ! -path '*/.*' ! -path '*/CVS/*' ! -path '*/_*'
+ }
+ 
+ 
+ list_dependant_files () {
+ 	cd "$1" &&
+ 	find . -type f \
+ 	! -name '.' ! -path '*/.*' ! -path '*/CVS/*' ! -path '*/_*' \
+ 	\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \)
+ }
+ 
+ 
+ list_newer_files() {
+ 	cd "$1" &&
+ 	find . -type f \
+ 	! -name '.' ! -path '*/.*' ! -path '*/CVS/*' \
+ 	-newer "$2"
+ }
+ 
+ 
+ has_partials() {
+ 	grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
+ }
+ 
+ 
+ list_affected_files() {
+ 	fs=$(list_newer_files "$1" "$2")
+ 
+ 	if echo "$fs" | has_partials
+ 	then list_dependant_files "$1"
+ 	else echo "$fs"
+ 	fi
+ }
+ 
+ 
+ render_html_files() {
+ 	while read -r f
+ 	do render_html_file "$1" "$3" "$4" "$5" "$6" < "$1/$f" > "$2/$f"
+ 	done
+ }
+ 
+ 
+ render_md_files() {
+ 	while read -r f
+ 	do
+ 		lowdown \
+ 		-D html-skiphtml \
+ 		-D smarty \
+ 		-d metadata \
+ 		-d autolink < "$1/$f" |
+ 		render_html_file "$1" "$3" "$4" "$5" "$6" \
+ 		> "$2/${f%\.md}.html"
+ 	done
+ }
+ 
+ 
+ render_html_file() {
+ 	body=$(cat)
+ 	src="$1"
+ 	site_title="$2"
+ 	head="$3"
+ 	header="$4"
+ 	footer="$5"
+ 
+ 	echo "$body" | grep -iq '<html' &&
+ 	echo "$body" && return
+ 
+ 	title=$(echo "$body" | awk 'tolower($0)~/^<h1/{gsub(/<[^>]*>/,"",$0);print;exit}')
+ 	echo '<!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="generator" content="romanzolotarev.com/bin/ssg3">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/png" href="/favicon.png">
+ <title>'"$(test -n "$title" && echo "$title - ")$site_title"'</title>
+ '"$head"'
+ </head>
+ <body>
+ '"$header"'
+ '"$body"'
+ '"$footer"'
+ </body>
+ </html>'
+ }
+ 
+ list_pages() {
+ 	cd "$1" && find . -type f ! -path '*/.*' ! -path '*/_*' \
+ 	\( -name '*.html' -o -name '*.md' \) |
+ 	sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
+ }
+ 
+ 
+ render_urls() {
+ 	while read -r url
+ 	do render_url "$1/$url" "$2"
+ 	done
+ }
+ 
+ 
+ render_url() {
+ 	url="$1"
+ 	date="$2"
+ 	echo '<url>
+ <loc>'"$url"'</loc>
+ <lastmod>'"$date"'</lastmod>
+ <priority>1.0</priority>
+ </url>'
+ }
+ 
+ 
+ render_sitemap() {
+ 	echo '<?xml version="1.0" encoding="UTF-8"?>
+ <urlset
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
+ http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
+ xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+ '"$(cat)"'
+ </urlset>'
+ }
+ 
+ 
+ main "$@"