typeset_gemini() { local pre=false while read -r line; do case "$line" in '```') flip pre continue ;; =\>*) gemini_link "$line" $pre ;; \#*) gemini_header "$line" $pre ;; \**) gemini_list "$line" $pre ;; *) gemini_text "$line" $pre ;; esac done } gemini_link() { local re="^(=>)[[:blank:]]*([^[:blank:]]+)[[:blank:]]*(.*)" local s t a # sigil, text, annotation(url) if ! ${2-false} && [[ "$1" =~ $re ]]; then s="${BASH_REMATCH[1]}" t="${BASH_REMATCH[3]}" a="${BASH_REMATCH[2]}" printf "$C_SIGIL%-${MARGIN}s" "$s" fold_line "$WIDTH" "$(printf "$C_LINK_TITLE%s $C_LINK_URL%s$C_RESET\n" \ "$t" "$a")" else gemini_pre "$1" fi } gemini_header() { local re="^(#+)[[:blank:]]*(.*)" local s t a # sigil, text, annotation(lvl) if ! ${2-false} && [[ "$1" =~ $re ]]; then s="${BASH_REMATCH[1]}" a="${#BASH_REMATCH[1]}" t="${BASH_REMATCH[2]}" local hdrfmt hdrfmt="$(eval echo "\$C_HEADER$a")" printf "$C_SIGIL%-${MARGIN}s$hdrfmt%s$C_RESET\n" \ "$s" "$(fold_line "$WIDTH" "$t")" else gemini_pre "$1" fi } gemini_list() { local re="^(\*)[[:blank:]]*(.*)" local s t a # sigil, text, annotation(n/a) if ! ${2-false} && [[ "$1" =~ $re ]]; then s="${BASH_REMATCH[1]}" t="${BASH_REMATCH[2]}" printf "$C_SIGIL%-${MARGIN}s$C_LIST%s$C_RESET\n" \ "$s" "$(fold_line "$WIDTH" "$t")" else gemini_pre "$1" fi } gemini_text() { printf "%${MARGIN}s" ' ' if ! ${2-false}; then fold_line "$WIDTH" "$1" else gemini_pre "$1" fi } gemini_pre() { printf "%${MARGIN}s%s" ' ' "$1" } flip() { # flip NAME [[ "${!1}" == true || "${!1}" == false ]] || return 1 if "${!1}"; then eval "$1=false" else eval "$1=true" fi } fold_line() { # fold_line WIDTH TEXT local width="$1" local ll=0 wl plain # shellcheck disable=2086 # TODO: determine if this is the best way to do it set -- $2 for word; do plain="${word//$'\x1b'\[*([0-9;])m/}" wl=$((${#plain} + 1)) if (((ll + wl) >= width)); then printf "\n%${MARGIN}s" ' ' ll=$wl else ll=$((ll + wl)) fi printf '%s ' "$word" done printf '\n' } # just here for reference strip() { # strip control sequences # https://stackoverflow.com/a/55872518 shopt -s extglob while IFS='' read -r x; do # remove colors echo "${x//$'\x1b'\[*([0-9;])m/}" done } test() { MARGIN=4 WIDTH=60 #shopt -s checkwinsize; (:;:) #WIDTH="$((COLUMNS - (MARGIN*2)))" C_LINK_TITLE=$'\e[34m' C_LINK_URL=$'\e[31m' C_RESET=$'\e[0m' typeset_gemini <<-'EOF' # Project Gemini ## Overview Gemini is a new internet protocol which: * Is heavier than gopher * Is lighter than the web * Will not replace either * Strives for maximum power to weight ratio * Takes user privacy very seriously ## Resources => docs/ Gemini documentation => software/ Gemini software => servers/ Known Gemini servers => https://lists.orbitalfox.eu/listinfo/gemini Gemini mailing list => gemini://gemini.conman.org/test/torture/ Gemini client torture test ## Web proxies => https://portal.mozz.us/?url=gemini%3A%2F%2Fgemini.circumlunar.space%2F&fmt=fixed Gemini-to-web proxy service => https://proxy.vulpes.one/gemini/gemini.circumlunar.space Another Gemini-to-web proxy service ## Search engines => gemini://gus.guru/ Gemini Universal Search engine => gemini://houston.coder.town Houston search engine ## Geminispace aggregators (experimental!) => capcom/ CAPCOM => gemini://rawtext.club:1965/~sloum/spacewalk.gmi Spacewalk ## Free Gemini hosting => users/ Users with Gemini content on this server EOF } test