about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--[-rwxr-xr-x]bollux257
1 files changed, 128 insertions, 129 deletions
diff --git a/bollux b/bollux index 277af05..30acd0a 100755..100644 --- a/bollux +++ b/bollux
@@ -93,14 +93,14 @@ bollux() {
93bollux_args() { 93bollux_args() {
94 while getopts :hvq OPT; do 94 while getopts :hvq OPT; do
95 case "$OPT" in 95 case "$OPT" in
96 (h) 96 h)
97 bollux_usage 97 bollux_usage
98 exit 98 exit
99 ;; 99 ;;
100 (v) BOLLUX_LOGLEVEL=DEBUG ;; 100 v) BOLLUX_LOGLEVEL=DEBUG ;;
101 (q) BOLLUX_LOGLEVEL=QUIET ;; 101 q) BOLLUX_LOGLEVEL=QUIET ;;
102 (:) die 1 "Option -$OPTARG requires an argument" ;; 102 :) die 1 "Option -$OPTARG requires an argument" ;;
103 (*) die 1 "Unknown option: -$OPTARG" ;; 103 *) die 1 "Unknown option: -$OPTARG" ;;
104 esac 104 esac
105 done 105 done
106 shift $((OPTIND - 1)) 106 shift $((OPTIND - 1))
@@ -142,16 +142,16 @@ bollux_config() {
142 : "${BOLLUX_TIMEOUT:=30}" # connection timeout 142 : "${BOLLUX_TIMEOUT:=30}" # connection timeout
143 : "${BOLLUX_MAXREDIR:=5}" # max redirects 143 : "${BOLLUX_MAXREDIR:=5}" # max redirects
144 : "${BOLLUX_PROTO:=gemini}" # default protocol 144 : "${BOLLUX_PROTO:=gemini}" # default protocol
145 : "${BOLLUX_GEMINI_PORT:=1965}" # default port for gemini 145 : "${BOLLUX_GEMINI_PORT:=1965}" # default port for gemini
146 : "${BOLLUX_GOPHER_PORT:=70}" # default port for gopher 146 : "${BOLLUX_GOPHER_PORT:=70}" # default port for gopher
147 : "${BOLLUX_URL:=}" # start url 147 : "${BOLLUX_URL:=}" # start url
148 : "${BOLLUX_BYEMSG:=See You Space Cowboy ...}" # bye message 148 : "${BOLLUX_BYEMSG:=See You Space Cowboy ...}" # bye message
149 ## lesskeys 149 ## lesskeys
150 : "${KEY_OPEN:=o}" # prompt for a link to open 150 : "${KEY_OPEN:=o}" # prompt for a link to open
151 : "${KEY_GOTO:=g}" # prompt for a page to 'goto' 151 : "${KEY_GOTO:=g}" # prompt for a page to 'goto'
152 : "${KEY_GOTO_FROM:=G}" # goto a page with current prefilled 152 : "${KEY_GOTO_FROM:=G}" # goto a page with current prefilled
153 : "${KEY_BACK:=[}" # go back in the history 153 : "${KEY_BACK:=[}" # go back in the history
154 : "${KEY_FORWARD:=]}" # go forward in the history 154 : "${KEY_FORWARD:=]}" # go forward in the history
155 : "${KEY_REFRESH:=r}" # refresh the page 155 : "${KEY_REFRESH:=r}" # refresh the page
156 : "${KEY_CYCLE_PRE:=p}" # cycle T_PRE_DISPLAY 156 : "${KEY_CYCLE_PRE:=p}" # cycle T_PRE_DISPLAY
157 : "${BOLLUX_CUSTOM_LESSKEY:=$BOLLUX_CONF_DIR/bollux.lesskey}" 157 : "${BOLLUX_CUSTOM_LESSKEY:=$BOLLUX_CONF_DIR/bollux.lesskey}"
@@ -252,24 +252,24 @@ run() { # run COMMAND...
252log() { # log LEVEL MESSAGE... 252log() { # log LEVEL MESSAGE...
253 # 'QUIET' means don't log anything. 253 # 'QUIET' means don't log anything.
254 [[ "$BOLLUX_LOGLEVEL" == QUIET ]] && return 254 [[ "$BOLLUX_LOGLEVEL" == QUIET ]] && return
255 local fmt # ANSI escape code 255 local fmt # ANSI escape code
256 256
257 case "$1" in 257 case "$1" in
258 ([dD]*) # Debug level -- only print if bollux -v. 258 [dD]*) # Debug level -- only print if bollux -v.
259 [[ "$BOLLUX_LOGLEVEL" == DEBUG ]] || return 259 [[ "$BOLLUX_LOGLEVEL" == DEBUG ]] || return
260 fmt=34 # Blue 260 fmt=34 # Blue
261 ;; 261 ;;
262 ([eE]*) # Error level -- always print. 262 [eE]*) # Error level -- always print.
263 fmt=31 # Red 263 fmt=31 # Red
264 ;; 264 ;;
265 (*) # Diagnostic level -- print unless QUIET. 265 *) # Diagnostic level -- print unless QUIET.
266 fmt=1 # Bold 266 fmt=1 # Bold
267 ;; 267 ;;
268 esac 268 esac
269 shift 269 shift
270 270
271 printf >&2 '\e[%sm%s:%-16s:\e[0m %s\n' \ 271 printf >&2 '\e[%sm%s:%-16s:\e[0m %s\n' \
272 "$fmt" "$PRGN" "${FUNCNAME[${LOG_FUNC:-1}]}" "$*" 272 "$fmt" "$PRGN" "${FUNCNAME[${LOG_FUNC:-1}]}" "$*"
273} 273}
274 274
275# Exit with an error and a message describing it. 275# Exit with an error and a message describing it.
@@ -298,10 +298,10 @@ list_cycle() { # list_cycle LIST<string> DELIM
298 # T_PRE_DISPLAY, which is user-configurable. I wanted it to be as easy 298 # T_PRE_DISPLAY, which is user-configurable. I wanted it to be as easy
299 # to configure for users who might not immediately know the bash array 299 # to configure for users who might not immediately know the bash array
300 # syntax, but can figure out 'variable=value' without much thought. 300 # syntax, but can figure out 'variable=value' without much thought.
301 local list="${!1}" # Pass the list by name, not value 301 local list="${!1}" # Pass the list by name, not value
302 local delim="$2" # The delimiter of the string 302 local delim="$2" # The delimiter of the string
303 local first="${list%%${delim}*}" # The first element 303 local first="${list%%${delim}*}" # The first element
304 local rest="${list#*${delim}}" # The rest of the elements 304 local rest="${list#*${delim}}" # The rest of the elements
305 # -v prints to the variable specified. 305 # -v prints to the variable specified.
306 printf -v "$1" '%s%s%s' "${rest}" "${delim}" "${first}" 306 printf -v "$1" '%s%s%s' "${rest}" "${delim}" "${first}"
307} 307}
@@ -325,7 +325,7 @@ prompt() { # prompt [-u] PROMPT [READ_ARGS...]
325 read_cmd+=(-i "$BOLLUX_URL") 325 read_cmd+=(-i "$BOLLUX_URL")
326 shift 326 shift
327 fi 327 fi
328 local prompt="$1" # How to prompt the user 328 local prompt="$1" # How to prompt the user
329 shift 329 shift
330 read_cmd+=(-p "$prompt> ") 330 read_cmd+=(-p "$prompt> ")
331 "${read_cmd[@]}" </dev/tty "$@" 331 "${read_cmd[@]}" </dev/tty "$@"
@@ -423,11 +423,11 @@ usplit() { # usplit URL_ARRAY<name> URL
423 # below performs a reverse lookup on the name to get the actual data. 423 # below performs a reverse lookup on the name to get the actual data.
424 # shellcheck disable=2034 424 # shellcheck disable=2034
425 local entire_url="${BASH_REMATCH[0]}" \ 425 local entire_url="${BASH_REMATCH[0]}" \
426 scheme="${BASH_REMATCH[2]}" \ 426 scheme="${BASH_REMATCH[2]}" \
427 authority="${BASH_REMATCH[4]}" \ 427 authority="${BASH_REMATCH[4]}" \
428 path="${BASH_REMATCH[5]}" \ 428 path="${BASH_REMATCH[5]}" \
429 query="${BASH_REMATCH[7]}" \ 429 query="${BASH_REMATCH[7]}" \
430 fragment="${BASH_REMATCH[9]}" 430 fragment="${BASH_REMATCH[9]}"
431 431
432 # Iterate through the 5 components of a URL and assign them to elements 432 # Iterate through the 5 components of a URL and assign them to elements
433 # of URL_ARRAY, as follows: 433 # of URL_ARRAY, as follows:
@@ -516,10 +516,10 @@ ucblank() { # ucblank COMPONENT<name>
516# `ucset' sets one component of a URL array and setting the 0th element to the 516# `ucset' sets one component of a URL array and setting the 0th element to the
517# new full URL. Use it instead of directly setting the array element with U[x], 517# new full URL. Use it instead of directly setting the array element with U[x],
518# because U[0] will fall out of sync with the rest of the contents. 518# because U[0] will fall out of sync with the rest of the contents.
519ucset() { # ucset URL_ARRAY_INDEX<name> NEW_VALUE 519ucset() { # ucset URL_ARRAY_INDEX<name> NEW_VALUE
520 local url_array_component="$1" # Of form 'URL_ARRAY[INDEX]' 520 local url_array_component="$1" # Of form 'URL_ARRAY[INDEX]'
521 local value="$2" 521 local value="$2"
522 522
523 # Assign $value to $url_array_component. 523 # Assign $value to $url_array_component.
524 # 524 #
525 # Wrapped in an 'eval' for the extra layer of indirection. 525 # Wrapped in an 'eval' for the extra layer of indirection.
@@ -538,8 +538,8 @@ uencode() { # uencode URL
538 for ((i = 0; i < ${#1}; i++)); do 538 for ((i = 0; i < ${#1}; i++)); do
539 : "${1:i:1}" 539 : "${1:i:1}"
540 case "$_" in 540 case "$_" in
541 ([a-zA-Z0-9.~_-]) printf '%s' "$_" ;; 541 [a-zA-Z0-9.~_-]) printf '%s' "$_" ;;
542 (*) printf '%%%02X' "'$_" ;; 542 *) printf '%%%02X' "'$_" ;;
543 esac 543 esac
544 done 544 done
545 printf '\n' 545 printf '\n'
@@ -704,7 +704,6 @@ gemini_request() { # gemini_request URL
704 port="$BOLLUX_GEMINI_PORT" 704 port="$BOLLUX_GEMINI_PORT"
705 fi 705 fi
706 706
707
708 # Build the SSL command to request the resource. 707 # Build the SSL command to request the resource.
709 # 708 #
710 # This is the beating heart of bollux, the command that does all the 709 # This is the beating heart of bollux, the command that does all the
@@ -716,12 +715,12 @@ gemini_request() { # gemini_request URL
716 # manual [9] it says not to use it, but who reads the manual, 715 # manual [9] it says not to use it, but who reads the manual,
717 # anyway? 716 # anyway?
718 openssl s_client 717 openssl s_client
719 -crlf # Automatically add CR+LF to line 718 -crlf # Automatically add CR+LF to line
720 -quiet # Don't print all the cert stuff 719 -quiet # Don't print all the cert stuff
721 # -ign_eof # `-quiet' implies `-ign_eof' 720 # -ign_eof # `-quiet' implies `-ign_eof'
722 -connect "${url[2]}:$port" # The server and port to connect 721 -connect "${url[2]}:$port" # The server and port to connect
723 -servername "${url[2]}" # SNI: Server Name Identification 722 -servername "${url[2]}" # SNI: Server Name Identification
724 -no_ssl3 -no_tls1 -no_tls1_1 # disable old TLS/SSL versions 723 -no_ssl3 -no_tls1 -no_tls1_1 # disable old TLS/SSL versions
725 ) 724 )
726 725
727 # Actually request the resource. 726 # Actually request the resource.
@@ -733,9 +732,9 @@ gemini_request() { # gemini_request URL
733 732
734# Handle the gemini response - see [3] Section 3. 733# Handle the gemini response - see [3] Section 3.
735gemini_response() { # gemini_response URL 734gemini_response() { # gemini_response URL
736 local code meta # received on the first line of the response 735 local code meta # received on the first line of the response
737 local title # determined by a clunky heuristic, see read loop: (2*) 736 local title # determined by a clunky heuristic, see read loop: (2*)
738 local url="$1" # the currently-visited URL. 737 local url="$1" # the currently-visited URL.
739 738
740 # Read the first line. 739 # Read the first line.
741 # 740 #
@@ -751,7 +750,7 @@ gemini_response() { # gemini_response URL
751 # `download', below), but I'm not sure how to remedy that issue either. 750 # `download', below), but I'm not sure how to remedy that issue either.
752 # It requires more research. 751 # It requires more research.
753 while read -t "$BOLLUX_TIMEOUT" -r code meta || 752 while read -t "$BOLLUX_TIMEOUT" -r code meta ||
754 { (($? > 128)) && die 99 "Timeout."; }; do 753 { (($? > 128)) && die 99 "Timeout."; }; do
755 break 754 break
756 done 755 done
757 log d "[$code] $meta" 756 log d "[$code] $meta"
@@ -763,20 +762,20 @@ gemini_response() { # gemini_response URL
763 # - I branch on the first digit of the status code, instead of both, to 762 # - I branch on the first digit of the status code, instead of both, to
764 # minimize the amount of duplicated code I need to write. 763 # minimize the amount of duplicated code I need to write.
765 case "$code" in 764 case "$code" in
766 (1*) # INPUT 765 1*) # INPUT
767 # Gemini allows GET-style requests, and the INPUT family of 766 # Gemini allows GET-style requests, and the INPUT family of
768 # response codes facilitate them. `10' is for standard input, 767 # response codes facilitate them. `10' is for standard input,
769 # and `11' is for sensitive information, like passwords. 768 # and `11' is for sensitive information, like passwords.
770 REDIRECTS=0 769 REDIRECTS=0
771 BOLLUX_URL="$url" 770 BOLLUX_URL="$url"
772 case "$code" in 771 case "$code" in
773 (10) run prompt "$meta" ;; 772 10) run prompt "$meta" ;;
774 (11) run prompt "$meta" -s ;; # sensitive input 773 11) run prompt "$meta" -s ;; # sensitive input
775 esac 774 esac
776 run history_append "$url" "${title:-}" 775 run history_append "$url" "${title:-}"
777 run blastoff "?$(uencode "$REPLY")" 776 run blastoff "?$(uencode "$REPLY")"
778 ;; 777 ;;
779 (2*) # OK 778 2*) # OK
780 # The `20' family of requests is like HTTP's `200' family: it 779 # The `20' family of requests is like HTTP's `200' family: it
781 # means that the request worked and the server is sending the 780 # means that the request worked and the server is sending the
782 # requested content. 781 # requested content.
@@ -811,7 +810,7 @@ gemini_response() { # gemini_response URL
811 passthru 810 passthru
812 } | run display "$meta" "${title:-}" 811 } | run display "$meta" "${title:-}"
813 ;; 812 ;;
814 (3*) # REDIRECT 813 3*) # REDIRECT
815 # Redirects are a fundamental part of any hypertext framework, 814 # Redirects are a fundamental part of any hypertext framework,
816 # and if I remember correctly, one of the main reasons 815 # and if I remember correctly, one of the main reasons
817 # solderpunk and others began thinking about gemini (the others 816 # solderpunk and others began thinking about gemini (the others
@@ -838,51 +837,51 @@ gemini_response() { # gemini_response URL
838 # will also be an option, however. 837 # will also be an option, however.
839 run blastoff "$meta" # TODO: confirm redirect 838 run blastoff "$meta" # TODO: confirm redirect
840 ;; 839 ;;
841 (4*) # TEMPORARY ERROR 840 4*) # TEMPORARY ERROR
842 # Since the 4* codes ([3] Appendix 1) are all server issues, 841 # Since the 4* codes ([3] Appendix 1) are all server issues,
843 # bollux can treat them all basically the same. This is an area 842 # bollux can treat them all basically the same. This is an area
844 # that could use some expansion. 843 # that could use some expansion.
845 local desc="Temporary error" 844 local desc="Temporary error"
846 case "$code" in 845 case "$code" in
847 (41) desc+=" (server unavailable)" ;; 846 41) desc+=" (server unavailable)" ;;
848 (42) desc+=" (CGI error)" ;; 847 42) desc+=" (CGI error)" ;;
849 (43) desc+=" (proxy error)" ;; 848 43) desc+=" (proxy error)" ;;
850 (44) desc+=" (slow down)" ;; # could be particularly improved 849 44) desc+=" (slow down)" ;; # could be particularly improved
851 esac 850 esac
852 REDIRECTS=0 851 REDIRECTS=0
853 die "$((100 + code))" "$desc [$code]: $meta" 852 die "$((100 + code))" "$desc [$code]: $meta"
854 ;; 853 ;;
855 (5*) # PERMANENT ERROR 854 5*) # PERMANENT ERROR
856 # The situation with the 5* codes is basically similar to the 4* 855 # The situation with the 5* codes is basically similar to the 4*
857 # codes. It could maybe use more thought as to what behavior to 856 # codes. It could maybe use more thought as to what behavior to
858 # implement. Maybe adding the (bad) requests to history, 857 # implement. Maybe adding the (bad) requests to history,
859 # subject to configuration? 858 # subject to configuration?
860 local desc="Permanent failure" 859 local desc="Permanent failure"
861 case "$code" in 860 case "$code" in
862 (51) desc+=" (not found)" ;; 861 51) desc+=" (not found)" ;;
863 (52) desc+=" (gone)" ;; 862 52) desc+=" (gone)" ;;
864 (53) desc+=" (proxy request refused)" ;; 863 53) desc+=" (proxy request refused)" ;;
865 # For some reason, codes 54--58 inclusive aren't used. 864 # For some reason, codes 54--58 inclusive aren't used.
866 (59) desc+=" (bad request)" ;; 865 59) desc+=" (bad request)" ;;
867 esac 866 esac
868 REDIRECTS=0 867 REDIRECTS=0
869 die "$((100 + code))" "$desc [$code]: $meta" 868 die "$((100 + code))" "$desc [$code]: $meta"
870 ;; 869 ;;
871 (6*) # CERTIFICATE ERROR (TODO) 870 6*) # CERTIFICATE ERROR (TODO)
872 # Dealing with certificates is honestly the most important 871 # Dealing with certificates is honestly the most important
873 # feature missing from bollux to get it to 1.0. Right now, 872 # feature missing from bollux to get it to 1.0. Right now,
874 # bollux deals with 6* status codes identically to 4* and 5* 873 # bollux deals with 6* status codes identically to 4* and 5*
875 # codes. This is not ideal, in the slightest. 874 # codes. This is not ideal, in the slightest.
876 local desc="Client certificate required" 875 local desc="Client certificate required"
877 case "$code" in 876 case "$code" in
878 (61) desc+=" (certificate not authorized)" ;; 877 61) desc+=" (certificate not authorized)" ;;
879 (62) desc+=" (certificate not valid)" ;; 878 62) desc+=" (certificate not valid)" ;;
880 esac 879 esac
881 REDIRECTS=0 880 REDIRECTS=0
882 log d "Not implemented: Client certificates" 881 log d "Not implemented: Client certificates"
883 die "$((100 + code))" "[$code] $meta" 882 die "$((100 + code))" "[$code] $meta"
884 ;; 883 ;;
885 (*) # UNKNOWN 884 *) # UNKNOWN
886 # Just in case we get a weird, un-spec-compliant status code. 885 # Just in case we get a weird, un-spec-compliant status code.
887 [[ -z "${code-}" ]] && die 100 "Empty response code." 886 [[ -z "${code-}" ]] && die 100 "Empty response code."
888 die "$((100 + code))" "Unknown response code: $code." 887 die "$((100 + code))" "Unknown response code: $code."
@@ -916,9 +915,9 @@ gopher_request() { # gopher_request URL
916 # [7] Section 2.1 915 # [7] Section 2.1
917 [[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]] 916 [[ "$url" =~ gopher://([^/?#:]*)(:([0-9]+))?(/((.))?(/?.*))?$ ]]
918 local server="${BASH_REMATCH[1]}" \ 917 local server="${BASH_REMATCH[1]}" \
919 port="${BASH_REMATCH[3]:-$BOLLUX_GOPHER_PORT}" \ 918 port="${BASH_REMATCH[3]:-$BOLLUX_GOPHER_PORT}" \
920 type="${BASH_REMATCH[6]:-1}" \ 919 type="${BASH_REMATCH[6]:-1}" \
921 path="${BASH_REMATCH[7]}" 920 path="${BASH_REMATCH[7]}"
922 log d "URL='$url' SERVER='$server' TYPE='$type' PATH='$path'" 921 log d "URL='$url' SERVER='$server' TYPE='$type' PATH='$path'"
923 922
924 # Bash has this really neat feature where it can open a TCP socket 923 # Bash has this really neat feature where it can open a TCP socket
@@ -952,25 +951,25 @@ gopher_response() { # gopher_response URL
952 # text-ish, it only concerns itself with those in this case statement. 951 # text-ish, it only concerns itself with those in this case statement.
953 # All the others are simply downloaded. 952 # All the others are simply downloaded.
954 case "$type" in 953 case "$type" in
955 (0) # Item is a file 954 0) # Item is a file
956 # Since gopher doesn't send MIME-type information in-band, we 955 # Since gopher doesn't send MIME-type information in-band, we
957 # just assume it's text/plain, and try to convert it later to 956 # just assume it's text/plain, and try to convert it later to
958 # UTF-8 with `iconv'. 957 # UTF-8 with `iconv'.
959 run display text/plain 958 run display text/plain
960 ;; 959 ;;
961 (1) # Item is a directory [gophermap] 960 1) # Item is a directory [gophermap]
962 # Since I've already written all the code to typeset gemini 961 # Since I've already written all the code to typeset gemini
963 # well, it's easy to convert a gophermap to text/gemini and 962 # well, it's easy to convert a gophermap to text/gemini and
964 # display it than to write a whole new gophermap typesetter. 963 # display it than to write a whole new gophermap typesetter.
965 run gopher_convert | run display text/gemini 964 run gopher_convert | run display text/gemini
966 ;; 965 ;;
967 (3) # Error 966 3) # Error
968 # I don't know all the gopher error cases, and the spec is 967 # I don't know all the gopher error cases, and the spec is
969 # pretty quiet on them. So bollux just signals failure and 968 # pretty quiet on them. So bollux just signals failure and
970 # bails. 969 # bails.
971 die 203 "GOPHER: failed" 970 die 203 "GOPHER: failed"
972 ;; 971 ;;
973 (7) # Item is an Index-Search server 972 7) # Item is an Index-Search server
974 # Gopher search queries are separated from their resources by a 973 # Gopher search queries are separated from their resources by a
975 # TAB. It's wild. 974 # TAB. It's wild.
976 if [[ "$url" =~ $'\t' ]]; then 975 if [[ "$url" =~ $'\t' ]]; then
@@ -980,7 +979,7 @@ gopher_response() { # gopher_response URL
980 run blastoff "$url $REPLY" 979 run blastoff "$url $REPLY"
981 fi 980 fi
982 ;; 981 ;;
983 (*) # Anything else 982 *) # Anything else
984 # The list at [6] Section 3.8 includes the following (noted where it 983 # The list at [6] Section 3.8 includes the following (noted where it
985 # might be good to differently handle them in the future): 984 # might be good to differently handle them in the future):
986 # 985 #
@@ -1030,19 +1029,19 @@ gopher_convert() {
1030 continue 1029 continue
1031 fi 1030 fi
1032 case "$type" in 1031 case "$type" in
1033 (.) # end of file 1032 .) # end of file
1034 printf '.\n' 1033 printf '.\n'
1035 break 1034 break
1036 ;; 1035 ;;
1037 (i) # label 1036 i) # label
1038 case "$label" in 1037 case "$label" in
1039 ('#'* | '*'[[:space:]]*) 1038 '#'* | '*'[[:space:]]*)
1040 if $pre; then 1039 if $pre; then
1041 printf '%s\n' '```' 1040 printf '%s\n' '```'
1042 pre=false 1041 pre=false
1043 fi 1042 fi
1044 ;; 1043 ;;
1045 (*) 1044 *)
1046 if ! $pre; then 1045 if ! $pre; then
1047 printf '%s\n' '```' 1046 printf '%s\n' '```'
1048 pre=true 1047 pre=true
@@ -1051,28 +1050,28 @@ gopher_convert() {
1051 esac 1050 esac
1052 printf '%s\n' "$label" 1051 printf '%s\n' "$label"
1053 ;; 1052 ;;
1054 (h) # html link 1053 h) # html link
1055 if $pre; then 1054 if $pre; then
1056 printf '%s\n' '```' 1055 printf '%s\n' '```'
1057 pre=false 1056 pre=false
1058 fi 1057 fi
1059 printf '=> %s %s\n' "${path:4}" "$label" 1058 printf '=> %s %s\n' "${path:4}" "$label"
1060 ;; 1059 ;;
1061 (T) # telnet link 1060 T) # telnet link
1062 if $pre; then 1061 if $pre; then
1063 printf '%s\n' '```' 1062 printf '%s\n' '```'
1064 pre=false 1063 pre=false
1065 fi 1064 fi
1066 printf '=> telnet://%s:%s/%s%s %s\n' \ 1065 printf '=> telnet://%s:%s/%s%s %s\n' \
1067 "$server" "$port" "$type" "$path" "$label" 1066 "$server" "$port" "$type" "$path" "$label"
1068 ;; 1067 ;;
1069 (*) # other type 1068 *) # other type
1070 if $pre; then 1069 if $pre; then
1071 printf '%s\n' '```' 1070 printf '%s\n' '```'
1072 pre=false 1071 pre=false
1073 fi 1072 fi
1074 printf '=> gopher://%s:%s/%s%s %s\n' \ 1073 printf '=> gopher://%s:%s/%s%s %s\n' \
1075 "$server" "$port" "$type" "$path" "$label" 1074 "$server" "$port" "$type" "$path" "$label"
1076 ;; 1075 ;;
1077 esac 1076 esac
1078 done 1077 done
@@ -1109,7 +1108,7 @@ display() { # display METADATA [TITLE]
1109 for ((i = 1; i <= "${#hdr[@]}"; i++)); do 1108 for ((i = 1; i <= "${#hdr[@]}"; i++)); do
1110 h="${hdr[$i]}" 1109 h="${hdr[$i]}"
1111 case "$h" in 1110 case "$h" in
1112 (*charset=*) charset="${h#*=}" ;; 1111 *charset=*) charset="${h#*=}" ;;
1113 esac 1112 esac
1114 done 1113 done
1115 1114
@@ -1119,7 +1118,7 @@ display() { # display METADATA [TITLE]
1119 log debug "mime='$mime'; charset='$charset'" 1118 log debug "mime='$mime'; charset='$charset'"
1120 1119
1121 case "$mime" in 1120 case "$mime" in
1122 (text/*) 1121 text/*)
1123 set_title "$title${title:+ - }bollux" 1122 set_title "$title${title:+ - }bollux"
1124 # Build the `less' command 1123 # Build the `less' command
1125 less_cmd=(less) 1124 less_cmd=(less)
@@ -1163,7 +1162,7 @@ display() { # display METADATA [TITLE]
1163 run "${less_cmd[@]}" && bollux_quit 1162 run "${less_cmd[@]}" && bollux_quit
1164 } || run handle_keypress "$?" 1163 } || run handle_keypress "$?"
1165 ;; 1164 ;;
1166 (*) run download "$BOLLUX_URL" ;; 1165 *) run download "$BOLLUX_URL" ;;
1167 esac 1166 esac
1168} 1167}
1169 1168
@@ -1173,8 +1172,8 @@ less_prompt_escape() { # less_prompt_escape STRING
1173 for ((i = 0; i < ${#1}; i++)); do 1172 for ((i = 0; i < ${#1}; i++)); do
1174 : "${1:i:1}" 1173 : "${1:i:1}"
1175 case "$_" in 1174 case "$_" in
1176 ([\?:\.%\\]) printf '\%s' "$_" ;; 1175 [\?:\.%\\]) printf '\%s' "$_" ;;
1177 (*) printf '%s' "$_" ;; 1176 *) printf '%s' "$_" ;;
1178 esac 1177 esac
1179 done 1178 done
1180 printf '\n' 1179 printf '\n'
@@ -1232,7 +1231,7 @@ typeset_gemini() {
1232 1231
1233 while IFS= read -r; do 1232 while IFS= read -r; do
1234 case "$REPLY" in 1233 case "$REPLY" in
1235 ('```'*) 1234 '```'*)
1236 PRE_LINE_FORCE=false 1235 PRE_LINE_FORCE=false
1237 if $pre; then 1236 if $pre; then
1238 pre=false 1237 pre=false
@@ -1240,28 +1239,28 @@ typeset_gemini() {
1240 pre=true 1239 pre=true
1241 fi 1240 fi
1242 case "${T_PRE_DISPLAY%%,*}" in 1241 case "${T_PRE_DISPLAY%%,*}" in
1243 (pre) 1242 pre)
1244 : 1243 :
1245 ;; 1244 ;;
1246 (alt | both) 1245 alt | both)
1247 $pre && PRE_LINE_FORCE=true \ 1246 $pre && PRE_LINE_FORCE=true \
1248 gemini_pre "${REPLY#\`\`\`}" 1247 gemini_pre "${REPLY#\`\`\`}"
1249 ;; 1248 ;;
1250 esac 1249 esac
1251 continue 1250 continue
1252 ;; 1251 ;;
1253 ('=>'*) 1252 '=>'*)
1254 : $((ln += 1)) 1253 : $((ln += 1))
1255 gemini_link "$REPLY" $pre "$ln" 1254 gemini_link "$REPLY" $pre "$ln"
1256 ;; 1255 ;;
1257 ('#'*) gemini_header "$REPLY" $pre ;; 1256 '#'*) gemini_header "$REPLY" $pre ;;
1258 ('*'[[:space:]]*) 1257 '*'[[:space:]]*)
1259 gemini_list "$REPLY" $pre 1258 gemini_list "$REPLY" $pre
1260 ;; 1259 ;;
1261 ('>'*) 1260 '>'*)
1262 gemini_quote "$REPLY" $pre 1261 gemini_quote "$REPLY" $pre
1263 ;; 1262 ;;
1264 (*) gemini_text "$REPLY" $pre ;; 1263 *) gemini_text "$REPLY" $pre ;;
1265 esac 1264 esac
1266 done 1265 done
1267} 1266}
@@ -1282,13 +1281,13 @@ gemini_link() {
1282 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" 1281 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
1283 printf "\e[${C_LINK_NUMBER}m[%d]${C_RESET} " "$ln" 1282 printf "\e[${C_LINK_NUMBER}m[%d]${C_RESET} " "$ln"
1284 fold_line -n -B "\e[${C_LINK_TITLE}m" -A "${C_RESET}" \ 1283 fold_line -n -B "\e[${C_LINK_TITLE}m" -A "${C_RESET}" \
1285 -l "$((${#ln} + 3))" -m "${T_MARGIN}" \ 1284 -l "$((${#ln} + 3))" -m "${T_MARGIN}" \
1286 "$WIDTH" "$(trim_string "$t")" 1285 "$WIDTH" "$(trim_string "$t")"
1287 fold_line -B " \e[${C_LINK_URL}m" \ 1286 fold_line -B " \e[${C_LINK_URL}m" \
1288 -A "${C_RESET}" \ 1287 -A "${C_RESET}" \
1289 -l "$((${#ln} + 3 + ${#t}))" \ 1288 -l "$((${#ln} + 3 + ${#t}))" \
1290 -m "$((T_MARGIN + ${#ln} + 2))" \ 1289 -m "$((T_MARGIN + ${#ln} + 2))" \
1291 "$WIDTH" "$a" 1290 "$WIDTH" "$a"
1292 else 1291 else
1293 gemini_pre "$1" 1292 gemini_pre "$1"
1294 fi 1293 fi
@@ -1306,7 +1305,7 @@ gemini_header() {
1306 1305
1307 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" 1306 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
1308 fold_line -B "\e[${hdrfmt}m" -A "${C_RESET}" -m "${T_MARGIN}" \ 1307 fold_line -B "\e[${hdrfmt}m" -A "${C_RESET}" -m "${T_MARGIN}" \
1309 "$WIDTH" "$t" 1308 "$WIDTH" "$t"
1310 else 1309 else
1311 gemini_pre "$1" 1310 gemini_pre "$1"
1312 fi 1311 fi
@@ -1321,7 +1320,7 @@ gemini_list() {
1321 1320
1322 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" 1321 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
1323 fold_line -B "\e[${C_LIST}m" -A "${C_RESET}" -m "$T_MARGIN" \ 1322 fold_line -B "\e[${C_LIST}m" -A "${C_RESET}" -m "$T_MARGIN" \
1324 "$WIDTH" "$t" 1323 "$WIDTH" "$t"
1325 else 1324 else
1326 gemini_pre "$1" 1325 gemini_pre "$1"
1327 fi 1326 fi
@@ -1336,7 +1335,7 @@ gemini_quote() {
1336 1335
1337 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s" 1336 printf "\e[${C_SIGIL}m%${S_MARGIN}s ${C_RESET}" "$s"
1338 fold_line -B "\e[${C_QUOTE}m" -A "${C_RESET}" -m "$T_MARGIN" \ 1337 fold_line -B "\e[${C_QUOTE}m" -A "${C_RESET}" -m "$T_MARGIN" \
1339 "$WIDTH" "$t" 1338 "$WIDTH" "$t"
1340 else 1339 else
1341 gemini_pre "$1" 1340 gemini_pre "$1"
1342 fi 1341 fi
@@ -1346,7 +1345,7 @@ gemini_text() {
1346 if ! ${2-false}; then 1345 if ! ${2-false}; then
1347 printf "%${S_MARGIN}s " ' ' 1346 printf "%${S_MARGIN}s " ' '
1348 fold_line -m "$T_MARGIN" \ 1347 fold_line -m "$T_MARGIN" \
1349 "$WIDTH" "$1" 1348 "$WIDTH" "$1"
1350 else 1349 else
1351 gemini_pre "$1" 1350 gemini_pre "$1"
1352 fi 1351 fi
@@ -1370,25 +1369,25 @@ fold_line() { # fold_line [OPTIONS...] WIDTH TEXT
1370 OPTIND=0 1369 OPTIND=0
1371 while getopts nm:f:l:B:A: OPT; do 1370 while getopts nm:f:l:B:A: OPT; do
1372 case "$OPT" in 1371 case "$OPT" in
1373 (n) # -n = no trailing newline 1372 n) # -n = no trailing newline
1374 newline=false 1373 newline=false
1375 ;; 1374 ;;
1376 (m) # -m MARGIN = margin for all lines 1375 m) # -m MARGIN = margin for all lines
1377 margin_all="$OPTARG" 1376 margin_all="$OPTARG"
1378 ;; 1377 ;;
1379 (f) # -f MARGIN = margin for first line 1378 f) # -f MARGIN = margin for first line
1380 margin_first="$OPTARG" 1379 margin_first="$OPTARG"
1381 ;; 1380 ;;
1382 (l) # -l LENGTH = length of line before starting fold 1381 l) # -l LENGTH = length of line before starting fold
1383 ll="$OPTARG" 1382 ll="$OPTARG"
1384 ;; 1383 ;;
1385 (B) # -B BEFORE = text to insert before each line 1384 B) # -B BEFORE = text to insert before each line
1386 before="$OPTARG" 1385 before="$OPTARG"
1387 ;; 1386 ;;
1388 (A) # -A AFTER = text to insert after each line 1387 A) # -A AFTER = text to insert after each line
1389 after="$OPTARG" 1388 after="$OPTARG"
1390 ;; 1389 ;;
1391 (*) return 1 ;; 1390 *) return 1 ;;
1392 esac 1391 esac
1393 done 1392 done
1394 shift "$((OPTIND - 1))" 1393 shift "$((OPTIND - 1))"
@@ -1426,37 +1425,37 @@ fold_line() { # fold_line [OPTIONS...] WIDTH TEXT
1426# use the exit code from less (see mklesskey) to do things 1425# use the exit code from less (see mklesskey) to do things
1427handle_keypress() { # handle_keypress CODE 1426handle_keypress() { # handle_keypress CODE
1428 case "$1" in 1427 case "$1" in
1429 (48) # o - open a link -- show a menu of links on the page 1428 48) # o - open a link -- show a menu of links on the page
1430 run select_url "$BOLLUX_PAGESRC" 1429 run select_url "$BOLLUX_PAGESRC"
1431 ;; 1430 ;;
1432 (49) # g - goto a url -- input a new url 1431 49) # g - goto a url -- input a new url
1433 prompt GO 1432 prompt GO
1434 run blastoff -u "$REPLY" 1433 run blastoff -u "$REPLY"
1435 ;; 1434 ;;
1436 (50) # [ - back in the history 1435 50) # [ - back in the history
1437 run history_back || { 1436 run history_back || {
1438 sleep 0.5 1437 sleep 0.5
1439 run blastoff "$BOLLUX_URL" 1438 run blastoff "$BOLLUX_URL"
1440 } 1439 }
1441 ;; 1440 ;;
1442 (51) # ] - forward in the history 1441 51) # ] - forward in the history
1443 run history_forward || { 1442 run history_forward || {
1444 sleep 0.5 1443 sleep 0.5
1445 run blastoff "$BOLLUX_URL" 1444 run blastoff "$BOLLUX_URL"
1446 } 1445 }
1447 ;; 1446 ;;
1448 (52) # r - re-request the current resource 1447 52) # r - re-request the current resource
1449 run blastoff "$BOLLUX_URL" 1448 run blastoff "$BOLLUX_URL"
1450 ;; 1449 ;;
1451 (53) # G - goto a url (pre-filled with current) 1450 53) # G - goto a url (pre-filled with current)
1452 run prompt -u GO 1451 run prompt -u GO
1453 run blastoff -u "$REPLY" 1452 run blastoff -u "$REPLY"
1454 ;; 1453 ;;
1455 (54) # ` - change alt-text visibility and refresh 1454 54) # ` - change alt-text visibility and refresh
1456 run list_cycle T_PRE_DISPLAY , 1455 run list_cycle T_PRE_DISPLAY ,
1457 run blastoff "$BOLLUX_URL" 1456 run blastoff "$BOLLUX_URL"
1458 ;; 1457 ;;
1459 (55) # 55-57 -- still available for binding 1458 55) # 55-57 -- still available for binding
1460 die "$?" "less(1) error" 1459 die "$?" "less(1) error"
1461 ;; 1460 ;;
1462 esac 1461 esac
@@ -1473,8 +1472,8 @@ select_url() { # select_url FILE
1473 PS3="OPEN> " 1472 PS3="OPEN> "
1474 select u in "${MAPFILE[@]}"; do 1473 select u in "${MAPFILE[@]}"; do
1475 case "$REPLY" in 1474 case "$REPLY" in
1476 (q) bollux_quit ;; 1475 q) bollux_quit ;;
1477 ([^0-9]*) run blastoff -u "$REPLY" && break ;; 1476 [^0-9]*) run blastoff -u "$REPLY" && break ;;
1478 esac 1477 esac
1479 run blastoff "${u%%[[:space:]]*}" && break 1478 run blastoff "${u%%[[:space:]]*}" && break
1480 done </dev/tty 1479 done </dev/tty
@@ -1543,12 +1542,12 @@ history_append() { # history_append URL TITLE
1543 1542
1544 # Print the URL and its title (if given) to $BOLLUX_HISTFILE. 1543 # Print the URL and its title (if given) to $BOLLUX_HISTFILE.
1545 local fmt='' 1544 local fmt=''
1546 fmt+='%(%FT%T)T\t' # %(_)T calls directly to 'strftime'. 1545 fmt+='%(%FT%T)T\t' # %(_)T calls directly to 'strftime'.
1547 if (( $# == 2 )); then 1546 if (($# == 2)); then
1548 fmt+='%s\t' # $url 1547 fmt+='%s\t' # $url
1549 fmt+='%s\n' # $title 1548 fmt+='%s\n' # $title
1550 else 1549 else
1551 fmt+='%s%s\n' # printf needs a field for every argument. 1550 fmt+='%s%s\n' # printf needs a field for every argument.
1552 fi 1551 fi
1553 run printf -- "$fmt" -1 "$url" "$title" >>"$BOLLUX_HISTFILE" 1552 run printf -- "$fmt" -1 "$url" "$title" >>"$BOLLUX_HISTFILE"
1554 1553
@@ -1567,26 +1566,26 @@ history_back() {
1567 # one with each call to `history_append'. If we subtract 1, we'll just 1566 # one with each call to `history_append'. If we subtract 1, we'll just
1568 # be at the end of the array again, reloading the page. 1567 # be at the end of the array again, reloading the page.
1569 ((HN -= 2)) 1568 ((HN -= 2))
1570 1569
1571 if ((HN < 0)); then 1570 if ((HN < 0)); then
1572 HN=0 1571 HN=0
1573 log e "Beginning of history." 1572 log e "Beginning of history."
1574 return 1 1573 return 1
1575 fi 1574 fi
1576 1575
1577 run blastoff "${HISTORY[$HN]}" 1576 run blastoff "${HISTORY[$HN]}"
1578} 1577}
1579 1578
1580# Move forward in session history. 1579# Move forward in session history.
1581history_forward() { 1580history_forward() {
1582 log d "HN=$HN" 1581 log d "HN=$HN"
1583 1582
1584 if ((HN >= ${#HISTORY[@]})); then 1583 if ((HN >= ${#HISTORY[@]})); then
1585 HN="${#HISTORY[@]}" 1584 HN="${#HISTORY[@]}"
1586 log e "End of history." 1585 log e "End of history."
1587 return 1 1586 return 1
1588 fi 1587 fi
1589 1588
1590 run blastoff "${HISTORY[$HN]}" 1589 run blastoff "${HISTORY[$HN]}"
1591} 1590}
1592 1591
@@ -1646,8 +1645,8 @@ blastoff() { # blastoff [-u] URL
1646 run "${url[1]}_response" "$url" 1645 run "${url[1]}_response" "$url"
1647 else 1646 else
1648 log d \ 1647 log d \
1649 "No response handler for '${url[1]}';" \ 1648 "No response handler for '${url[1]}';" \
1650 " passing thru" 1649 " passing thru"
1651 passthru 1650 passthru
1652 fi 1651 fi
1653 } 1652 }