------------------------------------------------------------------------ Shredder Concept to Quest ------------------------------------------------------------------------ Date: 2026-05-20 Author: paulr ------------------------------------------------------------------------ Shredder Concept to Quest Shredder: Concept to Quest Laboratory Report ? Automated POSIX Markdown Publishing Pipeline Paul R. ? SDF.org ? paulr.sdf.org ? 2026-05-19 Public Domain ? MIT License --- Abstract This report documents the design, architecture, and implementation of shredder.sh, a POSIX-compliant shell script that automates the publication of Markdown documents to HTML, Gopher text, and PDF formats on SDF.org BSD servers. The script applies a wizard pattern ? prompting for missing information through structured shell-function interactions rather than failing silently ? and frames the entire pipeline as a Teenage Mutant Ninja Turtles quest narrated by Master Splinter via cowsay(1) speech bubbles. Each processing stage is assigned a named character; the main orchestrator function is called SHREDDER. The system requires no external runtimes, no package managers, and no configuration files beyond a single variable block at the top of the script. It targets the NetBSD-based ARPA shell environment at SDF.org. This document is itself the inaugural post of the Shredder Quest on paulr.sdf.org, and will be published by the tool it describes. --- I. Introduction Public-access Unix systems such as SDF.org provide shell accounts, Gopher hosting, and web hosting with nothing but a terminal and an SSH client. Publishing to all three formats from a single Markdown source file is a three-step process that is easy to forget and tedious to repeat. The goal of shredder.sh was to reduce that to one command ? or zero arguments, if the document does not yet exist. A secondary goal emerged during design: the tool should not be silent. Traditional shell pipelines communicate through exit codes and sparse log lines. This script takes the opposite approach: every stage is announced by its assigned character, and the transitions between stages are narrated by Master Splinter, rendered as cowsay(1) speech bubbles with a custom ASCII rat cow file generated at runtime. The tertiary goal ? which was not planned ? was that the whole thing should be fun to run. This report documents the design decisions, function architecture, control-flow branches, utility dependencies, and shell-portability rationale. It is simultaneously a technical record and, upon being published by the tool it describes, the first entry in the Shredder Quest on paulr.sdf.org. This mission is yours, if you choose to accept it. --- II. Equipment and Operating Environment 2.1 Host System | Item | Detail | | Service | SDF.org Public Access Unix | | Account tier | ARPA (ARPA-level shell access) | | Operating system | NetBSD (primary target); any POSIX BSD Unix | | Shell | /bin/sh ? POSIX sh; not bash, not zsh | | Access method | SSH to tty.sdf.org | | Gopher hosting | gopher.sdf.org/users/$USER | | Web hosting | sdf.org/~$USER or $USER.sdf.org | SDF.org is a public-access Unix system founded in 1987, operating continuously since. MetaARPA accounts include Gopher server space, personal web hosting, email, and access to the full NetBSD base system. The arpa designation is a reference to ARPANET, the precursor to the modern internet, and to the academic culture of public shell accounts that predates the web. 2.2 Required Utilities shredder.sh depends exclusively on tools present in the NetBSD base system or otherwise standard on POSIX platforms. No package manager invocation is required. | Utility | Role in Pipeline | | sh | POSIX shell interpreter; set -eu throughout | | awk | Block-level Markdown?HTML and Markdown?groff-ms state machines | | sed | Inline Markdown?HTML substitutions; Gopher text stripping | | grep | Structural validation ? heading counts and fence balance | | spell | BSD spell(1) for interactive spell checking | | groff | PDF typesetting via the -ms macro package | | ps2pdf | PostScript-to-PDF conversion via Ghostscript | | fold | Line-wrapping Gopher text to 72 columns | | mktemp | Isolated temp workspace; cleaned by trap on EXIT | | ed | Line editor for interactive document creation in no-arg mode | | cowsay | Splinter's speech bubbles; optional, graceful fallback | | printf | POSIX-portable output; echo is not used | | date | ISO 8601 datestamps for derived output filenames | | chmod | File permission verification in the final stage | | mkdir | Output directory creation | --- III. Design Philosophy 3.1 The Wizard Pattern Command-line tools fail in two modes: silently (producing wrong output) or noisily (printing a usage string and exiting with status 1). Neither is satisfying when the tool is used infrequently by one person. A third mode exists: the wizard. The wizard detects what information is missing and asks for it before proceeding. GUI installers use this pattern. Terminal tools rarely do. shredder.sh applies the wizard pattern at three points in the pipeline: 1. No input file: rather than print usage and exit, the script prompts for a title, derives a filename and slug, seeds the file with a # Title heading, displays a Markdown syntax reference and an ed(1) quick-reference card, and drops the user into ed for composition. Control returns to the pipeline when ed exits. 2. Spell check gate: if spell(1) finds misspellings, the script displays them and prompts [y/N] before continuing. This is Raphael's contribution. He disagrees with everything. 3. Gophermap subtitle: before inserting the gophermap entry, the script offers the author the opportunity to supply an extended display title. If the subtitle was already provided in the no-arg wizard flow, this prompt is skipped. The wizard is implemented entirely in shell functions. No external expect, no Python, no curses. Each prompt uses printf and read -r. The functions share state through script-level variables ? $SUBTITLE being the primary example of a variable set in one branch and consumed in a later stage. 3.2 The Narrative Framework The mapping of pipeline stages to Teenage Mutant Ninja Turtles characters is structural, not merely cosmetic. The franchise centres on a group of specialists directed by a wise sensei toward a common goal, repeatedly encountering named antagonists along the way before facing the main villain. The pipeline has exactly this shape. | Character | Stage | Task | | Splinter | Narrator | Introduces each stage; guides the user | | April O'Neil | Logging (APRIL_*) | Chronicles every step with ==> lines | | Splinter | Stage 1 | Structural validation ? a master checks personally | | Raphael | Stage 2 | Spell check ? the sceptic who questions every word | | Leonardo | Stage 3 | HTML assembly ? disciplined structure | | Donatello | Stages 3 & 5 | AWK converters ? the technologist behind the scenes | | Michelangelo | Stage 4 | Gopher ASCII ? strips all formality into plain text | | Krang | Stage 5 | PDF via groff ? the disembodied brain applies typesetting | | Bebop | Stage 6 | Gophermap update ? chaotic energy, correctly directed | | Rocksteady | Stage 7 | Sitemap regeneration ? methodical, one entry at a time | | Baxter Stockman | Stage 8 | Permission verification ? compound eyes miss nothing | | Shredder | Orchestrator | The villain who makes everything happen | Splinter is rendered as a 12-line ASCII rat in a custom cowsay(1) cow file. The cow file is generated at runtime into the script's temp directory ($TMP/rat.cow). No external file is required; the art is embedded as a shell heredoc with backslashes doubled for Perl heredoc interpolation rules. --- IV. Architectural Overview 4.1 Single-File Design shredder.sh is a single POSIX sh file. All logic ? validation, conversion, PDF generation, gophermap management, sitemap regeneration, permission checking, narration ? lives in one file. There are no sourced libraries, no helper scripts, no configuration files. Site-specific parameters (hostnames, paths, URLs) are declared as a single variable block near the top and are the only values a new user needs to edit before first use. 4.2 Preconditions The following conditions must hold before the pipeline will run to completion: 1. The input file exists and contains at least one # Title line. 2. The HTML template exists at ~/html/template.html (overrideable with the -t flag). 3. If ~/gopher/gophermap exists, it contains a line reading exactly #top. If the file does not exist, the script creates a skeleton. 4. All utilities listed in $2.2 are present on $PATH. 5. The user has write access to ~/html/ and ~/gopher/. The script validates preconditions 1 and 2 explicitly before any stage runs. Precondition 3 is checked and remedied inside BEBOP. Preconditions 4 and 5 are assumed ? the script targets a known environment. 4.3 Temporary Workspace A single temp directory is created by mktemp -d /tmp/shredder.XXXXXX early in the script ? before argument parsing ? so that the rat.cow file is available for the first Splinter narration. A trap on EXIT, INT, and TERM removes the entire directory on script termination. All intermediate files (HTML body fragments, groff source, gophermap scratch files) are written into this directory and cleaned up automatically. 4.4 Output Artefacts Given an input file with heading # My Post run on 2026-05-19: | Artefact | Path | | HTML page | ~/html/2026-05-19-My-Post.html | | Gopher text | ~/gopher/2026-05-19-My-Post.txt | | PDF (Gopher) | ~/gopher/2026-05-19-My-Post.pdf | | PDF (web) | ~/html/2026-05-19-My-Post.pdf | | Gophermap | ~/gopher/gophermap (updated in place) | | Sitemap | ~/html/index.html (fully regenerated) | --- V. The Pipeline: Functions in Sequence The main controller function SHREDDER() calls eight stage functions in fixed order. Each must complete before the next begins. SHREDDER() ? ?? 1. SPLINTER() structural validation ?? 2. RAPHAEL() interactive spell check ?? 3. LEONARDO() HTML assembly ? ?? DONATELLO_html_body() awk block-level converter ? ?? DONATELLO_inline_sed() sed inline converter ?? 4. MICHELANGELO() Gopher ASCII text ?? 5. KRANG() PDF via groff -ms ? ?? DONATELLO_ms_body() awk groff-ms converter ?? 6. BEBOP() gophermap entry insertion ?? 7. ROCKSTEADY() sitemap regeneration ?? 8. BAXTER_STOCKMAN() file permission verification Stage 1 ? SPLINTER: Structural Validation Counts # heading lines with grep -c. Counts ` code-fence lines; verifies the count is even (balanced open and close fences). Calls APRIL_die on failure, which prints to stderr and exits with status 1. Stage 2 ? RAPHAEL: Spell Check Strips Markdown syntax with a sed pass into a temp file, then pipes through BSD spell(1). If misspellings are found, they are displayed and the user is prompted to continue (y) or abort (N). This is the first interactive branch point and the only stage that can abort the pipeline with user consent. Stage 3 ? LEONARDO: HTML Assembly Calls DONATELLO_html_body (awk) then DONATELLO_inline_sed (sed) to produce a body fragment. Extracts