Traditional Forths often provided simple editors oriented around blocks. A standard block is a single unit of text, with 16 lines of 64 symbols per line, or 1024 characters. While far less common now, blocks are still useful, and RETRO has had some support for them in most of its incarnations.
With my recent interest in Gopher, I've decided that my next take on blocks will use Gopher.
So the basic goal of this is to provide a Gopher server capable of transfer and update of blocks, and also a means of browsing the blocks via Gopher. As with all of my recent servers, this will run under tui, tcpserver or inetd.
The basic configuration settings are the number of blocks (MAX-BLOCKS) and the path to the block file (including the file name). This is also where the server URL (or IP) and port are set.
First up are a scratch variable (FID) to hold the file ID for use with reads/writes, and a safe buffer to store the currently loaded block. I will use the buffer: namespace for interacting with the block, so it needs to be one cell longer than the actual data length to account for the final ASCII NUL terminator.
block:locate moves the index in the blockfile to the actual starting point for a particular block.
block:copy copies the data for the current block into the Block buffer.
The top level block:get word sets the current buffer to Block, then loads the block file and copies the requested block into the buffer. It returns the address of the Block buffer.
block:set writes a string into a block. The string can be longer than a block, in which case it writes to subsequent blocks.
To be able to browse the blocks, we first need a means of displaying a top level index (returned when the Gopher client sends a request as an empty selector string).
I'll use generate-index for this. A Gopher directory line looks like:
The type of interest here is:
0 plain text
I define generate-entry to make a line for a block. It takes a description and selector and uses the SERVER and PORT variables to construct the line.
With this it's easy to define generate-index using a loop to make a usable directory index listing all blocks.
Displaying a block as plain text is very easy. Using block:get to fetch the data, it's just two loops (one for each line, one for each charaacter) displaying the characters and newlines as needed.
Tūporo decides what to do based on the selectors passed to it. These are what I will recognize:
/ directory index of all blocks /nnnn block #nnnn (as formatted text data) /r/nnnn block #nnnn (as raw text data) /s/nnnn/text change block #nnnn to specified raw text data
I have a Selector buffer for storing the selector the user passes in. This is sized to be big enough for the incoming block data (if using /s) with room to spare.
The prefix word returns the first two characters of the selector. This will be enough to identify what type of request we are dealing with.
raw-block returns a raw, unformatted block as text data. This will correspond to /r/nnnn selectors.
set-block updates a block with new text. This selector takes a form:
It's probably not a good idea to leave this exposed on a public server as there is no means provided of restricting writes using it.
And handle-block uses display-block to return a formatted text block when browsing.
The top level handle word decides how to handle each selector using the results of prefix. Selectors that don't match up to one of the handled ones just return a directory listing.
And finally, a quick bit from Atua to read in the selector and pass it to handle
It'd probably be a good idea to add some authentication so unknown users can't write changes to the block store.
Other than that, it's a simple, clean system for exposing a blockfile via Gopher.
Copyright (c) 2017, Charles Childers
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.