From afb216344735397615466174b8ca184691ea66f8 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 2 Mar 2021 12:57:38 -0600 Subject: Add commentary --- bollux | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 51 deletions(-) diff --git a/bollux b/bollux index 26dc316..a7c5f93 100644 --- a/bollux +++ b/bollux @@ -3,11 +3,45 @@ # Author: Case Duckworth # License: MIT # Version: 0.4.0 +# +# Commentary: +# +# The impetus for this program came from a Mastodon conversation I had where +# someone mentioned the "simplest possible Gemini client" was this: +# +# openssl s_client -gin_foe -quiet -connect $server:1965 <<< "$url" +# +# That's still at the heart of this program (see `gemini_request'): `bollux' is +# basically a half-functioning convenience wrapper around that openssl call. +# The first versions of `bollux' used `gawk' and a lot of other tools on top of +# bash, but after reading Dylan Araps' Pure Bash Bible[1] and other works, I +# decided to make as much of it in Bash as possible. Thus, currently `bollux' +# requires `bash' v. 4+, `less' (a recent, non-busybox version), `dd' for +# downloads, `openssl' for requests, and `iconv' to convert pages to UTF-8. +# Future versions will hopefully have a pager fully implemented in bash, so that +# I won't have to worry about less's weird incompatibilities and keybinding +# things. That's a major project though, and I'm scared. +# +# The following works were referenced when writing this, and I've tried to +# credit them in comments below. Following each link, I'll include a "short +# code" that I'll use to reference them in those comments, if necessary to keep +# them shorter than 80 characters. +# +# [1]: https://github.com/dylanaraps/pure-bash-bible [PBB] +# [2]: https://tools.ietf.org/html/rfc3986 [URLspec] +# [3]: https://gemini.circumlunar.space/docs/specification.html [GEMspec] +# [4]: https://tools.ietf.org/html/rfc1436 [GOPHERprotocol] +# [5]: https://tools.ietf.org/html/rfc4266 [GOPHERurl] +# [6]: [GOPHER_GEMINI]: +# https://github.com/jamestomasino/dotfiles-minimal/blob/master/bin/gophermap2gemini.awk +# +# Code: # Program information PRGN="${0##*/}" # Easiest way to get the script name VRSN=0.4.1 # I /try/ to follow semver? IDK. +# Print a useful help message (`bollux -h'). bollux_usage() { cat < <(:) || : } -# https://github.com/dylanaraps/pure-bash-bible/ +# Trim leading and trailing whitespace from a string. +# +# PBB: #trim-leading-and-trailing-white-space-from-string trim_string() { # trim_string STRING : "${1#"${1%%[![:space:]]*}"}" : "${_%"${_##*[![:space:]]}"}" printf '%s\n' "$_" } -# cycle a variable, e.g. from 'one,two,three' => 'two,three,one' +# Cycle a variable. +# +# e.g. 'cycle_list one,two,three' => 'two,three,one' cycle_list() { # cycle_list LIST DELIM local list="${!1}" delim="$2" local first="${list%%${delim}*}" @@ -58,12 +114,17 @@ cycle_list() { # cycle_list LIST DELIM printf -v "$1" '%s%s%s' "${rest}" "${delim}" "${first}" } -# determine the first element of a list, e.g. 'one,two,three' => 'one' +# Determine the first element of a delimited list. +# +# e.g. 'first one,two,three' => 'one' first() { # first LIST DELIM local list="${!1}" delim="$2" printf '%s\n' "${list%%${delim}*}" } +# Log a message to stderr (&2). +# +# TODO: document log() { # log LEVEL MESSAGE [[ "$BOLLUX_LOGLEVEL" == QUIET ]] && return local fmt @@ -83,22 +144,49 @@ log() { # log LEVEL MESSAGE printf >&2 '\e[%sm%s:%s:\e[0m\t%s\n' "$fmt" "$PRGN" "${FUNCNAME[1]}" "$*" } -# main entry point +# Set the terminal title. +set_title() { # set_title STRING + printf '\e]2;%s\007' "$*" +} + +# Prompt the user for input. +# +# This is a thin wrapper around `read', a bash built-in. Because of the +# way bollux messes around with stein and stdout, I need to read directly from +# the TTY with this function. +prompt() { # prompt [-u] PROMPT [READ_ARGS...] + local read_cmd=(read -e -r) + if [[ "$1" == "-u" ]]; then + read_cmd+=(-i "$BOLLUX_URL") + shift + fi + local prompt="$1" + shift + read_cmd+=(-p "$prompt> ") + "${read_cmd[@]}" ") - "${read_cmd[@]}" /dev/null 2>&1; then + if declare -F "${url[1]}_request" >/dev/null 2>&1; then run "${url[1]}_request" "$url" else die 99 "No request handler for '${url[1]}'" fi } | run normalize | { - if declare -Fp "${url[1]}_response" >/dev/null 2>&1; then + if declare -F "${url[1]}_response" >/dev/null 2>&1; then run "${url[1]}_response" "$url" else log d \ @@ -232,8 +337,23 @@ blastoff() { # blastoff [-u] URL } } -# URLS -## https://tools.ietf.org/html/rfc3986 +# URLS: https://tools.ietf.org/html/rfc3986 #################################### +# +# Most of these functions are Bash implementations of functionality laid out in +# the linked RFC specification. I'll refer to the section numbers above each +# function. +# +# In addition, most of these functions take arrays or array elements passed /by +# name/, instead of /value/ -- i.e., instead of calling `usplit $url', call +# `usplit url'. Passing values by name is necessary because of Bash's weird +# array handling. +# +################################################################################ + +# Make sure a URL is "well-formed:" add a default protocol if it's missing and +# trim whitespace. +# +# Useful for URLs that were probably input by humans. uwellform() { local u="$1" @@ -246,6 +366,13 @@ uwellform() { printf '%s\n' "$u" } +# Split a URL into its constituent parts, placing them all in the given array. +# +# The regular expression given at the top of the function ($re) is taken +# directly from RFC 3986, Appendix B -- and if the URL provided doesn't match +# it, the function bails. +# +# `usplit' takes advantage ... [CONTINUE HERE] usplit() { # usplit NAME:ARRAY URL:STRING local re='^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' [[ $2 =~ $re ]] || return $? @@ -408,7 +535,7 @@ pmerge() { fi } -# https://github.com/dylanaraps/pure-bash-bible/ +# PBB uencode() { # uencode URL:STRING local LC_ALL=C for ((i = 0; i < ${#1}; i++)); do @@ -425,7 +552,7 @@ uencode() { # uencode URL:STRING printf '\n' } -# https://github.com/dylanaraps/pure-bash-bible/ +# PBB udecode() { # udecode URL:STRING : "${1//+/ }" printf '%b\n' "${_//%/\\x}" @@ -598,7 +725,7 @@ passthru() { # convert gophermap to text/gemini (probably naive) gopher_convert() { local type label path server port regex - # cf. https://github.com/jamestomasino/dotfiles-minimal/blob/master/bin/gophermap2gemini.awk + # [GOPHER_GEMINI] while IFS= read -r; do printf -v regex '(.)([^\t]*)(\t([^\t]*)\t([^\t]*)\t([^\t]*))?' if [[ "$REPLY" =~ $regex ]]; then @@ -753,7 +880,9 @@ mklesskey() { # mklesskey if [[ -f "$BOLLUX_CUSTOM_LESSKEY" ]]; then log d "Using custom lesskey: '$BOLLUX_CUSTOM_LESSKEY'" BOLLUX_LESSKEY="${BOLLUX_CUSTOM_LESSKEY}" - elif [[ ! -f "$BOLLUX_LESSKEY" ]]; then + elif [[ -f "$BOLLUX_LESSKEY" ]]; then + log d "Found lesskey: '$BOLLUX_LESSKEY'" + else log d "Generating lesskey: '$BOLLUX_LESSKEY'" lesskey -o "$BOLLUX_LESSKEY" - <