Ken Johnson 57a6c1d854 Milestone 0.3.1 (partial): HWM for Squish
Adds Squish HWM via the .SQL lastread file (CRC32-keyed by
lowercased username, identical layout to JAM's .JLR per
Squish.doc).

src/formats/ma.fmt.squish.pas:
- New SqLastRead record type matching the Squish.doc spec.
- TSquishBase gains FSqlStream (lazy) + EnsureSqlStream +
  GetLastRead/GetHighRead/SetLastRead methods, mirroring JAM's
  .JLR pattern.
- Close releases the lastread stream alongside .SQD/.SQI.

src/formats/ma.fmt.squish.uni.pas:
- TSquishMessageBase wires DoGetHWMByName / DoSetHWMByName,
  reusing TJamBase.CalcUserCRC for the (shared) CRC32 algorithm.
- DoSetHWMByName preserves HighReadMsg monotonicity.

src/ma.lock.pas:
- TMessageLock gains optional APreserveSentinel constructor flag.
- Release no longer unlinks the sentinel when the flag is set.
- Required for Squish because the .SQL file is BOTH the lock
  sentinel and the lastread store; deleting it on lock release
  would wipe HWM data on every Open/Close cycle.

src/ma.api.pas:
- TMessageBase.Create passes APreserveSentinel = (Format = mbfSquish).

src/formats/ma.fmt.squish.pas (low-level open):
- EnsureSqlStream uses fpOpen directly on Unix instead of FPC's
  FileOpen wrapper. FileOpen defaults to a fpflock that conflicts
  with ma.lock's existing advisory lock on the same file (EAGAIN
  on every attempt). fpOpen bypasses the auto-flock; cross-process
  safety lives in ma.lock and doesn't need duplicating here.

tests/test_hwm.pas:
- New SeedSquish helper.
- TestSquishCapability + TestSquishSetGetPersistence verify
  capability flag, set/get round-trip, multi-user independence,
  and persistence across Open/Close.

Hudson, GoldBase and EzyCom were originally scoped for this
milestone but their per-(user, board) lastread layout needs more
design work (the LASTREAD.BBS format is shared across all 200
boards in a single per-user record, and TMessageBase doesn't
carry a "board" context). Deferred to a follow-up milestone.
Wildcat (SDK call) coming in 0.3.2.

Suite: 38/38 across 9 programs (test_hwm now 9/9 with Squish
coverage).
2026-04-17 16:01:38 -07:00

fpc-msgbase

A unified Free Pascal library for reading and writing classic BBS message bases.

Implements every supported format from the FTSC specifications and the original format authors' published documentation, behind one polymorphic API (TMessageBase). BBS software, mail tossers, message editors, and utilities can target a single interface regardless of the underlying format on disk.

Supported formats

Format Files Backend unit
Hudson MSGINFO/IDX/HDR/TXT/TOIDX.BBS ma.fmt.hudson.pas
JAM *.JHR *.JDT *.JDX *.JLR ma.fmt.jam.pas
Squish *.SQD *.SQI *.SQL ma.fmt.squish.pas
FTS-1 MSG numbered *.MSG per directory ma.fmt.msg.pas
FTN PKT *.pkt (Type-2 / 2+ / 2.2) ma.fmt.pkt.pas
PCBoard *.MSG + *.IDX ma.fmt.pcboard.pas
EzyCom MH#####.BBS / MT#####.BBS ma.fmt.ezycom.pas
GoldBase MSGINFO/IDX/HDR/TXT/TOIDX.DAT ma.fmt.goldbase.pas
Wildcat 4 WC SDK databases ma.fmt.wildcat.pas

Features

  • One TMessageBase abstract class — read, write, pack, reindex through the same methods regardless of format.
  • Lossless two-area message model. TUniMessage = Body (just the message text) + Attributes (key/value bag holding from/to/subject/dates/ addresses/MSGID/SEEN-BY/PATH and per-format extras). Same shape as RFC 822 email. Round-trip preservation enforced by the test suite.
  • Capabilities APIbase.SupportsAttribute('attr.returnreceipt') lets UIs hide controls the underlying backend has no slot for. Each backend publishes its key list via ClassSupportedAttributes. Full per-format matrix in docs/attributes-registry.md.
  • Layered locking: in-process TRTLCriticalSection + cross-process advisory lock (fpflock on Unix, LockFileEx on Windows, .LCK sentinel fallback)
    • the existing fmShareDenyWrite / fmShareDenyNone share modes.
  • Event hooks for logging, progress, and status reporting.
  • TPacketBatch worker pool for tossers that need to process many .pkt files concurrently while serialising writes per destination base.
  • Path / filename auto-derivation per format from a base directory plus optional area tag.

Building

Native Linux:

fpc -Fusrc -Fusrc/formats examples/example_read.pas

Lazarus package:

lazbuild fpc-msgbase.lpk

The repo includes a fpc.cfg template covering the multi-target build (i386-go32v2, i386-win32, i386-linux, i386-os2).

Layout

src/                ma.api, ma.types, ma.events, ma.lock, ma.paths, ma.batch
src/formats/        ma.fmt.<format>.pas — one per supported format
docs/               architecture, locking semantics, format notes
tests/              FPCUnit tests, sample data
examples/           small CLI programs that double as smoke tests

Unit-name convention follows the Fimail style: ma.<category>.<name>.pas (ma. is the library's namespace prefix; the project was originally named message_api, hence ma). All units use {$mode objfpc}{$H+}.

Documentation

Status

Early development. APIs may move until 1.0.

0.2 is a breaking change vs 0.1. TUniMessage lost its 13 named fields (WhoFrom/WhoTo/Subject/MsgNum/Attr/etc.) in favour of a strict Body + Attributes two-area model. Migration:

{ before }                       { after }
msg.WhoFrom                       msg.Attributes.Get('from')
msg.Subject := 'foo';             msg.Attributes.SetValue('subject', 'foo');
msg.Attr := MSG_ATTR_LOCAL;       msg.Attributes.SetBool('attr.local', true);
msg.OrigAddr                      msg.Attributes.GetAddr('addr.orig')
msg.DateWritten                   msg.Attributes.GetDate('date.written')

This change makes the unified API lossless — kludges (MSGID, SEEN-BY, PATH, etc.) round-trip cleanly, where 0.1 silently dropped them. See docs/attributes-registry.md for the full key catalog.

Format backends are spec-driven implementations validated against real-world sample bases.

Description
Unified Free Pascal library for reading/writing classic BBS message bases: Hudson, JAM, Squish, FTS-1 *.MSG, PCBoard, EzyCom, GoldBase, Wildcat 4.
Readme 1.2 MiB
Languages
Pascal 99.5%
Shell 0.5%