bopher-ng

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

commit 7dfc0c44699c2fb7c838037ebaaa7cb5fe43a83f
parent eefa9167ba4d7aaa05f494d5269033cf23e6cb83
Author: Luxferre <lux@ferre>
Date:   Mon,  3 Apr 2023 17:25:58 +0300

Added gmi2txt.sh tool

Diffstat:
Mtools/README-tools.md | 29+++++++++++++++++++++++++++++
Atools/gmi2txt.sh | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 101 insertions(+), 0 deletions(-)

diff --git a/tools/README-tools.md b/tools/README-tools.md @@ -104,3 +104,32 @@ iSee ya! ; ``` Note that this tool hasn't been tested for all possible edge cases yet, so I recommend to always verify the generated maps yourself. But it definitely makes their creation a lot easier than doing it by hand. + +## `gmi2txt.sh` + +This tool converts Gemtexts into CRLF-terminated and preformatted plaintext files ready to be served on Gopher. It has the same parameters as `gmi2map.sh` sans the placeholder character. Link lines are converted to the "[description]: [url]" format and, unlike `gmi2map.sh`, also are subject to reflows like everything else. + +Example - imagine we have this rudimentary Gemtext document in the `example.gmi` file: +``` +I write short Gemtext here. + +I write a bit longer Gemtext here to showcase reflow capabilities of Bopher Tools' gmi2txt.sh script, because it really is cool. + +=> gopher://happynetbox.com:79/1luxferre Visit my Gopher homepage +=> https://chronovir.us Or read my blog on Web + +See ya! +``` + +When running `cat example.gmi | bash gmi2txt.sh 67 5`, this file translates to the following plaintext: +``` + I write short Gemtext here. + + I write a bit longer Gemtext here to showcase reflow capabilities + of Bopher Tools' gmi2txt.sh script, because it really is cool. + + Visit my Gopher homepage: gopher://happynetbox.com:79/1luxferre + Or read my blog on Web: https://chronovir.us + + See ya! +``` diff --git a/tools/gmi2txt.sh b/tools/gmi2txt.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# A simple helper tool to create a formatted and CRLF-terminated plaintext from the Gemtext passed via standard input +# +# Usage: cat [file] | gmi2txt.sh [page_width] [leading_spaces] [trailing_spaces] +# (pass 0 as the reflow width if you want to pass the other parameters but don't want to turn on reflow logic) +# +# Created by Luxferre in 2023, released into public domain + +shopt -s extglob # enable extended pattern matching (just to be sure) + +TARGET_WIDTH="$1" +LSPACES="$2" +TSPACES="$3" + +SPC=$'\x20' + +[[ -z "$TARGET_WIDTH" ]] && TARGET_WIDTH=0 # reflow off by default +[[ -z "$LSPACES" ]] && LSPACES=0 +[[ -z "$TSPACES" ]] && TSPACES=0 + +reflowfmt="%-$(( LSPACES ))s%-${TARGET_WIDTH}s%-$(( TSPACES ))s\n" # params: smth, line, smth + +reflow_line() { # single-line logic from phlow.sh, adapted into a function and separating by LF only + local line="$1" + 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 + c="${line:i:1}" # get the current one + 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" == "$SPC" ]] && lastws="$cpos" + outbuf="${outbuf}${c}" # save the character itself + fi + done + [[ ! -z "$outbuf" ]] && printf "$reflowfmt" '' "$outbuf" '' # output the last unprocessed chunk +} + +readarray -t LINES -d $'\n' # read the input line array (split by LF) +for line in "${LINES[@]}"; do # iterate over the read text + line="${line%%$'\r'}" # remove a trailing CR if it is there + if [[ "${line:0:2}" == $'=>' ]]; then # we have a linkable resource + linkline="${line##=>*([[:blank:]])}" # remove the link signature and any leading whitespace + linkurl="${linkline%%[[:blank:]]*}" # treat anything until the next whitespace (or the end of line) as a URL + linkdesc="${linkline##${linkurl}*([[:blank:]])}" # remove the URL and any other leading whitespace to get the description + linkdesc="${linkdesc%%*([[:blank:]])}" # remove any trailing whitespace from the description + targetlink="$(printf '%s: %s' "$linkdesc" "$linkurl")" # reformat the link + rlink="$(reflow_line "$targetlink")" # reflow the final link text + printf '%s\r\n' "${rlink%%$'\n'}" # remove a trailing LF if it is there, but then add CRLF + else # we have an info line + infoline='' + [[ "${line:0:3}" != $'```' ]] && infoline="$line" # ignore the preformatting togglers, pass everything else + readarray -t reflowed_lines -d $'\n' < <(reflow_line "$infoline") + for rline in "${reflowed_lines[@]}"; do # iterate over the reflowed line parts + printf '%s\r\n' "$rline" + done + fi +done # file output finished