bopher-ng

A better Gopher client in pure Bash
git clone git://git.luxferre.top/bopher-ng.git
Log | Files | Refs | README | LICENSE

commit 50da581db6c2058d325a817e17e0aa83cb6c0784
parent 73b71d533f778b95c20a94ee1aec4573a0f7f7dd
Author: Luxferre <lux@ferre>
Date:   Fri, 31 Mar 2023 23:11:25 +0300

Implemented reflow for plaintexts in the client itself

Diffstat:
MREADME.md | 3++-
Mbopher-ng.sh | 40++++++++++++++++++++++++++++++++++++++--
2 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md @@ -6,7 +6,7 @@ ## What is it? -Bopher-NG is an ambitious attempt to write a full-featured Gopher client/browser in under 300 SLOC of pure Bash code. It started off as a really crude and unoptimized prototype developed right in [this blog post](https://chronovir.us/2023/03/28/I-wrote-a-browser/) for educational purposes. +Bopher-NG is an ambitious attempt to write a full-featured Gopher client/browser in under 350 SLOC of pure Bash code. It started off as a really crude and unoptimized prototype developed right in [this blog post](https://chronovir.us/2023/03/28/I-wrote-a-browser/) for educational purposes. Improvements over the original Bopher from that post: @@ -14,6 +14,7 @@ Improvements over the original Bopher from that post: - smoother rendering and scrolling - better edge-case stability (e.g. on macOS where using file descriptor 3 actually crashes everything) - better Gophermap processing according to the RFC1436 +- an actual text reflow when viewing plaintext documents with no hard wrapping - multi-level navigation history (although you can only go back) - status bar with currently opened resource name - ability to accept `gopher://` URLs from the command line diff --git a/bopher-ng.sh b/bopher-ng.sh @@ -8,6 +8,7 @@ # - smoother rendering and scrolling # - better edge-case stability # - better Gophermap processing according to the RFC1436 +# - an actual text reflow when viewing plaintext documents with no hard wrapping # - multi-level navigation history (although you can only go back) # - status bar with currently opened resource name # - ability to accept gopher:// URLs from the command line @@ -121,6 +122,38 @@ gmparse() { # args: line, curhost, curport printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$action" "$desc" "$rhost" "$rport" "$sel" "$rtype" # output the final formatted line } +phlow_lite() { # a single-parameter line reflow algorithm + local line="$1" + local TARGET_WIDTH="$2" + local reflowfmt="%-${TARGET_WIDTH}s\n" + local llen="${#line}" # get effective line length + if (( 0 == TARGET_WIDTH || llen < TARGET_WIDTH )); then # no need to run the logic for smaller lines or if TARGET_WIDTH is 0 + printf "$reflowfmt" "$line" + return + fi + local lastws=0 # variable to track last whitespace + local cpos=0 # variable to track current position within the page line + local pagepos=0 # variable to track the position of new line start + local outbuf='' # temporary output buffer + local c='' # temporary character buffer + for ((i=0;i<llen;i++,cpos++)); do # start iterating over characters + if (( cpos >= TARGET_WIDTH )); then # we already exceeded the page width + (( lastws == 0 )) && lastws=$TARGET_WIDTH # no whitespace encountered here + printf "$reflowfmt" "${outbuf:0:$lastws}" # truncate the buffer + outbuf='' + pagepos=$(( pagepos + lastws )) + cpos=0 + lastws=0 + i=$pagepos # update current iteration index from the last valid whitespace + else # save the whitespace position if found + c="${line:i:1}" # get the current character + [[ "$c" == $'\x20' ]] && lastws="$cpos" + outbuf="${outbuf}${c}" # save the character itself + fi + done + [[ ! -z "$outbuf" ]] && printf "$reflowfmt" "$outbuf" # output the last unprocessed chunk +} + # convert AM line back to gopher:// URL amtogopher() { # args: AM line readarray -d $'\t' -t fields < <(printf '%s' "$1") @@ -159,9 +192,13 @@ amclick() { # args: AM line[, forcedl], output: AM line(s) gmparse "$line" "$rhost" "$rport" done elif [[ 'P' == "$action" ]]; then # plain text content (can be delimited with both CRLF or LF) + read -r TERMROWS TERMCOLS < <(stty size) # get current terminal size info readarray -t lines -d $'\n' < <(gophetch "$rhost" "$rport" "$puresel" "$input") # split on LF for line in "${lines[@]}"; do # iterate over every fetched line - printf 'E\t%s\n' "${line%%$'\r'}" # remove a trailing CR if it's there + readarray -t reflow_lines -d $'\n' < <(phlow_lite "${line%%$'\r'}" "$TERMCOLS") # remove a trailing CR if it's there + for rline in "${reflow_lines[@]}"; do + printf 'E\t%s\n' "$rline" + done done fi } @@ -309,7 +346,6 @@ readmouseinput() { # correctly read the remaining mouse input in semi-long mode printf '%d %d %d %c\n' "$battr" "$mx" "$my" "$c" } - # entry point code starts here printf '%s' "$ALTBUFON$CLS" # enter ALTBUF mode and clear the screen