git3html

git clone https://orangeshoelaces.net/git/git3html.git

75e3a5f510280ce74cd86da7071d5f7494d528c9

Author: Vasilii Kolobkov on 01/13/2019

Committer: Vasilii Kolobkov on 01/13/2019

Have a second take on traversing repository tree

Also:
- update html escaping to work in attribute ctx as well as in text
- provision an isbinary function hailing from libgit2

Stats

git3html | 113 ++++++--
1 file changed, 85 insertions(+), 28 deletions(-)

Patch

diff --git a/git3html b/git3html
index e580830..de74a3a 100755
--- a/git3html
+++ b/git3html
@@ -16,11 +16,15 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 set -eu
+ht="$(printf '\t')"
 
 # ........ functions ........
 
 escape() {
-	sed 's,&,\&amp;,g; s,<,\&lt;,g; s,>,\&gt;,g'
+	# assume the input is in utf8
+	# \302\240 is U+00A0 or &nbsp; in utf8
+	sed "s,&,\&amp;,g; s,$(printf '\302\240'),\&nbsp;,g; \
+		s,\",\&quot;,g; s,<,\&lt;,g; s,>,\&gt;,g"
 }
 
 parent() {
@@ -31,6 +35,31 @@ parent() {
 	fi
 }
 
+# snatched from libgit2
+isbinary() {
+	od -A n -N 256 -t u1 | awk -f '
+	{
+		for(i=1; i <= NF; ++i) {
+			c=$i
+			if ((c > 31 && c != 127) || c == 8 || c == 27 || c == 12) {
+				# BS, ESC, FF and anything over US excluding DEL is printable
+				++printable
+			} else if (c == 0) {
+				null=1
+				exit
+			} else if (c < 9 || c > 13) {
+				 # everything else, sans [:space:] is not
+				 ++nonprintable
+			}
+		}
+	}
+
+	END {
+		if (null) print "y"
+		else print (printable / 128 < nonprintable) ? "y" : "n"
+	}'
+}
+
 g() {
 	git --git-dir="$repodir" "$@"
 }
@@ -48,11 +77,17 @@ defaultname() {
 	fi | sed 's/\.git$//'
 }
 
-header() {
+header() (
+	title="$1"
+	toroot="$2"
+
 	cat <<-END
 	<!doctype html>
 	<html>
 	<head>
+	END
+	echo "<title>$(echo "$title" | escape)</title>"
+	cat <<-END
 	</head>
 	<body>
 	<header>
@@ -62,15 +97,15 @@ header() {
 	<nav>
 	<ul>
 	END
-	echo "<li><a href=\"${1%/}/readme.html\">Readme</a></li>"
-	echo "<li><a href=\"${1%/}/log.html\">Log</a></li>"
-	echo "<li><a href=\"${1%/}/files.html\">Files</a></li>"
+	echo "<li><a href=\"${toroot%/}/readme.html\">Readme</a></li>"
+	echo "<li><a href=\"${toroot%/}/log.html\">Log</a></li>"
+	echo "<li><a href=\"${toroot%/}/files.html\">Files</a></li>"
 	cat <<-END
 	</ul>
 	</nav>
 	</header>
 	END
-}
+)
 
 footer() {
 	cat <<-END
@@ -80,7 +115,7 @@ footer() {
 }
 
 readme() (
-	header '.'
+	header "${projname} readme" '.'
 	root=$(tmrc)
 	echo '<pre>'
 	g ls-tree --name-only "$root" | grep '^README*' | while read -r readme; do
@@ -90,28 +125,50 @@ readme() (
 	footer
 )
 
+dirent() {
+	printf "<li><pre><code>d--------- <a href=\"%s\">%s</a></code></pre></li>\n" \
+		"$(echo "$2" | escape)" "$(echo "$1" | escape)"
+}
+
+blobent() {
+	printf "<li><pre><code>f--------- <a href=\"%s\">%s</code></pre></a></li>\n" \
+		"$(echo "$2" | escape)" "$(echo "$1" | escape)"
+}
+
 files() (
-	root="$1"
-	outdir="$2"
-	rootreldir="$3"
-
-	header "$rootreldir"
-	g ls-tree "$root" | awk '$2 == "tree"' | while read d; do
-		droot="$(echo "$d" | awk '{ print $3 }')"
-		dname="$(echo "$d" | awk '{ print $4 }')"
-		doutdir="${outdir%/}/$dname"
-		if [ ! -d "$doutdir" ]; then
-			mkdir "$doutdir"
+	tree="$1"
+	name="$2"
+	outdir="$3"
+	relbaseoutdir="$4"	# these two are
+	relparidx="$5"		# relative to outdir
+	reppath="$6"
+
+	{
+		header "${reppath}:" "$relbaseoutdir"
+		echo '<ul>'
+		if [ -n "$relparidx" ]; then
+			dirent '..' "$relparidx"
 		fi
-		files "$droot" "$doutdir" "$(parent "$rootreldir")" > "${doutdir%/}/files.html"
-		echo "<li><a href=\"$dname/files.html\">$(echo "$dname" | escape)</a></li>"
-	done
-
-	g ls-tree "$root" | awk '$2 == "blob"' | while read f; do
-		fname="$(echo "$f" | awk '{ print $4 }')"
-		echo "<li><a href=\"$fname.html\">$(echo "$fname" | escape)</a></li>"
-	done
-	footer
+		g ls-tree "$tree" | awk '$2 == "tree"' | while read rec; do
+			subtree="$(echo "$rec" | awk '{ print $3 }')"
+			stname="$(echo "$rec" | cut -d "$ht" -f2)"
+			stoutdir="${outdir%/}/${name}"
+			if [ ! -d "$stoutdir" ]; then
+				mkdir "$stoutdir"
+			fi
+
+			files "$subtree" "$stname" "${stoutdir}" "$(parent "${relbaseoutdir}")" \
+				"../${name}.html" "${reppath%/}/${stname}"
+
+			dirent "${stname}" "${name}/${stname}.html"
+		done
+		g ls-tree "$tree" | awk '$2 == "blob"' | while read rec; do
+			bname="$(echo "$rec" | cut -d "$ht" -f2)"
+			blobent "$bname" "${name}/${bname}.html"
+		done
+		echo '</ul>'
+		footer
+	} > "${outdir%/}/${name}.html"
 )
 
 # ........ driver ........
@@ -154,4 +211,4 @@ fi
 
 readme > "${htmldir%/}/readme.html"
 #log
-files "$(tmrc)" "$htmldir" '.' > "${htmldir%/}/files.html"
+files "$(tmrc)" 'files' "$htmldir" '.' '' '/'