Files
bopher-ng/tools/gmi2map.sh
T

97 lines
4.6 KiB
Bash
Raw Normal View History

2023-03-31 19:17:11 +03:00
#!/bin/bash
# A simple helper tool to create a valid Gophermap from the Gemtext passed via standard input
#
# Usage: cat [file] | gmi2map.sh [page_width] [leading_spaces] [trailing_spaces] [placeholder_char]
# (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"
DELIM="$4"
TAB=$'\t'
SPC=$'\x20'
CRLF=$'\r\n'
[[ -z "$TARGET_WIDTH" ]] && TARGET_WIDTH=0 # reflow off by default
[[ -z "$LSPACES" ]] && LSPACES=0
[[ -z "$TSPACES" ]] && TSPACES=0
[[ -z "$DELIM" ]] && DELIM=';'
2023-04-03 21:37:12 +03:00
FORMAT_WIDTH=0 # make formatting width distinct from the target reflow width
(( TSPACES > 0 )) && FORMAT_WIDTH="$TARGET_WIDTH" # and only use it if there are trailing spaces
2023-03-31 19:17:11 +03:00
# format strings to use in different situations:
2023-04-03 21:37:12 +03:00
reflowfmt="%-$(( LSPACES ))s%-${FORMAT_WIDTH}s%-$(( TSPACES ))s\n" # params: smth, line, smth
2023-03-31 19:17:11 +03:00
infofmt="i%s${TAB}%s${TAB}%s${TAB}0${CRLF}" # params: line, DELIM, DELIM
gopherlinkfmt="%s%s${TAB}%s${TAB}%s${TAB}%d${CRLF}" # params: type, name, selector, host, port
extlinkfmt="h%s${TAB}URL:%s${TAB}%s${TAB}0${CRLF}" # params: name, URL, DELIM
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
if [[ "$linkurl" =~ ^gopher:// ]]; then # now, proceed according to the URL type (just like in Bopher-NG)
preurl="${linkurl#gopher://}" # remove the scheme to ease parsing
hostport="${preurl%%/*}" # extract the host+:port part (where :port is also optional)
selpath="${preurl##$hostport}" # extract the selector+path part
reshost="${hostport%%:*}" # extract the hostname
resport="${hostport:(( 1 + ${#reshost} ))}" # extract the port
restype="${selpath:1:1}" # extract the type character
ressel="${selpath:2}" # extract the selector
[[ -z "$resport" ]] && resport=70 # default port is 70
[[ -z "$ressel" ]] && ressel="/" # default selector is root
[[ -z "$restype" ]] && restype=1 # default request type is a Gophermap
printf "$gopherlinkfmt" "$restype" "$linkdesc" "$ressel" "$reshost" "$resport"
else
printf "$extlinkfmt" "$linkdesc" "$linkurl" "$DELIM"
fi
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 "$infofmt" "$rline" "$DELIM" "$DELIM"
done
fi
done
printf '.\r\n' # finish the Gophermap generation