Compare commits

...

3 Commits

Author SHA1 Message Date
Case Duckworth 3db6bfeb30 Merge branch 'main' of tildegit.org:acdw/bollux 2021-12-11 14:36:16 -06:00
Case Duckworth cccc29e31a WTF re-re-format apparently 2021-12-11 14:34:49 -06:00
Case Duckworth d1029c2573 Finally fix bruno's bug report
Thanks, bruno!

Instead of escaping the brackets at all in KEY_BACK and KEY_FORWARD, I just put
them in there raw.  It's possible that this is a difference between Bash 4 and
Bash 5, since I assume I tried it that way before, but I don't remember.  Bug
reports for Bash 4 users are welcome.
2021-12-11 14:32:32 -06:00
1 changed files with 128 additions and 129 deletions

257
bollux Executable file → Normal file
View File

@ -93,14 +93,14 @@ bollux() {
bollux_args() { bollux_args() {
while getopts :hvq OPT; do while getopts :hvq OPT; do
case "$OPT" in case "$OPT" in
(h) h)
bollux_usage bollux_usage
exit exit
;; ;;
(v) BOLLUX_LOGLEVEL=DEBUG ;; v) BOLLUX_LOGLEVEL=DEBUG ;;
(q) BOLLUX_LOGLEVEL=QUIET ;; q) BOLLUX_LOGLEVEL=QUIET ;;
(:) die 1 "Option -$OPTARG requires an argument" ;; :) die 1 "Option -$OPTARG requires an argument" ;;
(*) die 1 "Unknown option: -$OPTARG" ;; *) die 1 "Unknown option: -$OPTARG" ;;
esac esac
done done
shift $((OPTIND - 1)) shift $((OPTIND - 1))
@ -142,16 +142,16 @@ bollux_config() {
: "${BOLLUX_TIMEOUT:=30}" # connection timeout : "${BOLLUX_TIMEOUT:=30}" # connection timeout
: "${BOLLUX_MAXREDIR:=5}" # max redirects : "${BOLLUX_MAXREDIR:=5}" # max redirects
: "${BOLLUX_PROTO:=gemini}" # default protocol : "${BOLLUX_PROTO:=gemini}" # default protocol
: "${BOLLUX_GEMINI_PORT:=1965}" # default port for gemini : "${BOLLUX_GEMINI_PORT:=1965}" # default port for gemini
: "${BOLLUX_GOPHER_PORT:=70}" # default port for gopher : "${BOLLUX_GOPHER_PORT:=70}" # default port for gopher
: "${BOLLUX_URL:=}" # start url : "${BOLLUX_URL:=}" # start url
: "${BOLLUX_BYEMSG:=See You Space Cowboy ...}" # bye message : "${BOLLUX_BYEMSG:=See You Space Cowboy ...}" # bye message
## lesskeys ## lesskeys
: "${KEY_OPEN:=o}" # prompt for a link to open : "${KEY_OPEN:=o}" # prompt for a link to open
: "${KEY_GOTO:=g}" # prompt for a page to 'goto' : "${KEY_GOTO:=g}" # prompt for a page to 'goto'
: "${KEY_GOTO_FROM:=G}" # goto a page with current prefilled : "${KEY_GOTO_FROM:=G}" # goto a page with current prefilled
: "${KEY_BACK:='['}" # go back in the history : "${KEY_BACK:=[}" # go back in the history
: "${KEY_FORWARD:=']'}" # go forward in the history : "${KEY_FORWARD:=]}" # go forward in the history
: "${KEY_REFRESH:=r}" # refresh the page : "${KEY_REFRESH:=r}" # refresh the page
: "${KEY_CYCLE_PRE:=p}" # cycle T_PRE_DISPLAY : "${KEY_CYCLE_PRE:=p}" # cycle T_PRE_DISPLAY
: "${BOLLUX_CUSTOM_LESSKEY:=$BOLLUX_CONF_DIR/bollux.lesskey}" : "${BOLLUX_CUSTOM_LESSKEY:=$BOLLUX_CONF_DIR/bollux.lesskey}"
@ -252,24 +252,24 @@ run() { # run COMMAND...
log() { # log LEVEL MESSAGE... log() { # log LEVEL MESSAGE...
# 'QUIET' means don't log anything. # 'QUIET' means don't log anything.
[[ "$BOLLUX_LOGLEVEL" == QUIET ]] && return [[ "$BOLLUX_LOGLEVEL" == QUIET ]] && return
local fmt # ANSI escape code local fmt # ANSI escape code
case "$1" in case "$1" in
([dD]*) # Debug level -- only print if bollux -v. [dD]*) # Debug level -- only print if bollux -v.
[[ "$BOLLUX_LOGLEVEL" == DEBUG ]] || return [[ "$BOLLUX_LOGLEVEL" == DEBUG ]] || return
fmt=34 # Blue fmt=34 # Blue
;; ;;
([eE]*) # Error level -- always print. [eE]*) # Error level -- always print.
fmt=31 # Red fmt=31 # Red
;; ;;
(*) # Diagnostic level -- print unless QUIET. *) # Diagnostic level -- print unless QUIET.
fmt=1 # Bold fmt=1 # Bold
;; ;;
esac esac
shift shift
printf >&2 '\e[%sm%s:%-16s:\e[0m %s\n' \ printf >&2 '\e[%sm%s:%-16s:\e[0m %s\n' \
"$fmt" "$PRGN" "${FUNCNAME[${LOG_FUNC:-1}]}" "$*" "$fmt" "$PRGN" "${FUNCNAME[${LOG_FUNC:-1}]}" "$*"
} }
# Exit with an error and a message describing it. # Exit with an error and a message describing it.
@ -298,10 +298,10 @@ list_cycle() { # list_cycle LIST<string> DELIM
# T_PRE_DISPLAY, which is user-configurable. I wanted it to be as easy # T_PRE_DISPLAY, which is user-configurable. I wanted it to be as easy
# to configure for users who might not immediately know the bash array # to configure for users who might not immediately know the bash array
# syntax, but can figure out 'variable=value' without much thought. # syntax, but can figure out 'variable=value' without much thought.
local list="${!1}" # Pass the list by name, not value local list="${!1}" # Pass the list by name, not value
local delim="$2" # The delimiter of the string local delim="$2" # The delimiter of the string
local first="${list%%${delim}*}" # The first element local first="${list%%${delim}*}" # The first element
local rest="${list#*${delim}}" # The rest of the elements local rest="${list#*${delim}}" # The rest of the elements
# -v prints to the variable specified. # -v prints to the variable specified.
printf -v "$1" '%s%s%s' "${rest}" "${delim}" "${first}" printf -v "$1" '%s%s%s' "${rest}" "${delim}" "${first}"
} }
@ -325,7 +325,7 @@ prompt() { # prompt [-u] PROMPT [READ_ARGS...]
read_cmd+=(-i "$BOLLUX_URL") read_cmd+=(-i "$BOLLUX_URL")
shift shift
fi fi
local prompt="$1" # How to prompt the user local prompt="$1" # How to prompt the user
shift shift
read_cmd+=(-p "$prompt> ") read_cmd+=(-p "$prompt> ")
"${read_cmd[@]}" </dev/tty "$@" "${read_cmd[@]}" </dev/tty "$@"
@ -423,11 +423,11 @@ usplit() { # usplit URL_ARRAY<name> URL
# below performs a reverse lookup on the name to get the actual data. # below performs a reverse lookup on the name to get the actual data.
# shellcheck disable=2034 # shellcheck disable=2034
local entire_url="${BASH_REMATCH[0]}" \ local entire_url="${BASH_REMATCH[0]}" \
scheme="${BASH_REMATCH[2]}" \ scheme="${BASH_REMATCH[2]}" \
authority="${BASH_REMATCH[4]}" \ authority="${BASH_REMATCH[4]}" \
path="${BASH_REMATCH[5]}" \ path="${BASH_REMATCH[5]}" \
query="${BASH_REMATCH[7]}" \ query="${BASH_REMATCH[7]}" \
fragment="${BASH_REMATCH[9]}" fragment="${BASH_REMATCH[9]}"
# Iterate through the 5 components of a URL and assign them to elements # Iterate through the 5 components of a URL and assign them to elements
# of URL_ARRAY, as follows: # of URL_ARRAY, as follows:
@ -516,10 +516,10 @@ ucblank() { # ucblank COMPONENT<name>
# `ucset' sets one component of a URL array and setting the 0th element to the # `ucset' sets one component of a URL array and setting the 0th element to the
# new full URL. Use it instead of directly setting the array element with U[x], # new full URL. Use it instead of directly setting the array element with U[x],
# because U[0] will fall out of sync with the rest of the contents. # because U[0] will fall out of sync with the rest of the contents.
ucset() { # ucset URL_ARRAY_INDEX<name> NEW_VALUE ucset() { # ucset URL_ARRAY_INDEX<name> NEW_VALUE
local url_array_component="$1" # Of form 'URL_ARRAY[INDEX]' local url_array_component="$1" # Of form 'URL_ARRAY[INDEX]'
local value="$2" local value="$2"
# Assign $value to $url_array_component. # Assign $value to $url_array_component.
# #
# Wrapped in an 'eval' for the extra layer of indirection. # Wrapped in an 'eval' for the extra layer of indirection.
@ -538,8 +538,8 @@ uencode() { # uencode URL
for ((i = 0; i < ${#1}; i++)); do for ((i = 0; i < ${#1}; i++)); do
: "${1:i:1}" : "${1:i:1}"
case "$_" in case "$_" in
([a-zA-Z0-9.~_-]) printf '%s' "$_" ;; [a-zA-Z0-9.~_-]) printf '%s' "$_" ;;
(*) printf '%%%02X' "'$_" ;; *) printf '%%%02X' "'$_" ;;
esac esac
done done
printf '\n' printf '\n'
@ -704,7 +704,6 @@ gemini_request() { # gemini_request URL
port="$BOLLUX_GEMINI_PORT" port="$BOLLUX_GEMINI_PORT"
fi fi
# Build the SSL command to request the resource. # Build the SSL command to request the resource.
# #
# This is the beating heart of bollux, the command that does all the # This is the beating heart of bollux, the command that does all the
@ -716,12 +715,12 @@ gemini_request() { # gemini_request URL
# manual [9] it says not to use it, but who reads the manual, # manual [9] it says not to use it, but who reads the manual,
# anyway? # anyway?
openssl s_client openssl s_client
-crlf # Automatically add CR+LF to line -crlf # Automatically add CR+LF to line
-quiet # Don't print all the cert stuff -quiet # Don't print all the cert stuff
# -ign_eof # `-quiet' implies `-ign_eof' # -ign_eof # `-quiet' implies `-ign_eof'
-connect "${url[2]}:$port" # The server and port to connect -connect "${url[2]}:$port" # The server and port to connect
-servername "${url[2]}" # SNI: Server Name Identification -servername "${url[2]}" # SNI: Server Name Identification
-no_ssl3 -no_tls1 -no_tls1_1 # disable old TLS/SSL versions -no_ssl3 -no_tls1 -no_tls1_1 # disable old TLS/SSL versions
) )
# Actually request the resource. # Actually request the resource.
@ -733,9 +732,9 @@ gemini_request() { # gemini_request URL
# Handle the gemini response - see [3] Section 3. # Handle the gemini response - see [3] Section 3.
gemini_response() { # gemini_response URL gemini_response() { # gemini_response URL
local code meta # received on the first line of the response local code meta # received on the first line of the response
local title # determined by a clunky heuristic, see read loop: (2*) local title # determined by a clunky heuristic, see read loop: (2*)
local url="$1" # the currently-visited URL. local url="$1" # the currently-visited URL.
# Read the first line. # Read the first line.
# #
@ -751,7 +750,7 @@ gemini_response() { # gemini_response URL
# `download', below), but I'm not sure how to remedy that issue either. # `download', below), but I'm not sure how to remedy that issue either.
# It requires more research. # It requires more research.
while read -t "$BOLLUX_TIMEOUT" -r code meta || while read -t "$BOLLUX_TIMEOUT" -r code meta ||
{ (($? > 128)) && die 99 "Timeout."; }; do { (($? > 128)) && die 99 "Timeout."; }; do
break break
done done
log d "[$code] $meta" log d "[$code] $meta"
@ -763,20 +762,20 @@ gemini_response() { # gemini_response URL
# - I branch on the first digit of the status code, instead of both, to # - I branch on the first digit of the status code, instead of both, to
# minimize the amount of duplicated code I need to write. # minimize the amount of duplicated code I need to write.
case "$code" in case "$code" in
(1*) # INPUT 1*) # INPUT
# Gemini allows GET-style requests, and the INPUT family of # Gemini allows GET-style requests, and the INPUT family of
# response codes facilitate them. `10' is for standard input, # response codes facilitate them. `10' is for standard input,
# and `11' is for sensitive information, like passwords. # and `11' is for sensitive information, like passwords.
REDIRECTS=0 REDIRECTS=0
BOLLUX_URL="$url" BOLLUX_URL="$url"
case "$code" in case "$code" in
(10) run prompt "$meta" ;; 10) run prompt "$meta" ;;
(11) run prompt "$meta" -s ;; # sensitive input 11) run prompt "$meta" -s ;; # sensitive input
esac esac
run history_append "$url" "${title:-}" run history_append "$url" "${title:-}"
run blastoff "?$(uencode "$REPLY")" run blastoff "?$(uencode "$REPLY")"
;; ;;
(2*) # OK 2*) # OK
# The `20' family of requests is like HTTP's `200' family: it # The `20' family of requests is like HTTP's `200' family: it
# means that the request worked and the server is sending the # means that the request worked and the server is sending the
# requested content. # requested content.
@ -811,7 +810,7 @@ gemini_response() { # gemini_response URL
passthru passthru
} | run display "$meta" "${title:-}" } | run display "$meta" "${title:-}"
;; ;;
(3*) # REDIRECT 3*) # REDIRECT
# Redirects are a fundamental part of any hypertext framework, # Redirects are a fundamental part of any hypertext framework,
# and if I remember correctly, one of the main reasons # and if I remember correctly, one of the main reasons
# solderpunk and others began thinking about gemini (the others # solderpunk and others began thinking about gemini (the others
@ -838,51 +837,51 @@ gemini_response() { # gemini_response URL
# will also be an option, however. # will also be an option, however.
run blastoff "$meta" # TODO: confirm redirect run blastoff "$meta" # TODO: confirm redirect
;; ;;
(4*) # TEMPORARY ERROR 4*) # TEMPORARY ERROR
# Since the 4* codes ([3] Appendix 1) are all server issues, # Since the 4* codes ([3] Appendix 1) are all server issues,
# bollux can treat them all basically the same. This is an area # bollux can treat them all basically the same. This is an area
# that could use some expansion. # that could use some expansion.
local desc="Temporary error" local desc="Temporary error"
case "$code" in case "$code" in
(41) desc+=" (server unavailable)" ;; 41) desc+=" (server unavailable)" ;;
(42) desc+=" (CGI error)" ;; 42) desc+=" (CGI error)" ;;
(43) desc+=" (proxy error)" ;; 43) desc+=" (proxy error)" ;;
(44) desc+=" (slow down)" ;; # could be particularly improved 44) desc+=" (slow down)" ;; # could be particularly improved
esac esac
REDIRECTS=0 REDIRECTS=0
die "$((100 + code))" "$desc [$code]: $meta" die "$((100 + code))" "$desc [$code]: $meta"
;; ;;
(5*) # PERMANENT ERROR 5*) # PERMANENT ERROR
# The situation with the 5* codes is basically similar to the 4* # The situation with the 5* codes is basically similar to the 4*
# codes. It could maybe use more thought as to what behavior to # codes. It could maybe use more thought as to what behavior to
# implement. Maybe adding the (bad) requests to history, # implement. Maybe adding the (bad) requests to history,
# subject to configuration? # subject to configuration?
local desc="Permanent failure" local desc="Permanent failure"
case "$code" in case "$code" in
(51) desc+=" (not found)" ;; 51) desc+=" (not found)" ;;
(52) desc+=" (gone)" ;; 52) desc+=" (gone)" ;;
(53) desc+=" (proxy request refused)" ;; 53) desc+=" (proxy request refused)" ;;
# For some reason, codes 54--58 inclusive aren't used. # For some reason, codes 54--58 inclusive aren't used.
(59) desc+=" (bad request)" ;; 59) desc+=" (bad request)" ;;
esac esac
REDIRECTS=0 REDIRECTS=0
die "$((100 + code))" "$desc [$code]: $meta" die "$((100 + code))" "$desc [$code]: $meta"
;; ;;
(6*) # CERTIFICATE ERROR (TODO) 6*) # CERTIFICATE ERROR (TODO)
# Dealing with certificates is honestly the most important # Dealing with certificates is honestly the most important
# feature missing from bollux to get it to 1.0. Right now, # feature missing from bollux to get it to 1.0. Right now,
# bollux deals with 6* status codes identically to 4* and 5* # bollux deals with 6* status codes identically to 4* and 5*
# codes. This is not ideal, in the slightest. # codes. This is not ideal, in the slightest.
local desc="Client certificate required" local desc="Client certificate required"
case "$code" in case "$code" in
(61) desc+=" (certificate not authorized)" ;; 61) desc+=" (certificate not authorized)" ;;
(62) desc+=" (certificate not valid)" ;; 62) desc+=" (certificate not valid)" ;;
esac esac
REDIRECTS=0 REDIRECTS=0
log d "Not implemented: Client certificates" log d "Not implemented: Client certificates"
die "$((100 + code))" "[$code] $meta" die "$((100 + code))" "[$code] $meta"
;; ;;
(*) # UNKNOWN *) # UNKNOWN
# Just in case we get a weird, un-spec-compliant status code. # Just in case we get a weird, un-spec-compliant status code.
[[ -z "${code-}" ]] && die 100 "Empty response code." [[ -z "${code-}" ]] && die 100 "Empty response code."
die "$((100 + code))" "Unknown response code: $code." die "$((100 + code))" "Unknown response code: $code."
@ -916,9 +915,9 @@ gopher_request() { # gopher_request URL
# [7] Section 2.1 # [7] Section 2.1
[[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]] [[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]]
local server="${BASH_REMATCH[1]}" \ local server="${BASH_REMATCH[1]}" \
port="${BASH_REMATCH[3]:-$BOLLUX_GOPHER_PORT}" \ port="${BASH_REMATCH[3]:-$BOLLUX_GOPHER_PORT}" \
type="${BASH_REMATCH[6]:-1}" \ type="${BASH_REMATCH[6]:-1}" \
path="${BASH_REMATCH[7]}" path="${BASH_REMATCH[7]}"
log d "URL='$url' SERVER='$server' TYPE='$type' PATH='$path'" log d "URL='$url' SERVER='$server' TYPE='$type' PATH='$path'"
# Bash has this really neat feature where it can open a TCP socket # Bash has this really neat feature where it can open a TCP socket
@ -952,25 +951,25 @@ gopher_response() { # gopher_response URL
# text-ish, it only concerns itself with those in this case statement. # text-ish, it only concerns itself with those in this case statement.
# All the others are simply downloaded. # All the others are simply downloaded.
case "$type" in case "$type" in
(0) # Item is a file 0) # Item is a file
# Since gopher doesn't send MIME-type information in-band, we # Since gopher doesn't send MIME-type information in-band, we
# just assume it's text/plain, and try to convert it later to # just assume it's text/plain, and try to convert it later to
# UTF-8 with `iconv'. # UTF-8 with `iconv'.
run display text/plain run display text/plain
;; ;;
(1) # Item is a directory [gophermap] 1) # Item is a directory [gophermap]
# Since I've already written all the code to typeset gemini # Since I've already written all the code to typeset gemini
# well, it's easy to convert a gophermap to text/gemini and # well, it's easy to convert a gophermap to text/gemini and
# display it than to write a whole new gophermap typesetter. # display it than to write a whole new gophermap typesetter.
run gopher_convert | run display text/gemini run gopher_convert | run display text/gemini
;; ;;
(3) # Error 3) # Error
# I don't know all the gopher error cases, and the spec is # I don't know all the gopher error cases, and the spec is
# pretty quiet on them. So bollux just signals failure and # pretty quiet on them. So bollux just signals failure and
# bails. # bails.
die 203 "GOPHER: failed" die 203 "GOPHER: failed"
;; ;;
(7) # Item is an Index-Search server 7) # Item is an Index-Search server
# Gopher search queries are separated from their resources by a # Gopher search queries are separated from their resources by a
# TAB. It's wild. # TAB. It's wild.
if [[ "$url" =~ $'\t' ]]; then if [[ "$url" =~ $'\t' ]]; then
@ -980,7 +979,7 @@ gopher_response() { # gopher_response URL
run blastoff "$url $REPLY" run blastoff "$url $REPLY"
fi fi
;; ;;
(*) # Anything else *) # Anything else
# The list at [6] Section 3.8 includes the following (noted where it # The list at [6] Section 3.8 includes the following (noted where it
# might be good to differently handle them in the future): # might be good to differently handle them in the future):
# #
@ -1030,19 +1029,19 @@ gopher_convert() {
continue continue
fi fi
case "$type" in case "$type" in
(.) # end of file .) # end of file
printf '.\n' printf '.\n'
break break
;; ;;
(i) # label i) # label
case "$label" in case "$label" in
('#'* | '*'[[:space:]]*) '#'* | '*'[[:space:]]*)
if $pre; then if $pre; then
printf '%s\n' '```' printf '%s\n' '```'
pre=false pre=false
fi fi
;; ;;
(*) *)
if ! $pre; then if ! $pre; then
printf '%s\n' '```' printf '%s\n' '```'
pre=true pre=true
@ -1051,28 +1050,28 @@ gopher_convert() {
esac esac
printf '%s\n' "$label" printf '%s\n' "$label"
;; ;;
(h) # html link h) # html link
if $pre; then if $pre; then
printf '%s\n' '```' printf '%s\n' '```'
pre=false pre=false
fi fi
printf '=> %s %s\n' "${path:4}" "$label" printf '=> %s %s\n' "${path:4}" "$label"
;; ;;
(T) # telnet link T) # telnet link
if $pre; then if $pre; then
printf '%s\n' '```' printf '%s\n' '```'
pre=false pre=false
fi fi
printf '=> telnet://%s:%s/%s%s %s\n' \ printf '=> telnet://%s:%s/%s%s %s\n' \
"$server" "$port" "$type" "$path" "$label" "$server" "$port" "$type" "$path" "$label"
;; ;;
(*) # other type *) # other type
if $pre; then if $pre; then
printf '%s\n' '```' printf '%s\n' '```'
pre=false pre=false
fi fi
printf '=> gopher://%s:%s/%s%s %s\n' \ printf '=> gopher://%s:%s/%s%s %s\n' \
"$server" "$port" "$type" "$path" "$label" "$server" "$port" "$type" "$path" "$label"
;; ;;
esac esac
done done
@ -1109,7 +1108,7 @@ display() { # display METADATA [TITLE]
for ((i = 1; i <= "${#hdr[@]}"; i++)); do for ((i = 1; i <= "${#hdr[@]}"; i++)); do
h="${hdr[$i]}" h="${hdr[$i]}"
case "$h" in case "$h" in
(*charset=*) charset="${h#*=}" ;; *charset=*) charset="${h#*=}" ;;
esac esac
done done
@ -1119,7 +1118,7 @@ display() { # display METADATA [TITLE]
log debug "mime='$mime'; charset='$charset'" log debug "mime='$mime'; charset='$charset'"
case "$mime" in case "$mime" in
(text/*) text/*)
set_title "$title${title:+ - }bollux" set_title "$title${title:+ - }bollux"
# Build the `less' command # Build the `less' command
less_cmd=(less) less_cmd=(less)
@ -1163,7 +1162,7 @@ display() { # display METADATA [TITLE]
run "${less_cmd[@]}" && bollux_quit run "${less_cmd[@]}" && bollux_quit
} || run handle_keypress "$?" } || run handle_keypress "$?"
;; ;;
(*) run download "$BOLLUX_URL" ;; *) run download "$BOLLUX_URL" ;;
esac esac
} }
@ -1173,8 +1172,8 @@ less_prompt_escape() { # less_prompt_escape STRING
for ((i = 0; i < ${#1}; i++)); do for ((i = 0; i < ${#1}; i++)); do
: "${1:i:1}" : "${1:i:1}"
case "$_" in case "$_" in
([\?:\.%\\]) printf '\%s' "$_" ;; [\?:\.%\\]) printf '\%s' "$_" ;;
(*) printf '%s' "$_" ;; *) printf '%s' "$_" ;;
esac esac
done done
printf '\n' printf '\n'
@ -1232,7 +1231,7 @@ typeset_gemini() {
while IFS= read -r; do while IFS= read -r; do
case "$REPLY" in case "$REPLY" in
('```'*) '```'*)
PRE_LINE_FORCE=false PRE_LINE_FORCE=false
if $pre; then if $pre; then
pre=false pre=false
@ -1240,28 +1239,28 @@ typeset_gemini() {
pre=true pre=true
fi fi
case "${T_PRE_DISPLAY%%,*}" in case "${T_PRE_DISPLAY%%,*}" in
(pre) pre)
: :
;; ;;
(alt | both) alt | both)
$pre && PRE_LINE_FORCE=true \ $pre && PRE_LINE_FORCE=true \
gemini_pre "${REPLY#\`\`\`}" gemini_pre "${REPLY#\`\`\`}"
;; ;;
esac esac
continue continue
;; ;;
('=>'*) '=>'*)
: $((ln += 1)) : $((ln += 1))
gemini_link "$REPLY" $pre "$ln" gemini_link "$REPLY" $pre "$ln"
;; ;;
('#'*) gemini_header "$REPLY" $pre ;; '#'*) gemini_header "$REPLY" $pre ;;
('*'[[:space:]]*) '*'[[:space:]]*)
gemini_list "$REPLY" $pre gemini_list "$REPLY" $pre
;; ;;
('>'*) '>'*)
gemini_quote "$REPLY" $pre gemini_quote "$REPLY" $pre
;; ;;
(*) gemini_text "$REPLY" $pre ;; *) gemini_text "$REPLY" $pre ;;
esac esac
done done
} }
@ -1282,13 +1281,13 @@ gemini_link() {
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
printf "\e[${C_LINK_NUMBER}m[%d]${C_RESET} " "$ln" printf "\e[${C_LINK_NUMBER}m[%d]${C_RESET} " "$ln"
fold_line -n -B "\e[${C_LINK_TITLE}m" -A "${C_RESET}" \ fold_line -n -B "\e[${C_LINK_TITLE}m" -A "${C_RESET}" \
-l "$((${#ln} + 3))" -m "${T_MARGIN}" \ -l "$((${#ln} + 3))" -m "${T_MARGIN}" \
"$WIDTH" "$(trim_string "$t")" "$WIDTH" "$(trim_string "$t")"
fold_line -B " \e[${C_LINK_URL}m" \ fold_line -B " \e[${C_LINK_URL}m" \
-A "${C_RESET}" \ -A "${C_RESET}" \
-l "$((${#ln} + 3 + ${#t}))" \ -l "$((${#ln} + 3 + ${#t}))" \
-m "$((T_MARGIN + ${#ln} + 2))" \ -m "$((T_MARGIN + ${#ln} + 2))" \
"$WIDTH" "$a" "$WIDTH" "$a"
else else
gemini_pre "$1" gemini_pre "$1"
fi fi
@ -1306,7 +1305,7 @@ gemini_header() {
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
fold_line -B "\e[${hdrfmt}m" -A "${C_RESET}" -m "${T_MARGIN}" \ fold_line -B "\e[${hdrfmt}m" -A "${C_RESET}" -m "${T_MARGIN}" \
"$WIDTH" "$t" "$WIDTH" "$t"
else else
gemini_pre "$1" gemini_pre "$1"
fi fi
@ -1321,7 +1320,7 @@ gemini_list() {
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
fold_line -B "\e[${C_LIST}m" -A "${C_RESET}" -m "$T_MARGIN" \ fold_line -B "\e[${C_LIST}m" -A "${C_RESET}" -m "$T_MARGIN" \
"$WIDTH" "$t" "$WIDTH" "$t"
else else
gemini_pre "$1" gemini_pre "$1"
fi fi
@ -1336,7 +1335,7 @@ gemini_quote() {
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
fold_line -B "\e[${C_QUOTE}m" -A "${C_RESET}" -m "$T_MARGIN" \ fold_line -B "\e[${C_QUOTE}m" -A "${C_RESET}" -m "$T_MARGIN" \
"$WIDTH" "$t" "$WIDTH" "$t"
else else
gemini_pre "$1" gemini_pre "$1"
fi fi
@ -1346,7 +1345,7 @@ gemini_text() {
if ! ${2-false}; then if ! ${2-false}; then
printf "%${S_MARGIN}s " ' ' printf "%${S_MARGIN}s " ' '
fold_line -m "$T_MARGIN" \ fold_line -m "$T_MARGIN" \
"$WIDTH" "$1" "$WIDTH" "$1"
else else
gemini_pre "$1" gemini_pre "$1"
fi fi
@ -1370,25 +1369,25 @@ fold_line() { # fold_line [OPTIONS...] WIDTH TEXT
OPTIND=0 OPTIND=0
while getopts nm:f:l:B:A: OPT; do while getopts nm:f:l:B:A: OPT; do
case "$OPT" in case "$OPT" in
(n) # -n = no trailing newline n) # -n = no trailing newline
newline=false newline=false
;; ;;
(m) # -m MARGIN = margin for all lines m) # -m MARGIN = margin for all lines
margin_all="$OPTARG" margin_all="$OPTARG"
;; ;;
(f) # -f MARGIN = margin for first line f) # -f MARGIN = margin for first line
margin_first="$OPTARG" margin_first="$OPTARG"
;; ;;
(l) # -l LENGTH = length of line before starting fold l) # -l LENGTH = length of line before starting fold
ll="$OPTARG" ll="$OPTARG"
;; ;;
(B) # -B BEFORE = text to insert before each line B) # -B BEFORE = text to insert before each line
before="$OPTARG" before="$OPTARG"
;; ;;
(A) # -A AFTER = text to insert after each line A) # -A AFTER = text to insert after each line
after="$OPTARG" after="$OPTARG"
;; ;;
(*) return 1 ;; *) return 1 ;;
esac esac
done done
shift "$((OPTIND - 1))" shift "$((OPTIND - 1))"
@ -1426,37 +1425,37 @@ fold_line() { # fold_line [OPTIONS...] WIDTH TEXT
# use the exit code from less (see mklesskey) to do things # use the exit code from less (see mklesskey) to do things
handle_keypress() { # handle_keypress CODE handle_keypress() { # handle_keypress CODE
case "$1" in case "$1" in
(48) # o - open a link -- show a menu of links on the page 48) # o - open a link -- show a menu of links on the page
run select_url "$BOLLUX_PAGESRC" run select_url "$BOLLUX_PAGESRC"
;; ;;
(49) # g - goto a url -- input a new url 49) # g - goto a url -- input a new url
prompt GO prompt GO
run blastoff -u "$REPLY" run blastoff -u "$REPLY"
;; ;;
(50) # [ - back in the history 50) # [ - back in the history
run history_back || { run history_back || {
sleep 0.5 sleep 0.5
run blastoff "$BOLLUX_URL" run blastoff "$BOLLUX_URL"
} }
;; ;;
(51) # ] - forward in the history 51) # ] - forward in the history
run history_forward || { run history_forward || {
sleep 0.5 sleep 0.5
run blastoff "$BOLLUX_URL" run blastoff "$BOLLUX_URL"
} }
;; ;;
(52) # r - re-request the current resource 52) # r - re-request the current resource
run blastoff "$BOLLUX_URL" run blastoff "$BOLLUX_URL"
;; ;;
(53) # G - goto a url (pre-filled with current) 53) # G - goto a url (pre-filled with current)
run prompt -u GO run prompt -u GO
run blastoff -u "$REPLY" run blastoff -u "$REPLY"
;; ;;
(54) # ` - change alt-text visibility and refresh 54) # ` - change alt-text visibility and refresh
run list_cycle T_PRE_DISPLAY , run list_cycle T_PRE_DISPLAY ,
run blastoff "$BOLLUX_URL" run blastoff "$BOLLUX_URL"
;; ;;
(55) # 55-57 -- still available for binding 55) # 55-57 -- still available for binding
die "$?" "less(1) error" die "$?" "less(1) error"
;; ;;
esac esac
@ -1473,8 +1472,8 @@ select_url() { # select_url FILE
PS3="OPEN> " PS3="OPEN> "
select u in "${MAPFILE[@]}"; do select u in "${MAPFILE[@]}"; do
case "$REPLY" in case "$REPLY" in
(q) bollux_quit ;; q) bollux_quit ;;
([^0-9]*) run blastoff -u "$REPLY" && break ;; [^0-9]*) run blastoff -u "$REPLY" && break ;;
esac esac
run blastoff "${u%%[[:space:]]*}" && break run blastoff "${u%%[[:space:]]*}" && break
done </dev/tty done </dev/tty
@ -1543,12 +1542,12 @@ history_append() { # history_append URL TITLE
# Print the URL and its title (if given) to $BOLLUX_HISTFILE. # Print the URL and its title (if given) to $BOLLUX_HISTFILE.
local fmt='' local fmt=''
fmt+='%(%FT%T)T\t' # %(_)T calls directly to 'strftime'. fmt+='%(%FT%T)T\t' # %(_)T calls directly to 'strftime'.
if (( $# == 2 )); then if (($# == 2)); then
fmt+='%s\t' # $url fmt+='%s\t' # $url
fmt+='%s\n' # $title fmt+='%s\n' # $title
else else
fmt+='%s%s\n' # printf needs a field for every argument. fmt+='%s%s\n' # printf needs a field for every argument.
fi fi
run printf -- "$fmt" -1 "$url" "$title" >>"$BOLLUX_HISTFILE" run printf -- "$fmt" -1 "$url" "$title" >>"$BOLLUX_HISTFILE"
@ -1567,26 +1566,26 @@ history_back() {
# one with each call to `history_append'. If we subtract 1, we'll just # one with each call to `history_append'. If we subtract 1, we'll just
# be at the end of the array again, reloading the page. # be at the end of the array again, reloading the page.
((HN -= 2)) ((HN -= 2))
if ((HN < 0)); then if ((HN < 0)); then
HN=0 HN=0
log e "Beginning of history." log e "Beginning of history."
return 1 return 1
fi fi
run blastoff "${HISTORY[$HN]}" run blastoff "${HISTORY[$HN]}"
} }
# Move forward in session history. # Move forward in session history.
history_forward() { history_forward() {
log d "HN=$HN" log d "HN=$HN"
if ((HN >= ${#HISTORY[@]})); then if ((HN >= ${#HISTORY[@]})); then
HN="${#HISTORY[@]}" HN="${#HISTORY[@]}"
log e "End of history." log e "End of history."
return 1 return 1
fi fi
run blastoff "${HISTORY[$HN]}" run blastoff "${HISTORY[$HN]}"
} }
@ -1646,8 +1645,8 @@ blastoff() { # blastoff [-u] URL
run "${url[1]}_response" "$url" run "${url[1]}_response" "$url"
else else
log d \ log d \
"No response handler for '${url[1]}';" \ "No response handler for '${url[1]}';" \
" passing thru" " passing thru"
passthru passthru
fi fi
} }