+#!/bin/bash
+# Desc: Generates Mediawiki subpage navigation links
+# Input: file text file with list of chapters
+# stdin text with list of chapters
+# Output: [[../Chapter 4|Next]], [[../Chapter 2|Previous]], [[../|Up]]
+# Version: 0.0.1
+# Attrib: Steven Baltakatei Sandoval. (2024-01-29). reboil.com
+# License: GPLv3+
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+read_input() {
+ # Note: May accept specified file or stdin, but not both
+ # Input: arg input text (newline delimited)
+ # stdin input text (newline delimited)
+ # Output: stdout output text (newline delimited)
+ # Depends: BK-2020-03 read_stdin (v0.1.1), yell(), die()
+
+ # Parse args
+ ## Only 1 argument.
+ if [[ "$#" -gt 1 ]]; then die "FATAL:Too many arguments ($#):$*"; fi;
+ ## File specified.
+ if [[ "$#" -eq 1 ]] && [[ -f "$1" ]] && [[ ! -p /dev/stdin ]]; then
+ while read -r line; do
+ printf "%s\n" "$line";
+ done < "$1" && return 0; fi;
+ if [[ "$#" -eq 0 ]] && [[ -p /dev/stdin ]]; then
+ read_stdin && return 0; fi;
+ die "FATAL:Unknown error.";
+};
+read_stdin() {
+ # Desc: Consumes stdin; outputs as stdout lines
+ # Input: stdin (consumes)
+ # Output: stdout (newline delimited)
+ # return 0 stdin read
+ # return 1 stdin not present
+ # Example: printf "foo\nbar\n" | read_stdin
+ # Depends: GNU bash (version 5.1.16), GNU Coreutils 8.32 (cat)
+ # Version: 0.1.1
+ # Attrib: Steven Baltakatei Sandoval (2024-01-29). reboil.com
+ local input_stdin output;
+
+ # Store stdin
+ if [[ -p /dev/stdin ]]; then
+ input_stdin="$(cat -)" || {
+ echo "FATAL:Error reading stdin." 1>&2; return 1; };
+ else
+ return 1;
+ fi;
+
+ # Store as output array elements
+ ## Read in stdin
+ if [[ -n $input_stdin ]]; then
+ while read -r line; do
+ output+=("$line");
+ done < <(printf "%s\n" "$input_stdin") || {
+ echo "FATAL:Error parsing stdin."; return 1; };
+ fi;
+
+ # Print to stdout
+ printf "%s\n" "${output[@]}";
+
+ return 0;
+}; # read stdin to stdout lines
+validate_subpage_list() {
+ # Desc: Check for illegal characters in subpage titles
+ # Input: stdin unvalidated subpage list
+ # Output: stdout validated subpage list
+ # Depends: BK-2020-03 read_stdin(), yell(), die()
+ # GNU sed v4.8
+ while read -r line; do
+
+ # Reject chars illegal in Mediawiki page titles.
+ re_illegal='[][><|}{#_]'; # match illegal page names chars #, <, >, [, ], _, {, |, }
+ if [[ "$line" =~ $re_illegal ]]; then
+ die "FATAL:Illegal char. Not allowed: #, <, >, [, ], _, {, |, }:$line";
+ fi;
+
+ # Reject trailing spaces.
+ re_ts=' $'; # match trailing space
+ if [[ "$line" =~ $re_ts ]]; then
+ die "FATAL:Trailing spaces not allowed:$line";
+ fi;
+
+ # Replace some chars with HTML-style codes
+ ## replace ampersand & with & # must be first
+ ## replace double quote " with "
+ ## replace single quote ' with '
+ line="$(sed \
+ -e 's/&/\&/g' \
+ -e 's/"/\"/g' \
+ -e "s/'/\'/g" \
+ <<< "$line" )" || { echo "FATAL:Error running sed."; };
+ printf "%s\n" "$line";
+ done || {
+ echo "FATAL:Error reading stdin." 1>&2; return 1; };
+};
+generate_wikicode() {
+ # Input: stdin validated subpage list
+ # Output: stdout wikicode
+ local lprev lnext;
+
+ n=0;
+ while read -r line; do
+ #yell "$n:Processing line:$line"; # debug
+ lprev="$lcurr";
+ lcurr="$lnext";
+ lnext="$line";
+ #declare -p lprev lcurr lnext; # debug
+
+ # Skip first iteration
+ if [[ "$n" -eq 0 ]]; then
+ # yell "$n:DEBUG:Skipping first iteration."; # debug
+ ((n++));
+ #printf -- "----\n" 1>&2; # debug
+ continue; fi;
+
+ # Handle first valid input set
+ # yell "$n:DEBUG:Handling first valid input set."; # debug
+ if [[ "$n" -eq 1 ]]; then
+ printf "[[../%s|Next]], [[../|Up]]\n" \
+ "$lnext";
+ #printf -- "----\n" 1>&2; # debug
+ ((n++)); continue; fi;
+
+ # Handle middle lines
+ # yell "$n:DEBUG:Handling middle lines."; # debug
+ printf "[[../%s|Next]], [[../%s|Previous]], [[../|Up]]\n" \
+ "$lnext" "$lprev";
+ ((n++));
+ #printf -- "----\n" 1>&2; # debug
+ done;
+
+ # Handle last line
+ lprev="$lcurr";
+ lcurr="$lnext";
+ lnext="$line";
+ printf "[[../%s|Previous]], [[../|Up]]\n" \
+ "$lprev";
+ ((n++));
+};
+main() {
+ read_input "$@" | validate_subpage_list | generate_wikicode;
+}; # main program
+
+main "$@";