Still. More. Documentation.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Case Duckworth 2021-03-03 17:57:07 -06:00
parent c274c4f723
commit 8629f74d16
1 changed files with 162 additions and 45 deletions

207
bollux
View File

@ -99,13 +99,6 @@ bollux_quit() {
# SIGINT is C-c, and I want to make sure bollux quits when it's typed.
trap bollux_quit SIGINT
# Bash built-in replacement for `sleep`
#
# [1]: #use-read-as-an-alternative-to-the-sleep-command
sleep() { # sleep SECONDS
read -rt "$1" <> <(:) || :
}
# Trim leading and trailing whitespace from a string.
#
# [1]: #trim-leading-and-trailing-white-space-from-string
@ -177,6 +170,29 @@ prompt() { # prompt [-u] PROMPT [READ_ARGS...]
"${read_cmd[@]}" </dev/tty "$@"
}
# Bash built-in replacement for `cat'
#
# One of the more pedantic bits of bollux (is 'pedantic' the right word?) --
# `cat' is more than likely installed on any system with bash, so this function
# is really just here so I can say that bollux is written as purely in bash as
# possible.
passthru() {
while IFS= read -r; do
printf '%s\n' "$REPLY"
done
}
# Bash built-in replacement for `sleep'
#
# The commentary for `passthru' applies here as well, though I didn't write this
# function -- Dylan Araps did.
#
# [1]: #use-read-as-an-alternative-to-the-sleep-command
sleep() { # sleep SECONDS
read -rt "$1" <> <(:) || :
}
# MAIN BOLLUX DISPATCH FUNCTIONS ###############################################
# Main entry point into `bollux'.
@ -772,71 +788,140 @@ gemini_response() { # gemini_response URL
run blastoff "$meta" # TODO: confirm redirect
;;
(4*) # TEMPORARY ERROR
# Since the 4* codes ([3], Appendix 1) are all server issues,
# bollux can treat them all basically the same. This is an area
# that could use some expansion.
local desc="Temporary error"
case "$code" in
(41) desc+=" (server unavailable)" ;;
(42) desc+=" (CGI error)" ;;
(43) desc+=" (proxy error)" ;;
(44) desc+=" (slow down)" ;; # could be particularly improved
esac
REDIRECTS=0
die "$((100 + code))" "Temporary error [$code]: $meta"
die "$((100 + code))" "$desc [$code]: $meta"
;;
(5*) # PERMANENT ERROR
# The situation with the 5* codes is basically similar to the 4*
# codes. It could maybe use more thought as to what behavior to
# implement. Maybe adding the (bad) requests to history,
# subject to configuration?
local desc="Permanent failure"
case "$code" in
(51) desc+=" (not found)" ;;
(52) desc+=" (gone)" ;;
(53) desc+=" (proxy request refused)" ;;
# For some reason, codes 54--58 inclusive aren't used.
(59) desc+=" (bad request)" ;;
esac
REDIRECTS=0
die "$((100 + code))" "Permanent error [$code]: $meta"
die "$((100 + code))" "$desc [$code]: $meta"
;;
(6*) # CERTIFICATE ERROR
(6*) # CERTIFICATE ERROR (TODO)
# Dealing with certificates is honestly the most important
# feature missing from bollux to get it to 1.0. Right now,
# bollux deals with 6* status codes identically to 4* and 5*
# codes. This is not ideal, in the slightest.
local desc="Client certificate required"
case "$code" in
(61) desc+=" (certificate not authorized)" ;;
(62) desc+=" (certificate not valid)" ;;
esac
REDIRECTS=0
log d "Not implemented: Client certificates"
die "$((100 + code))" "[$code] $meta"
;;
(*)
(*) # UNKNOWN
# Just in case we get a weird, un-spec-compliant status code.
[[ -z "${code-}" ]] && die 100 "Empty response code."
die "$((100 + code))" "Unknown response code: $code."
;;
esac
}
# GOPHER
# GOPHER #######################################################################
# https://tools.ietf.org/html/rfc1436 protocol
# https://tools.ietf.org/html/rfc4266 url
#
# Gopher is the grand-daddy of gemini (or maybe just weird uncle? hm..),
# invented in 1991 as a fancier FTP. There's been a sort of resurgence in it as
# a consequence of the shittifying of the WWW, but it's shown its age (which is
# why Gemini was born). But why am I telling you this? You're reading the
# source code of a Gemini browser! You're a meganerd just like me. Welcome to
# the club, kid.
#
# Since gopher is so old, it actually has two RFCs: RFC 1436 [6] for the
# protocol itself, and RFC 4266 [7] for the URL format (gopher predates the
# URL!). However, requesting and handling responses is still fundamentally the
# same to gemini, so it was pretty easy to implement this. I don't think bollux
# handles all the possible item types, but it should get the main ones.
#
################################################################################
# Request a resource.
gopher_request() { # gopher_request URL
local url server port type path
url="$1"
port=70
local url="$1"
# RFC 4266
# [7] § 2.1
[[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]]
server="${BASH_REMATCH[1]}"
port="${BASH_REMATCH[3]:-70}"
type="${BASH_REMATCH[6]:-1}"
path="${BASH_REMATCH[7]}"
local server="${BASH_REMATCH[1]}" \
port="${BASH_REMATCH[3]:-$BOLLUX_GOPHER_PORT}" \
type="${BASH_REMATCH[6]:-1}" \
path="${BASH_REMATCH[7]}"
log d "URL='$url' SERVER='$server' TYPE='$type' PATH='$path'"
# Bash has this really neat feature where it can open a TCP socket
# directly. bollux uses that feature here to ask the server for the
# resource and then `passthru' it to the next thing.
exec 9<>"/dev/tcp/$server/$port"
printf '%s\r\n' "$path" >&9
passthru <&9
}
# Handle a server response.
gopher_response() { # gopher_response URL
local url pre type cur_server
pre=false
url="$1"
# RFC 4266
local url="$1" pre=false
# [7] § 2.1
#
# Note that this duplicates the code in `gopher_request'. There might
# be a good way to thread this data through so that it's not computed
# twice.
[[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]]
cur_server="${BASH_REMATCH[1]}"
type="${BASH_REMATCH[6]:-1}"
local cur_server="${BASH_REMATCH[1]}"
local type="${BASH_REMATCH[6]:-1}"
run history_append "$url" "" # gopher doesn't really have titles, huh
log d "TYPE='$type'"
# Gopher has a concept of 'line types', or maybe 'item types' --
# basically, each line in a gophermap starts with a character, its type,
# and then is followed by a series of tab-separated fields describing
# where that type is and how to display it. The full list of original
# line types can be found in [6] § 3.8, though the types have also been
# extended over the years. Since bollux can only display types that are
# text-ish, it only concerns itself with those in this case statement.
# All the others are simply downloaded.
case "$type" in
(0) # text
(0) # Item is a file
# Since gopher doesn't send MIME-type information in-band, we
# just assume it's text/plain, and try to convert it later to
# UTF-8 with `iconv'.
run display text/plain
;;
(1) # menu
(1) # Item is a directory [gophermap]
# Since I've already written all the code to typeset gemini
# well, it's easy to convert a gophermap to text/gemini and
# display it than to write a whole new gophermap typesetter.
run gopher_convert | run display text/gemini
;;
(3) # failure
(3) # Error
# I don't know all the gopher error cases, and the spec is
# pretty quiet on them. So bollux just signals failure and
# bails.
die 203 "GOPHER: failed"
;;
(7) # search
(7) # Item is an Index-Search server
# Gopher search queries are separated from their resources by a
# TAB. It's wild.
if [[ "$url" =~ $'\t' ]]; then
run gopher_convert | run display text/gemini
else
@ -844,16 +929,42 @@ gopher_response() { # gopher_response URL
run blastoff "$url $REPLY"
fi
;;
(*) # something else
(*) # Anything else
# The list at [6] § 3.8 includes the following (noted where it
# might be good to differently handle them in the future):
#
# 2. Item is a CSO phone-book server *****
# 4. Item is a BinHexed Macintosh file
# 5. Item is DOS binary archive of some sort
# 6. Item is a UNIX uuencoded file
# 8. Item points to a text-based telnet session *****
# 9. Item is a binary file! [exclamation point sic. -- ed.]
# +. Item is a redundant server *****
# T. Item points to a text-based tn3270 session
# g. Item is a GIF format graphics file
# I. Item is some kind of image file
#
# As mentioned, there are other line types floating around as
# well. Since I don't browse gopher much, there's not much
# personal motivation to extend `gopher_response'; however pull
# requests are always welcome.
run download "$url"
;;
esac
}
# convert gophermap to text/gemini (probably naive)
# Convert a gophermap naively to a gemini page.
#
# Based strongly on [8], but bash-ified. Due to the properties of link lines in
# gemini, many of the item types in `gemini_reponse' can be linked to the proper
# protocol handlers here -- so if a user is trying to reach a TCP link through
# gopher, bollux won't have to handle it, for example.*
#
# * Ideally -- right now, bollux simply errors out on all unknown protocols.
# More research needs to be done into how to farm out to `xdg-open' or a
# similar generic opener.
gopher_convert() {
local type label path server port regex
# [GOPHER_GEMINI]
while IFS= read -r; do
printf -v regex '(.)([^\t]*)(\t([^\t]*)\t([^\t]*)\t([^\t]*))?'
if [[ "$REPLY" =~ $regex ]]; then
@ -922,13 +1033,12 @@ gopher_convert() {
exec 9>&-
}
# 'cat' but in pure bash
passthru() {
while IFS= read -r; do
printf '%s\n' "$REPLY"
done
}
# HANDLING CONTENT #############################################################
#
# After fetching the resource requested by the user, bollux needs to display or
# otherwise 'give' the resource to the user for consumption.
#
################################################################################
# display the fetched content
display() { # display METADATA [TITLE]
@ -959,8 +1069,15 @@ display() { # display METADATA [TITLE]
case "$mime" in
(text/*)
set_title "$title${title:+ - }bollux"
# render ANSI color escapes and don't wrap pre-formatted blocks
less_cmd=(less -RS)
# Build the `less' command
less_cmd=(less)
# Render ANSI color escapes ONLY (as opposed to `-r', which
# renders all escapes)
less_cmd+=(-R)
# Don't wrap text. `fold_line' takes care of wrapping normal
# text, and pre-formatted text shouldn't wrap.
less_cmd+=(-S)
# Load the keybindings (see `lesskey').
mklesskey && less_cmd+=(-k "$BOLLUX_LESSKEY")
local helpline="${KEY_OPEN}:open, "
helpline+="${KEY_GOTO}/"