💾 Archived View for gmn.clttr.info › sources › astro.git › tree › astro.txt captured on 2023-03-20 at 18:22:08.
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
#!/bin/sh version="0.20.0" usage() { echo "astro v$version: Browse the gemini web on the terminal." echo "" echo "Usage: astro [url]|[option]" echo "" echo "Options:" echo " -h, --help show this help" echo " -v, --version show version info" echo "" echo "Commands available inside the browser:" echo " q quit" echo " g go to a link" echo " r reload current page" echo " b go back one page" echo " u jump one path segment up" echo " o open an address" echo " s save current page" echo " H go to homepage" echo " m add bookmark" echo " M go to a bookmark" echo " K remove bookmark for current url" echo "" echo "Examples:" echo " astro Start browsing the default webpage" echo " astro url Start browsing url" echo " astro --help Show help" echo "" echo "Debugging:" echo " debug=1 astro Will start astro in debug mode" echo "" echo "Report bugs to: bleemayer@gmail.com" echo "Home page: <https://www.github.com/blmayer/astro/>" echo "General help: <https://www.github.com/blmayer/astro/wiki>" } version() { echo "astro $version" echo "Copyright (C) 2021-2023 Brian Mayer." echo "License MIT: MIT License <https://opensource.org/licenses/MIT>" echo "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND," echo "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF" echo "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT." echo "" echo "Written by Brian Lee Mayer." } debug() { [ "$debug" ] && echo "DEBUG: $*" >&2 && sleep 2 } # Parse arguments args="$*" case "$args" in '--help'* | '-h'*) usage exit ;; '--version'* | '-v'*) version exit ;; esac # Save terminal tput smcup # Configuration confighome=${XDG_CONFIG_HOME:-$HOME/.config} mkdir -p "$confighome/astro" configfile="$confighome/astro/astro.conf" bookmarkfile="$confighome/astro/bookmarks" LESSKEY="$confighome/astro/less.keys" certdir="$confighome/astro/certs" mkdir -p "$certdir" [ -z "$cachehome" ] && cachehome=${XDG_CACHE_HOME:-$HOME/.cache} cachedir="$cachehome/astro" # This is the final binary form, to save space, it corresponds to: # o (49): go to a URL # r (50): reload page # g (51): go to a link # b (52): go back # H (53): go to homepage # m (54): add page to bookmarks # M (55): go to a bookmark # K (56): remove bookmark for current url # u (57): jump one path element up [ -n "$LESSKEY" ] && echo "AE0rR2MtAG8AmDEAcgCYMgBnAJgzAGIAmDQASACYNQBtAJg2AE0AmDcASwCYOAB1AJg5AGUAAHYAAHhFbmQ=" | \ base64 -d > "$LESSKEY" margin=8 if [ ! -s "$configfile" ] then # Default values cat << EOF > "$configfile" cachedir="$cachehome" margin=8 homepage="gemini.circumlunar.space/" sty_header1='35;7;1' sty_header2='35;4;1' sty_header3='35;4' sty_quote='2;3' sty_linkb='35' sty_linkt='36;3' sty_listb='35;1' sty_listt='0' EOF fi # shellcheck disable=SC1090 . "$configfile" mkdir -p "$cachedir" histfile="$cachedir/history" linksfile="$cachedir/links" # Restore terminal trap 'tput rmcup && rm -f $histfile $linksfile > /dev/null 2>&1; exit' EXIT INT HUP # ANSI style code sty_header1="\\033[${sty_header1}m" sty_header2="\\033[${sty_header2}m" sty_header3="\\033[${sty_header3}m" sty_quote="\\033[${sty_quote}m" sty_linkb="\\033[${sty_linkb}m" sty_linkt="\\033[${sty_linkt}m" sty_listb="\\033[${sty_listb}m" sty_listt="\\033[${sty_listt}m" getprevious() { sed -i '$d' "$histfile" prev="$(tail -n 1 "$histfile")" sed -i '$d' "$histfile" echo "$prev" } # Returns the complete url scheme with gemini defaults # Parameters: url parseurl() { # Credits: https://stackoverflow.com/a/6174447/7618649 debug "parsing: $url oldhost: $oldhost oldpath: $oldpath" proto="$(echo "$url" | grep :// | sed -e 's,^\(.*://\).*,\1,g')" if [ "$proto" ] then url="$(echo "$url" | sed -e "s@$proto@@g")" else if [ "$oldhost" ] then case "$url" in "/"*) url="$oldhost$url" ;; *) oldpath="/${oldpath#/*}"; url="$oldhost${oldpath%/*}/$url" ;; esac fi fi debug "url: $url" proto="$(echo "$proto" | sed -e 's,:\?//,,g')" user="$(echo "$url" | grep @ | cut -d@ -f1)" hostport="$(echo "$url" | sed -e "s/$user@//g" | cut -d/ -f1)" host="$(echo "$hostport" | sed -e 's,:.*,,g')" port="$(echo "$hostport" | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')" path="$(echo "${url#/*}" | sed "s@/\?$hostport@@")" debug "parsed: proto: ${proto:-gemini} host: $host port: ${port:-1965} path: ${path#/*}" echo "${proto:-gemini}" "$host" "${port:-1965}" "${path#/*}" "$rest" } typesetgmi() { linkcount=1 while IFS='' read -r line do line="$(echo "$line" | tr -d '\r')" # shellcheck disable=SC2016 echo "$line" | grep -q '^```' && pre=$((1 - pre)) && line="" # Add margins and fold if [ "$pre" = 1 ] then printf '%*s%s\n' "$margin" "" "$line" continue fi case "$line" in "### "*) sty="$sty_header3" && line="$(echo "$line" | cut -c 5- )" ;; "## "*) sty="$sty_header2" && line="$(echo "$line" | cut -c 4-)" ;; "# "*) sty="$sty_header1" && line="$(echo "$line" | cut -c 3-)" ;; "> "*) sty=" $sty_quote" && line="$(echo "$line" | cut -c 3-)" ;; "=>"*) link="$(echo "$line" | sed -e 's/^=> *\(\S\+\)\(\s*.*\)/\1 \2/g')" echo "$link" >> "$linksfile" # shellcheck disable=SC2086 line="$(echo $link | cut -d' ' -f2-)" [ -z "$line" ] && line="$link" sty="$sty_linkt" line="$sty_linkb$linkcount>$sty_linkt $line" linkcount=$((linkcount+1)) ;; '* '*) sty="" && line=" $sty_listb•$sty_listt$(echo "$line" | cut -c 2-)";; *) sty="";; esac echo "$line" | sed 's/%/%%/g' | fold -w "$width" -s | { while IFS='' read -r txt do printf "%*s" "$margin" "" # shellcheck disable=SC2059 printf -- "$sty$txt\\n" done } done } # borrowed from https://gist.github.com/cdown/1163649 urlencode() { old_lang=$LANG LANG=C old_lc_collate=$LC_COLLATE LC_COLLATE=C length="${#1}" i=1 while [ "$i" -le "$length" ] do c=$(printf '%s' "$1" | cut -c $i) case $c in [a-zA-Z0-9.~_-]) printf '%s' "$c" ;; *) printf '%%%02X' "'$c" ;; esac i=$((i+1)) done LC_COLLATE=$old_lc_collate LANG=$old_lang } # Fetches the gemini response from server # Parameters: proto, host, port and path # Spec draft is here: https://gemini.circumlunar.space/docs/specification.html fetch() { if [ ! "$1" = "gemini" ] then echo "Only gemini links are supported." echo "Type a key to continue." read -r i <&1 read -r proto host port path << EOF $(getprevious) EOF url="$proto://$host:$port/$path" url="${url:-$homepage}" debug "previous page: $url" return fi # Some setup first cols=$(tput cols) width=$((cols - (2*margin))) debug "text width: $width" debug "requesting $1://$2:$3/$4$5" printf '\033]2;%s\007' "astro: $2/$4" echo "$1 $2 $3 $4 $5" >> "$histfile" clear certfile="" if [ -f "$certdir/$2.crt" ] && [ -f "$certdir/$2.key" ] then certfile="-cert \"$certdir/$2.crt\" -key \"$certdir/$2.key\"" debug "using client cert for domain: $certfile" fi echo "$1://$2:$3/$4$5" | eval openssl s_client \ -connect "$2:$3" "$certfile" -crlf -quiet \ -ign_eof 2> /dev/null > "$cachedir/curpage" # First line is status and meta information read -r status meta < "$cachedir/curpage" status="$(echo "$status" | tr -d '\r\n')" meta="$(echo "$meta" | tr -d '\r\n')" sed -i '1d' "$cachedir/curpage" debug "response header: $status $meta" # Validate case "$status" in 10) echo "Input needed: $meta" >&2 echo "Please provide the input:" >&2 read -r input <&1 url="$1://$2:$3/$4?$(urlencode "$input")" return 0 ;; 11) echo "Sensitive input needed: $meta" >&2 read -r input <&1 url="$1://$2:$3/$4?$(urlencode "$input")" return 0 ;; 31|32) # Redirect debug "redirecting to: $meta" # shellcheck disable=SC2046 read -r proto host port path << EOF $(oldhost="$2" oldpath="$4" url="$meta" parseurl) EOF url="$proto://$host:$port/$path" return 0 ;; 40) echo "Temporary failure" >&2 return 3 ;; 41) return 4 ;; 42) return 5 ;; 43) return 6 ;; 44) return 7 ;; 51*) echo "Page not found!" >&2 read -r proto host port path << EOF $(getprevious) EOF url="$proto://$host:$port/$path" debug "previous page: $url" return 0 ;; 52) return 10 ;; 53) return 11 ;; 59) echo "Bad request: $meta" >&2 return 12 ;; 60) printf "client certificate required, to create a client cert use the following command:\n\n" printf "\topenssl req -x509 -newkey rsa:4096 -keyout %s/%s.key -out %s/%s.crt -days 36500 -nodes\n\n" "$certdir" "$2" "$certdir" "$2" printf "press 'return' to reload the page or 'b' to go back to the previous page:\n" read -r in <&1 if [ "$in" = "b" ] then read -r proto host port path << EOF $(getprevious) EOF url="$proto://$host:$port/$path" else url="$1://$2:$3/$4?$5" fi return 0 ;; 61) return 14 ;; 62) return 15 ;; esac # Success [ -f "$linksfile" ] && rm "$linksfile" # Set charset charset="$(echo "$meta" | grep -i "charset=" | sed -e 's/.*charset=\([^;]\+\).*/\1/Ig')" case "$charset" in "iso-8859-1" | "ISO-8859-1") charset="iso8859" ;; "utf-8" | "UTF-8" | "") charset="utf8" ;; "us-ascii" | "US-ASCII") charset="ascii" ;; esac debug "charset: $charset" case $4 in *".gmi"|"") typesetgmi ;; *.*) cat ;; *) typesetgmi ;; esac < "$cachedir/curpage" | LESSCHARSET="$charset" less -k "$LESSKEY" +k -R code="$?" rm "$cachedir/curpage" # Choose what to do next debug "pager exit code: $code" case "$code" in 0) exit 0 ;; 49) # Open url printf "Type url: " read -r url <&1 ;; 50) url="$1://$2:$3/$4" ;; 51) # Follow link cat -n "$linksfile" printf "Enter link number: " read -r i <&1 debug "selected $i" url="$(sed "${i}q;d" "$linksfile" | cut -d' ' -f1)" oldhost="$2"; oldpath="$4" ;; 52) read -r proto host port path << EOF $(getprevious) EOF url="$proto://$host:$port/$path" ;; 53) url="$homepage"; shift $# ;; 54) echo "Enter description: (optional)" read -r desc <&1 url="$1://$2:$3/$4" echo "$url $desc" >> "$bookmarkfile" ;; 55) cat -n "$bookmarkfile" printf "Enter link number: " read -r i <&1 url="$(sed "${i}q;d" "$bookmarkfile" | cut -d' ' -f1)" ;; 56) url="$1://$2:$3/$4" grep -iv "^$url " "$bookmarkfile" > "$cachedir/bookmarks" mv "$cachedir/bookmarks" "$bookmarkfile" ;; 57) newpath=$(echo "/$4" | tr -d '/' | rev | cut -d'/' -f2- | rev) url="$1://$2:$3$newpath" ;; esac debug "new url: $url" } # Execution export LESS='-P Keys\: qgrubosHmMK, to see a description run astro -h' # First request url="${args:-$homepage}" while : do # shellcheck disable=SC2046 fetch $(parseurl) done