@@ 1,4 1,4 @@
-# Bopher Tools: a set of simple Bash scripts to not just browse Gopher but also create for it
+# Bopher Tools: a set of simple Bash scripts to not just browse Gopher but also create content for it
Bopher Tools is a small collection of simple shell scripts that are:
@@ 15,9 15,9 @@ Without further ado, let's begin!
This tool converts every line of the standard input into a valid CRLF-terminated Gophermap line of the `i` type that obeys RFC1436. It accepts three **optional** parameters: the number of leading spaces (0 by default), the number of trailing spaces (0 by default) and the placeholder (';' by default) to put into the selector and host fields unused in the `i` type (and the port field is always set to 0). It also pads every input line with the amount of spaces required to make the line length the same as the longest one in the input. All this is done to make your output Gophermap look nice when viewed in plaintext as well.
-Note that the tool **does not** output the trailing dot to end the Gophermap, because its output can be used as a part of other Gophermaps. To end the map manually according to the RFC1436, just invoke `printf '.\r\n'` afterwards redirecting to the same output.
+Note that the tool **does not** output the trailing dot to end the Gophermap, because its output can be used as a part of another Gophermap. To end the map manually according to the RFC1436, just invoke `printf '.\r\n'` afterwards redirecting to the same output.
-Example of using `gopherinfo.sh` in combination with FIGlet, using 4 leading spaces, 0 trailing spaces and `|` as the placeholder:
+Example of using `gopherinfo.sh` in combination with FIGlet, using 4 leading spaces, 0 trailing spaces and `|` as the placeholder (CR and Tab characters in the output are not shown):
```
$ figlet 'Bash it' | bash gopherinfo.sh 4 0 '|'
i ____ _ _ _ | | 0
@@ 36,4 36,30 @@ A non-trivial task would be using this output in some Web-based services like Ha
4. Paste your escaped string and hit Enter. If everything went correctly, the output should not be mangled.
5. Copy your `console.log` output again (make sure to fully copy the last line too!) and paste it in your Web service's posting textarea.
-This hack also appies to any Gophermaps and not just the ones generated with this tool.
+This hack also appies to any Gophermaps and not just the fragments generated with this tool.
+
+## `phlow.sh`
+
+This tool processes every line of text from the standard input to fit exactly the given amount of characters, optionally adding some leading and/or trailing whitespaces **after** the reflow is done. It also replaces all LF line endings with CRLF in the output to achieve maximum compatibility with legacy clients.
+
+As `phlow.sh` does not know anything about hyphenation rules and doesn't do any heuristics, it just breaks at whatever whitespace is the closest to the page's right edge.
+
+Example - fitting the previous paragraph into 30-character width and prepend every line with 5 spaces (CR characters in the output are not shown):
+```
+$ echo 'This tool processes every line of text from the standard input to fit exactly the given amount of characters, optionally adding some leading and/or trailing whitespaces **after** the reflow is done. It also replaces all LF line endings with CRLF in the output to achieve maximum compatibility with legacy clients.' | bash phlow.sh 30 5
+ This tool processes every
+ line of text from the
+ standard input to fit
+ exactly the given amount of
+ characters, optionally
+ adding some leading and/or
+ trailing whitespaces
+ **after** the reflow is
+ done. It also replaces all
+ LF line endings with CRLF in
+ the output to achieve
+ maximum compatibility with
+ legacy clients.
+```
+
+
@@ 0,0 1,48 @@
+#!/bin/bash
+# A simple helper tool to reflow the text from the standard input to a given width
+#
+# Usage: cat [file] | phlow.sh [width] [leading_spaces] [trailing_spaces]
+#
+# Created by Luxferre in 2023, released into public domain
+
+TARGET_WIDTH="$1"
+LSPACES="$2"
+TSPACES="$3"
+
+CRLF=$'\r\n'
+SPC=$'\x20'
+
+[[ -z "$TARGET_WIDTH" ]] && TARGET_WIDTH=67 # default page width
+[[ -z "$LSPACES" ]] && LSPACES=0
+[[ -z "$TSPACES" ]] && TSPACES=0
+
+fmtstr="%-$(( LSPACES ))s%-${TARGET_WIDTH}s%-$(( TSPACES ))s${CRLF}" # params: smth, line, smth
+
+while read -rs line; do # fully line-based operation
+ line="${line%%$'\r'}" # remove a trailing CR if it is there
+ llen="${#line}" # get effective line length
+ if (( llen < TARGET_WIDTH )); then # no need to run the logic for smaller lines
+ printf "$fmtstr" '' "$line" ''
+ continue
+ fi
+ lastws=0 # variable to track last whitespace
+ cpos=0 # variable to track current position within the page line
+ pagepos=0 # variable to track the position of new line start
+ outbuf='' # temporary output 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 "$fmtstr" '' "${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 "$fmtstr" '' "$outbuf" ''
+done