gmi2map.sh (4756B)
1 #!/bin/bash 2 # A simple helper tool to create a valid Gophermap from the Gemtext passed via standard input 3 # 4 # Usage: cat [file] | gmi2map.sh [page_width] [leading_spaces] [trailing_spaces] [placeholder_char] 5 # (pass 0 as the reflow width if you want to pass the other parameters but don't want to turn on reflow logic) 6 # 7 # Created by Luxferre in 2023, released into public domain 8 9 shopt -s extglob # enable extended pattern matching (just to be sure) 10 11 TARGET_WIDTH="$1" 12 LSPACES="$2" 13 TSPACES="$3" 14 DELIM="$4" 15 16 TAB=$'\t' 17 SPC=$'\x20' 18 CRLF=$'\r\n' 19 20 [[ -z "$TARGET_WIDTH" ]] && TARGET_WIDTH=0 # reflow off by default 21 [[ -z "$LSPACES" ]] && LSPACES=0 22 [[ -z "$TSPACES" ]] && TSPACES=0 23 [[ -z "$DELIM" ]] && DELIM=';' 24 25 FORMAT_WIDTH=0 # make formatting width distinct from the target reflow width 26 (( TSPACES > 0 )) && FORMAT_WIDTH="$TARGET_WIDTH" # and only use it if there are trailing spaces 27 28 # format strings to use in different situations: 29 reflowfmt="%-$(( LSPACES ))s%-${FORMAT_WIDTH}s%-$(( TSPACES ))s\n" # params: smth, line, smth 30 infofmt="i%s${TAB}%s${TAB}%s${TAB}0${CRLF}" # params: line, DELIM, DELIM 31 gopherlinkfmt="%s%s${TAB}%s${TAB}%s${TAB}%d${CRLF}" # params: type, name, selector, host, port 32 extlinkfmt="h%s${TAB}URL:%s${TAB}%s${TAB}0${CRLF}" # params: name, URL, DELIM 33 34 reflow_line() { # single-line logic from phlow.sh, adapted into a function and separating by LF only 35 local line="$1" 36 local llen="${#line}" # get effective line length 37 if (( 0 == TARGET_WIDTH || llen < TARGET_WIDTH )); then # no need to run the logic for smaller lines or if TARGET_WIDTH is 0 38 printf "$reflowfmt" '' "$line" '' 39 return 40 fi 41 local lastws=0 # variable to track last whitespace 42 local cpos=0 # variable to track current position within the page line 43 local pagepos=0 # variable to track the position of new line start 44 local outbuf='' # temporary output buffer 45 local c='' # temporary character buffer 46 for ((i=0;i<llen;i++,cpos++)); do # start iterating over characters 47 c="${line:i:1}" # get the current one 48 if (( cpos >= TARGET_WIDTH )); then # we already exceeded the page width 49 (( lastws == 0 )) && lastws=$TARGET_WIDTH # no whitespace encountered here 50 printf "$reflowfmt" '' "${outbuf:0:$lastws}" '' # truncate the buffer 51 outbuf='' 52 pagepos=$(( pagepos + lastws )) 53 cpos=0 54 lastws=0 55 i=$pagepos # update current iteration index from the last valid whitespace 56 else # save the whitespace position if found 57 [[ "$c" == "$SPC" ]] && lastws="$cpos" 58 outbuf="${outbuf}${c}" # save the character itself 59 fi 60 done 61 [[ ! -z "$outbuf" ]] && printf "$reflowfmt" '' "$outbuf" '' # output the last unprocessed chunk 62 } 63 64 readarray -t LINES -d $'\n' # read the input line array (split by LF) 65 for line in "${LINES[@]}"; do # iterate over the read text 66 line="${line%%$'\r'}" # remove a trailing CR if it is there 67 if [[ "${line:0:2}" == $'=>' ]]; then # we have a linkable resource 68 linkline="${line##=>*([[:blank:]])}" # remove the link signature and any leading whitespace 69 linkurl="${linkline%%[[:blank:]]*}" # treat anything until the next whitespace (or the end of line) as a URL 70 linkdesc="${linkline##${linkurl}*([[:blank:]])}" # remove the URL and any other leading whitespace to get the description 71 linkdesc="${linkdesc%%*([[:blank:]])}" # remove any trailing whitespace from the description 72 if [[ "$linkurl" =~ ^gopher:// ]]; then # now, proceed according to the URL type (just like in Bopher-NG) 73 preurl="${linkurl#gopher://}" # remove the scheme to ease parsing 74 hostport="${preurl%%/*}" # extract the host+:port part (where :port is also optional) 75 selpath="${preurl##$hostport}" # extract the selector+path part 76 reshost="${hostport%%:*}" # extract the hostname 77 resport="${hostport:(( 1 + ${#reshost} ))}" # extract the port 78 restype="${selpath:1:1}" # extract the type character 79 ressel="${selpath:2}" # extract the selector 80 [[ -z "$resport" ]] && resport=70 # default port is 70 81 [[ -z "$ressel" ]] && ressel="/" # default selector is root 82 [[ -z "$restype" ]] && restype=1 # default request type is a Gophermap 83 printf "$gopherlinkfmt" "$restype" "$linkdesc" "$ressel" "$reshost" "$resport" 84 else 85 printf "$extlinkfmt" "$linkdesc" "$linkurl" "$DELIM" 86 fi 87 else # we have an info line 88 infoline='' 89 [[ "${line:0:3}" != $'```' ]] && infoline="$line" # ignore the preformatting togglers, pass everything else 90 readarray -t reflowed_lines -d $'\n' < <(reflow_line "$infoline") 91 for rline in "${reflowed_lines[@]}"; do # iterate over the reflowed line parts 92 printf "$infofmt" "$rline" "$DELIM" "$DELIM" 93 done 94 fi 95 done 96 printf '.\r\n' # finish the Gophermap generation