astro

a POSIX shell compatible gemini client (mirror of https://github.com/blmayer/astro)
git clone https://git.clttr.info/astro.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

commit c10f2b1e2a8fde46e1c951563d97441428833e3c
parent 153b75371b89cfe259868b6cd8c4e26e2b52d525
Author: Brian Mayer <bleemayer@gmail.com>
Date:   Tue,  5 Jul 2022 14:28:16 -0300

Improved rendering cycle

- No longer a recursive function
- Page is saved to a temporary file
- Separated typesetting into a function
Diffstat:
Mastro | 419+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 217 insertions(+), 202 deletions(-)

diff --git a/astro b/astro @@ -60,19 +60,17 @@ getprevious() { # Parameters: url parseurl() { # Credits: https://stackoverflow.com/a/6174447/7618649 - debug "parsing: $1" - proto="$(echo "$1" | grep :// | sed -e 's,^\(.*://\).*,\1,g')" + debug "parsing: $url oldhost: $oldhost oldpath: $oldpath" + proto="$(echo "$url" | grep :// | sed -e 's,^\(.*://\).*,\1,g')" if [ "$proto" ] then - url="$(echo "$1" | sed -e "s@$proto@@g")" + url="$(echo "$url" | sed -e "s@$proto@@g")" else - url="$1" if [ "$oldhost" ] then - debug "oldhost: $oldhost oldpath: $oldpath" - case "$1" in + case "$url" in "/"*) url="$oldhost$1" ;; - *) oldpath="/${oldpath#/*}"; url="$oldhost${oldpath%/*}/$1" ;; + *) oldpath="/${oldpath#/*}"; url="$oldhost${oldpath%/*}/$url" ;; esac fi fi @@ -89,10 +87,68 @@ parseurl() { echo "${proto:-gemini}" "$host" "${port:-1965}" "${path#/*}" "$rest" } +typesetgmi() { + i=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=>$sty_linkt $line" + ;; + '* '*) sty="" && line=" $sty_listb•$sty_listt$(echo "$line" | cut -c 2-)";; + *) sty="";; + esac + echo "$line" | fold -w $width -s | { + while IFS='' read -r txt + do + printf "%*s" "$margin" "" + # shellcheck disable=SC2059 + printf -- "$sty$txt\\n" + done + } + done +} + # 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))) @@ -113,203 +169,159 @@ fetch() { echo "$1://$2:$3/$4$5" | eval openssl s_client \ -connect "$2:$3" "$certfile" -crlf -quiet \ - -ign_eof 2> /dev/null | { - - # First line is status and meta information - read -r status meta - status="$(echo "$status" | tr -d '\r\n')" - meta="$(echo "$meta" | tr -d '\r\n')" - 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 - fetch "$1" "$2" "$3" "$4" "?$input" - return 1 - ;; - 11) - echo "Sensitive input needed: $meta" >&2 - return 2 - ;; - 31|32) - # Redirect - debug "redirecting to: $meta" - - # shellcheck disable=SC2046 - read -r proto host port path << EOF - $(oldhost="$2" oldpath="$4" parseurl "$meta") + -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?$input" + return 0 + ;; + 11) + echo "Sensitive input needed: $meta" >&2 + read -r input <&1 + url="$1://$2:$3/$4?$input" + return 0 + ;; + 31|32) + # Redirect + debug "redirecting to: $meta" + + # shellcheck disable=SC2046 + read -r proto host port path << EOF + $(oldhost="$2" oldpath="$4" parseurl "$meta") EOF - fetch "$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 - url="$(getprevious)" - debug "previous page: $url" - - # shellcheck disable=SC2086 - fetch $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 - url="$(getprevious)" - - # word splitting here is intentional - # shellcheck disable=SC2086 - fetch $url - else - fetch "$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" - - i=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 ] + 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 - printf '%*s%s\n' "$margin" "" "$line" + read -r proto host port path << EOF + $(getprevious) +EOF + url="$proto://$host:$port/$path" else - 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=>$sty_linkt $line" - ;; - '* '*) sty="" && line=" $sty_listb•$sty_listt$(echo "$line" | cut -c 2-)";; - *) sty="";; - esac - echo "$line" | fold -w $width -s | { - while IFS='' read -r txt - do - printf "%*s" "$margin" "" - # shellcheck disable=SC2059 - printf -- "$sty$txt\\n" - done - } + url="$1://$2:$3/$4?$5" fi - done | LESSCHARSET="$charset" less -k "$LESSKEY" +k -R - code="$?" - - # 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 - debug "new url: $url" - set -- - ;; - 50) url="$1://$2:$3/$4" ;; - 51) - # Follow link - cat -n "$linksfile" - printf "Enter link number: " - read -r i <&1 - url="$(sed "${i}q;d" "$linksfile" | cut -d' ' -f1)" - ;; - 52) - # shellcheck disable=SC2046 - fetch $(getprevious) - return - ;; - 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)" - esac - - read -r proto host port path << EOF -$(oldhost="$2" oldpath="$4" parseurl "$url") + 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 ;; + 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 - if [ ! "$proto" = "gemini" ] - then - echo "Only gemini links are supported." - echo "Type a key to continue." + 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 - proto="$1"; host="$2"; port="$3"; path="$4" - fi + url="$(sed "${i}q;d" "$bookmarkfile" | cut -d' ' -f1)" + esac - fetch "$proto" "$host" "$port" "$path" - } + debug "new url: $url" } # Execution @@ -405,9 +417,12 @@ sty_linkt="\\033[${sty_linkt}m" sty_listb="\\033[${sty_listb}m" sty_listt="\\033[${sty_listt}m" -# shellcheck disable=SC2031 -debug "starting with ${args:-$homepage} margin: $margin" - # First request -# shellcheck disable=SC2046 -fetch $(parseurl "${args:-$homepage}") +url="${args:-$homepage}" + +while : +do + # shellcheck disable=SC2086 + fetch $(parseurl) +done +