2020-05-22 20:53:35 +00:00
|
|
|
#!/usr/bin/env bash
|
2020-05-26 02:41:42 +00:00
|
|
|
# bollux: a bash gemini client
|
|
|
|
# Author: Case Duckworth
|
|
|
|
# License: MIT
|
2020-06-05 01:14:05 +00:00
|
|
|
# Version: 0.4.0
|
2020-05-26 02:37:55 +00:00
|
|
|
|
|
|
|
# Program information
|
|
|
|
PRGN="${0##*/}"
|
2020-06-05 01:14:05 +00:00
|
|
|
VRSN=0.4.0
|
2020-05-26 02:37:55 +00:00
|
|
|
|
2020-05-26 02:41:42 +00:00
|
|
|
bollux_usage() {
|
|
|
|
cat <<END
|
|
|
|
$PRGN (v. $VRSN): a bash gemini client
|
|
|
|
usage:
|
|
|
|
$PRGN [-h]
|
|
|
|
$PRGN [-q] [-v] [URL]
|
|
|
|
flags:
|
|
|
|
-h show this help and exit
|
|
|
|
-q be quiet: log no messages
|
|
|
|
-v verbose: log more messages
|
|
|
|
parameters:
|
|
|
|
URL the URL to start in
|
|
|
|
If not provided, the user will be prompted.
|
|
|
|
END
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
run() { # run COMMAND...
|
2021-02-25 23:01:10 +00:00
|
|
|
trap bollux_quit SIGINT
|
2020-06-08 00:23:14 +00:00
|
|
|
log debug "$*"
|
2020-05-26 02:37:55 +00:00
|
|
|
"$@"
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
die() { # die EXIT_CODE MESSAGE
|
2020-06-05 01:14:05 +00:00
|
|
|
local ec="$1"
|
2020-05-26 02:37:55 +00:00
|
|
|
shift
|
|
|
|
log error "$*"
|
|
|
|
exit "$ec"
|
2020-05-24 00:45:21 +00:00
|
|
|
}
|
2020-05-22 20:53:35 +00:00
|
|
|
|
2020-06-08 20:37:03 +00:00
|
|
|
# builtin replacement for `sleep`
|
|
|
|
# https://github.com/dylanaraps/pure-bash-bible#use-read-as-an-alternative-to-the-sleep-command
|
|
|
|
sleep() { # sleep SECONDS
|
|
|
|
read -rt "$1" <> <(:) || :
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# https://github.com/dylanaraps/pure-bash-bible/
|
|
|
|
trim_string() { # trim_string STRING
|
2020-05-30 19:35:17 +00:00
|
|
|
: "${1#"${1%%[![:space:]]*}"}"
|
|
|
|
: "${_%"${_##*[![:space:]]}"}"
|
|
|
|
printf '%s\n' "$_"
|
|
|
|
}
|
2020-05-26 02:37:55 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
log() { # log LEVEL MESSAGE
|
2020-05-26 02:37:55 +00:00
|
|
|
[[ "$BOLLUX_LOGLEVEL" == QUIET ]] && return
|
2020-06-05 01:14:05 +00:00
|
|
|
local fmt
|
|
|
|
|
2020-05-22 20:53:35 +00:00
|
|
|
case "$1" in
|
2020-06-04 04:22:55 +00:00
|
|
|
[dD]*) # debug
|
2020-05-26 02:37:55 +00:00
|
|
|
[[ "$BOLLUX_LOGLEVEL" == DEBUG ]] || return
|
|
|
|
fmt=34
|
2020-05-22 22:12:15 +00:00
|
|
|
;;
|
2020-06-04 04:22:55 +00:00
|
|
|
[eE]*) # error
|
2020-05-26 02:37:55 +00:00
|
|
|
fmt=31
|
2020-05-22 20:53:35 +00:00
|
|
|
;;
|
2020-05-26 02:37:55 +00:00
|
|
|
*) fmt=1 ;;
|
2020-05-22 20:53:35 +00:00
|
|
|
esac
|
2020-05-26 02:37:55 +00:00
|
|
|
shift
|
2020-06-05 01:14:05 +00:00
|
|
|
|
2020-06-08 15:13:34 +00:00
|
|
|
printf >&2 '\e[%sm%s:%s:\e[0m\t%s\n' "$fmt" "$PRGN" "${FUNCNAME[1]}" "$*"
|
2020-05-26 02:37:55 +00:00
|
|
|
}
|
2020-05-22 13:38:40 +00:00
|
|
|
|
2020-05-26 02:37:55 +00:00
|
|
|
# main entry point
|
|
|
|
bollux() {
|
2020-06-05 01:14:05 +00:00
|
|
|
run bollux_config # TODO: figure out better config method
|
|
|
|
run bollux_args "$@" # and argument parsing
|
|
|
|
run bollux_init
|
2020-05-26 02:37:55 +00:00
|
|
|
|
2020-06-04 04:22:55 +00:00
|
|
|
if [[ ! "${BOLLUX_URL:+x}" ]]; then
|
2020-05-26 02:37:55 +00:00
|
|
|
run prompt GO BOLLUX_URL
|
2020-05-22 20:53:35 +00:00
|
|
|
fi
|
2020-05-26 02:37:55 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
log d "BOLLUX_URL='$BOLLUX_URL'"
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
run blastoff -u "$BOLLUX_URL"
|
2020-05-22 13:38:40 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# process command-line arguments
|
2020-05-26 02:37:55 +00:00
|
|
|
bollux_args() {
|
2020-05-26 02:41:42 +00:00
|
|
|
while getopts :hvq OPT; do
|
2020-05-26 02:37:55 +00:00
|
|
|
case "$OPT" in
|
2020-05-26 02:41:42 +00:00
|
|
|
h)
|
|
|
|
bollux_usage
|
|
|
|
exit
|
|
|
|
;;
|
2020-05-26 02:37:55 +00:00
|
|
|
v) BOLLUX_LOGLEVEL=DEBUG ;;
|
|
|
|
q) BOLLUX_LOGLEVEL=QUIET ;;
|
|
|
|
:) die 1 "Option -$OPTARG requires an argument" ;;
|
|
|
|
*) die 1 "Unknown option: -$OPTARG" ;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
shift $((OPTIND - 1))
|
|
|
|
if (($# == 1)); then
|
|
|
|
BOLLUX_URL="$1"
|
|
|
|
fi
|
|
|
|
}
|
2020-05-22 20:53:35 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# process config file and set variables
|
2020-05-26 02:37:55 +00:00
|
|
|
bollux_config() {
|
2020-05-31 13:40:26 +00:00
|
|
|
: "${BOLLUX_CONFIG:=${XDG_CONFIG_DIR:-$HOME/.config}/bollux/bollux.conf}"
|
2020-05-26 02:37:55 +00:00
|
|
|
|
|
|
|
if [ -f "$BOLLUX_CONFIG" ]; then
|
|
|
|
# shellcheck disable=1090
|
|
|
|
. "$BOLLUX_CONFIG"
|
|
|
|
else
|
|
|
|
log debug "Can't load config file '$BOLLUX_CONFIG'."
|
|
|
|
fi
|
|
|
|
|
2020-05-30 21:00:12 +00:00
|
|
|
## behavior
|
2020-06-08 20:24:43 +00:00
|
|
|
: "${BOLLUX_TIMEOUT:=30}" # connection timeout
|
|
|
|
: "${BOLLUX_MAXREDIR:=5}" # max redirects
|
|
|
|
: "${BOLLUX_PORT:=1965}" # port number
|
|
|
|
: "${BOLLUX_PROTO:=gemini}" # default protocol
|
|
|
|
: "${BOLLUX_URL:=}" # start url
|
|
|
|
: "${BOLLUX_BYEMSG:=See You Space Cowboy ...}" # bye message
|
2020-06-03 03:24:49 +00:00
|
|
|
## files
|
|
|
|
: "${BOLLUX_DATADIR:=${XDG_DATA_DIR:-$HOME/.local/share}/bollux}"
|
|
|
|
: "${BOLLUX_DOWNDIR:=.}" # where to save downloads
|
|
|
|
: "${BOLLUX_LESSKEY:=$BOLLUX_DATADIR/lesskey}" # where to store binds
|
|
|
|
: "${BOLLUX_PAGESRC:=$BOLLUX_DATADIR/pagesrc}" # where to save the source
|
|
|
|
BOLLUX_HISTFILE="$BOLLUX_DATADIR/history" # where to save the history
|
2020-05-30 21:00:12 +00:00
|
|
|
## typesetting
|
2021-02-26 01:36:14 +00:00
|
|
|
: "${T_MARGIN:=4}" # left and right margin
|
|
|
|
: "${T_WIDTH:=0}" # width of the viewport -- 0 = get term width
|
2020-05-30 21:00:12 +00:00
|
|
|
# colors -- these will be wrapped in \e[ __ m
|
|
|
|
C_RESET='\e[0m' # reset
|
|
|
|
: "${C_SIGIL:=35}" # sigil (=>, #, ##, ###, *, ```)
|
|
|
|
: "${C_LINK_NUMBER:=1}" # link number
|
|
|
|
: "${C_LINK_TITLE:=4}" # link title
|
|
|
|
: "${C_LINK_URL:=36}" # link URL
|
|
|
|
: "${C_HEADER1:=1;4}" # header 1 formatting
|
|
|
|
: "${C_HEADER2:=1}" # header 2 formatting
|
|
|
|
: "${C_HEADER3:=3}" # header 3 formatting
|
|
|
|
: "${C_LIST:=0}" # list formatting
|
2020-06-08 05:36:42 +00:00
|
|
|
: "${C_QUOTE:=3}" # quote formatting
|
2020-05-30 21:00:12 +00:00
|
|
|
: "${C_PRE:=0}" # preformatted text formatting
|
2020-06-18 13:24:01 +00:00
|
|
|
## state
|
|
|
|
UC_BLANK=':?:'
|
2020-05-22 13:38:40 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# quit happily
|
2020-05-31 15:58:44 +00:00
|
|
|
bollux_quit() {
|
2020-06-08 20:24:43 +00:00
|
|
|
printf '\e[1m%s\e[0m:\t\e[3m%s\e[0m\n' "$PRGN" "$BOLLUX_BYEMSG"
|
2020-05-31 15:58:44 +00:00
|
|
|
exit
|
|
|
|
}
|
2021-02-25 23:01:10 +00:00
|
|
|
# trap C-c
|
|
|
|
trap bollux_quit SIGINT
|
2020-05-31 15:58:44 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# set the terminal title
|
|
|
|
set_title() { # set_title STRING
|
2020-06-07 23:43:28 +00:00
|
|
|
printf '\e]2;%s\007' "$*"
|
2020-05-31 15:58:44 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# prompt for input
|
2020-06-03 12:43:46 +00:00
|
|
|
prompt() { # prompt [-u] PROMPT [READ_ARGS...]
|
2020-06-05 01:14:05 +00:00
|
|
|
local read_cmd=(read -e -r)
|
2020-06-03 12:43:46 +00:00
|
|
|
if [[ "$1" == "-u" ]]; then
|
|
|
|
read_cmd+=(-i "$BOLLUX_URL")
|
|
|
|
shift
|
|
|
|
fi
|
2020-06-05 01:14:05 +00:00
|
|
|
local prompt="$1"
|
2020-05-23 01:34:27 +00:00
|
|
|
shift
|
2020-06-03 12:43:46 +00:00
|
|
|
read_cmd+=(-p "$prompt> ")
|
|
|
|
"${read_cmd[@]}" </dev/tty "$@"
|
2020-05-23 01:34:27 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# load a URL
|
|
|
|
blastoff() { # blastoff [-u] URL
|
2020-06-18 13:24:01 +00:00
|
|
|
local u
|
|
|
|
|
2020-05-26 02:37:55 +00:00
|
|
|
if [[ "$1" == "-u" ]]; then
|
2020-06-18 13:24:01 +00:00
|
|
|
u="$(run uwellform "$2")"
|
|
|
|
else
|
|
|
|
u="$1"
|
2020-05-26 02:37:55 +00:00
|
|
|
fi
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
local -a url
|
|
|
|
run utransform url "$BOLLUX_URL" "$u"
|
|
|
|
if ! ucdef url[1]; then
|
|
|
|
run ucset url[1] "$BOLLUX_PROTO"
|
2020-05-26 02:37:55 +00:00
|
|
|
fi
|
|
|
|
|
2020-06-05 01:14:05 +00:00
|
|
|
{
|
2020-06-18 13:24:01 +00:00
|
|
|
if declare -Fp "${url[1]}_request" >/dev/null 2>&1; then
|
|
|
|
run "${url[1]}_request" "$url"
|
2020-06-05 01:14:05 +00:00
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
die 99 "No request handler for '${url[1]}'"
|
2020-06-05 01:14:05 +00:00
|
|
|
fi
|
2020-06-18 13:24:01 +00:00
|
|
|
} | run normalize | {
|
|
|
|
if declare -Fp "${url[1]}_response" >/dev/null 2>&1; then
|
|
|
|
run "${url[1]}_response" "$url"
|
|
|
|
else
|
|
|
|
log d "No response handler for '${url[1]}', passing thru"
|
|
|
|
passthru
|
|
|
|
fi
|
|
|
|
}
|
2020-05-26 02:37:55 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
# URLS
|
|
|
|
## https://tools.ietf.org/html/rfc3986
|
|
|
|
uwellform() {
|
|
|
|
local u="$1"
|
2021-02-26 01:36:14 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
if [[ "$u" != *://* ]]; then
|
|
|
|
u="$BOLLUX_PROTO://$u"
|
|
|
|
fi
|
|
|
|
|
|
|
|
u="$(trim_string "$u")"
|
|
|
|
|
|
|
|
printf '%s\n' "$u"
|
|
|
|
}
|
|
|
|
|
|
|
|
usplit() { # usplit NAME:ARRAY URL:STRING
|
|
|
|
local re='^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?'
|
|
|
|
[[ $2 =~ $re ]] || return $?
|
|
|
|
|
2021-02-26 03:37:30 +00:00
|
|
|
# shellcheck disable=2034
|
|
|
|
local scheme="${BASH_REMATCH[2]}" \
|
|
|
|
authority="${BASH_REMATCH[4]}" \
|
|
|
|
path="${BASH_REMATCH[5]}" \
|
|
|
|
query="${BASH_REMATCH[7]}" \
|
|
|
|
fragment="${BASH_REMATCH[9]}"
|
2020-06-18 13:24:01 +00:00
|
|
|
|
|
|
|
# 0=url 1=scheme 2=authority 3=path 4=query 5=fragment
|
|
|
|
local i=1 c
|
|
|
|
for c in scheme authority path query fragment; do
|
|
|
|
if [[ "${!c}" || "$c" == path ]]; then
|
|
|
|
printf -v "$1[$i]" '%s' "${!c}"
|
|
|
|
else
|
2021-02-26 03:37:30 +00:00
|
|
|
# shellcheck disable=2059
|
2020-06-18 13:24:01 +00:00
|
|
|
printf -v "$1[$i]" "$UC_BLANK"
|
|
|
|
fi
|
2021-02-26 01:36:14 +00:00
|
|
|
((i += 1))
|
2020-06-18 13:24:01 +00:00
|
|
|
done
|
2021-02-26 03:37:30 +00:00
|
|
|
# shellcheck disable=2059
|
2020-06-18 13:24:01 +00:00
|
|
|
printf -v "$1[0]" "$(ujoin "$1")" # inefficient I'm sure
|
|
|
|
}
|
|
|
|
|
|
|
|
ujoin() { # ujoin NAME:ARRAY
|
|
|
|
local -n U="$1"
|
|
|
|
|
|
|
|
if ucdef U[1]; then
|
|
|
|
printf -v U[0] "%s:" "${U[1]}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if ucdef U[2]; then
|
|
|
|
printf -v U[0] "${U[0]}//%s" "${U[2]}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
printf -v U[0] "${U[0]}%s" "${U[3]}"
|
2021-02-26 01:36:14 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
if ucdef U[4]; then
|
|
|
|
printf -v U[0] "${U[0]}?%s" "${U[4]}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if ucdef U[5]; then
|
|
|
|
printf -v U[0] "${U[0]}#%s" "${U[5]}"
|
2020-05-30 18:38:18 +00:00
|
|
|
fi
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
log d "${U[0]}"
|
|
|
|
}
|
|
|
|
|
|
|
|
ucdef() { [[ "${!1}" != "$UC_BLANK" ]]; } # ucdef NAME
|
|
|
|
ucblank() { [[ -z "${!1}" ]]; } # ucblank NAME
|
2021-02-26 01:36:14 +00:00
|
|
|
ucset() { # ucset NAME VALUE
|
2020-06-18 13:24:01 +00:00
|
|
|
run eval "${1}='$2'"
|
2021-02-26 01:36:14 +00:00
|
|
|
run ujoin "${1/\[*\]/}"
|
2020-06-18 13:24:01 +00:00
|
|
|
}
|
|
|
|
|
2021-02-26 01:36:14 +00:00
|
|
|
utransform() { # utransform TARGET:ARRAY BASE:STRING REFERENCE:STRING
|
|
|
|
local -a B R # base, reference
|
2020-06-18 13:24:01 +00:00
|
|
|
local -n T="$1" # target
|
|
|
|
usplit B "$2"
|
|
|
|
usplit R "$3"
|
|
|
|
|
|
|
|
# initialize T
|
2021-02-26 01:36:14 +00:00
|
|
|
for ((i = 1; i <= 5; i++)); do
|
2020-06-18 13:24:01 +00:00
|
|
|
T[$i]="$UC_BLANK"
|
|
|
|
done
|
|
|
|
|
|
|
|
# 0=url 1=scheme 2=authority 3=path 4=query 5=fragment
|
|
|
|
if ucdef R[1]; then
|
|
|
|
T[1]="${R[1]}"
|
|
|
|
if ucdef R[2]; then
|
|
|
|
T[2]="${R[2]}"
|
|
|
|
fi
|
|
|
|
if ucdef R[3]; then
|
|
|
|
T[3]="$(pundot "${R[3]}")"
|
|
|
|
fi
|
|
|
|
if ucdef R[4]; then
|
|
|
|
T[4]="${R[4]}"
|
|
|
|
fi
|
2020-05-30 18:38:18 +00:00
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
if ucdef R[2]; then
|
|
|
|
T[2]="${R[2]}"
|
|
|
|
if ucdef R[2]; then
|
|
|
|
T[3]="$(pundot "${R[3]}")"
|
|
|
|
fi
|
|
|
|
if ucdef R[4]; then
|
|
|
|
T[4]="${R[4]}"
|
|
|
|
fi
|
2020-05-26 02:37:55 +00:00
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
if ucblank R[3]; then
|
|
|
|
T[3]="${B[3]}"
|
|
|
|
if ucdef R[4]; then
|
|
|
|
T[4]="${R[4]}"
|
2020-05-30 18:38:18 +00:00
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
T[4]="${B[4]}"
|
2020-05-30 18:38:18 +00:00
|
|
|
fi
|
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
if [[ "${R[3]}" == /* ]]; then
|
|
|
|
T[3]="$(pundot "${R[3]}")"
|
2020-05-30 18:38:18 +00:00
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
T[3]="$(pmerge B R)"
|
|
|
|
T[3]="$(pundot "${T[3]}")"
|
|
|
|
fi
|
|
|
|
if ucdef R[4]; then
|
|
|
|
T[4]="${R[4]}"
|
2020-05-30 18:38:18 +00:00
|
|
|
fi
|
|
|
|
fi
|
2020-06-18 13:24:01 +00:00
|
|
|
T[2]="${B[2]}"
|
2020-05-26 02:37:55 +00:00
|
|
|
fi
|
2020-06-18 13:24:01 +00:00
|
|
|
T[1]="${B[1]}"
|
2020-05-26 02:37:55 +00:00
|
|
|
fi
|
2020-06-18 13:24:01 +00:00
|
|
|
if ucdef R[5]; then
|
|
|
|
T[5]="${R[5]}"
|
2020-05-30 18:38:18 +00:00
|
|
|
fi
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
ujoin T
|
2020-05-26 02:37:55 +00:00
|
|
|
}
|
2020-05-22 20:53:35 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
pundot() { # pundot PATH:STRING
|
2020-05-30 18:38:18 +00:00
|
|
|
local input="$1"
|
2020-06-05 01:14:05 +00:00
|
|
|
local output
|
2020-05-30 18:38:18 +00:00
|
|
|
while [[ "$input" ]]; do
|
|
|
|
if [[ "$input" =~ ^\.\.?/ ]]; then
|
|
|
|
input="${input#${BASH_REMATCH[0]}}"
|
|
|
|
elif [[ "$input" =~ ^/\.(/|$) ]]; then
|
|
|
|
input="/${input#${BASH_REMATCH[0]}}"
|
|
|
|
elif [[ "$input" =~ ^/\.\.(/|$) ]]; then
|
|
|
|
input="/${input#${BASH_REMATCH[0]}}"
|
|
|
|
[[ "$output" =~ /?[^/]+$ ]]
|
|
|
|
output="${output%${BASH_REMATCH[0]}}"
|
|
|
|
elif [[ "$input" == . || "$input" == .. ]]; then
|
|
|
|
input=
|
|
|
|
else
|
2020-06-18 13:24:01 +00:00
|
|
|
[[ $input =~ ^(/?[^/]*)(/?.*)$ ]] || return 1
|
2020-05-30 18:38:18 +00:00
|
|
|
output="$output${BASH_REMATCH[1]}"
|
|
|
|
input="${BASH_REMATCH[2]}"
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
printf '%s\n' "${output//\/\//\//}"
|
2020-05-24 00:46:58 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
pmerge() {
|
|
|
|
local -n b="$1"
|
|
|
|
local -n r="$2"
|
2020-05-30 18:38:18 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
if ucblank r[3]; then
|
|
|
|
printf '%s\n' "${b[3]//\/\//\//}"
|
|
|
|
return
|
|
|
|
fi
|
2020-05-30 18:38:18 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
if ucdef b[2] && ucblank b[3]; then
|
|
|
|
printf '/%s\n' "${r[3]//\/\//\//}"
|
|
|
|
else
|
|
|
|
local bp=""
|
|
|
|
if [[ "${b[3]}" == */* ]]; then
|
|
|
|
bp="${b[3]%/*}"
|
|
|
|
fi
|
|
|
|
printf '%s/%s\n' "${bp%/}" "${r[3]#/}"
|
|
|
|
fi
|
2020-05-24 00:46:58 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# https://github.com/dylanaraps/pure-bash-bible/
|
2020-06-18 13:24:01 +00:00
|
|
|
uencode() { # uencode URL:STRING
|
2020-06-08 00:23:14 +00:00
|
|
|
local LC_ALL=C
|
|
|
|
for ((i = 0; i < ${#1}; i++)); do
|
|
|
|
: "${1:i:1}"
|
|
|
|
case "$_" in
|
|
|
|
[a-zA-Z0-9.~_-])
|
|
|
|
printf '%s' "$_"
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
printf '%%%02X' "'$_"
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
printf '\n'
|
|
|
|
}
|
|
|
|
|
|
|
|
# https://github.com/dylanaraps/pure-bash-bible/
|
2021-02-26 01:36:14 +00:00
|
|
|
udecode() { # udecode URL:STRING
|
2020-06-08 00:23:14 +00:00
|
|
|
: "${1//+/ }"
|
|
|
|
printf '%b\n' "${_//%/\\x}"
|
2020-06-05 01:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# GEMINI
|
2020-06-08 00:23:14 +00:00
|
|
|
# https://gemini.circumlunar.space/docs/specification.html
|
|
|
|
gemini_request() { # gemini_request URL
|
2020-06-18 13:24:01 +00:00
|
|
|
local -a url
|
|
|
|
usplit url "$1"
|
|
|
|
|
|
|
|
# get rid of userinfo
|
|
|
|
ucset url[2] "${url[2]#*@}"
|
|
|
|
|
|
|
|
local port
|
|
|
|
if [[ "${url[2]}" == *:* ]]; then
|
|
|
|
port="${url[2]#*:}"
|
|
|
|
ucset url[2] "${url[2]%:*}"
|
|
|
|
else
|
|
|
|
port=1965 # TODO variablize
|
|
|
|
fi
|
2020-05-30 18:38:18 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
local ssl_cmd=(
|
2021-02-26 01:36:14 +00:00
|
|
|
openssl s_client
|
|
|
|
-crlf -quiet -connect "${url[2]}:$port"
|
|
|
|
-servername "${url[2]}" # SNI
|
|
|
|
-no_ssl3 -no_tls1 -no_tls1_1 # disable old TLS/SSL versions
|
2020-06-18 13:24:01 +00:00
|
|
|
)
|
2020-06-02 22:16:28 +00:00
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
run "${ssl_cmd[@]}" <<<"$url"
|
2020-05-22 13:38:40 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
gemini_response() { # gemini_response URL
|
2020-06-05 01:14:05 +00:00
|
|
|
local url code meta
|
|
|
|
local title
|
|
|
|
url="$1"
|
|
|
|
|
|
|
|
# we need a loop here so it waits for the first line
|
2020-06-07 18:34:59 +00:00
|
|
|
while read -t "$BOLLUX_TIMEOUT" -r code meta ||
|
2020-06-05 01:14:05 +00:00
|
|
|
{ (($? > 128)) && die 99 "Timeout."; }; do
|
|
|
|
break
|
|
|
|
done
|
2020-05-22 13:38:40 +00:00
|
|
|
|
2020-06-04 04:22:55 +00:00
|
|
|
log d "[$code] $meta"
|
2020-05-22 20:53:35 +00:00
|
|
|
|
|
|
|
case "$code" in
|
2020-06-05 01:14:05 +00:00
|
|
|
1*) # input
|
2020-05-26 02:37:55 +00:00
|
|
|
REDIRECTS=0
|
2020-06-08 05:41:52 +00:00
|
|
|
BOLLUX_URL="$url"
|
2020-06-07 21:51:48 +00:00
|
|
|
case "$code" in
|
|
|
|
10) run prompt "$meta" ;;
|
|
|
|
11) run prompt "$meta" -s ;; # password input
|
|
|
|
esac
|
2020-06-18 13:24:01 +00:00
|
|
|
run blastoff "?$(uencode "$REPLY")"
|
2020-05-22 13:38:40 +00:00
|
|
|
;;
|
2020-06-05 01:14:05 +00:00
|
|
|
2*) # OK
|
2020-05-26 02:37:55 +00:00
|
|
|
REDIRECTS=0
|
2020-06-08 05:41:52 +00:00
|
|
|
BOLLUX_URL="$url"
|
2020-06-03 02:51:24 +00:00
|
|
|
# read ahead to find a title
|
2020-06-05 01:14:05 +00:00
|
|
|
local pretitle
|
2020-06-03 02:51:24 +00:00
|
|
|
while read -r; do
|
2020-06-03 03:37:27 +00:00
|
|
|
pretitle="$pretitle$REPLY"$'\n'
|
2020-06-03 02:51:24 +00:00
|
|
|
if [[ "$REPLY" =~ ^#[[:space:]]*(.*) ]]; then
|
|
|
|
title="${BASH_REMATCH[1]}"
|
|
|
|
break
|
|
|
|
fi
|
|
|
|
done
|
2020-06-05 01:14:05 +00:00
|
|
|
run history_append "$url" "${title:-}"
|
|
|
|
# read the body out and pipe it to display
|
2020-06-03 02:51:24 +00:00
|
|
|
{
|
|
|
|
printf '%s' "$pretitle"
|
2020-06-05 01:14:05 +00:00
|
|
|
passthru
|
|
|
|
} | run display "$meta" "${title:-}"
|
2020-05-22 13:38:40 +00:00
|
|
|
;;
|
2020-06-05 01:14:05 +00:00
|
|
|
3*) # redirect
|
2020-05-26 02:37:55 +00:00
|
|
|
((REDIRECTS += 1))
|
|
|
|
if ((REDIRECTS > BOLLUX_MAXREDIR)); then
|
|
|
|
die $((100 + code)) "Too many redirects!"
|
|
|
|
fi
|
2020-06-08 20:30:20 +00:00
|
|
|
BOLLUX_URL="$url"
|
2020-06-05 01:14:05 +00:00
|
|
|
run blastoff "$meta" # TODO: confirm redirect
|
2020-05-22 13:38:40 +00:00
|
|
|
;;
|
2020-06-05 01:14:05 +00:00
|
|
|
4*) # temporary error
|
2020-05-26 02:37:55 +00:00
|
|
|
REDIRECTS=0
|
2020-06-05 01:14:05 +00:00
|
|
|
die "$((100 + code))" "Temporary error [$code]: $meta"
|
2020-05-22 13:38:40 +00:00
|
|
|
;;
|
2020-06-05 01:14:05 +00:00
|
|
|
5*) # permanent error
|
2020-05-26 02:37:55 +00:00
|
|
|
REDIRECTS=0
|
2020-06-05 01:14:05 +00:00
|
|
|
die "$((100 + code))" "Permanent error [$code]: $meta"
|
2020-05-22 13:38:40 +00:00
|
|
|
;;
|
2020-06-05 01:14:05 +00:00
|
|
|
6*) # certificate error
|
2020-05-26 02:37:55 +00:00
|
|
|
REDIRECTS=0
|
2020-06-05 01:14:05 +00:00
|
|
|
log d "Not implemented: Client certificates"
|
|
|
|
# TODO: recheck the speck
|
|
|
|
die "$((100 + code))" "[$code] $meta"
|
2020-05-22 13:38:40 +00:00
|
|
|
;;
|
2020-05-30 21:54:30 +00:00
|
|
|
*)
|
|
|
|
[[ -z "${code-}" ]] && die 100 "Empty response code."
|
2020-06-05 01:14:05 +00:00
|
|
|
die "$((100 + code))" "Unknown response code: $code."
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
}
|
|
|
|
|
|
|
|
# GOPHER
|
|
|
|
# https://tools.ietf.org/html/rfc1436 protocol
|
|
|
|
# https://tools.ietf.org/html/rfc4266 url
|
2020-06-08 00:23:14 +00:00
|
|
|
gopher_request() { # gopher_request URL
|
2020-06-05 01:14:05 +00:00
|
|
|
local url server port type path
|
|
|
|
url="$1"
|
|
|
|
port=70
|
|
|
|
|
|
|
|
# RFC 4266
|
|
|
|
[[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]]
|
|
|
|
server="${BASH_REMATCH[1]}"
|
|
|
|
port="${BASH_REMATCH[3]:-70}"
|
|
|
|
type="${BASH_REMATCH[6]:-1}"
|
|
|
|
path="${BASH_REMATCH[7]}"
|
|
|
|
|
|
|
|
log d "URL='$url' SERVER='$server' TYPE='$type' PATH='$path'"
|
|
|
|
|
|
|
|
exec 9<>"/dev/tcp/$server/$port"
|
|
|
|
printf '%s\r\n' "$path" >&9
|
|
|
|
passthru <&9
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
gopher_response() { # gopher_response URL
|
2020-06-05 01:14:05 +00:00
|
|
|
local url pre type cur_server
|
|
|
|
pre=false
|
|
|
|
url="$1"
|
|
|
|
# RFC 4266
|
|
|
|
[[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]]
|
|
|
|
cur_server="${BASH_REMATCH[1]}"
|
|
|
|
type="${BASH_REMATCH[6]:-1}"
|
|
|
|
|
2020-06-18 13:24:01 +00:00
|
|
|
run history_append "$url" "" # gopher doesn't really have titles, huh
|
2020-06-05 01:14:05 +00:00
|
|
|
|
|
|
|
log d "TYPE='$type'"
|
|
|
|
|
|
|
|
case "$type" in
|
|
|
|
0) # text
|
|
|
|
run display text/plain
|
|
|
|
;;
|
|
|
|
1) # menu
|
|
|
|
run gopher_convert | run display text/gemini
|
|
|
|
;;
|
|
|
|
3) # failure
|
|
|
|
die 203 "GOPHER: failed"
|
|
|
|
;;
|
|
|
|
7) # search
|
2020-06-05 04:51:28 +00:00
|
|
|
if [[ "$url" =~ $'\t' ]]; then
|
|
|
|
run gopher_convert | run display text/gemini
|
|
|
|
else
|
2020-06-07 18:34:59 +00:00
|
|
|
run prompt 'SEARCH'
|
2020-06-05 04:51:28 +00:00
|
|
|
run blastoff "$url $REPLY"
|
|
|
|
fi
|
2020-06-05 01:14:05 +00:00
|
|
|
;;
|
|
|
|
*) # something else
|
2020-06-05 04:51:28 +00:00
|
|
|
run download "$url"
|
2020-05-30 21:54:30 +00:00
|
|
|
;;
|
2020-05-22 13:38:40 +00:00
|
|
|
esac
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# 'cat' but in pure bash
|
2020-06-05 01:14:05 +00:00
|
|
|
passthru() {
|
|
|
|
while IFS= read -r; do
|
|
|
|
printf '%s\n' "$REPLY"
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# convert gophermap to text/gemini (probably naive)
|
2020-06-05 01:14:05 +00:00
|
|
|
gopher_convert() {
|
|
|
|
local type label path server port regex
|
|
|
|
# cf. https://github.com/jamestomasino/dotfiles-minimal/blob/master/bin/gophermap2gemini.awk
|
|
|
|
while IFS= read -r; do
|
|
|
|
printf -v regex '(.)([^\t]*)(\t([^\t]*)\t([^\t]*)\t([^\t]*))?'
|
|
|
|
if [[ "$REPLY" =~ $regex ]]; then
|
|
|
|
type="${BASH_REMATCH[1]}"
|
|
|
|
label="${BASH_REMATCH[2]}"
|
|
|
|
path="${BASH_REMATCH[4]:-/}"
|
|
|
|
server="${BASH_REMATCH[5]:-$cur_server}"
|
|
|
|
port="${BASH_REMATCH[6]}"
|
|
|
|
else
|
|
|
|
log e "CAN'T PARSE LINE"
|
|
|
|
printf '%s\n' "$REPLY"
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
case "$type" in
|
|
|
|
.) # end of file
|
|
|
|
printf '.\n'
|
|
|
|
break
|
|
|
|
;;
|
|
|
|
i) # label
|
|
|
|
case "$label" in
|
|
|
|
'#'* | '*'[[:space:]]*)
|
|
|
|
if $pre; then
|
|
|
|
printf '%s\n' '```'
|
|
|
|
pre=false
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
if ! $pre; then
|
|
|
|
printf '%s\n' '```'
|
|
|
|
pre=true
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
printf '%s\n' "$label"
|
|
|
|
;;
|
|
|
|
h) # html link
|
|
|
|
if $pre; then
|
|
|
|
printf '%s\n' '```'
|
|
|
|
pre=false
|
|
|
|
fi
|
|
|
|
printf '=> %s %s\n' "${path:4}" "$label"
|
|
|
|
;;
|
|
|
|
T) # telnet link
|
|
|
|
if $pre; then
|
|
|
|
printf '%s\n' '```'
|
|
|
|
pre=false
|
|
|
|
fi
|
|
|
|
printf '=> telnet://%s:%s/%s%s %s\n' \
|
|
|
|
"$server" "$port" "$type" "$path" "$label"
|
|
|
|
;;
|
|
|
|
*) # other type
|
|
|
|
if $pre; then
|
|
|
|
printf '%s\n' '```'
|
|
|
|
pre=false
|
|
|
|
fi
|
|
|
|
printf '=> gopher://%s:%s/%s%s %s\n' \
|
|
|
|
"$server" "$port" "$type" "$path" "$label"
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
if $pre; then
|
|
|
|
printf '%s\n' '```'
|
|
|
|
fi
|
|
|
|
# close the connection
|
|
|
|
exec 9<&-
|
|
|
|
exec 9>&-
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# display the fetched content
|
2020-06-05 01:14:05 +00:00
|
|
|
display() { # display METADATA [TITLE]
|
|
|
|
local -a less_cmd
|
|
|
|
local i mime charset
|
2020-05-31 15:07:03 +00:00
|
|
|
# split header line
|
|
|
|
local -a hdr
|
2020-06-05 01:14:05 +00:00
|
|
|
IFS=';' read -ra hdr <<<"$1"
|
|
|
|
# title is optional but nice looking
|
|
|
|
local title
|
|
|
|
if (($# == 2)); then
|
|
|
|
title="$2"
|
|
|
|
fi
|
2020-05-31 15:07:03 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
mime="$(trim_string "${hdr[0],,}")"
|
2020-05-31 15:58:44 +00:00
|
|
|
for ((i = 1; i <= "${#hdr[@]}"; i++)); do
|
2020-06-05 01:14:05 +00:00
|
|
|
h="${hdr[$i]}"
|
2020-05-31 15:07:03 +00:00
|
|
|
case "$h" in
|
2020-06-05 01:14:05 +00:00
|
|
|
*charset=*) charset="${h#*=}" ;;
|
2020-05-31 15:07:03 +00:00
|
|
|
esac
|
|
|
|
done
|
2020-05-26 02:37:55 +00:00
|
|
|
|
|
|
|
[[ -z "$mime" ]] && mime="text/gemini"
|
2020-06-05 01:14:05 +00:00
|
|
|
[[ -z "$charset" ]] && charset="utf-8"
|
2020-05-24 04:49:48 +00:00
|
|
|
|
2020-05-31 15:07:03 +00:00
|
|
|
log debug "mime='$mime'; charset='$charset'"
|
2020-05-24 04:49:48 +00:00
|
|
|
|
|
|
|
case "$mime" in
|
2020-05-26 02:37:55 +00:00
|
|
|
text/*)
|
2020-06-07 23:43:28 +00:00
|
|
|
set_title "$title${title:+ - }bollux"
|
2021-02-26 01:39:28 +00:00
|
|
|
# render ANSI color escapes and don't wrap pre-formatted blocks
|
|
|
|
less_cmd=(less -RS)
|
2020-06-03 12:43:46 +00:00
|
|
|
mklesskey "$BOLLUX_LESSKEY" && less_cmd+=(-k "$BOLLUX_LESSKEY")
|
2020-06-18 13:24:01 +00:00
|
|
|
local helpline="o:open, g/G:goto, [:back, ]:forward, r:refresh"
|
2020-05-26 03:18:43 +00:00
|
|
|
less_cmd+=(
|
2020-06-08 20:20:14 +00:00
|
|
|
-Pm"$(less_prompt_escape "$BOLLUX_URL") - bollux$" # 'status'line
|
2021-02-26 01:36:14 +00:00
|
|
|
-P="$(less_prompt_escape "$helpline")$" # helpline
|
|
|
|
-m # start with statusline
|
|
|
|
+k # float content to the top
|
2020-05-26 03:18:43 +00:00
|
|
|
)
|
2020-05-26 02:37:55 +00:00
|
|
|
|
2020-06-05 01:14:05 +00:00
|
|
|
local typeset
|
|
|
|
local submime="${mime#*/}"
|
2020-06-07 23:43:28 +00:00
|
|
|
if declare -Fp "typeset_$submime" &>/dev/null; then
|
2020-06-05 01:14:05 +00:00
|
|
|
typeset="typeset_$submime"
|
2020-05-26 02:37:55 +00:00
|
|
|
else
|
2020-06-05 01:14:05 +00:00
|
|
|
typeset="passthru"
|
2020-05-26 02:37:55 +00:00
|
|
|
fi
|
2020-06-05 01:14:05 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
run iconv -f "${charset^^}" -t "UTF-8" |
|
|
|
|
run tee "$BOLLUX_PAGESRC" |
|
2020-06-08 20:04:26 +00:00
|
|
|
run "$typeset" | #cat
|
2020-06-05 01:14:05 +00:00
|
|
|
run "${less_cmd[@]}" && bollux_quit
|
|
|
|
} || run handle_keypress "$?"
|
2020-05-22 22:12:15 +00:00
|
|
|
;;
|
2020-05-26 02:37:55 +00:00
|
|
|
*) run download "$BOLLUX_URL" ;;
|
2020-05-22 22:12:15 +00:00
|
|
|
esac
|
2020-05-22 20:53:35 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# escape strings for the less prompt
|
|
|
|
less_prompt_escape() { # less_prompt_escape STRING
|
2020-06-07 23:43:28 +00:00
|
|
|
local i
|
|
|
|
for ((i = 0; i < ${#1}; i++)); do
|
|
|
|
: "${1:i:1}"
|
|
|
|
case "$_" in
|
|
|
|
[\?:\.%\\]) printf '\%s' "$_" ;;
|
|
|
|
*) printf '%s' "$_" ;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
printf '\n'
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# generate a lesskey(1) file for custom keybinds
|
|
|
|
mklesskey() { # mklesskey FILENAME
|
2020-05-26 02:37:55 +00:00
|
|
|
lesskey -o "$1" - <<-END
|
|
|
|
#command
|
|
|
|
o quit 0 # 48 open a link
|
|
|
|
g quit 1 # 49 goto a url
|
|
|
|
[ quit 2 # 50 back
|
|
|
|
] quit 3 # 51 forward
|
|
|
|
r quit 4 # 52 re-request / download
|
2020-06-03 12:43:46 +00:00
|
|
|
G quit 5 # 53 goto a url (pre-filled)
|
2020-06-02 03:55:40 +00:00
|
|
|
# other keybinds
|
2020-06-03 12:43:46 +00:00
|
|
|
\\40 forw-screen-force
|
2020-06-07 22:59:36 +00:00
|
|
|
h left-scroll
|
|
|
|
l right-scroll
|
2020-06-07 23:43:28 +00:00
|
|
|
? status # 'status' will show a little help thing.
|
|
|
|
= noaction
|
2020-05-26 02:37:55 +00:00
|
|
|
END
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# normalize files
|
2020-06-05 01:14:05 +00:00
|
|
|
normalize() {
|
2020-05-31 13:47:46 +00:00
|
|
|
shopt -s extglob
|
2020-05-30 21:00:12 +00:00
|
|
|
while IFS= read -r; do
|
2020-06-08 00:23:14 +00:00
|
|
|
# normalize line endings
|
2020-05-30 21:00:12 +00:00
|
|
|
printf '%s\n' "${REPLY//$'\r'?($'\n')/}"
|
2020-05-30 19:35:17 +00:00
|
|
|
done
|
2020-05-30 22:09:52 +00:00
|
|
|
shopt -u extglob
|
2020-05-24 04:49:48 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# typeset a text/gemini document
|
2020-05-26 02:37:55 +00:00
|
|
|
typeset_gemini() {
|
2020-05-30 21:00:12 +00:00
|
|
|
local pre=false
|
|
|
|
local ln=0 # link number
|
|
|
|
|
|
|
|
if ((T_WIDTH == 0)); then
|
|
|
|
shopt -s checkwinsize
|
|
|
|
(
|
|
|
|
:
|
|
|
|
:
|
2020-06-08 16:16:13 +00:00
|
|
|
) # dumb formatting brought to you by shfmt
|
2020-05-30 21:00:12 +00:00
|
|
|
log d "LINES=$LINES; COLUMNS=$COLUMNS"
|
|
|
|
T_WIDTH=$COLUMNS
|
|
|
|
fi
|
2020-05-30 21:45:56 +00:00
|
|
|
WIDTH=$((T_WIDTH - T_MARGIN))
|
|
|
|
((WIDTH < 0)) && WIDTH=80 # default if dumb
|
|
|
|
S_MARGIN=$((T_MARGIN - 1)) # spacing
|
2020-05-30 21:00:12 +00:00
|
|
|
|
|
|
|
log d "T_WIDTH=$T_WIDTH"
|
|
|
|
log d "WIDTH=$WIDTH"
|
|
|
|
|
|
|
|
while IFS= read -r; do
|
|
|
|
case "$REPLY" in
|
2020-06-05 01:14:05 +00:00
|
|
|
'```'*)
|
2020-05-30 21:00:12 +00:00
|
|
|
if $pre; then
|
|
|
|
pre=false
|
|
|
|
else
|
|
|
|
pre=true
|
|
|
|
fi
|
|
|
|
continue
|
|
|
|
;;
|
2020-06-04 04:22:55 +00:00
|
|
|
'=>'*)
|
2020-05-30 21:00:12 +00:00
|
|
|
: $((ln += 1))
|
|
|
|
gemini_link "$REPLY" $pre "$ln"
|
|
|
|
;;
|
2020-06-04 04:22:55 +00:00
|
|
|
'#'*) gemini_header "$REPLY" $pre ;;
|
2020-06-05 01:14:05 +00:00
|
|
|
'*'[[:space:]]*)
|
|
|
|
gemini_list "$REPLY" $pre
|
2020-05-30 21:45:56 +00:00
|
|
|
;;
|
2020-06-08 05:14:10 +00:00
|
|
|
'>'*)
|
|
|
|
gemini_quote "$REPLY" $pre
|
|
|
|
;;
|
2020-05-30 21:00:12 +00:00
|
|
|
*) gemini_text "$REPLY" $pre ;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
gemini_link() {
|
|
|
|
local re="^(=>)[[:blank:]]*([^[:blank:]]+)[[:blank:]]*(.*)"
|
2020-06-08 20:19:54 +00:00
|
|
|
local s t a # sigil, text, annotation(url)
|
2020-06-08 20:04:26 +00:00
|
|
|
local ln="$3"
|
2020-05-30 21:00:12 +00:00
|
|
|
if ! ${2-false} && [[ "$1" =~ $re ]]; then
|
|
|
|
s="${BASH_REMATCH[1]}"
|
|
|
|
a="${BASH_REMATCH[2]}"
|
|
|
|
t="${BASH_REMATCH[3]}"
|
|
|
|
if [[ -z "$t" ]]; then
|
|
|
|
t="$a"
|
|
|
|
a=
|
|
|
|
fi
|
|
|
|
|
2020-05-30 21:45:56 +00:00
|
|
|
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
|
2020-06-08 20:04:26 +00:00
|
|
|
printf "\e[${C_LINK_NUMBER}m[%d]${C_RESET} " "$ln"
|
|
|
|
fold_line -n -B "\e[${C_LINK_TITLE}m" -A "${C_RESET}" \
|
|
|
|
-l "$((${#ln} + 3))" -m "${T_MARGIN}" \
|
|
|
|
"$WIDTH" "$(trim_string "$t")"
|
|
|
|
fold_line -B " \e[${C_LINK_URL}m" -A "${C_RESET}" \
|
2020-06-08 20:19:54 +00:00
|
|
|
-l "$((${#ln} + 3 + ${#t}))" -m "$((T_MARGIN + ${#ln} + 2))" \
|
2020-06-08 20:04:26 +00:00
|
|
|
"$WIDTH" "$a"
|
2020-05-30 21:00:12 +00:00
|
|
|
else
|
|
|
|
gemini_pre "$1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
gemini_header() {
|
|
|
|
local re="^(#+)[[:blank:]]*(.*)"
|
2020-06-08 20:19:54 +00:00
|
|
|
local s t a # sigil, text, annotation(lvl)
|
2020-05-30 21:00:12 +00:00
|
|
|
if ! ${2-false} && [[ "$1" =~ $re ]]; then
|
|
|
|
s="${BASH_REMATCH[1]}"
|
|
|
|
a="${#BASH_REMATCH[1]}"
|
|
|
|
t="${BASH_REMATCH[2]}"
|
|
|
|
local hdrfmt
|
|
|
|
hdrfmt="$(eval echo "\$C_HEADER$a")"
|
|
|
|
|
2020-05-30 21:45:56 +00:00
|
|
|
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
|
2020-06-08 20:04:26 +00:00
|
|
|
fold_line -B "\e[${hdrfmt}m" -A "${C_RESET}" -m "${T_MARGIN}" \
|
|
|
|
"$WIDTH" "$t"
|
2020-05-30 21:00:12 +00:00
|
|
|
else
|
|
|
|
gemini_pre "$1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
gemini_list() {
|
|
|
|
local re="^(\*)[[:blank:]]*(.*)"
|
2020-06-08 20:19:54 +00:00
|
|
|
local s t # sigil, text
|
2020-05-30 21:00:12 +00:00
|
|
|
if ! ${2-false} && [[ "$1" =~ $re ]]; then
|
|
|
|
s="${BASH_REMATCH[1]}"
|
|
|
|
t="${BASH_REMATCH[2]}"
|
|
|
|
|
2020-06-08 05:36:42 +00:00
|
|
|
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
|
2020-06-08 20:04:26 +00:00
|
|
|
fold_line -B "\e[${C_LIST}m" -A "${C_RESET}" -m "$T_MARGIN" \
|
|
|
|
"$WIDTH" "$t"
|
2020-05-30 21:00:12 +00:00
|
|
|
else
|
|
|
|
gemini_pre "$1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2020-06-08 05:14:10 +00:00
|
|
|
gemini_quote() {
|
|
|
|
local re="^(>)[[:blank:]]*(.*)"
|
2020-06-08 20:19:54 +00:00
|
|
|
local s t # sigil, text
|
2020-06-08 05:14:10 +00:00
|
|
|
if ! ${2-false} && [[ "$1" =~ $re ]]; then
|
|
|
|
s="${BASH_REMATCH[1]}"
|
|
|
|
t="${BASH_REMATCH[2]}"
|
|
|
|
|
2020-06-08 05:36:42 +00:00
|
|
|
printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
|
2020-06-08 20:04:26 +00:00
|
|
|
fold_line -B "\e[${C_QUOTE}m" -A "${C_RESET}" -m "$T_MARGIN" \
|
|
|
|
"$WIDTH" "$t"
|
2020-06-08 05:14:10 +00:00
|
|
|
else
|
|
|
|
gemini_pre "$1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2020-05-30 21:00:12 +00:00
|
|
|
gemini_text() {
|
|
|
|
if ! ${2-false}; then
|
2020-05-30 21:45:56 +00:00
|
|
|
printf "%${S_MARGIN}s " ' '
|
2020-06-08 20:04:26 +00:00
|
|
|
fold_line -m "$T_MARGIN" \
|
|
|
|
"$WIDTH" "$1"
|
2020-05-30 21:00:12 +00:00
|
|
|
else
|
|
|
|
gemini_pre "$1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
gemini_pre() {
|
2020-05-30 21:45:56 +00:00
|
|
|
printf "\e[${C_SIGIL}m%${S_MARGIN}s " '```'
|
2020-05-30 21:00:12 +00:00
|
|
|
printf "\e[${C_PRE}m%s${C_RESET}\n" "$1"
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# wrap lines on words to WIDTH
|
2020-06-08 20:04:26 +00:00
|
|
|
fold_line() {
|
|
|
|
# fold_line [-n] [-m MARGIN] [-f MARGIN] [-l LENGTH] [-B BEFORE] [-A AFTER] WIDTH TEXT
|
|
|
|
local newline=true
|
|
|
|
local -i margin_all=0 margin_first=0 width ll=0 wl=0 wn=0
|
|
|
|
local before="" after=""
|
|
|
|
OPTIND=0
|
|
|
|
while getopts nm:f:l:B:A: OPT; do
|
|
|
|
case "$OPT" in
|
|
|
|
n) # -n = no trailing newline
|
|
|
|
newline=false
|
|
|
|
;;
|
|
|
|
m) # -m MARGIN = margin for all lines
|
|
|
|
margin_all="$OPTARG"
|
|
|
|
;;
|
|
|
|
f) # -f MARGIN = margin for first line
|
|
|
|
margin_first="$OPTARG"
|
|
|
|
;;
|
|
|
|
l) # -l LENGTH = length of line before starting fold
|
|
|
|
ll="$OPTARG"
|
|
|
|
;;
|
|
|
|
B) # -B BEFORE = text to insert before each line
|
|
|
|
before="$OPTARG"
|
|
|
|
;;
|
|
|
|
A) # -A AFTER = text to insert after each line
|
|
|
|
after="$OPTARG"
|
|
|
|
;;
|
|
|
|
*) return 1 ;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
shift "$((OPTIND - 1))"
|
|
|
|
width="$1"
|
|
|
|
ll=$((ll % width))
|
|
|
|
#shellcheck disable=2086
|
|
|
|
set -- $2
|
|
|
|
|
|
|
|
local plain=""
|
|
|
|
if ((margin_first > 0 && ll == 0)); then
|
|
|
|
printf "%${margin_first}s" " "
|
|
|
|
fi
|
|
|
|
if [[ -n "$before" ]]; then
|
|
|
|
printf '%b' "$before"
|
2020-05-30 21:00:12 +00:00
|
|
|
fi
|
|
|
|
for word; do
|
2020-06-08 20:04:26 +00:00
|
|
|
((wn += 1))
|
2020-06-08 16:16:13 +00:00
|
|
|
shopt -s extglob
|
2020-05-30 21:00:12 +00:00
|
|
|
plain="${word//$'\x1b'\[*([0-9;])m/}"
|
2020-06-08 16:16:13 +00:00
|
|
|
shopt -u extglob
|
2020-05-30 21:00:12 +00:00
|
|
|
wl=$((${#plain} + 1))
|
|
|
|
if (((ll + wl) >= width)); then
|
2020-06-08 20:04:26 +00:00
|
|
|
printf "${after:-}\n%${margin_all}s${before:-}" ' '
|
2020-05-30 21:00:12 +00:00
|
|
|
ll=$wl
|
|
|
|
else
|
2020-06-08 20:04:26 +00:00
|
|
|
((ll += wl))
|
2020-05-30 21:00:12 +00:00
|
|
|
fi
|
2020-06-08 20:04:26 +00:00
|
|
|
printf '%s' "$word"
|
|
|
|
((wn != $#)) && printf ' '
|
2020-05-30 21:00:12 +00:00
|
|
|
done
|
2020-06-08 20:04:26 +00:00
|
|
|
[[ -n "$after" ]] && printf '%b' "$after"
|
|
|
|
$newline && printf '\n'
|
2020-05-24 04:49:48 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# use the exit code from less (see mklesskey) to do things
|
|
|
|
handle_keypress() { # handle_keypress CODE
|
2020-05-26 02:37:55 +00:00
|
|
|
case "$1" in
|
|
|
|
48) # o - open a link -- show a menu of links on the page
|
|
|
|
run select_url "$BOLLUX_PAGESRC"
|
|
|
|
;;
|
|
|
|
49) # g - goto a url -- input a new url
|
2020-06-05 01:14:05 +00:00
|
|
|
prompt GO
|
|
|
|
run blastoff -u "$REPLY"
|
2020-05-26 02:37:55 +00:00
|
|
|
;;
|
|
|
|
50) # [ - back in the history
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
run history_back || {
|
|
|
|
sleep 0.5
|
|
|
|
run blastoff "$BOLLUX_URL"
|
|
|
|
}
|
2020-05-26 02:37:55 +00:00
|
|
|
;;
|
|
|
|
51) # ] - forward in the history
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
run history_forward || {
|
|
|
|
sleep 0.5
|
|
|
|
run blastoff "$BOLLUX_URL"
|
|
|
|
}
|
2020-05-26 02:37:55 +00:00
|
|
|
;;
|
|
|
|
52) # r - re-request the current resource
|
|
|
|
run blastoff "$BOLLUX_URL"
|
|
|
|
;;
|
2020-06-03 12:43:46 +00:00
|
|
|
53) # G - goto a url (pre-filled with current)
|
2020-06-18 13:24:01 +00:00
|
|
|
run prompt -u GO
|
2020-06-05 01:14:05 +00:00
|
|
|
run blastoff -u "$REPLY"
|
2020-06-03 12:43:46 +00:00
|
|
|
;;
|
2020-06-05 01:14:05 +00:00
|
|
|
*) # 54-57 -- still available for binding
|
|
|
|
die "$?" "less(1) error"
|
2020-05-26 02:37:55 +00:00
|
|
|
;;
|
|
|
|
esac
|
2020-05-23 01:34:27 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# select a URL from a text/gemini file
|
|
|
|
select_url() { # select_url FILE
|
2020-05-26 02:37:55 +00:00
|
|
|
run mapfile -t < <(extract_links <"$1")
|
2020-06-08 20:37:29 +00:00
|
|
|
if ((${#MAPFILE[@]} == 0)); then
|
|
|
|
log e "No links on this page!"
|
|
|
|
sleep 0.5
|
|
|
|
run blastoff "$BOLLUX_URL"
|
|
|
|
fi
|
2020-06-04 04:22:55 +00:00
|
|
|
PS3="OPEN> "
|
2020-05-26 02:37:55 +00:00
|
|
|
select u in "${MAPFILE[@]}"; do
|
2020-05-31 15:58:44 +00:00
|
|
|
case "$REPLY" in
|
|
|
|
q) bollux_quit ;;
|
2020-06-05 01:14:05 +00:00
|
|
|
[^0-9]*) run blastoff -u "$REPLY" && break ;;
|
2020-05-31 15:58:44 +00:00
|
|
|
esac
|
2020-06-04 04:22:55 +00:00
|
|
|
run blastoff "${u%%[[:space:]]*}" && break
|
2020-05-26 02:37:55 +00:00
|
|
|
done </dev/tty
|
2020-05-23 01:16:32 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# extract the links from a text/gemini file
|
2020-05-26 02:37:55 +00:00
|
|
|
extract_links() {
|
2020-06-03 02:07:46 +00:00
|
|
|
local url alt
|
2020-06-04 04:22:55 +00:00
|
|
|
while read -r; do
|
|
|
|
if [[ "$REPLY" =~ ^=\>[[:space:]]*([^[:space:]]+)([[:space:]]+(.*))?$ ]]; then
|
2020-06-03 02:07:46 +00:00
|
|
|
url="${BASH_REMATCH[1]}"
|
|
|
|
alt="${BASH_REMATCH[3]}"
|
|
|
|
|
|
|
|
if [[ "$alt" ]]; then
|
|
|
|
printf '%s \e[34m(%s)\e[0m\n' "$url" "$alt"
|
|
|
|
else
|
|
|
|
printf '%s\n' "$url"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
done
|
2020-05-24 00:49:20 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# download $BOLLUX_URL
|
2020-05-26 02:37:55 +00:00
|
|
|
download() {
|
|
|
|
tn="$(mktemp)"
|
|
|
|
log x "Downloading: '$BOLLUX_URL' => '$tn'..."
|
|
|
|
dd status=progress >"$tn"
|
|
|
|
fn="$BOLLUX_DOWNDIR/${BOLLUX_URL##*/}"
|
|
|
|
if [[ -f "$fn" ]]; then
|
|
|
|
log x "Saved '$tn'."
|
|
|
|
elif mv "$tn" "$fn"; then
|
|
|
|
log x "Saved '$fn'."
|
|
|
|
else
|
|
|
|
log error "Error saving '$fn': downloaded to '$tn'."
|
|
|
|
fi
|
2020-05-23 01:16:32 +00:00
|
|
|
}
|
2020-05-23 01:34:27 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# initialize bollux
|
2020-06-05 01:14:05 +00:00
|
|
|
bollux_init() {
|
|
|
|
# Trap cleanup
|
|
|
|
trap bollux_cleanup INT QUIT EXIT
|
|
|
|
# State
|
|
|
|
REDIRECTS=0
|
|
|
|
set -f
|
|
|
|
# History
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
declare -a HISTORY # history is kept in an array
|
|
|
|
HN=0 # position of history in the array
|
2020-06-03 02:51:24 +00:00
|
|
|
run mkdir -p "${BOLLUX_HISTFILE%/*}"
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# clean up on exit
|
2020-06-05 01:14:05 +00:00
|
|
|
bollux_cleanup() {
|
2020-06-07 19:02:57 +00:00
|
|
|
# Stubbed in case of need in future
|
2020-06-05 01:14:05 +00:00
|
|
|
:
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# append a URL to history
|
|
|
|
history_append() { # history_append URL TITLE
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
BOLLUX_URL="$1"
|
2020-06-03 02:51:24 +00:00
|
|
|
# date/time, url, title (best guess)
|
|
|
|
run printf '%(%FT%T)T\t%s\t%s\n' -1 "$1" "$2" >>"$BOLLUX_HISTFILE"
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
HISTORY[$HN]="$BOLLUX_URL"
|
|
|
|
((HN += 1))
|
|
|
|
}
|
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# move back in history (session)
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
history_back() {
|
|
|
|
log d "HN=$HN"
|
|
|
|
((HN -= 2))
|
2020-06-01 20:36:33 +00:00
|
|
|
if ((HN < 0)); then
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
HN=0
|
|
|
|
log e "Beginning of history."
|
|
|
|
return 1
|
|
|
|
fi
|
2020-06-03 02:51:24 +00:00
|
|
|
run blastoff "${HISTORY[$HN]}"
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
}
|
2020-06-05 01:14:05 +00:00
|
|
|
|
2020-06-08 00:23:14 +00:00
|
|
|
# move forward in history (session)
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
history_forward() {
|
|
|
|
log d "HN=$HN"
|
|
|
|
if ((HN >= ${#HISTORY[@]})); then
|
|
|
|
HN="${#HISTORY[@]}"
|
|
|
|
log e "End of history."
|
|
|
|
return 1
|
|
|
|
fi
|
2020-06-03 02:51:24 +00:00
|
|
|
run blastoff "${HISTORY[$HN]}"
|
Add rudimentary history support.
This commit adds `history_init`, `history_append`, `history_back`, and
`history_forward` functions.
The history is implemented as an array, HISTORY, which is appended to
with history_append when pages are visited, and a pointer, HN, which
points to the current position in the array. When history_back is
called, it moves the pointer back 2 and calls blastoff to the URL there.
When history_forward is called, it does not move the pointer and
blastsoff to the URL. Why back and forward basically have an off-by-one
error is beyond me at this time.
ISSUES:
- if the user goes back then forward, the history is not rewritten for
later URLs. for example, if the history is
1.gmi 2.gmi 3.gmi*
where the * indicates the current history position, when the user goes
back 2, it looks like this:
1.gmi* 2.gmi 3.gmi
if the user then navigates to another page:
1.gmi 4.gmi* 3.gmi
3.gmi is still in the history, so the user can go forward from 4. This
is unexpected as far as every other client that I know of goes.
2020-06-01 19:58:51 +00:00
|
|
|
}
|
2020-05-22 20:53:35 +00:00
|
|
|
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
2020-05-26 02:37:55 +00:00
|
|
|
run bollux "$@"
|
2020-05-22 20:53:35 +00:00
|
|
|
fi
|