From ae9c64df188b23f348aa20d5b6eb2dbd08475822 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Mon, 25 Sep 2000 08:40:02 +0000
Subject: [PATCH] Initial check-in:  v2.30b (for DOS and OS/2) release (circa
 1997).

---
 src/sbbs2/smb/docs/smb.hst     |  327 +++
 src/sbbs2/smb/docs/smb.src     | 3592 ++++++++++++++++++++++++++++++++
 src/sbbs2/smb/docs/smb_read.me |   64 +
 3 files changed, 3983 insertions(+)
 create mode 100644 src/sbbs2/smb/docs/smb.hst
 create mode 100644 src/sbbs2/smb/docs/smb.src
 create mode 100644 src/sbbs2/smb/docs/smb_read.me

diff --git a/src/sbbs2/smb/docs/smb.hst b/src/sbbs2/smb/docs/smb.hst
new file mode 100644
index 0000000000..c13f8c1a98
--- /dev/null
+++ b/src/sbbs2/smb/docs/smb.hst
@@ -0,0 +1,327 @@
+Synchronet Message Base Specification Modification History
+==========================================================
+
+******************************
+01/20/94 Initial Release v1.00
+******************************
+
+Quite a few spelling errors in SMB.TXT corrected.
+
+Estimated maximum number of messages (in Introduction of SMB.TXT) formula
+corrected (1500 rounded to nearest 256 is 1536, not 2048).
+
+Pages 32 and 38 (SENDEREXT and RECIPIENTEXT header field types) incorrectly
+stated the index record contains CRC-32s, when in fact the index contains
+CRC-16s of the agent names or extensions.
+
+FORWARDTO header field types incorrectly stated they were for replies, rather
+than forwarding instructions.
+
+Trigger header field types incorrectly stated they were "attachments" rather
+than triggers.
+
+Bug fixed in smb_open() function in smblib.c: was allocating buffer via
+setvbuf for wrong file pointer when opening header file. Caused loss of 
+memory (2k) everytime smb_open() was called.
+
+Bug fixed in smb_getmsghdr() function in smblib.c: msg->from_agent,to_agent,
+and replyto_agent were not initialized properly.
+
+Added from_ext, to_ext, and replyto_ext fields to msg struct. Initialized
+with call to smb_getmsghdr() function. These are convenience pointers for
+the to, from, and replyto agent extensions (user numbers, usually).
+
+Fixed a nasty bug in the smb_freemsgdat() function, causing odd values in the
+SDA file and cross-linked messages when fast allocation mode was not used for
+new messages.
+
+smb_putmsghdr() function replaced by smb_putmsg() and it calls smb_putmsghdr()
+and smb_putmsgidx() - previously smb_putmsghdr() actually wrote the header
+and the index. So you must change any occurances of smb_putmsghdr() to
+smb_putmsg() if you intend to write to both the index and header records.
+
+smb_open() now takes a "retry_time" parameter (number of seconds) to lock
+and read the message base header and verify the "id" and "version". Added
+new error codes: -1 indicates failure to lock message base header, -2 indicates
+id mismatch, and -3 indicates incompatible message base version.
+
+rewind() and clearerr() calls added to library functions to eliminate the
+problem of "sticky" error flags when using fread() and fwrite(). Reordered
+the use of chsize() to force seeking (with fseek()) before using the file
+descriptor operation (chsize()) would would fail under some circumstances.
+
+smb_getmsgidx() function now uses "fast" message number look-up algorythm.
+
+Added smb_getlastidx() function to retrieve the last index in the message
+base.
+
+Allocation strategy for hfield and dfield elements of the smbmsg_t structure
+were changed to require less memory. Rather than an array of pointers (pointer
+to a pointer) of type hfield_t or dfield_t, they are simply an array (pointer)
+of type hfield_t or dfield_t. The only change you need to make are any
+references to "smbmsg_t.hfield[x]->etc" to "smbmsg_t.hfield[x].etc".
+
+Added several new error codes to smb_getmsghdr(). -7 indicates missing one
+of the three mandatory header fields (sender, recipient, or subject). -8
+indicates that total_dfields element of smbhdr_t is incorrect (extends beyond
+smbhdr_t.length). -9 indicates incompatible header version.
+
+New error code to smb_addcrc(), -4 indicates error getting length of file.
+
+smb_putmsghdr() returns -2 on failure write header, -3 on failure to write
+dfield, -4 on failure to write hfield (fixed), -5 on failure to write hfield
+(data), and -6 on failure to pad record with NULLs.
+
+Bugs fixed in sample conversion source code and SMBUTIL: Overwriting first
+two bytes of next record when using self-packing storage, SBBSFIDO would
+crash on blank kludge lines, SEEN-BYs weren't converted properly by SBBSFIDO,
+and more.
+
+*** IMPORTANT ***
+Index format changed, added message time field to idxrec_t for fast pointer
+manipulation by (import) date/time stamp. This is SMB format v1.10 and is
+not compatible with the v1.00 format. A conversion program (100TO110.EXE)
+is included with source code for converted any existing message bases.
+
+CHKSMB, a utility for checking message bases for corruption, included with
+SMBLIB.
+
+SMBUTIL and CHKSMB both accept wildcard arguments for processing multiple
+message bases.
+
+idxrec_t.to and idxrec_t.from may contain USER NUMBER instead of CRC of user
+names. This implementation is currently specific to E-mail boxes on Synchronet
+BBS. E-mail from non-local users are stored with a "from" field of 0.
+
+**********************
+03/28/94 Release v1.10
+**********************
+
+Forgot to include this file (oops).
+
+***********************
+03/28/94 Release v1.10a
+***********************
+
+Fixed problem with buffered fread() routine attempting to read into a locked
+regions.
+
+Added smb_stack() function for saving (pushing) currently open message base
+and ability to pop it off later.
+
+Added fix for keeping original sender information in smbmsg_t structure when
+a message has been forwarded.
+
+CHKSMB now correctly support multiple destination messages (without reporting
+missallocated active data blocks) and now reports packable bytes available.
+
+SMBUTIL can now conditionally compress a message base, only if so many K are
+compressable.
+
+Fixed problem with incorrect UTC value.
+
+Added NET_WWIV network type.
+
+**********************
+06/02/94 Release v1.11
+**********************
+
+Created Watcom compatible MAKEFILE for DOS, 32-bit DOS, and 32-bit OS/2
+versions of SMBUTIL.
+
+Enhanced macro definitions in SMBDEFS.H for MALLOC, FREE, and REALLOC to
+support Watcom and other compilers other than Borland. Also added LMALLOC
+and LFREE macros for large allocation (>64k) operations.
+
+Fixed problem with multiple calls to setvbuf() when using Watcom C libraries.
+Memory allocated by setvbuf() would never be released. To work around this
+Watcom C bug, a static buffer, shd_buf[], is now used instead.
+
+Fixed bug in smb_getmsghdr() that would cause it to return -6 when attempting
+to allocate 0 length header fields.
+
+Added LZH.C to SMBLIB for message data compression/decompression.
+
+Removed the 16-bit CRC functions from SMBUTIL and put them in CRC16.C (now part
+of SMBLIB) for easier integration into other applications.
+
+Added Hyper Allocation storage method to specification: smbstatus_t.reserved
+changed to smbstatus_t.attr and SMB_HYPERALLOC bit is set when Hyper Allocation
+storage is used for a message base. This is the only change between v1.10 and 
+v1.20 of the message base format. No other bits in smbstatus_t.attr are 
+currently defined. See the specification for definition of the Hyper Allocation
+storage method.
+
+Changed SMB Storage and Retrieval Protocol chapters in the specification to
+Pseudo-code examples.
+
+Added SMBLIB Storage and Retrieval C example chapters to the specification.
+
+Added field descriptions for message base header record fields and Status Info
+(base header #1) record fields to the specification.
+
+Changed SMBLIB smb_addmsghdr() 'fast' argument to 'storage' to specify the
+storage method to use (either SMB_SELFPACK, SMB_FASTALLOC, or SMB_HYPERALLOC).
+Previous usage (0 for self-pack, 1 for fast) still compatible.
+
+Added smb_hallocdat() and smb_hallochdr() to SMBLIB functions for the Hyper
+Allocation storage method.
+
+Added SMB_STACK_XCHNG operation to smb_stack() function, to exchange the top
+of the stack with the currently open message base.
+
+**********************
+02/14/95 Release v1.20
+**********************
+
+smb_close() was modified in the following ways:
+	Only attempts to close non-NULL file pointers
+	If shd_fp is currently open, calls smb_unlocksmbhdr() before closing
+		(just in case it was left locked by application)
+
+smb_open() was modified in the following ways:
+	Initializes all file pointers to NULL
+	calls smb_close() if any of the files couldn't be opened
+
+smb_locksmbhdr() was modified in the following ways:
+	If lock fails, attempts to unlock the header before next lock
+		attempt (just incase application attempts to lock a locked
+		header without first unlocking it)
+
+smb_lockmsghdr() was modified in the following ways:
+	If lock fails, attempts to unlock the header before next lock
+		attempt (same reason stated above)
+
+CHKSMB (1.21) was modified in the following ways:
+	Tests for valid translations strings in the .SDT files
+	Displays totals for bytes used by header and data blocks, bytes used
+		by deleted messages, and bytes saved by LZH compression
+
+SMBUTIL (1.21) underwent some minor cosmetic changes.
+
+***********************
+03/18/95 Release v1.20a
+***********************
+
+SMBUTIL (1.22) now compiles correctly under Watcom, using correct time zone
+information. SMBUTIL maint() will no longer delete messages if max_age is
+specified in the SMB header and the when_imported time for a message is in the
+future.
+
+Fixed problem in LZH.C that would cause lzh_decode() to generate exception
+errors when compiled with a 32-bit compiler.
+
+CHKSMB (1.22) now supports /E command line switch to display extended
+information about corrupted messages. CHKSMB.C is now Watcom compatible.
+
+Added fflush() calls to all smblib functions that write to the SDT, SHA, or SDA
+files. This eliminates the problem of duplicate data offsets (data blocks for
+multiple messages pointing to the same location) causing corrupted SDT files
+(unsupported translation types reported by CHKSMB).
+
+Added fflush() call after writing to SDT file in SMB_PUT example in SMB.TXT.
+
+Fixed example *2SMB.C files to be compatible with SMBLIB v1.20 smb_create()
+function.
+
+Fixed smb_hallocdat() function prototype to stop redeclaration warnings.
+
+Minor typos in specification fixed.
+
+***********************
+04/24/95 Release v1.20b
+***********************
+
+Added SMB_EMAIL (1<<0) attribute definition for the message base status header
+to specify a local e-mail message base where index "to" and "from" fields are
+stored as user numbers, not CRC-16s (this is the only change between v1.20
+and v1.21 of the SMB format).
+
+Version 1.21 of FIXSMB no longer needs the "/M" switch to correctly fix a
+Synchronet e-mail message base (as long as the SMB_EMAIL status attr is set).
+
+Version 1.23 of SMBUTIL can now import text into a Synchronet e-mail message
+base (as long as the SMB_EMAIL status attr has been set). It will ask for the
+sender and recipient user numbers. Fixed an apparently nasty bug that would
+cause damage to message bases when (I)mporting (check for SMB_HYPERALLOC
+attribute was backwards). Excess baggage in the SMBUTIL source code was also
+removed (no longer supports /L, /F, and /D command line switches). Only
+supports self-packing and hyper-allocated message bases (it will not damage
+fast allocated bases, however). "SMBUTIL M" now correctly handles message bases
+that contain messages that have been pre-flagged for deletion (MSG_DELETE attr
+set).
+
+Changed smb_open(int retry_count) function: if retry_count is 0, then a "fast
+open" method is used which doesn't lock and read the status header to check
+for compatibility and validity of message base.
+
+Added SMBLIB_VERSION definition (string constant) to contain the current
+version and revision (if applicable) of the SMB library. This version number
+may not always be the same as SMB_VERSION (version of the SMB format) or the
+current version of the specification. It may be helpful to include a display
+of this version number somewhere in your application (i.e. printf("SMBLIB v%s"
+,SMBLIB_VERSION);).
+
+Changed smb_stack() example in the Performance Issues chapter to keep two
+message bases always open using SMB_STACK_XCHNG.
+
+Fixed bug in SMBUTIL that would cause an infinite loop when analysing an
+extrememly corrupted HyperAllocated message base.
+
+Fixed bug in smb_stack() function when using SMB_STACK_XCHNG operation. Would
+previously set current message base to an undefined message base, rather than
+the message base on top of the stack (last pushed or exchanged).
+
+CHKSMB v1.22 compiled in the 1.20b release did not support wildcards (wasn't
+linked with WILDARGS.OBJ) - this has been fixed and is the only change in
+CHKSMB v1.23.
+
+**********************
+05/31/95 Release v1.21
+**********************
+
+Add uchar forwarded element to smbmsg_t structure. This variable is initialized
+by the smb_getmsghdr() function. If the message has been forwarded, it will
+be set to non-zero.
+
+Fixed bug in LZH.C which caused lzh_decode() to produce incorrect results
+(usually t's and spaces) when compiled with a 32-bit compiler.
+
+Added HUGE16 and FAR16 macros to SMBDEFS.H for huge and far pointers for
+16-bit compilers only (default pointer type for 32-bit compilers).
+
+Fixed bug in FIXSMB.C that would initialize the SDA file to the wrong length.
+
+Added net type NET_MHS for MHS gateways.
+
+SMBUTIL (1.24) will no longer free deleted headers when maintaining a message
+base if the /A (no analysis) command line switch is used. This speeds up the
+message base maintenance/pack operation significantly by using: SMBUTIL /A MP
+
+***********************
+08/31/95 Release v1.21a
+***********************
+
+All smb_*() functions changed to accept pointers to an smb_t structure. This
+eliminates the use of global variables (making DLLs difficult or impossible
+to program). A pointer is used even if the function doesn't modify the data
+to maintain a consistent API and compatibility with languages that don't
+support the passing of user defined types by value (e.g. Visual Basic).
+
+The smb_t structure contains a retry_time element that defines the maximum
+number of seconds to wait while trying to open a locked message base or record.
+If this element of the passed smb_t variable is 0, a default value of 10
+seconds is used.
+
+Added smb_getmsgtxt() and smb_freemsgtxt() functions to SMBLIB.C (only included
+if SMB_GETMSGTXT is defined).
+
+*******************
+11/18/95 Beta v2.00
+*******************
+
+Bug in smb_getmsgtxt() with message data field lengths less than 2.
+
+**********************
+03/22/96 Release v2.01
+**********************
diff --git a/src/sbbs2/smb/docs/smb.src b/src/sbbs2/smb/docs/smb.src
new file mode 100644
index 0000000000..35fe9b22c9
--- /dev/null
+++ b/src/sbbs2/smb/docs/smb.src
@@ -0,0 +1,3592 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                     Synchronet Message Base Specification
+								 Version 1.21
+							   Updated 08/31/95
+
+						Copyright 1995 Digital Dynamics
+
+                                  PO Box 501
+                             Yorba Linda, CA 92686
+
+				 Voice: 714-529-6328   BBS: 714-529-9525 V.32/V.32bis
+				   FAX: 714-529-9721		714-529-9547 V.FC
+				  Fido: 1:103/705	   ftp: netcom.com /pub/sb/sbbs
+
+Table of Contents
+=================
+&&Contents
+
+Introduction....................................................@@INTRO___
+Implementation Levels...........................................@@IMPLEVEL
+Definitions.....................................................@@DEFINES_
+        Acronyms................................................@@ACRONYMS
+        Data Types..............................................@@DATATYPE
+File Formats....................................................@@FILEFORM
+        Index.....................(*.SID).......................@@SID_FORM
+        Header....................(*.SHD).......................@@SHD_FORM
+        Header Allocation.........(*.SHA).......................@@SHA_FORM
+        Data......................(*.SDT).......................@@SDT_FORM
+        Data Allocation...........(*.SDA).......................@@SDA_FORM
+        CRC History...............(*.SCH).......................@@SCH_FORM
+Header Field Types..............................................@@HFIELD_T
+Data Field Types................................................@@DFIELD_T
+Messsage Attributes.............................................@@ATTRBITS
+Translation Types...............................................@@XLATTYPE
+Agent Types.....................................................@@AGENTTYP
+Network Types...................................................@@NETWORKS
+Media Types.....................................................@@MEDIATYP
+Message Storage Pseudo Code.....................................@@STORPCOD
+Message Retrieval Pseudo Code...................................@@READPCOD
+SMBUTIL.........................................................@@SMBUTIL_
+CHKSMB..........................................................@@CHKSMB__
+FIXSMB..........................................................@@FIXSMB__
+SMBLIB (C library)..............................................@@SMBLIB__
+		Data Types and Constants..(SMBDEFS.H)...................@@SMBDEFS_
+        Global Variables..........(SMBVARS.C)...................@@SMBVARS_
+        Function Prototypes.......(SMBLIB.H)....................@@SMBLIB.H
+        Library Functions.........(SMBLIB.C)....................@@SMBLIB.C
+		Miscellaneous.............(CRC*.* and LZH.*)............@@SMB_MISC
+SMBLIB Storage Example..........................................@@SMB_PUT_
+SMBLIB Retrieval Example........................................@@SMB_GET_
+SMBLIB Performance Issues.......................................@@PERFORM_
+Bibliography....................................................@@BIBLIOGR
+Implementations.................................................@@IMPLEMEN
+
+Introduction
+============
+&&Introduction
+$$INTRO___
+
+Q. What is SMB?
+
+A. SMB (Synchronet Message Base) is a technical specification for the storage
+   format of electronic mail messages. These e-mail messages may all be
+   contained in one database, or, more commonly, separated into catagorized
+   databases. These message databases (or message bases) are also referred to
+   as "sub-boards", "forums", "conferences", and "SIGs". The messages may be
+   directed to an individual person, sent to a group of individuals, or sent
+   to everyone who can read messages in that message base. Messages may be
+   created and read soley at one physical location, or imported from and
+   exported to a message network that may span continents. Message bases that
+   are connected to a message network are often called "echoes".
+
+
+Q. Why SMB?
+
+A. The Synchronet Message Base is designed to store high volumes of messages
+   while maintaining optimum search, retrieval, and creation performance.
+   These messages are not limited to mere text. In addition to text, SMB
+   defines the storage of digitized sound, MIDI, graphics, fonts, animation,
+   as well as other multimedia data and triggers for localized multimedia.
+   SMB thrives on a multi-user environment where messages are being created,
+   read, modified, and deleted by multiple tasks simultaneously. With the
+   large message networks of today being the rule, rather than the exception,
+   and high volumes of messages being imported on a daily, sometimes hourly
+   basis, creation and deletion speed is of the utmost importance. This is
+   where SMB really shines. Being extensible enough to handle message formats
+   from networks of today and tomorrow, and fast enough to import more messages
+   that humanly readable, the SMB format will more than meet your message
+   storage needs.
+
+
+Q. Why a specification?
+
+A. Message bases are often accessed and modified by a number of different
+   programs. Often these programs are developed by individuals or companies
+   other than the original designer of the message base format. This
+   specification is an attempt to aid developers in creating programs that
+   access or modify a message base stored in the SMB format.
+
+
+Q. Who can use this specification?
+
+A. Anyone that has interest in the Synchronet Message Base format at either
+   an educational or professional level. Specifically, software developers
+   interested or currently involved in the development of message readers,
+   editors, echomail (toss/scan) programs, message transfer agents (MTAs),
+   network gateways, and bulletin board systems. Much of the information in
+   this specification is intended for those with preexisting programming
+   knowledge, so those with little or no programming experience may find it
+   hard to comprehend.
+
+
+Q. What does the SMB specification include?
+
+A. The text you are reading is part of the SMB specification: a single text
+   document that defines the storage format of each of the six files of an
+   SMB format message base and how they are related to each other.
+
+   Included with this specification is C source code to be used as an example
+   to programmers of how to access an SMB format message base and public domain
+   library functions (SMBLIB) that can be compiled and linked into programs
+   that access an SMB format message base developed by third parties. An SMB
+   utility program (SMBUTIL) is also included with C source code as an example
+   of how to use the SMBLIB functions.
+
+
+Q. Where did the SMB specification come from?
+
+A. Digital Dynamics (southern California based software development company)
+   released "Synchronet Multinode BBS Software Version 1a" in June of 1992 as
+   one of the first BBS packages to be designed from the ground-up to operate
+   in a multinode environment with incredible speed and reliability, with a
+   large suite of multinode specific features and design innovations.
+
+   The original message base format was designed with localized messaging and
+   low volume message networks in mind. By January of 1993, it was clear that
+   high volume message networks (FidoNet, RelayNet, Usenet, etc.) were the
+   preference of most BBS users and a new message base format was required to
+   allow for high volume message storage, improved storage, retrieval, and
+   maintenance performance, as well as lower storage space requirements.
+
+   Rather than introduce another new message format, Digital Dynamics sought
+   to implement an existing public specification for a format that would meet
+   current and future message storage needs. More than a few specifications
+   were seriously considered at one time or another, but after careful
+   examination, design flaws and lack of extensibility eliminated them from the
+   long term plans of Digital Dynamics and Synchronet BBS Software. Thus began
+   the design of the "Synchronet Message Base" (SMB) format.
+
+   At the request of many message related program developers, Digital Dynamics
+   created and released the SMB specification before the release of "Synchronet
+   Version 2.00" to allow lead-time on developing support programs for the new
+   format.
+
+   Digital Dynamics strongly encourages developers of message related programs
+   (including software that directly competes with Synchronet or other Digital
+   Dynamics products) to implement support for SMB. Though this is a public
+   specification and Digital Dynamics encourages developer suggestions, it will
+   remain under the sole control of Digital Dynamics unless specifically stated
+   otherwise in a future revision of this specification.
+
+   Digital Dynamics requests that any organizations that wish to adopt or
+   ratify this specification, in part or whole, notify Digital Dynamics through
+   any of the contact methods listed at the beginning of this document.
+
+
+Q. How does SMB store messages?
+
+A. Each message base is stored in a set of binary files. This set consists
+   of between three and six files depending the storage method used. The base
+   filename (maximum of eight characters under DOS) is the same for all six
+   files of the same message base and unique amoung the filenames of other
+   message bases in the same directory. The six files each have a different
+   three character extension. The first character of the extension is always
+   the letter 'S' (for SMB), while the second and third characters define the
+   contents of the file.
+
+   Two of the six files associated with each message base are not recreatable
+   and therefore are the most important when considering data integrity. These
+   two files are the data file (with a .SDT extension) and header file (.SHD
+   extension). Both of these files use 256 byte blocks and have associated
+   block allocation tables (stored in .SDA and .SHA respectively) so that
+   deleted message blocks may be used by new messages without creating odd
+   sized unused 'holes' in the files. The block allocation table files (.SDA
+   and .SHA) can be recreated with the information stored in the header (.SHD)
+   file. When using Hyper Allocation storage method, the allocation files (.SDA
+   and .SHA) are not used.
+
+   For fast indexing, there is a small fixed length index file (with a .SID
+   extension). This file allows for the immediate location of message header
+   records based on sender's name or user number, recipient's name or user
+   number, subject, message number, or message attributes. This file can be
+   recreated with the data stored in the header (.SHD) file.
+
+   The last file is an optional CRC history (.SCH) file. It contains 32-bit
+   CRCs of a configurable number of messages imported or created locally. This
+   is to help eliminate duplicate messages created by user or program error.
+   The CRC history file can be recreated with the combination of information
+   stored in the data (.SDT) and header (.SHD) files.
+
+Q. How fast do messages import into an SMB message base?
+
+A. This is a very important question for systems for that import large volumes
+   of messages. Of course, the answer depends on the storage format which you
+   are importing from, the average length of messages, the design of the
+   program which is peforming the import process, as well as the hardware and
+   system software being used. What's important is that SMB will allow the
+   fastest import process possible with any given combination of the above
+   factors.
+
+   Since system storage capacity is rarely infinite, neither is the number
+   of messages which can be stored in a message base. System operators must
+   define the maximum number of messages to be stored in a message base, the
+   maximum age of the messages in that message base, or a combination of both.
+   When using the Self-packing storage method (defined later in this document),
+   the smaller the number of messages stored in a message base, the faster the
+   import process. The SMB format is flexible enough to support multiple levels
+   of import performance based on optimizations for storage space or speed.
+   Most system operators will almost invariably choose speed over space, but
+   which choices are available is determined by the importing program. This
+   specification defines three storage methods, from slowest to fastest:
+   Self-packing, Fast Allocation, and Hyper Allocation. Other options defined
+   in this specification may affect storage performance, including duplicate
+   message checking and message compression/encryption.
+
+
+Q. How much storage is required for an SMB message base?
+
+A. The biggest factor in determining storage requirements for a message base
+   is the maximum number of messages to be stored in the base (defined by the
+   system operator) and the average size of each message. The minimum required
+   storage for a message base is 32 bytes plus 532 bytes per message (plus four
+   bytes per message if duplicate message checking is used and three bytes
+   per message if Self-packing or Fast Allocation storage methods are used).
+
+   The SMB format was originally designed to be "self-packing", meaning purged
+   (deleted) message header and data blocks will be used automatically by new
+   messages. Relying solely on self-packing, an SMB format message base will
+   never "shrink" in size. This is not to say that it will continually "grow"
+   in size, but that without specific packing procedures, deleted message
+   blocks may remain unused for extended periods of time, meanwhile using some
+   amount of storage space that could be recovered using specific packing
+   procedures. The Fast Allocation and Hyper Allocation storage methods do not
+   use deleted message blocks for new messages so specific packing procedures
+   must be used if any messages are deleted and that storage space is to ever
+   be recovered.
+
+   Limiting the maximum age of messages in an SMB message base is another way
+   to control the storage requirements. While maximum message age definition is
+   optional, the definition of the maximum number of messages is not.
+
+Q. How many messages can be stored per SMB message base?
+
+A. Without considering storage limitations or message data lengths greater than
+   256, the theoretical maximum number of messages that can be stored in a
+   single SMB message base is 16.7 million. Considering the variable length
+   nature of message and header data, it is suggested that the system operator
+   allow no more than 1 million messages per base.
+
+   To determine an estimated maximum number of messages for a message base
+   using the average message data length as a factor, use the following
+   formula:
+
+   4.2 billion divided by the average message length rounded up to be evenly
+   divisible by 256.
+
+   If the average message data length is 1500 bytes, the estimated maximum
+   number of messages would be 2,734,375 (4.2 billion divided by 1536).
+
+   Implementations of this format may be further limited by available system
+   memory.
+
+Implementation Levels
+=====================
+&&Implementation Levels
+$$IMPLEVEL
+The SMB format can be implemented to varying degrees between programs without
+creating compatibilty issues. Rather than have developers specifically state
+which features they have and have not implemented, we have defined seven levels
+of implementation (represented by Roman numerals I through VII). For a program
+or software package to meet an implementation level, it must have all of the
+features listed for that level and all of those for each level below it. The
+minimum suggested imlementation is level I. The SMBUTIL program included with
+this specification is an example of a level I implementation with features
+from some of the higher implementation levels.
+
+Level I
+-------
+The minimum suggested level of implementation. Messages contain merely ASCII
+text displayable on an ANSI terminal. Messages can be added to the message
+base and if the maximum number of messages is exceeded, messages are removed
+or marked for deletion.
+
+Level II
+--------
+The addition of file attachments, multiple index/header entries per message
+(multiple destinations), multiple text bodies for the separation of message
+text and tag/origin lines (for example), forwarding, threading, and specific
+FidoNet kludge header field support makes this level of implementation more
+realistic for bulletin board system and EchoMail software implementation.
+
+Synchronet Multinode BBS Software v2.00 has a level II implementation of this
+specification.
+
+Level III
+---------
+This implementation adds support for translation strings defined later in this
+document for data compression, encryption, escaping, and encoding. This level
+is still limited to basic ASCII text and ANSI escape sequence entry and
+retrieval.
+
+Synchronet Multinode BBS Software v2.10 has a level III implementation of this
+specification.
+
+Level IV
+--------
+The storage and retrieval of embedded and attached images is added in this
+level of implementation. Supported images are limited to single binary or text
+data blocks that can be displayed or transferred to the user (automatically,
+or by request) if their display and translation protocols define specific
+support for the image type.
+
+Level V
+-------
+This level of implementation adds support for embedded and attached sound data.
+This includes digitized sound and MIDI data. Supported sounds are limited to
+single binary or text data blocks that can be played or transferred to the user
+(automatically or by request) if their presentation and translation protocols
+define specific support for the sound type.
+
+Level VI
+--------
+Localized sound and image data can be triggered by messages stored and
+retrieved in an implementation of this level.
+
+Level VII
+---------
+Complete multimedia support is reached in this implementation level with
+support for embedded and attached animation, sound, and video data.
+
+
+Definitions
+===========
+&&Definitions
+$$DEFINES_
+
+Control Characters
+------------------
+When specifying control characters (ASCII 1 through 31), the caret symbol "^"
+or the abreviation "ctrl-" followed by a character will be used to indicate the
+value. ^A is equivalent to ASCII 1, ^B ASCII 2, etc. The case of the control
+character is not significant (i.e. ^z and ^Z are equivalent). The control
+character ^@ (ASCII 0) will be specified as NULL or 0.
+
+
+Hexadecimal
+-----------
+Base sixteen numbering system which includes the digits 0-9 and A-F.
+Hexadecimal numbers are represented in this document with a prefix of "0x" or
+"\x" or a suffix of "h". Hexadecimal letter digits are not case sensitive
+(i.e. the number 0xff is the same as 0xFF).
+
+
+File dump
+---------
+When example file dumps are displayed, the format is similar to that of the
+output from the DOS DEBUG program. With the exception of the ASCII characters,
+all numbers are in hexadecimal.
+
+Offset    Byte values                                          ASCII characters
+
+000000	  53 4D 42 1A 10 01 20 00	F4 01 00 00 F4 01 00 00    SMB... .�...�...
+000010    20 00 00 00 D0 07 00 00   D0 07 00 00 00 00 00 00     ...�...�.......
+
+
+Bit values
+----------
+Bit (or flag) values are represented in C notation as (1<<x) where x is the bit
+number. (i.e. bit number 7 (1<<7) is the same as 0x80).
+
+
+Word storage
+------------
+All words (16-bit) and double words (32-bit) are stored in Intel 80x86 (little
+endian) format with bytes stored from low to high (reverse of the Motorola
+680x0 word storage format).
+
+A 16-bit word with the value 1234h is stored as 34h 12h.
+
+Translation strings
+-------------------
+Translation strings (xlat variables) are arrays of words (16-bit) in the order
+of the original storage translation. The last translation type is followed by a
+16-bit zero (defined later as XLAT_NONE). If there are no translations, then
+the first and only element of the array is XLAT_NONE.
+
+If multiple translations are used, the translation order must be reversed
+upon retrieval to obtain the proper data.
+
+
+Local e-mail
+------------
+When referring to the local e-mail message base of a Synchronet BBS, we are
+referring specifically the message base with the name "MAIL" stored in the
+"DATA" directory (e.g. \SBBS\DATA\MAIL).
+
+Messages stored in this message base are different in the following respects:
+
+	The SMB_EMAIL status header attribute is set ON
+	Hyper Allocation storage method is not supported
+	The "To" and and "From" fields of the message indexes do NOT contain CRCs
+
+Acronyms:
+========
+&&Definition of Acronyms
+$$ACRONYMS
+
+ANSI            American National Standards Institute
+ASCII           American Standard Code for Information Interchange
+BBS             Bulletin Board System
+C               The C programming language as defined by ANSI X3.159-1989
+CR              Carriage Return character (ASCII 13)
+CRC             Cyclic Redundancy Check
+CRC-16			Standard 16-bit CRC using 1021h polynomial (seed 0)
+CRC-32			Standard 32-bit CRC using EDB88320h polynomial (seed -1)
+CRLF            Carriage Return character followed by a Line Feed character
+FSC             FidoNet Standards Commitee (FTS proposal)
+FTN             FidoNet Technology Network
+FTS             FidoNet Technical Standard
+LF              Line Feed character (ASCII 10)
+QWK 			Compressed message packet format for message reading/networking
+RFC             Request for Comments
+SMB             Synchronet Message Base
+UT              Universal Time (formerly called "Greenwhich Mean Time")
+
+Data types
+==========
+&&Definition of Data Types
+$$DATATYPE
+
+uchar           Unsigned 8-bit value (0 through 255).
+                C example:
+
+                #define uchar unsigned char
+
+
+short           Signed 16-bit value (-32768 through 32767).
+				"short" is a C keyword indicating "short int".
+
+
+ushort          Unsigned 16-bit value (0 through 65535).
+                C example:
+
+                #define ushort unsigned short
+
+
+ulong           Unsigned 32-bit value (0 through 4294967295).
+                C example:
+
+                #define ulong unsigned long
+
+
+time_t          Unsigned 32-bit value.
+                Seconds since 00:00 Jan 01 1970 (Unix format).
+                Used for all time/date storage in SMB as part of the when_t
+                data type. This time format will support dates through the year
+                2105.
+                time_t is defined by ANSI C as a long (signed) which can
+                limit its date support to the year 2038 depending on the
+                library routines used.
+
+
+ASCII           String (aka character array) of 8-bit ASCII characters.
+                Characters with the bit 7 set (80h through FFh) represent
+                the IBM PC extended ASCII character set. When data or header
+                fields of this type are stored in the header, a NULL
+                terminator may or may not be present.
+                C example:
+
+                uchar str[80];
+
+
+ASCIIZ          ASCII string with (non-optional) NULL terminator.
+                C example:
+
+                uchar str[81];
+
+nulstr          ASCII string immediately terminated by NULL.
+                C example:
+
+                uchar *nulstr="";
+
+
+undef           Data buffer with undefined contents.
+                C example:
+
+                uchar buf[BUF_LEN];
+
+when_t          Date/Time stamp including time-zone adjustment information.
+                C example:
+
+                typedef struct {
+
+                    time_t  time;   // Time stamp (in local time)
+                    short   zone;   // Zone constant or Minutes (+/-) from UT
+
+                    } when_t;
+
+                time:
+
+                A time value of 0 is invalid and indicates an uninitialized
+                time stamp.
+
+				Time stamps are always stored in universal time. i.e.
+				Regardless of what the local time zone is, Jan 1st 1994 00:00
+				will always be stored as 2D24BD00h.
+
+                zone:
+
+				If the zone is in the range -720 to +720, it represents the
+				number of minutes east or west of UT. Values in this range
+				should only be used for time zones not otherwise represented
+				here.
+
+                If the zone is greater than 720 or less than -720, then the
+                following bits have special meaning:
+
+                (1<<12)         // Non-US time zone     (east of UT)
+                (1<<13)         // Non-US time zone     (west of UT)
+                (1<<14)         // U.S. time zone
+                (1<<15)         // Daylight savings
+
+                The lower 12 bits (0 through 11) contain the number of minutes
+                east or west of UT (not accounting for daylight savings).
+
+                If the time zone is one specified in the U.S. Uniform Time Act,
+                the following values represent the zone:
+
+                AST 0x40F0      // Atlantic             (-04:00)
+                EST 0x412C      // Eastern              (-05:00)
+                CST 0x4168      // Central              (-06:00)
+                MST 0x41A4      // Mountain             (-07:00)
+                PST 0x41E0      // Pacific              (-08:00)
+                YST 0x421C      // Yukon                (-09:00)
+                HST 0x4258      // Hawaii/Alaska        (-10:00)
+                BST 0x4294      // Bering               (-11:00)
+
+                With bit 15 set, the following values represent the same zone
+                with the presence of daylight savings:
+
+                ADT 0xC0F0      // Atlantic             (-03:00)
+                EDT 0xC12C      // Eastern              (-04:00)
+                CDT 0xC168      // Central              (-05:00)
+                MDT 0xC1A4      // Mountain             (-06:00)
+                PDT 0xC1E0      // Pacific              (-07:00)
+                YDT 0xC21C      // Yukon                (-08:00)
+                HDT 0xC258      // Hawaii/Alaska        (-09:00)
+                BDT 0xC294      // Bering               (-10:00)
+
+                The following non-standard time zone specifications may also be
+                used:
+
+                MID 0x2294      // Midway               (-11:00)
+                VAN 0x21E0      // Vancouver            (-08:00)
+                EDM 0x21A4      // Edmonton             (-07:00)
+                WIN 0x2168      // Winnipeg             (-06:00)
+                BOG 0x212C      // Bogota               (-05:00)
+                CAR 0x20F0      // Caracas              (-04:00)
+                RIO 0x20B4      // Rio de Janeiro       (-03:00)
+                FER 0x2078      // Fernando de Noronha  (-02:00)
+                AZO 0x203C      // Azores               (-01:00)
+                LON 0x1000      // London               (+00:00)
+                BER 0x103C      // Berlin               (+01:00)
+                ATH 0x1078      // Athens               (+02:00)
+                MOS 0x10B4      // Moscow               (+03:00)
+                DUB 0x10F0      // Dubai                (+04:00)
+                KAB 0x110E      // Kabul                (+04:30)
+                KAR 0x112C      // Karachi              (+05:00)
+                BOM 0x114A      // Bombay               (+05:30)
+                KAT 0x1159      // Kathmandu            (+05:45)
+                DHA 0x1168      // Dhaka                (+06:00)
+                BAN 0x11A4      // Bangkok              (+07:00)
+                HON 0x11E0      // Hong Kong            (+08:00)
+                TOK 0x121C      // Tokyo                (+09:00)
+                SYD 0x1258      // Sydney               (+10:00)
+                NOU 0x1294      // Noumea               (+11:00)
+                WEL 0x12D0      // Wellington           (+12:00)
+
+fidoaddr_t      FidoNet address stored as four ushorts that represent the zone,
+                network, node, and point (in that order).
+                C example:
+
+                typedef struct {
+
+                    ushort zone,
+                           net,
+                           node,
+                           point;
+
+                    } fidoaddr_t;
+
+
+typestr_t       ASCIIZ string with ushort type prefix.
+                C example:
+
+                typedef struct {
+
+                    ushort  type;   // Specifier for type of 'str'
+                    uchar   str[];  // ASCIIZ filename or other string data
+
+                    } typestr_t;
+
+
+mattach_t       File attachment information with type prefix, translation
+                string, and filename.
+                C example:
+
+                typedef struct {
+
+                    ushort  type;   // Attachment type
+                    ushort  xlat[]; // Translations of data in attachment
+                    uchar   str[];  // ASCIIZ filename
+
+                    } mattach_t;
+
+vattach_t		Video file attachment information with type, compression,
+				translation string, and filename.
+                C example:
+
+                typedef struct {
+
+                    ushort  type;   // Attachment type
+					ushort	comp;	// Compression method
+                    ushort  xlat[]; // Translations of data in attachment
+                    uchar   str[];  // ASCIIZ filename
+
+					} vattach_t;
+
+mtext_t         Message text with translation string prefix.
+                C example:
+
+                typedef struct {
+
+                    ushort  xlat[]; // Translations of text
+                    uchar   text[]; // Actual text data
+
+					} mtext_t;
+
+
+ftext_t         Formatted message text with translation string prefix and
+                format type.
+                C example:
+
+                typedef struct {
+
+					ushort	type;	// See Image Types for valid types
+					ushort	xlat[]; // Translations of data
+					uchar	data[]; // Actual formatted text data
+
+					} ftext_t;
+
+
+membed_t        Embedded data with type prefix, translation string, and ASCIIZ
+                description.
+                C example:
+
+                typedef struct {
+
+                    ushort  type;   // Specifier for type of 'dat'
+                    ushort  xlat[]; // Translations of embedded data
+                    uchar   name[]; // ASCIIZ char description of embedded data
+					uchar	data[]; // Binary data
+
+                    } membed_t;
+
+vembed_t		Embedded video data with type, compression method, translation
+				string, and ASCIIZ description.
+                C example:
+
+                typedef struct {
+
+                    ushort  type;   // Specifier for type of 'dat'
+					ushort	comp;	// Compression method
+                    ushort  xlat[]; // Translations of embedded data
+                    uchar   name[]; // ASCIIZ char description of embedded data
+					uchar	data[]; // Binary data
+
+					} vembed_t;
+
+File formats
+============
+&&File Formats
+$$FILEFORM
+&&Index (*.SID) File Format
+$$SID_FORM
+
+Index File (*.SID)
+------------------
+The index file for each message base contains one record per message in the
+base. Each record is fixed length using the following format:
+
+Index Record:
+------------
+C example:
+
+typedef struct {
+
+	ushort	to; 	// 16-bit CRC of recipient name (lower case) or user number
+	ushort	from;	// 16-bit CRC of sender name (lower case) or user number
+	ushort	subj;	// 16-bit CRC of title/subject (lower case)
+	ushort	attr;	// attributes (MSG_PRIVATE, MSG_READ, etc. flags)
+	ulong	offset; // byte offset of message header in header file
+	ulong	number; // message serial number (1 based)
+	time_t	time;	// import date/time stamp (Unix format)
+
+    } idxrec_t;
+
+
+Example file dump (16 messages starting with message number 15):
+---------------------------------------------------------------
+000000	  36 4F 13 07 2A 77 00 00	20 00 00 00 0F 00 00 00    6O..*w.. .......
+000010    BE 62 76 2C 36 4F 46 0A   7F B2 00 00 20 01 00 00    �bv,6OF.�.. ...
+000020    10 00 00 00 C7 29 78 2C   36 4F 70 6F 46 FF 00 00    ....�)x,6OpoF�..
+000030    20 02 00 00 11 00 00 00   AD D3 7A 2C 70 6F 13 07     .......��z,po..
+000040    46 FF 00 00 20 03 00 00   12 00 00 00 D6 F8 7F 2C    F�.. .......��,
+000050    36 4F E1 EA E7 E9 00 00   20 04 00 00 13 00 00 00    6O����.. .......
+000060    1E 7B 85 2C 37 0D 2E DF   4D 79 00 00 20 05 00 00    .{�,7..�My.. ...
+000070    14 00 00 00 5C E1 A1 2C   90 54 2D 5A 86 62 00 00    ....\�,�T-Z�b..
+000080    20 06 00 00 15 00 00 00   39 2E A2 2C 70 6F 1A 8B     .......9.�,po.�
+000090    46 FF 00 00 20 07 00 00   16 00 00 00 D0 7B A8 2C    F�.. .......�{�,
+0000A0    2E DF 1A 8B 4D 79 00 00   20 08 00 00 17 00 00 00    .�.�My.. .......
+0000B0    FF 7B A8 2C B4 D9 35 7C   23 B1 00 00 20 09 00 00    �{�,��5|#�.. ...
+0000C0    18 00 00 00 CE D4 BA 2C   36 4F BC D8 B2 E7 00 00    ....�Ժ,6O�ز�..
+0000D0    20 0A 00 00 19 00 00 00   14 5F C3 2C BA A8 4E B0     ........_�,��N�
+0000E0    67 76 00 00 20 0B 00 00   1A 00 00 00 6F 89 C3 2C    gv.. .......o��,
+0000F0    36 4F 0C 01 19 9C 00 00   20 0C 00 00 1B 00 00 00    6O...�.. .......
+000100    F8 30 C6 2C 36 4F FA 48   0E 55 00 00 20 0D 00 00    �0�,6O�H.U.. ...
+000110    1C 00 00 00 6A 94 D3 2C   36 4F F1 CE CF A2 00 00    ....j��,6O��Ϣ..
+000120    20 0E 00 00 1D 00 00 00   53 DB D5 2C 8D A6 21 CE     .......S��,��!�
+000130    F7 AB 00 00 20 0F 00 00   1E 00 00 00 31 29 DC 2C    ��.. .......1)�,
+
+
+Field descriptions:
+------------------
+To:
+The 'To' field is the CRC-16 of the name of the intended recipient agent of
+this message or the intended recipient's user number. If the CRC is stored, the
+text must be converted to lower case (A-Z changed to a-z) before the CRC is
+calculated. If the message is forwarded to another agent, the original or new
+index record must be changed to contain the CRC-16 of the new recipient name or
+user number. This field must always contain the recipient user number for local
+e-mail on a Synchronet BBS. Outbound netmail stored in the Synchronet local
+e-mail message base will contain 0 in this field.
+
+From:
+This field, similar to the 'To' field, contains the CRC-16 of the name of the
+sending agent of this message or the sender's user number. If the CRC is
+stored, the text must be converted to lower case (A-Z changed to a-z) before
+the CRC is calculated. If the message is forwarded to another agent, the
+original or new index record must be changed to contain the CRC-16 of the new
+sender name or user number. If the message was imported into the local e-mail
+message base on a Synchronet BBS via netmail, this field will contain 0.
+
+Subj:
+The 'Subj' field contains the CRC-16 of the message's subject. The subject
+must be converted to lower case (A-Z changed to a-z) and all preceeding
+"re: "'s and "re:"'s removed before calculating the CRC-16.
+
+Attr:
+This ushort is a bit field of the specific attributes for this message.
+It is a clone of the 'attr' element of the msghdr_t structure.
+
+Offset:
+This ulong is the offset (in bytes) in the header file for this message's
+header record.
+
+Number:
+This ulong is the serial number of this message. Valid values are 1 through
+0xffffffff. No two index records in the same message base may have the same
+message number. All index records must have sequential, but not necessarily
+consequetive, message numbers.
+
+Time:
+This field is the date/time stamp the message was imported to or posted in
+the message base. It is a clone of the 'when_imported.time' element of the
+msghdr_t structure.
+
+Header File (*.SHD)
+===================
+&&Header File (*.SHD) Format
+$$SHD_FORM
+
+Each SMB header file is made up of two distinct sections: base header records
+and message header records (usually the bulk of the file).
+
+Base Header Records:
+-------------------
+Base header records are blocks of data that apply to the entire message base
+and are of variable length. This specification defines only one base header
+record, the "Status info" (smbstatus_t) record. This status info record must be
+the first base header record in the file and must be modified if additional
+base header records are added.
+
+Additional header records allow other developers to store configuration and
+status information particular to their application needs. It also allows for
+future header record definitions as part of this specification without causing
+backward compatibility issues.
+
+Each base header record contains a fixed length portion (smbhdr_t) and an
+optional variable length portion.
+
+Whenever a base header record is read or updated (written), it must first
+be successfully locked and subsequently unlocked.
+
+The first base header record (Status Info) is used as a semaphore when writing
+to the message index (.SID) file and, when using the Hyper Allocation storage
+method, writing to the message data (.SDT) file. This record must be
+succesfully locked before writing and subsequently unlocked. This is to insure
+that multiple applications simultaneously writing to the same message base
+does result in corrrupted data.
+
+
+Message Header Records:
+----------------------
+Following the last base header record is the first message header record. Each
+header record is stored in one or more 256 byte blocks. There must be exactly
+one active message header record for every index record in the index file.
+(Note: This does not include deleted message headers that have not been
+overwritten by a new message header).
+
+Each message header record contains a fixed length portion (msghdr_t), a list
+of zero or more fixed length data fields (dfield_t), and a list of three or
+more variable length header fields (hfield_t).
+
+The value of the data stored in the zero or more unused bytes of the last
+header record block have an undefined value, though whenever possible
+developers should initialize to binary zero for human readability.
+
+Whenever a message header record is read or updated (written), it must first
+be successfully locked and subsequently unlocked.
+
+Base Header Record (Fixed Portion):
+----------------------------------
+C example:
+
+typedef struct {
+
+    uchar   id[4];          // text or binary unique hdr ID
+    ushort  version;        // version number (initially 100h for 1.00)
+    ushort  length;         // length including this struct
+
+    } smbhdr_t;
+
+
+Base Header Record Field Descriptions:
+-------------------------------------
+Id:
+This is a four byte unique ID identifying the type of the base header record.
+The bytes may contain any value, but printable ASCII characters are preferred.
+The only ID defined in this specification is "SMB^Z" used by the Status Info
+base header record.
+
+Version:
+This is a version number of the base header record type. Base header records
+of different versions may have different formats or contain different
+information. This is to aid the application in determining if the record
+is pertinent and if so, to what degree. The Status Info base header record
+uses this version field to define the version of the format for the entire
+message base (currently 0x121 for version 1.21).
+
+Length:
+This is entire length in bytes of this header record (including both fixed
+and variable portions).
+
+
+Base Header #1 (Status info) Record (Variable Portion):
+------------------------------------------------------
+C example:
+
+typedef struct {
+
+	ulong	last_msg;		// last message number posted or imported
+	ulong	total_msgs; 	// total messages currently in message base
+    ulong   header_offset;  // byte offset to first header record
+    ulong   max_crcs;       // Maximum number of CRCs to keep in history
+	ulong	max_msgs;		// Maximum number of messages to keep in base
+    ushort  max_age;        // Maximum age of messages (days) to keep in base
+	ushort	attr;			// Attribute bits
+
+    } smbstatus_t;
+
+Base Header #1 (Status Info) Record (Variable Portion) Field Descriptions:
+-------------------------------------------------------------------------
+Last_msg:
+This is the serial number of the last message imported or posted into this
+message base. The index, header, and data records for this message may possibly
+not exist (due to deletion). This field is used for determining the message
+number to give to a new message being imported or posted into this message
+base. This field must be updated for every message added to the message base.
+
+Total_msgs:
+This is the total number of active messages currently in the message base.
+This number should match the number of records in the index (.SID) file
+and active header records in the header (.SHD) file. This field must be
+updated whenever a message is added to or removed from the message base.
+
+Header_offset:
+This is the byte offset to the first message header record. It is useful
+for skipping all the base header records and going directly to the first
+message header record.
+
+Max_crcs:
+This is the maximum number of message CRCs to store in the CRC history (.SCH)
+file for duplicate message checking. If this field contains 0, then duplicate
+message checking is disabled.
+
+Max_msgs:
+This is the preferred maximum number of messages to keep in this message
+base as specified by the system operator. It is used by maintenance programs
+that trim the message base down by removing old messages. This field should
+be ignored by applications importing or posting messages allowing them to
+exceed this maximum at will.
+
+Max_age:
+This field is the maximum age (in days) of messages to keep in the message
+base. It is used by maintenance programs to purge out-dated messages from
+the message base.
+
+Attr:
+This is a bit field containing specific attributes (or flags) that may define
+the way messages are stored or retrieved from the this message base. The
+following attributes are defined:
+
+	SMB_EMAIL		(1<<0)
+
+	Indicates the message base is specifically for messages to or from local
+	users. When this bit is set, the idxrec.to and idxrec.from fields will
+	contain the user numbers (or 0 for non-user destination/source) instead of
+	the CRC-16 of the agent name.
+
+	SMB_HYPERALLOC	(1<<1)
+
+	Indicates the message base uses the Hyper Allocation storage method. This
+	bit should not be cleared by an application without first deleting all the
+	messages in the message base. This is due to the fact the Hyper Allocation
+	is not downward compatible with the Self-packing and Fast Allocation
+	storage methods.
+
+When used with Synchronet BBS software, a message base must NOT have both of
+the above attributes set. The only message base that should have the SMB_EMAIL
+attribute set is the DATA\MAIL message base.
+
+
+Base Header #1 (Status info) Record Contents:
+--------------------------------------------
+smbhdr.id="SMB\x1a";        // SMB^Z
+smbhdr.version=0x121;		// v1.21
+smbhdr.length=sizeof(smbhdr_t)+sizeof(smbstatus_t); smbstatus_t status;
+
+
+Additional Base Headers:
+-----------------------
+Additional headers from developers must have initial 8 bytes in smbhdr_t
+format, length must include size of smbhdr_t, and header_offset of smbstatus_t
+must be changed to include the size of the additional header(s).
+
+
+Example file dump (base header portion only):
+--------------------------------------------
+000000	  53 4D 42 1A 20 01 20 00	F4 01 00 00 F4 01 00 00    SMB. . .�...�...
+000010    20 00 00 00 D0 07 00 00   D0 07 00 00 00 00 00 00     ...�...�.......
+
+
+Message Header Record (Fixed portion):
+-------------------------------------
+C example:
+
+typedef struct {
+
+    uchar   id[4];          // SHD^Z (same for all types and versions)
+    ushort  type;           // Message type (this is the definition of type 0)
+    ushort  version;        // Version of type (initially 100h for 1.00)
+    ushort  length;         // Total length of fixed portion + all fields
+    ushort  attr;           // Attributes (bit field) (duplicated in SID)
+    ulong   auxattr;        // Auxillary attributes (bit field)
+    ulong   netattr;        // Network attributes (bit field)
+    when_t  when_written;   // Date/Time message was originally created
+    when_t  when_imported;  // Date/Time message was imported (locally)
+    ulong   number;         // Message number (unique, not necessarily seq.)
+    ulong   thread_orig;    // Original message number in thread
+    ulong   thread_next;    // Next message in thread
+    ulong   thread_first;   // Number of first reply to this message
+    uchar   reserved[16];   // 16 reserved bytes for future use
+    ulong   offset;         // Offset for buffer into data file (0 or mod 256)
+    ushort  total_dfields;  // Total number of data fields
+
+    } msghdr_t;
+
+typedef struct {
+
+    ushort  type;           // See "Data Field Types" values
+    ulong   offset;         // Offset into buffer 
+    ulong   length;         // Length of data field in buffer
+
+    } dfield_t;
+
+typedef struct {
+
+    ushort  type;           // See "Header Field Types" for values
+    ushort  length;         // Length of buffer
+    uchar   dat[length];
+
+    } hfield_t;
+
+Example file dump (one header record, both fixed and variable length portions):
+------------------------------------------------------------------------------
+000020	  53 48 44 1A 00 00 20 01	F5 00 00 00 00 00 00 00    SHD... .�.......
+000030    00 00 00 00 46 DB F7 2C   00 00 7D D7 29 2D 00 00    ....F��,..}�)-..
+000040    01 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00    ................
+000050    00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00    ................
+000060    00 00 00 00 02 00 00 00   00 00 00 00 4A 01 00 00    ............J...
+000070    02 00 4A 01 00 00 53 00   00 00 00 00 13 00 4D 61    ..J...S.......Ma
+000080    72 69 61 6E 6E 65 20 4D   6F 6E 74 67 6F 6D 65 72    rianne Montgomer
+000090    79 30 00 0C 00 43 61 72   6F 6C 20 47 61 69 73 65    y0...Carol Gaise
+0000A0    72 60 00 07 00 46 61 72   6E 68 61 6D A4 00 14 00    r`...Farnham�...
+0000B0    31 3A 31 33 38 2F 31 30   32 2E 30 20 32 63 66 38    1:138/102.0 2cf8
+0000C0    30 35 37 36 A5 00 14 00   31 3A 33 34 33 2F 31 30    0576�...1:343/10
+0000D0    30 2E 30 20 32 63 66 33   62 39 30 61 A3 00 23 00    0.0 2cf3b90a�.#.
+0000E0    31 33 38 2F 31 30 32 20   31 20 32 37 30 2F 31 30    138/102 1 270/10
+0000F0    31 20 32 30 39 2F 32 30   39 20 31 30 33 2F 30 20    1 209/209 103/0 
+000100    33 35 35 02 00 02 00 02   00 03 00 08 00 01 00 8A    355............�
+000110    00 66 00 00 00 00 00 00   00 00 00 00 00 00 00 00    .f..............
+
+Contents of example header:
+--------------------------
+id					 SHD^Z
+type				 0000h
+version 			 0120h
+length               245
+attr                 0000h
+auxattr              00000000h
+netattr              00000000h
+when_written         Sat Nov 27 17:57:10 1993
+when_imported        Tue Jan 04 15:54:21 1994
+number               1
+thread_orig          0
+thread_next          0
+thread_first         0
+reserved[16]         
+offset               0
+total_dfields        2
+
+dfield[0].type       00h
+dfield[0].offset     0
+dfield[0].length     330
+dfield[1].type		 02h
+dfield[1].offset     330
+dfield[1].length     83
+
+hfield[0].type       00h
+hfield[0].length     19
+hfield[0]_dat        Marianne Montgomery
+hfield[1].type		 30h
+hfield[1].length     12
+hfield[1]_dat        Carol Gaiser
+hfield[2].type		 60h
+hfield[2].length     7
+hfield[2]_dat        Farnham
+hfield[3].type		 A4h
+hfield[3].length     20
+hfield[3]_dat        1:138/102.0 2cf80576
+hfield[4].type		 A5h
+hfield[4].length     20
+hfield[4]_dat        1:343/100.0 2cf3b90a
+hfield[5].type		 A3h
+hfield[5].length     35
+hfield[5]_dat        138/102 1 270/101 209/209 103/0 355
+hfield[6].type		 02h
+hfield[6].length     2
+hfield[6]_dat		 02 00
+hfield[7].type		 03h
+hfield[7].length     8
+hfield[7]_dat        01 00 8A 00 66 00 00 00
+
+Fixed Portion Field descriptions:
+--------------------------------
+Id:
+This field (regardless of the header type or version) must always contain the
+the string "SHD^Z". This is to aid in the restoration of a corrupted header
+file and give a visual indication of the beginning of a new header record when
+viewing dumps of the header file.
+
+Type:
+This is the message header type. Only one type is currently defined by this
+specification (type 0). Any and all future header types will have the first
+4 fields (10 bytes) in the same format of type 0. This allows other types
+(with different lengths) to be skipped because the 4th field (length) will
+always be in the same position.
+
+Version:
+This is the version of this header type. This specification defines version
+1.21 of message header type 0 (stored as 121h).
+
+Length:
+This is the total length of this message header record (including both fixed
+and variable length portions, but NOT including unused block space).
+
+Attr:
+This is a bit field (16-bit) containing basic message attributes (flags) for
+this message. An exact duplicate of this field is stored in the index file as
+well. They must always match.
+
+Auxattr:
+This is a bit field (32-bit) containing the auxillary attributes (flags) for
+this message. The attributes stored in this variable are more specific in
+nature and less critical than those in the Attr field.
+
+Netattr:
+This is a bit field (32-bit) containing the network attributes (flags) for this
+message. The attributes stored in this variable are related solely to message
+networking.
+
+When_written:
+This is the date and time when the message was originally created.
+
+When_imported:
+This is the date and time when the message was posted on or imported into the
+local message system.
+
+Number:
+This is the message's unique serial number (from 1 to FFFFFFFFh). This field
+is duplicated in the index file. They must always match.
+
+Thread_orig:
+If this message is a reply, then this field contains the number of the original
+message that was replied to. If this message was not a reply, this field will
+contain the value 0.
+
+Thread_next:
+If this message is a reply, and there are later replies to that message
+(the message number contained in the Thread_orig field), then this field will
+contain the number of the next reply in the chain. If this message is the only
+reply to the orignal message, this field will contain the value 0.
+
+Thread_first:
+If there are any replies to this message (after it has been posted), this field
+will contain the number of the first reply to this message. If there are no
+replies to this message, this field will contain the value 0.
+
+Reserved:
+Unused bytes, reserved for future definition in the message header type 0
+specification.
+
+Offset:
+The byte offset into the data file, specifying the start of the buffer for
+all data associated with this message. This value must be either 0 or modula
+256. When retrieving the actual data portion of data fields, the physical
+offset into the file will be the offset of the message data buffer (this field)
+plus the offset of the individual data field (msghdr_t.offset+dfield_t.offset).
+
+Total_dfields:
+This field contains the total number of data fields associated with this
+message. The value of this field must match the actual number of data fields
+stored in the header (dfield_t data types following the fixed portion of the
+message header).
+
+
+Variable Portion Field descriptions:
+-----------------------------------
+See the Header Field Type and Data Field Type sections for the descriptions
+of the values contained in these fields.
+
+Message Header Block Allocation (*.SHA)
+=======================================
+&&Header Allocation File (*.SHA) Format
+$$SHA_FORM
+
+If this message base uses the Hyper Allocation storage method (the
+SMB_HYPERALLOC bit is set in the smbstatus_t.attr field), then this file is
+not created or used.
+
+This file contains no header or signature data. Each byte (uchar) in the file
+specifies the allocation state of the corresponding 256 byte block in the
+header (*.SHD) file. A value of 0 indicates a free header block, and a value of
+1 indicates an allocated block. Other non-zero values are undefined.
+
+This file must always be opened DENY ALL (non-shareable).
+
+Message Data (*.SDT)
+====================
+&&Data File (*.SDT) Format
+$$SDT_FORM
+
+This file contains no header or signature data. It contains the text and other
+embedded data for the messages in a single message base. The data for each
+message always begins on a 256 byte block boundary. The data in the unused
+portion of a data block is undefined, but should be initialized to NULL
+whenever possible.
+
+This file must always be opened DENY NONE (shareable).
+
+Data fields of type TEXT_BODY and TEXT_TAIL must have all trailing white space
+and control characters removed (i.e. the last character of the data record
+must be in the range 21h to FFh). The only exception to this rule, is if the
+TEXT_BODY is terminated with multiple contiguous CRLFs, only the last CRLF
+should be removed. A CRLF should always be appended to the text data when it is
+displayed.
+
+When reading from this file, it is a good idea to make sure the message header
+for the data being read is currently locked (though no single message header
+should be locked for extended durations of time). This will insure that no
+other application will write to this portion of the file while it's being
+read (read from disk, not displayed).
+
+When using the Hyper Allocation storage method, the Status Info message base
+header must be successfully locked before writing to this file and subsequently
+unlocked.
+
+Message Data Block Allocation (*.SDA)
+=====================================
+&&Data Allocation File (*.SDA) Format
+$$SDA_FORM
+
+If this message base uses the Hyper Allocation storage method (the
+SMB_HYPERALLOC bit is set in the smbstatus_t.attr field), then this file is
+not created or used.
+
+This file contains no header or signature data. Each word (ushort) in the file
+specifies the allocation state of the corresponding 256 byte block in the data
+(*.SDT) file. A value of 0 indicates a free block, and a non-zero value
+indicates the number of message header records associated with this message
+data (most often 1). Each block can be used by up to 65,535 header records.
+
+This file must always be opened DENY ALL (non-shareable).
+
+CRC history for duplicate message checking (*.SCH)
+==================================================
+&&CRC History File (*.SCH) Format
+$$SCH_FORM
+
+This file is optional and contains no header or signature data. Each long word
+(ulong) in the file contains a CRC-32 of previously posted/imported messages.
+These CRCs can be used to check a candidate message for posting/import to be
+sure the message isn't a duplicate created by human or program error. The
+maximum number of CRCs to store is defined in the first message base header
+record (smbstatus_t.max_crcs).
+
+The CRC is calculated on the first TEXT_BODY data field before any translations
+are applied (e.g. encoding, compression, encryption).
+
+This file must always be opened DENY ALL (non-shareable).
+
+Header Field Types:
+==================
+&&Header Field Types
+$$HFIELD_T
+
+These are the defined valid values for hfield_t.type:
+
+Name     : SENDER
+Value    : 00h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : Yes
+Summary  : Name of agent that sent this message
+
+If blank (0 length or nulstr), assumed "Anonymous". If multiple SENDER fields
+exist, then the message has been forwarded and the order of the fields in the
+record must match the forwarding order (chronologically). When forwarding a
+message, the original SENDER field should be left intact and new SENDER,
+FORWARDED, and RECIPIENT fields added to the end of the record.
+
+Name     : SENDERAGENT
+Value    : 01h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : AGENT_PERSON or previous SENDERAGENT if exists
+Summary  : Type of agent that sent this message
+
+If multiple SENDER fields exist, then the message has been forwarded. If any of the
+forwarding agents is of a type other than AGENT_PERSON, then this field must
+follow that SENDER field to specify the agent type.
+
+Name     : SENDERNETTYPE
+Value    : 02h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : NET_NONE or previous SENDERNETTYPE if exists
+Summary  : Type of network message was sent from
+
+If multiple SENDERNETADDR fields are included, a SENDERNETTYPE field should be
+included before each to determine what data type the address is stored in.
+
+Name     : SENDERNETADDR
+Value    : 03h
+Data     : undef
+Multiple : Yes, order significant
+Required : No
+Default  : Previous SENDERNETADDR if exists
+Summary  : Network address for agent that sent this message
+
+The SENDERNETTYPE field indicates the data type of this field. If the
+SENDERNETTYPE is of type NET_INTERNET, the local-part of the Internet
+address is optional. If the local-part separator character ('@') is omitted,
+the SENDER field is assumed to be the local-part of the address.
+
+Name     : SENDEREXT
+Value    : 04h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : Previous SENDEREXT if exists
+Summary  : Extension of sending agent
+
+This field is useful for storing the sending agent's extension, when the
+agent's extension binds more tightly than the agent's name.
+
+For example, Synchronet Multinode BBS Software stores local e-mail with the
+sending and receiving agent's user numbers stored as their respective
+extensions. This is done so that if a user name changes for some reason,
+messages will not "disappear" from the user's mail box.
+
+If the SMB_EMAIL status header attribute is set, then the "From" field in the
+index must contain the binary value of this field rather than the CRC-16 of the
+SENDER (name) field.
+
+Name     : SENDERPOS
+Value    : 05h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : Previous SENDERPOS if exists
+Summary  : Position of sending agent
+
+Primarily for documentary purposes, this field contains the position of the
+sending agent (i.e. President, Sysop, C.E.O., MIS Director, etc).
+
+It can also be useful for getting a message or reply to the intended
+recipient when the agent name is not located or is unknown, but the position
+of the agent is known and specified.
+
+Name     : SENDERORG
+Value    : 06h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : Previous SENDERORG if exists
+Summary  : Organization name of sending agent
+
+Primarily for documentary purposes, this field contains the organization to
+which the sending agent belongs (i.e. Microsoft, Joe's BBS, SoCal User's Group,
+etc).
+
+Name     : AUTHOR
+Value    : 10h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Default  : First SENDER
+Summary  : Name of agent that created this message
+
+This field can only be added by the process that originally creates the
+message. It should not be included if same as first SENDER field. If multiple
+AUTHOR fields exist, then the message was created by multiple agents and is
+considered valid. The order of multiple AUTHOR fields in the record is not
+significant.
+
+Name     : AUTHORAGENT
+Value    : 11h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : SENDERAGENT or previous AUTHORAGENT if exists
+Summary  : Type of agent that created this message
+
+This field can only be added by the process that originally creates the
+message. It should not be included if same as first SENDERAGENT field. If
+multiple AUTHOR fields exist, then the message was created by multiple agents
+and if the agent type for any of the authors is other than AGENT_PERSON, an
+AUTHORAGENT field must follow to specify the agent type.
+
+Name     : AUTHORNETTYPE
+Value    : 12h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : SENDERNETTYPE or previous AUTHORNETTYPE if exists
+Summary  : Type of network this author is member of
+
+Name     : AUTHORNETADDR
+Value    : 13h
+Data     : undef
+Multiple : Yes, order significant
+Required : No
+Default  : SENDERNETADDR or previous AUTHORNETADDR if exists
+Summary  : Network address of this author
+
+Name     : AUTHOREXT
+Value    : 14h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : SENDEREXT or previous AUTHOREXT if exists
+Summary  : Extension of this author
+
+Name     : AUTHORPOS
+Value    : 15h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : SENDERPOS or previous AUTHORPOS if exists
+Summary  : Position of this author
+
+Name     : AUTHORORG
+Value    : 16h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : SENDERORG or previous AUTHORORG if exists
+Summary  : Organization this author belongs to
+
+Name     : REPLYTO
+Value    : 20h
+Data     : ASCII
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDER
+Summary  : Name of agent that replies should go to
+
+Name     : REPLYTOAGENT
+Value    : 21h
+Data     : ushort
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDERAGENT
+Summary  : Type of agent that replies should go to
+
+Name     : REPLYTONETTYPE
+Value    : 22h
+Data     : ushort
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDERNETTYPE
+Summary  : Type of network that replies should go to
+
+Name     : REPLYTONETADDR
+Value    : 23h
+Data     : undef
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDERNETADDR
+Summary  : Network address that replies should go to
+
+Name     : REPLYTOEXT
+Value    : 24h
+Data     : ASCII
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDEREXT
+Summary  : Extension of agent that replies should go to
+
+Name     : REPLYTOPOS
+Value    : 25h
+Data     : ASCII
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDERPOS
+Summary  : Position of agent that replies should go to
+
+Name     : REPLYTOORG
+Value    : 26h
+Data     : ASCII
+Multiple : Yes, but only last is valid
+Required : No
+Default  : SENDERORG
+Summary  : Organization of agent that replies should go to
+
+Name     : RECIPIENT
+Value    : 30h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : Yes
+Default  : "All"
+Summary  : Name of agent to receive this message
+
+If multiple RECIPIENT fields exist, the message has been forwarded and for each
+additional RECIPIENT field (after the initial RECIPIENT), there should be a
+FORWARDED field. The order of the RECIPIENT fields in the record must match the
+order in which the message was sent and forwarded (chronologically).
+
+Name     : RECIPIENTAGENT
+Value    : 31h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : AGENT_PERSON or previous RECIPIENTAGENT if exists
+Summary  : Type of agent to receive this message
+
+If multiple RECIPIENT fields exist, the message has been forwarded. If any of
+the recipient agents are of a type other than AGENT_PERSON, this field must
+follow the RECIPIENT field to specify the agent type.
+
+Name     : RECIPIENTNETTYPE
+Value    : 32h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : NET_NONE or previous RECIPIENTNETTYPE if exists
+Summary  : Type of network to receive this message
+
+Name     : RECIPIENTNETADDR
+Value    : 33h
+Data     : undef
+Multiple : Yes, order significant
+Required : No
+Default  : Previous RECIPIENTNETADDR if exists
+Summary  : Address of network to receive this message
+
+Name     : RECIPIENTEXT
+Value    : 34h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : Previous RECIPIENTEXT if exists
+Summary  : Extension of agent to receive this message
+
+If SMB_EMAIL status header attribute is set, then the "To" field in the index
+must contain the binary value of this field rather than the CRC-16 of the
+RECIPIENT (name) field. This is the case specifically with the local e-mail
+message base on a Synchronet BBS.
+
+Name     : RECIPIENTPOS
+Value    : 35h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : Previous RECIPIENTPOS if exists
+Summary  : Position of agent to receive this message
+
+Name     : RECIPIENTORG
+Value    : 36h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : Previous RECIPIENTORG if exists
+Summary  : Type of agent to receive this message
+
+Name     : FORWARDTO
+Value    : 40h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Summary  : Name of agent this message is to be forwarded to
+
+Name     : FORWARDTOAGENT
+Value    : 41h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTAGENT or previous FORWARDTOAGENT if exists
+Summary  : Type of agent this message is to be forwarded to
+
+Name     : FORWARDTONETTYPE
+Value    : 42h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTNETTYPE or previous FORWARDTONETTYPE if exists
+Summary  : Type of network this message is to be forwarded to
+
+Name     : FORWARDTONETADDR
+Value    : 43h
+Data     : undef
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTNETADDR or previous FORWARDTONETADDR if exists
+Summary  : Network address this message is to be forwarded to
+
+Name     : FORWARDTOEXT
+Value    : 44h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTEXT or previous FORWARDTOEXT if exists
+Summary  : Extension of agent this message is to be forwarded to
+
+Name     : FORWARDTOPOS
+Value    : 45h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTPOS or previous FORWARDTOPOS if exists
+Summary  : Position of agent this message is to be forwarded to
+
+Name     : FORWARDTOORG
+Value    : 46h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTORG or previous FORWARDTOORG if exists
+Summary  : Organization of agent this message is to be forwarded to
+
+Name     : FORWARDED
+Value    : 48h
+Data     : when_t
+Multiple : Yes, order significant
+Required : Yes, if forwarded
+Summary  : Date/Time this message was forwarded to another agent
+
+Name     : RECEIVEDBY
+Value    : 50h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : Yes, if receiving agent is other than RECIPIENT
+Summary  : Name of agent that received this message
+
+Name     : RECEIVEDBYAGENT
+Value    : 51h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTAGENT or previous RECEIVEDBYAGENT if exists
+Summary  : Type of agent that received this message
+
+Name     : RECEIVEDBYNETTYPE
+Value    : 52h
+Data     : ushort
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTNETTYPE or previous RECEIVEDBYNETTYPE if exists
+Summary  : Type of network that received this message
+
+Name     : RECEIVEDBYNETADDR
+Value    : 53h
+Data     : undef
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTNETADDR or previous RECEIVEDBYNETADDR if exists
+Summary  : Network address that received this message
+
+Name     : RECEIVEDBYEXT
+Value    : 54h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTEXT or previous RECEIVEDBYEXT if exists
+Summary  : Extension of agent that received this message
+
+Name     : RECEIVEDBYPOS
+Value    : 55h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTPOS or previous RECEIVEDBYPOS if exists
+Summary  : Position of agent that received this message
+
+Name     : RECEIVEDBYORG
+Value    : 56h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Default  : RECIPIENTORG or previous RECEIVEDBYORG if exists
+Summary  : Organization of agent that received this message
+
+Name     : RECEIVED
+Value    : 58h
+Data     : when_t
+Multiple : Yes, order significant
+Required : Yes, if received
+Default  : NULL
+Summary  : Date/Time this message was received
+
+Name     : SUBJECT
+Value    : 60h
+Data     : ASCII
+Multiple : No
+Required : Yes, but may be blank (0 length or nulstr)
+Summary  : Subject/title of message
+
+Name     : SUMMARY
+Value    : 61h
+Data     : ASCII
+Multiple : No
+Required : No
+Summary  : Summary of message contents, created by AUTHOR
+
+Name     : COMMENT
+Value    : 62h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Comment about this message, created by SENDER
+
+This field is useful for adding notes to a message when forwarding to a new
+recipient.
+
+Name     : CARBONCOPY
+Value    : 63h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : List of agents this message was also sent to
+
+This field is optional and only for the use of notifying the recipient of who
+else received the message.
+
+Name     : GROUP
+Value    : 64h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Name of group of users to receive message on recipient system
+
+This field is used when sending to a group name across a network, where the
+group can be expanded into multiple header records for each agent on the
+destination system.
+
+Name     : EXPIRATION
+Value    : 65h
+Data     : when_t
+Multiple : No
+Required : No
+Summary  : Date/Time that this message will expire
+
+Name     : PRIORITY
+Value    : 66h
+Data     : ulong
+Multiple : No
+Required : No
+Default  : 0
+Summary  : Message priority (0 is lowest, FFFFFFFFh is highest)
+
+Name     : FILEATTACH
+Value    : 70h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Name/file specification of attached file(s)
+
+Name of attached file(s). Wildcards allowed. MSG_FILEATTACH attribute must be
+set. If the MSG_FILEATTACH attribute is set but this field is not included,
+the SUBJECT field is assumed to be the filename(s).
+
+Name     : DESTFILE
+Value    : 71h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Summary  : Destination name for attached file(s)
+
+Wildcards allowed. FILEATTACH field must also be included.
+
+Name     : FILEATTACHLIST
+Value    : 72h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Name of ASCII list of attached filenames
+
+Wildcards not allowed in ASCII list filename. Wildcards allowed in ASCII list.
+MSG_FILEATTACH attribute must be set.
+
+Name     : DESTFILELIST
+Value    : 73h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Summary  : Name of ASCII list of destination filenames
+
+Wildcards not allowed in ASCII list filename. Wildcards allowed in ASCII list.
+
+Name     : FILEREQUEST
+Value    : 74h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Name of requested file
+
+Wildcards allowed. MSG_FILEREQUEST attribute must be set
+
+Name     : FILEPASSWORD
+Value    : 75h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Summary  : Password for FILEREQUEST
+
+Name     : FILEREQUESTLIST
+Value    : 76h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Name of ASCII list of filenames to request
+
+Wildcards allowed.
+
+Name     : FILEPASSWORDLIST
+Value    : 77h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Summary  : Name of ASCII list of passwords for FILEREQUESTLIST
+
+Name     : IMAGEATTACH
+Value    : 80h
+Data     : mattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of attached image file for display
+
+MSG_FILEATTACH attribute must be set. See Image Types for valid
+mattach_t.type values.
+
+Name     : ANIMATTACH
+Value    : 81h
+Data     : mattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of attached graphical animation file for display
+
+MSG_FILEATTACH attribute must be set. See Animation Types for valid
+mattach_t.type values.
+
+Name     : FONTATTACH
+Value    : 82h
+Data     : mattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of attached font definition file
+
+MSG_FILEATTACH attribute must be set. See Font Types for valid mattach_t.type
+values.
+
+Name     : SOUNDATTACH
+Value    : 83h
+Data     : mattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of attached sound file for playback
+
+MSG_FILEATTACH attribute must be set. See Sound Types for valid mattach_t.type
+values.
+
+Name     : PRESENTATTACH
+Value    : 84h
+Data     : mattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of attached presentation definition file
+
+MSG_FILEATTACH attribute must be set. See Present Types for valid
+mattach_t.type values.
+
+Name     : VIDEOATTACH
+Value    : 85h
+Data	 : vattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of attached interleaved video/sound file
+
+MSG_FILEATTACH attribute must be set. See Video Types for valid
+vattach_t.type values and Video Compression Types for valid vattach_t.comp
+values.
+
+Name     : APPDATAATTACH
+Value    : 86h
+Data     : mattach_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Name of attached application data file for process/display
+
+MSG_FILEATTACH attribute must be set. See Application Data Types for valid
+mattach_t.type values.
+
+Name     : IMAGETRIGGER
+Value    : 90h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of image file to trigger for display
+
+See Image Types for valid typestr_t.type values.
+
+Name     : ANIMTRIGGER
+Value    : 91h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of animation file to trigger for display
+
+See Animation Types for valid typestr_t.type values.
+
+Name     : FONTTRIGGER
+Value    : 92h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of font definition file to trigger
+
+See Font Types for valid typestr_t.type values.
+
+Name     : SOUNDTRIGGER
+Value    : 93h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of sound file to trigger for playback
+
+See Sound Types for valid typestr_t.type values.
+
+Name     : PRESENTTRIGGER
+Value    : 94h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of presentation definition file to trigger
+
+See Present Types for valid typestr_t.type values.
+
+Name     : VIDEOTRIGGER
+Value    : 95h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of interleaved video/sound file to trigger
+
+See Video Types for valid typestr_t.type values.
+
+Name     : APPDATATRIGGER
+Value    : 96h
+Data     : typestr_t
+Multiple : Yes, order significant
+Required : No
+Summary  : Type and filename of application data file to trigger
+
+See Application Data Types for valid typestr_t.type values.
+
+Name     : FIDOCTRL
+Value    : A0h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Format   : keyword ":" [" "] appdata
+Summary  : FTS/FSC-compliant control information line
+
+Any FidoNet FTS/FSC-compliant control information ("kludge") line that
+does not have an equivalent representation here. All data not unique to the
+actual control line, including leading and trailing white space, Ctrl-A (01h)
+character and terminating CR must be ommited. Defined in FTS-0001.
+
+Name     : FIDOAREA
+Value    : A1h
+Data     : ASCII
+Multiple : No
+Required : No
+Summary  : FTN EchoMail conference name.
+
+Defined in FTS-0004.
+
+Name     : FIDOSEENBY
+Value    : A2h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Format   : net"/"node [" "[net"/"]node] [...]
+Summary  : Used to store two-dimensional (net/node) SEEN-BY information
+
+Often used in FTN EchoMail environments. Only the actual SEEN-BY data is stored
+and SEEN-BY: is stripped along with any leading and trailing white space
+characters. Defined in FTS-0004.
+
+Name     : FIDOPATH
+Value    : A3h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Format   : net"/"node [" "[net"/"]node] [...]
+Summary  : Used to store two-dimensional (net/node)
+
+Defined in FTS-0004. ^aPATH: is stripped along with any leading and trailing
+white space characters.
+
+Name     : FIDOMSGID
+Value    : A4h
+Data     : ASCII
+Multiple : No
+Required : No
+Format   : origaddr " " serialno
+Summary  : MSGID field as specified in FTS-0009.
+
+Name     : FIDOREPLYID
+Value    : A5h
+Data     : ASCII
+Multiple : No
+Required : No
+Format   : origaddr " " serialno
+Summary  : REPLY field as specified in FTS-0009.
+
+Name     : FIDOPID
+Value    : A6h
+Data     : ASCII
+Multiple : No
+Required : No
+Format   : pID " " version [" "serialno]
+Summary  : Indentification string of program that created this message
+
+Defined FSC-0046. "^aPID:" and any white space is not included.
+
+Name     : FIDOFLAGS
+Value    : A7h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Used to store the FTN FLAGS kludge information
+
+Note that all FLAG options that have binary representation in the message
+header must be removed from the FLAGS string prior to storing it. Only the
+actual flags option string is stored and ^aFLAGS is stripped along with any
+leading and trailing white space characters. Defined in FSC-0053.
+
+Name     : RFC822HEADER
+Value    : B0h
+Data     : ASCII
+Multiple : Yes, order significant
+Required : No
+Format   : field-name ":" [field-body] [CRLF]
+Summary  : Undefined RFC-822 header field
+
+Internet Message storage format, that does not have an equivalent
+representation here. Folded header fields are allowed. Terminating CRLF may be
+ommited.
+
+Name     : RFC822MSGID
+Value    : B1h
+Data     : ASCII
+Multiple : No
+Required : No
+Format   : "<" addr-spec ">"
+Summary  : Message-ID field as specified in RFC-822.
+
+Name     : RFC822REPLYID
+Value    : B2h
+Data     : ASCII
+Multiple : No
+Required : No
+Format   : "<" addr-spec ">"
+Summary  : In-Reply-To field as specified in RFC-822.
+
+Name     : UNKNOWN
+Value    : F0h
+Data     : undef
+Multiple : Yes
+Required : No
+Summary  : Undefined header field of undefined type
+
+This field is useful for retaining binary header fields (that do not have an
+equivalent representation here) between message storage formats.
+
+Name     : UNKNOWNASCII
+Value    : F1h
+Data     : ASCII
+Multiple : Yes
+Required : No
+Summary  : Undefined header field of type ASCII
+
+This field is useful for retaining ASCII header fields (that do not have an
+equivalent representation here) between message storage formats.
+
+Name     : UNUSED
+Value    : FFh
+Data     : undef
+Multiple : Yes
+Required : No
+Summary  : Unused (deleted) header field
+
+The data contained in this header field is of an unknown type and should not be
+processed.
+
+
+Note:
+----
+Specifically, not defined are the values F000h through FFFFh. These values
+are to be used for user or system defined header fields. Digital Dynamics
+requests that any developers or organizations that wish to have additional
+header fields added to this specification notify Digital Dynamics through any
+of the contact methods listed at the beginning of this document.
+
+Data Field Types:
+================
+&&Data Field Types
+$$DFIELD_T
+
+These are the defined valid values for dfield_t.type:
+
+
+Val Name                Data        Description
+--- ----                ----        -----------
+00h TEXT_BODY           mtext_t     Displayable text (body of message).
+                                    Included in duplicate message checking.
+									All terminating white space and control
+									characters are to be truncated from data
+									(except when multiple contiguous CRLFs
+									terminate the text, only the last CRLF
+									is removed).
+
+01h TEXT_SOUL           mtext_t     Non-displayed text.
+                                    Not normally displayed. Not necessarily
+                                    displayable.
+                                    Included in duplicate message checking.
+
+02h TEXT_TAIL			mtext_t 	Displayable text (tag/tear/origin lines,
+									etc).
+                                    Not included in duplicate message checking.
+									All terminating white space and control
+                                    characters are to be truncated from data.
+
+03h TEXT_WING           mtext_t     Non-displayed text.
+                                    Not normally displayed. Not necessarily
+                                    displayable.
+                                    Not included in duplicate message checking.
+
+10h FTEXT_BODY			ftext_t 	Formatted equivalent of TEXT_BODY to be
+									displayed in place of TEXT_BODY if format
+									is supported. See Image Types for valid
+									values of ftext_t.type.
+
+12h FTEXT_TAIL			ftext_t 	Formatted equivalent of TEXT_TAIL to be
+									displayed in place of TEXT_TAIL if format
+									is supported. See Image Types for valid
+									values of ftext_t.type.
+
+20h IMAGEEMBED          membed_t    Type and data of embedded raster image file
+                                    for display.
+									See Image Types for valid membed.type
+									values.
+
+21h ANIMEMBED			membed_t	Type and data of embedded graphical
+									animation file for display.
+									See Animation Types for valid membed.type
+                                    values.
+
+22h FONTEMBED			membed_t	Type and data of embedded font definition
+									file. See Font Types for valid
+									membed_t.type values.
+
+23h SOUNDEMBED          membed_t    Type and data of embedded sound file for
+                                    playback.
+									See Sound Types for valid membed_t.type
+									values.
+
+24h PRESENTEMBED        membed_t    Type and data of embedded presentation
+                                    definition file.
+                                    See Present Types for valid membed_t.type
+                                    values.
+
+25h VIDEOEMBED			vembed_t	Type and data of embedded video/sound file
+                                    for playback.
+									See Video Types for valid vembed_t.type
+                                    values.
+									See Video Compression Types for valid
+									vembed_t.comp values.
+
+26h APPDATAEMBED		membed_t	Type and data of embedded application data
+									file for process/display.
+                                    See Application Data Types for valid
+                                    membed_t.type values.
+
+FFh UNUSED              undef       Space allocated for future update/expansion
+
+
+Specifically, not defined are the values F000h through FFFFh. These values
+are to be used for user or system defined data fields. Digital Dynamics
+requests that any developers or organizations that wish to have additional
+data fields added to this specification notify Digital Dynamics through any
+of the contact methods listed at the beginning of this document.
+
+
+Message Attributes:
+------------------
+&&Message Attributes
+$$ATTRBITS
+
+These are the bit values for idxrec_t.attr and msghdr_t.attr:
+
+MSG_PRIVATE         (1<<0)  // Private
+MSG_READ            (1<<1)  // Read by addressee
+MSG_PERMANENT       (1<<2)  // Permanent
+MSG_LOCKED          (1<<3)  // Msg is locked, no editing possible
+MSG_DELETE          (1<<4)  // Msg is marked for deletion
+MSG_ANONYMOUS       (1<<5)  // Anonymous author
+MSG_KILLREAD        (1<<6)  // Delete message after it has been read
+MSG_MODERATED		(1<<7)	// This message must be validated before export
+MSG_VALIDATED       (1<<8)  // This message has been validated by a moderator
+
+
+Auxillary Attributes:
+--------------------
+These are the bit values for msghdr_t.auxattr:
+
+MSG_FILEREQUEST     (1<<0)  // File request
+MSG_FILEATTACH      (1<<1)  // File(s) attached to Msg
+MSG_TRUNCFILE       (1<<2)  // Truncate file(s) when sent
+MSG_KILLFILE        (1<<3)  // Delete file(s) when sent
+MSG_RECEIPTREQ      (1<<4)  // Return receipt requested
+MSG_CONFIRMREQ      (1<<5)  // Confirmation receipt requested
+MSG_NODISP          (1<<6)  // Msg may not be displayed to user
+
+
+Network Attributes:
+------------------
+These are the bit values for msghdr_t.netattr:
+
+MSG_LOCAL           (1<<0)  // Msg created locally
+MSG_INTRANSIT       (1<<1)  // Msg is in-transit
+MSG_SENT            (1<<2)  // Sent to remote
+MSG_KILLSENT        (1<<3)  // Kill when sent
+MSG_ARCHIVESENT     (1<<4)  // Archive when sent
+MSG_HOLD            (1<<5)  // Hold for pick-up
+MSG_CRASH           (1<<6)  // Crash
+MSG_IMMEDIATE       (1<<7)  // Send Msg now, ignore restrictions
+MSG_DIRECT          (1<<8)  // Send directly to destination
+MSG_GATE            (1<<9)  // Send via gateway
+MSG_ORPHAN          (1<<10) // Unknown destination
+MSG_FPU             (1<<11) // Force pickup
+MSG_TYPELOCAL       (1<<12) // Msg is for local use only
+MSG_TYPEECHO        (1<<13) // Msg is for conference distribution
+MSG_TYPENET         (1<<14) // Msg is direct network mail
+
+Translation Types:
+-----------------
+&&Translation Types
+$$XLATTYPE
+
+Definition for values of *.xlat[x]:
+
+XLAT_NONE           0       // No translation/End of translation list
+XLAT_LF2CRLF        1       // Expand sole LF to CRLF
+XLAT_ESCAPED        2       // 7-bit ASCII escaping for ctrl and 8-bit data
+XLAT_HUFFMAN        3       // Static and adaptive Huffman coding compression
+XLAT_LZW			4		// LZW (Lempel-Ziv-Welch) encoding for compression
+							// Terry Welch, IEEE Computer Vol 17, No 6
+							// June 1984, pp 8-19
+XLAT_LZC			5		// LZC (modified LZW) encoding for compression
+							// Unix compress program
+XLAT_RLE            6       // Run length encoding compression
+XLAT_IMPLODE		7		// Implode compression (PKZIP v1.x)
+XLAT_SHRINK 		8		// Shrink compression (PKZIP v1.x)
+XLAT_LZH			9		// LZH dynamic Huffman coding
+							// Haruyasu Yoshizaki, LHarc
+							// November, 1988
+
+Agent Types:
+-----------
+&&Agent Types
+$$AGENTTYP
+
+AGENT_PERSON		0		// To or from person
+AGENT_PROCESS       1       // Unknown process, identified by agent name
+
+Agent types E000h through EFFFh are reserved for Synchronet process types
+(defined specifically by Digital Dynamics).
+
+Note:
+----
+Specifically not defined are agent types F000h through FFFFh. These values
+are to be used for user or system defined agent types. Digital Dynamics
+requests that any developers or organizations that wish to have additional
+agent types added to this specification notify Digital Dynamics through any
+of the contact methods listed at the beginning of this document.
+
+Network Types:
+-------------
+&&Network Types
+$$NETWORKS
+
+							// Net Type 			Address Format
+							// -----------------------------------
+NET_NONE			0		// Locally created		none
+NET_UNKNOWN 		1		// Unknown				undef
+NET_FIDO			2		// FTN network			fidoaddr_t
+NET_POSTLINK		3		// PostLink network 	none
+NET_QWK 			4		// QWK based network	ASCII
+NET_INTERNET		5		// The Internet 		ASCII
+NET_WWIV			6		// WWIV based network	ulong
+NET_MHS 			7		// MHS network			ASCII
+
+
+Media Types:
+===========
+&&Media Types
+$$MEDIATYP
+
+Image Types:
+-----------
+
+IMAGE_UNKNOWN       0x00    // Use image signature header to determine format
+IMAGE_ASC           0x01    // ASCII text/IBM extended ASCII graphics
+IMAGE_ANS           0x02    // ANSI X3.64 terminal escape sequences
+IMAGE_AVT           0x03    // AVATAR terminal escape sequences
+IMAGE_LVI           0x04    // LVI terminal escape sequences
+IMAGE_GIF           0x05    // Compuserve Graphics Interchange Format (GIF)
+IMAGE_TIF           0x06    // Tagged Image Format (AKA TIFF)
+IMAGE_JPG           0x07    // Joint Photographers Electronics Group (JPEG)
+IMAGE_T16           0x08    // TrueVision 16-bit bitmap (TGA)
+IMAGE_T24           0x09    // TrueVision 24-bit bitmap (TGA)
+IMAGE_T32           0x0a    // TrueVision 32-bit bitmpa (TGA)
+IMAGE_PCX           0x0b    // ZSoft PaintBrush graphics
+IMAGE_BMP           0x0c    // Windows bitmap
+IMAGE_RLE           0x0d    // Windows bitmap (compressed)
+IMAGE_DIB           0x0e    // Display independant bitmap
+IMAGE_PCD           0x0f    // Kodak PhotoCD
+IMAGE_G3F           0x10    // Group 3 FAX
+IMAGE_EPS           0x11    // Ecapsulated PostScript
+IMAGE_RTF           0x12    // Rich text format
+IMAGE_RIP           0x13    // Remote Imaging Protocol Script (RIPscrip)
+IMAGE_NAP           0x14    // NAPLPS
+IMAGE_CDR           0x15    // Corel Draw!
+IMAGE_CGM           0x16    // Computer graphics metafile
+IMAGE_WMF           0x17    // Windows metafile
+IMAGE_DFX           0x18    // Autodesk AutoCAD
+IMAGE_IFF			0x19	// Amiga Interchange File Format
+IMAGE_HTM			0x20	// HyperText Markup Language (MTML) Document
+IMAGE_OS2			0x21	// OS/2 bitmap (BMP)
+
+Animation Types:
+---------------
+
+ANIM_UNKNOWN        0       // Use file signature header to determine format
+ANIM_FLI            1       // Autodesk animator
+ANIM_FLC            2       // Autodesk
+ANIM_GL             3       // Grasprt
+ANIM_IFF			4		// Amiga Interchange File Format
+
+
+Video Types:
+-----------
+
+VIDEO_UNKNOWN       0       // Use file signature header to determine format
+VIDEO_QTIME         1       // Apple Quick-time
+VIDEO_FQTIME        2       // Apple Flattened Quick-time
+VIDEO_AVI           3       // Windows Auto/Video Interleave
+VIDEO_ULT           4       // OS/2 Ultimotion
+
+Video Compression Types:
+-----------------------
+
+VCOMP_UNKNOWN		0		// Use file signature header to determine codec
+VCOMP_RLE			1		// Apple animation
+VCOMP_SMC			2		// Apple graphics
+VCOMP_RPZA			3		// Apple video
+VCOMP_KLIC			4		// Captain crunch
+VCOMP_CVID			5		// CinePak
+VCOMP_RT21			6		// Intel indeo R2
+VCOMP_IV31			7		// Intel indeo R3
+VCOMP_YVU9			8		// Intel YVU9
+VCOMP_JPEG			9		// JPEG
+VCOMP_MRLE			10		// Microsoft RLE
+VCOMP_MSVC			11		// Microsoft video 1
+
+
+Font Types:
+----------
+
+FONT_UNKNOWN        0       // Use file signature header to determine format
+FONT_TTF            1       // Windows TrueType
+FONT_PFB            2       // PostScript Type 1 Font Binary
+FONT_PFM            3       // PostScript Type 1 Font Metric
+FONT_AMIGA			4		// Amiga Bitmapped
+FONT_AGFA			5		// CompuGraphic Fonts
+
+
+Sound Types:
+-----------
+
+SOUND_UNKNOWN       0       // Use file signature header to determine format
+SOUND_MOD           1       // MOD format
+SOUND_VOC           2       // Sound Blaster VOC format
+SOUND_WAV			3		// Windows 3.1 WAV RIFF format
+SOUND_MID           4       // MIDI format
+SOUND_GMID          5       // General MIDI format (standardized patches)
+SOUND_SMP			6		// Turtle Beach SampleVision format
+SOUND_SF			7		// IRCAM format
+SOUND_AU			8		// Sun Microsystems AU format
+SOUND_IFF			9		// Amiga Interchange File Format
+
+Application Data Types:
+----------------------
+
+APPDATA_UNKNOWN     0       // Use file signature header to determine format
+APPDATA_WORDPERFECT 1       // WordPerfect Document
+APPDATA_WKS         2       // Lotus 123 Worksheet (?)
+APPDATA_WK1         3       // Lotus 123 Worksheet rev 1
+APPDATA_WK2         4       // Lotus 123 Worksheet rev 2
+APPDATA_WK3         5       // Lotus 123 Worksheet rev 3
+APPDATA_DBF         6       // dBase III data file
+APPDATA_PDX         7       // Paradox data file
+APPDATA_EXCEL		8		// Excel data file
+APPDATA_QUATRO		9		// Borland Quatro Pro file
+APPDATA_WORD		10		// Microsoft Word
+
+Message Storage Pseudo Code
+===========================
+&&Message Storage Pseudo Code
+$$STORPCOD
+
+The following is a "C like" pseudo code listing example of adding a message to
+an SMB message base. SMBLIB contains C functions to do most of the following
+operations. We are supplying this pseudo code as a general definition of the
+order of required operations in writing to the message base. Many details have
+been left out to simplify the code and to demonstrate only the basic
+principles.
+
+shd = open ( MSGBASE.SHD , READ/WRITE/DENY_NONE )
+sdt = open ( MSGBASE.SDT , READ/WRITE/DENY_NONE )
+sid = open ( MSGBASE.SDT , READ/WRITE/DENY_NONE )
+
+lock ( shd , smbhdr )
+read ( shd , smbstatus )
+
+if ( smbstatus.attr & SMB_HYPERALLOC )
+	msg.hdr.offset = filelength ( sdt )
+
+else {
+	number_of_blocks = length_of_message_data / SDT_BLOCK_LEN
+	if ( length_of_message_data % SDT_BLOCK_LEN )	/* unevenly divisible */
+		number_of_blocks = number_of_blocks + 1
+
+	sda = open ( MSGBASE.SDA , READ/WRITE/DENY_ALL )
+
+	if ( fast_allocation_mode )
+		seek ( sda , END_OF_FILE )
+
+	else {
+		seek ( sda , BEGINNING_OF_FILE )
+		while ( not end_of_file ( sda ) ) {
+			read ( sda , allocated , number_of_blocks * 2 )
+			if ( allocated = 0 ) {
+				seek_backwards ( sda , number_of_blocks * 2 )
+				break
+			}
+		}
+	}
+
+	msg.hdr.offset = ( current_position ( sda ) / 2 ) * SDT_BLOCK_LEN
+
+	allocated = 1
+
+	write ( sda , allocated , number_of_blocks * 2 )
+
+	close ( sda )
+}
+
+seek ( sdt , msg.hdr.offset )
+
+write ( sdt , message_data )
+
+if ( smbstatus.attr & SMB_HYPERALLOC )
+	msg.idx.offset = filelength ( shd )
+
+else {
+	number_of_blocks = length_of_message_header / SHD_BLOCK_LEN
+	if ( length_of_message_header % SHD_BLOCK_LEN )   /* unevenly divisible */
+		number_of_blocks = number_of_blocks + 1
+
+	sha = open ( MSGBASE.SHA , READ/WRITE/DENY_ALL )
+
+	if ( fast_allocation_mode )
+		seek ( sha , END_OF_FILE )
+
+	else {
+		seek ( sha , BEGINNING_OF_FILE )
+		while ( not end_of_file ( sha ) ) {
+			read ( sha , allocated , number_of_blocks )
+			if ( allocated = 0 ) {
+				seek_backwards ( sha , number_of_blocks )
+				break
+			}
+		}
+	}
+
+	msg.idx.offset = ( current_position ( sha ) * SHD_BLOCK_LEN )
+	msg.idx.offset = msg.idx.offset + smbstatus.header_offset
+
+	allocated = 1
+
+	write ( sha , allocated , number_of_blocks )
+
+	close ( sha )
+}
+
+seek ( shd , msg.idx.offset )
+
+msg.hdr.number = smbstatus.last_msg+1
+
+write ( shd , msg.hdr )
+
+smbstatus.total_msgs = smbstatus.total_msgs + 1
+smbstatus.last_msg = msg.hdr.number
+
+write ( shd , smbstatus )
+
+write ( sid , msg.idx )
+
+unlock ( shd , smbstatus )
+
+Message Retrieval Pseudo Code
+=============================
+&&Message Retrieval Pseudo Code
+$$READPCOD
+
+shd = open ( MSGBASE.SHD , READ/WRITE/DENY_NONE )
+sdt = open ( MSGBASE.SDT , READ/WRITE/DENY_NONE )
+sid = open ( MSGBASE.SDT , READ/WRITE/DENY_NONE )
+
+read ( sid , msg.idx )
+
+seek ( shd , msg.idx.offset )
+
+lock ( shd , msg.hdr )
+
+read ( shd , msg.hdr )
+
+seek ( sdt , msg.hdr.offset )
+
+read ( sdt , msg.hdr.data_length )
+
+unlock ( shd , msg.hdr )
+
+SMBUTIL
+=======
+&&SMBUTIL
+$$SMBUTIL_
+
+SMBUTIL is a utility that can perform various functions on an SMB message base.
+The primary purpose of SMBUTIL is as an example to C programmers of how to use
+the SMBLIB functions to access and modify an SMB message base. The complete C
+source code for SMBUTIL is included and functions from it can be used or
+modified by developers at their own discretion. The following files make up
+SMBUTIL:
+
+SMBUTIL.EXE 	Compiled and linked for 16-bit DOS (ready to run)
+SMBUTIL.C		C functions
+SMBUTIL.H		C definitions and variable prototypes
+SMBUTIL.WAT 	Makefile for Watcom C/C++ (type wmake -f smbutil.wat)
+SMBUTIL.BOR 	Makefile for Borland C/C++ (type make -f smbutil.bor)
+
+The usage syntax is as follows:
+
+SMBUTIL [/opts] cmd smb_filespec.shd
+
+where cmd is one or more of the following:
+
+	   l[n] = list msgs starting at number n
+	   r[n] = read msgs starting at number n
+	   v[n] = view msg headers starting at number n
+	   k[n] = kill (delete) n msgs
+	   i<f> = import from text file f
+	   s	= display msg base status
+	   c	= change msg base status
+	   m	= maintain msg base - delete old msgs and msgs over max
+       p[k] = pack msg base (k specifies minimum packable Kbytes)
+
+where opts is one or more of the following:
+
+	   a	= always (force) packing
+	   z<n> = set time zone (n=min +/- from UT or 'EST','EDT','CST',etc)
+
+and smb_filespec is the base filename or file specification (wildcards) for the
+message base. If wildcards are used, the ".SHD" extension must be specified.
+
+An example command line:
+
+SMBUTIL MP C:\SBBS\DATA\SUBS\*.SHD
+
+would maintain and pack all the message bases found in the C:\SBBS\DATA\SUBS
+directory.
+
+CHKSMB
+======
+&&CHKSMB
+$$CHKSMB__
+
+CHKSMB is a utility that performs a comprehensive analysis of a message base
+to find any possible errors and calculate the number of packable bytes. It does
+not "fix" a message base if any errors are found, it only reports the specific
+errors (and exits with a non-zero error level). If any errors are reported,
+packing the message base with SMBUTIL may rebuild the damaged files. If that
+doesn't work, then use FIXSMB as a last resort.
+
+C source code for CHKSMB is also included as an example to programmers of how
+to use SMBLIB functions.
+
+The usage syntax is as follows:
+
+CHKSMB [/opts] smb_filespec.shd
+
+where opts is one or more of the following:
+
+		q	= quiet mode (no beeps)
+		s	= stop after an errored message base (for use with wildcards)
+		p	= pause after an errored message base (wait for key press)
+		t	= don't check for unsupported translation strings (faster)
+		e	= display extended information on corrupted messages
+
+An example command line:
+
+CHKSMB /QP C:\SBBS\DATA\SUBS\*.SHD
+
+would check all the message bases in the C:\SBBS\DATA\SUBS directory, without
+beeping on errors, and pausing after an errored message base.
+
+FIXSMB
+======
+&&FIXSMB
+$$FIXSMB__
+
+FIXSMB is a utility that will rebuild the index and allocation files for a
+message base. Since the message headers are not necessarily stored
+sequentially, the order of the messages in the index may be changed when the
+index is rebuilt. Messages are also re-numbered, so only use this program if
+the index is corrupted and the messages are extremely important.
+
+C source code for FIXSMB is also included as an example to programmers of how
+to use SMBLIB functions.
+
+The usage syntax is as follows:
+
+FIXSMB [/M] smb_file
+
+An example command line:
+
+FIXSMB \SBBS\DATA\MAIL
+
+Only use the "/M" command line switch if fixing an older Synchronet e-mail
+message base (created with SBBS v2.1 or earlier). Once the SMB_EMAIL status
+attr is set ("SMBUTIL S" will report a status attr of 1), the "/M" is not
+required.
+
+SMBLIB
+======
+&&SMBLIB
+$$SMBLIB__
+
+SMBLIB is a library of C functions for accessing and storing messages in an
+SMB format message base. It can eliminate much of the development time for
+developers that wish to use the library in whole or in part, or use the
+functions as examples for their own message base function library. The library
+consists of the following files:
+
+SMBDEFS.H		Constant definitions, macros, and data types
+SMBLIB.H		Library constants and function prototypes
+SMBLIB.C		Function definitions
+SMBVARS.C		Global variable definitions (doubles as declaration file)
+
+For developers to use this library with their program, they must include the
+"SMBLIB.H" header file at the top of each C file that uses any of the library
+functions, global variables, data types, macros, and constants. This can be
+done by simply adding the following line to each .C file:
+
+#include "smblib.h"
+
+If SMBLIB.H is included, there is no need to include SMBDEFS.H or SMBVARS.C.
+
+To link the library functions and variables with a main program, the files
+SMBVARS.OBJ and SMBLIB.OBJ must be linked with the main program .OBJ files.
+If the operating system is DOS, be sure that all .OBJ files are compiled for
+the same memory model.
+
+Example MAKEFILEs for compiling and linking SMBUTIL with Borland C/C++
+(SMBUTIL.BOR) and Watcom C/C++ (SMBUTIL.WAT) are included.
+
+SMBDEFS.H
+=========
+&&SMBDEFS.H
+$$SMBDEFS_
+
+The SMBDEFS.H file contains important constant definitions and data types (also
+defined in this document). If ever this document and SMBDEFS.H are inconsistent
+with each other, then SMBDEFS.H is to be considered correct and this document
+in error. If such a discrepency is found, please notifiy Digital Dynamics so it
+can be corrected in a future revision of the specification.
+
+Most notable of the data types is a structure called smbmsg_t (not defined
+in this document). It contains the fixed and variable portions of a message's
+header record as well as convenience pointers to the sender's name
+(smbmsg_t.to), recipient's name (smbmsg_t.from), network addresses, and more.
+If multiple SENDER header fields are included (for example), then smbmsg_t.to
+will point to the last SENDER header field in the header record. Convenience
+pointers for other data items work in the same fasion if multiple header fields
+of the same type exist in the header record.
+
+Variables of the smbmsg_t data type (and pointers to variables of smbmsg_t
+type) are used as arguments to many of the SMBLIB functions.
+
+SMBVARS.C
+=========
+&&SMBVARS.C
+$$SMBVARS_
+
+The SMBVARS.C file contains definitions of the global variables used by the
+SMBLIB functions. It is a fairly small file since there are a small number of
+global variables (by design). This file is used for both definitions and
+declarations, so no "extern" declarations need to be made in developers source
+code as long as SMBVARS.C or (preferably) SMBLIB.H is included in the source
+code.
+
+SMBLIB.H
+=======
+&&SMBLIB.H
+$$SMBLIB.H
+
+The SMBLIB.H file contains prototypes of all the functions in the SMBLIB.C
+file. It is necessary to include this file in C source code if any of the
+SMBLIB functions are used. The following C source line will include this file:
+
+#include "smblib.h"
+
+and should be placed near the top of all C source files that use SMBLIB
+functions, variables, constants, or data types.
+
+Function prototypes are necessary for compilers to know the correct calling
+syntax of a function and detect incorrect usage. Prototypes are also useful
+as a quick reference for programmers as to the correct calling syntax of a
+specific function.
+
+SMBLIB.C
+=======
+&&SMBLIB.C
+$$SMBLIB.C
+
+The SMBLIB.C file contains the actual SMBLIB library functions. This source
+file is not a stand alone program, but instead must be compiled and linked
+with a main source file to create the executable program.
+
+The functions in this file are organized in a logical order, but their order
+is actually irrelevant to the compiling, linking, and execution of the
+resulting program.
+
+A comment block preceeds each function, explaining what the function does,
+how the passed parameters are used, and what the return code (if any)
+indicates. A more detailed explanation of each function is included here:
+
+int smb_open(int retry_time)
+----------------------------
+The smb_open() function must be called before the message base is accessed
+(read from or written to). The parameter, retry_time, is the maximum number
+of seconds to wait while retrying to lock the message base header. If
+retry_time is 0, then the message base header is not locked or read (this is
+called "Fast Open" and should only be used when speed is more important than
+checking for compatibility and validity upon opening). The global variable
+smb_file must be initialized with the path and base filename of the message
+base. This function returns 0 on success, 1 if the .SDT file could not be
+opened, 2 if the .SHD file could not be opened, and 3 if the .SID file could
+not be opened. If the message base header could not be locked, this function
+returns -1. If the message base ID is incorrect, it returns -2. And if the
+message base is of an incompatible version, it returns -3.
+
+The errno global variable (standard of most C libraries) will most likely
+contain the error code for open failure.
+
+int smb_open_da(int retry_time)
+-------------------------------
+The smb_open_da() function is used to open the data block allocation file for
+writing messages to a message base. The parameter, retry_time, is the maximum
+number of seconds to wait while retrying to open the file. This function
+returns 0 on success. -1 is returned if an open error other than "Access
+Denied" is returned from the operating system, and the global variable errno
+will contain the error code. -2 is returned if the retry_time has been
+reached, and -3 is returned if the file descriptor could not be converted to
+a stream by the fdopen() function.
+
+fclose(sda_fp) should be called immediately after all necessary file access
+has been completed.
+
+This function is not used with the Hyper Allocation storage method.
+
+int smb_open_ha(int retry_time)
+-------------------------------
+The smb_open_ha() function is used to open the header block allocation file for
+writing messages to a message base. The parameter, retry_time, is the maximum
+number of seconds to wait while retrying to open the file. This function
+returns 0 on success. -1 is returned if an open error other than "Access
+Denied" is returned from the operating system, and the global variable errno
+will contain the error code. -2 is returned if the retry_time has been
+reached, and -3 is returned if the file descriptor could not be converted to
+a stream by the fdopen() function.
+
+fclose(sha_fp) should be called immediately after all necessary file access
+has been completed.
+
+This function is not used with the Hyper Allocation storage method.
+
+int smb_create(ulong max_crcs, ulong max_msgs, ushort max_age, ushort attr
+	,int retry_time)
+--------------------------------------------------------------------------
+The smb_create() function is used to create a new message base or reset an
+existing message base. The parameters max_crcs, max_msgs, max_age, and attr
+are used to set the initial status of the message base status header. The
+parameter, retry_time is the maximum number of seconds to wait while retrying
+to lock the message base header. This functions returns 0 on success or 1 if
+the message base header could not be locked.
+
+int smb_trunchdr(int retry_time)
+--------------------------------
+The smb_trunchdr() function is used to truncate the header file when packing
+the message base and writing the new header information back to the header
+file. The parameter, retry_time is the maximum number of seconds to wait while
+retrying to truncate the header file. Returns 0 on success, -1 if error was
+other than "Access Denied", or -2 if retry_time reached.
+
+int smb_locksmbhdr(int retry_time)
+----------------------------------
+The smb_locksmbhdr() function is used to lock the first message base (status)
+header. The parameter, retry_time is the number of seconds to wait while
+retrying to lock the header. The smb_unlocksmbhdr() function should always be
+used to unlock the header after accessing the message base header (usually
+with smb_getstatus() and/or smb_putstatus()). Returns 0 if successful, -1 if
+unsuccessful.
+
+int smb_unlocksmbhdr()
+----------------------
+The smb_unlocksmbhdr() function is used to unlock a previously locked message
+base header (using smb_lockmsghdr()). Returns 0 on success, non-zero on
+failure.
+
+int smb_getstatus(smbstatus_t *hdr)
+-----------------------------------
+The smb_getstatus() function is used to read the status message base header
+into the hdr structure. Returns 0 on success, 1 on failure.
+
+int smb_putstatus(smbstatus_t hdr)
+----------------------------------
+The smb_putstatus() function is used to write the status information to the
+first message base header. The parameter hdr, contains the status information
+to be written. Returns 0 on success, 1 on failure.
+
+int smb_getmsgidx(smbmsg_t *msg)
+--------------------------------
+The smb_getmsgidx() function is used to get the byte offset for a specific
+message header in the message header file based on the message base index.
+
+If msg->hdr.number is non-zero when this function is called, then the index
+will be searched for this message number. If the message number is found in
+the index, the msg->idx.offset is set to the byte offset of the message header
+record in the header file and msg->offset is set to the record offset of the
+index record in the index file, and the function returns 0. If the message
+number is not found in the index, the function returns 1.
+
+If msg->hdr.number is zero, msg->idx.offset and msg->idx.number are obtained
+from the index record at record offset msg->offset. If msg->offset is an
+invalid record offset when this function is called, the function returns 1.
+Otherwise, the function returns 0.
+
+int smb_getlastidx(idxrec_t *idx)
+---------------------------------
+Reads the last index record of the currently open message base into the
+idxrec_t structure pointed to by idx. Returns 0 if successful, -1 if the index
+is empty or unopened, or -2 if the record can't be read.
+
+int smb_getmsghdrlen(smbmsg_t msg)
+----------------------------------
+The smb_getmsghdrlen() function is used to calculate the total length of
+message header msg including both fixed and variable length portions. This
+function returns the length of the header record in bytes.
+
+long smb_getmsgdatlen(smbmsg_t msg)
+-----------------------------------
+The smb_getmsgdatlen() function is used to calculate the total length of the
+data for message msg. This function returns the length of all data fields
+combined.
+
+int smb_lockmsghdr(smbmsg_t msg, int retry_time)
+------------------------------------------------
+The smb_lockmsghdr() function is used to lock the header record for message
+msg. The parameter retry_time is the maximum number of seconds to wait while
+retrying to lock the header. Returns 0 on success, -1 on failure. The function
+smb_unlockmsghdr() should immediately be called after accessing the message
+header (usually with smb_getmsghdr() or smb_putmsghdr()).
+
+int smb_getmsghdr(smbmsg_t *msg)
+--------------------------------
+The function smb_getmsghdr() is used to read the header record for message
+msg. msg->idx.offset must be initialized to the byte offset of the header
+record in the header file before this function is called. The function
+smb_freemsgmem() must be called to free the memory allocated by this function
+for the header and data felds. This function returns 0 on success, -1 if
+the fixed portion of the message header record could not be read, -2 if the
+message header ID was incorrect, -3 if memory could not be allocated, -4
+if a data field could not be read, -5 if the fixed length portion of a header
+field could not be read, -6 if the variable length portion of a header field
+could not be read, -7 if one or more of the mandatory header fields (SENDER,
+RECIPIENT, or SUBJECT) are missing, -8 if total_dfields extends beyond the
+end of the header record, or -9 if incompatible header version.
+
+Several convenience pointers in the msg structure are initialized by this
+function to point to the last occurance of the SENDER (msg->from), RECIPIENT
+(msg->to), SUBJECT (msg->subj), etc.
+
+int smb_unlockmsghdr(smbmsg_t msg)
+----------------------------------
+The smb_unlockmsghdr() function is used to unlock a previously locked message
+header (with smb_lockmsghdr()). This function returns 0 on success, non-zero
+on failure.
+
+int smb_addcrc(ulong max_crcs, ulong crc, int retry_time)
+---------------------------------------------------------
+The smb_addcrc() function is used to add a CRC-32 to the CRC history file
+for a message base, automatically checking for duplicates. The parameter
+max_crcs should be the max_crcs defined in the status header of the message
+base. The parameter crc, is the CRC-32 of the TEXT_BODY and TEXT_SOUL data
+fields for the message. The parameter retry_time is the maximum number of
+seconds to wait when retrying to open the CRC history file.
+
+This function returns -1 if there was an open error, -2 if the retry_time
+was reached, -3 if there was a memory allocation error, 1 if the CRC already
+exists in the CRC history file (indicating a duplicate message), or 0 on
+success (and no duplicate).
+
+int smb_hfield(smbmsg_t *msg, ushort type, ushort length, void *data)
+---------------------------------------------------------------------
+The smb_hfield() function is used to add a header field to the structure msg.
+The parameters type, length, and data, must be specified according to the
+header field values listed in this specification. This function returns 0
+on success, non-zero on memory allocation error. The function smb_freemsgmem()
+must be called to free the memory allocated by this function.
+
+int smb_dfield(smbmsg_t *msg, ushort type, ulong length)
+--------------------------------------------------------
+The smb_dfield() function is used to add a data field to the structure msg.
+The parameters type and length must be specified according to the data field
+values listed in this specification. This function returns 0 on success,
+non-zero on memory allocation error. The function smb_freemsgmem() must be
+called to free the memory allocated by this function.
+
+int smb_addmsghdr(smbmsg_t *msg,smbstatus_t *status,int storage,int retry_time)
+-------------------------------------------------------------------------------
+The smb_addmsghdr() function is used to add a new message header to the message
+header file and update the index file. The msg and status structures are
+updated to reflect the new total messages, last message number, etc. The
+storage parameter is used to indicate the storage method to use (either
+SMB_SELFPACK, SMB_FASTALLOC, or SMB_HYPERALLOC). If the storage type is
+SMB_SELFPACK, the header block allocation file will be searched for unused
+block(s) to store this header. If the storage type is SMB_FASTALLOC or
+SMB_HYPERALLOC, the header is stored at the end of the header file. Returns 0
+on success, non-zero on failure. The parameter retry_time is the maximum number
+of seconds to wait while retrying to lock and open files.
+
+int smb_putmsg(smbmsg_t msg)
+----------------------------
+The smb_putmsg() function calls both the smb_putmsghdr() and smb_putmsgidx()
+functions to write the header and index elements of a message to the
+appropriate files. Returns 0 on success, non-zero on failure.
+
+int smb_putmsgidx(smbmsg_t msg)
+-------------------------------
+The smb_putmsgidx() function is used to store a message index in the message
+index file. The message index can be for a new message or an existing
+message. Returns 0 on success, non-zero on failure.
+
+int smb_putmsghdr(smbmsg_t msg)
+-------------------------------
+The smb_putmsghdr() function is used to store a message header in the message
+header file. The message header can be for a new message or an existing
+message. Returns 0 on success, non-zero on failure.
+
+void smb_freemsgmem(smbmsg_t msg)
+---------------------------------
+Frees allocated memory for the header and data fields in the msg structure.
+This function must be called to free the memory allocated by the functions
+smb_hfield(), smb_dfield(), and smb_getmsghdr().
+
+long smb_hdrblocks(ulong length)
+--------------------------------
+The smb_hdrblocks() function is used to calculate the number of blocks
+required to store a message header of length size (in bytes). This function
+returns the number of blocks required.
+
+long smb_datblocks(ulong length)
+--------------------------------
+The smb_datblocks() function is used to calculate the number of blocks
+required to store message data of length size (in byte). This function returns
+the number of blocks required.
+
+long smb_allochdr(ulong length)
+-------------------------------
+The smb_allochdr() function is used to search for free blocks to store a
+message header of length bytes and mark the free blocks as allocated in the
+header allocation file. This function returns the byte offset to the header
+record or a negative number on error. The function smb_open_ha() should be
+called prior to calling this function and fclose(sha_fp) should be called
+after. The function is called from smb_addmsghdr(), so you probably have no
+need to call this function directly.
+
+long smb_fallochdr(ulong length)
+--------------------------------
+The smb_fallochdr() function works exactly the same as the smb_allochdr()
+function except it is much faster because the header allocation file is not
+searched for free blocks. The function is called from smb_addmsghdr(), so you
+probably have no need to call this function directly.
+
+long smb_hallochdr(ulong header_offset)
+---------------------------------------
+This smb_hallochdr() functions works exactly the same as the smb_fallochdr()
+function except the status.header_offset is passed as the argument and the
+header allocation (.SHA) file is not updated so smb_open_ha() need not be
+called. The function is called from smb_addmsghdr(), so you probably have no
+need to call this function directly.
+
+long smb_allocdat(ulong length, ushort headers)
+-----------------------------------------------
+The smb_allocdat() function is used to search for free blocks to store length
+amount of data for a message. The parameter headers, indicates the number of
+message headers that are associated with this data. Normally, the headers
+parameter will be 1, unless this message is part of a mass mailing. The offset
+to the allocated data blocks is returned, or a negative value on error. The
+function smb_open_da() should be called prior to calling this function and
+fclose(sda_fp) should be called after.
+
+long smb_fallocdat(ulong length, ushort headers)
+------------------------------------------------
+The smb_fallocdat() function works exactly the same as the smb_allocdat()
+function except it is much faster because the data allocation file is not
+searched for free blocks.
+
+long smb_hallocdat()
+--------------------
+The smb_hallocdat() function works exactly the same as the smb_hallocdat()
+function except no argument is passed and the data allocation file (.SDA) is
+not updated so smb_open_da() need not be called.
+
+int smb_incdat(ulong offset, ulong length, ushort headers)
+----------------------------------------------------------
+The smb_incdat() function is used to increment the header counter in the data
+allocation file for the data starting at the byte offset and length size in
+bytes. The parameter headers, indicates the number of headers to add to the
+current allocation value in the data allocation file. Returns 0 on success,
+non-zero on failure.
+
+int smb_freemsg(smbmsg_t msg, smbstatus_t status)
+-------------------------------------------------
+The smb_freemsg() function is used to free the disk space allocated for the
+header and data fields of the message msg. Returns 0 on success, non-zero on
+failure. The parameter, status, must be the current status from the message
+base header for this message base.
+
+int smb_freemsgdat(ulong offset, ulong length, ushort headers)
+--------------------------------------------------------------
+The smb_freemsgdat() function is used to decrement the data block allocation
+records in the data allocation file associated with the data in the data file
+by the value of the headers parameter (normally 1). The parameter offset
+indicates the byte offset to the beginning of the message data in the data
+file and the parameter length is the total length of the message data.
+Returns 0 on success, non-zero on failure.
+
+int smb_freemsghdr(ulong offset, ulong length)
+----------------------------------------------
+The smb_freemsghdr() function is used to set the header block allocation
+records in the header allocation file to 0 (indicated non-allocated block).
+The parameter offset indicates the byte offset to the beginning of the header
+record being freed and the parameter length indicates the total length of the
+header record. Returns 0 on success, non-zero on failure.
+
+int smb_stack(int op)
+---------------------
+The smb_stack() function is used to save and restore message base information
+so that multiple message bases can be open simultaneously. The stack can
+save up to 4 message bases (allowing 5 simultaneously open message bases).
+The stack is a "last in, first out" storage area for open message bases.
+If the op parameter is SMB_STACK_PUSH, smb_stack() will save (push) the current
+message base onto the stack. Calling smb_stack(SMB_STACK_POP) will restore
+(pop) the most recently pushed message base off the stack. Calling
+smb_stack(SMB_STACK_XCHNG) will exchange the most recently pushed message base
+and the current message base (replacing the top of the stack with the current
+message base).
+
+void smb_close()
+----------------
+Closes the header, data, and index files for the currently open message base.
+
+
+Miscellaneous SMBLIB Files
+==========================
+&&Miscellaneous SMBLIB Files
+$$SMB_MISC
+
+CRC32.H 		C header file for CRC-32 calculations
+-----------------------------------------------------
+This file contains a static 32-bit CRC table (crc32tbl[]) and a macro (ucrc32)
+that uses this table to calculate 32-bit CRCs one byte at a time.
+
+Example:
+
+	ulong crc=0xffffffff;
+
+for(i=0;i<length;i++)
+	crc=ucrc32(buf[i],crc);
+crc=~crc;
+
+
+CRC16.C 		C functions for 16-bit CRC calculations
+-------------------------------------------------------
+This file contains a function (ucrc16), to calculate 16-bit CRCs one byte at a
+time and a function (crc16) that uses the ucrc16() function to calculate the
+16-bit CRC of an ASCIIZ character string.
+
+Example:
+
+	ushort crc;
+
+crc=crc16("Text");
+
+LZH.H			Function prototypes for LZH.C
+---------------------------------------------
+This file contains function prototypes for the two most important functions
+in LZH.C, lzh_encode() and lzh_decode().
+
+Example:
+
+	uchar str[256],lzh[512];
+	long length;
+
+strcpy(str,"This is a string of text");
+length=lzh_encode(str,strlen(str),lzh);
+lzh_decode(lzh,length,str);
+
+
+LZH.C           C functions for LZH encoding (compression/decompression)
+------------------------------------------------------------------------
+This file contains the functions for encoding and decoding LZH compressed
+data. If the macro LZH_DYNAMIC_BUF is defined when this file is compiled,
+temporary buffers will be dynamically allocated as opposed to static. This
+may be slower than the static buffer method, but frees the allocated memory
+after encoding or decoding. If free memory for your application is an issue,
+then define this macro when compiling this file.
+
+Example (Borland C):
+
+bcc -c -DLZH_DYNAMIC_BUF lzh
+
+Example (Watcom C):
+
+wcc -dLZH_DYNAMIC_BUF lzh
+
+SMBLIB Storage Example
+======================
+&&SMBLIB Storage Example
+$$SMB_PUT_
+
+#include "smblib.h"
+#include "crc16.c"
+
+int main(void)
+{
+	char	str[256]						// General purpose string
+		   ,*msg_text="Hello, world!"       // Message text
+		   ,nul_buf[SDT_BLOCK_LEN]={0}		// NULL initialized buffer
+		   ;
+	int 	i								// General purpose integer
+		   ,storage=SMB_SELFPACK			// Default storage method
+		   ,retry=10						// Retry for opening/locking files
+		   ;
+	ushort	max_age=0						// Default maximum age of messages
+		   ,xlat=XLAT_NONE					// Translation string
+		   ,tzone=PST						// Time zone
+		   ,copies=1						// Number of copies of this msg
+		   ;
+	ulong	max_msgs=500					// Default max number of msgs
+		   ,max_crcs=0						// Default max crcs
+		   ,length							// Length of msg text
+		   ,offset							// Offset to msg text in data file
+		   ;
+	smbmsg_t	msg;						// Message structure
+	smbstatus_t status; 					// Message base status record
+
+strcpy(smb_file,"MSGBASE");                 // We'll use "MSGBASE" for the name
+if((i=smb_open(retry))!=0) {				// Can't open!?!
+	printf("smb_open returned %d\n",i);
+	return(1); }
+
+if(!filelength(fileno(shd_fp))) 			// Message base not created yet
+	smb_create(max_crcs 					// Create with default settings
+			  ,max_msgs
+			  ,max_age
+			  ,storage==SMB_HYPERALLOC
+					? SMB_HYPERALLOC : 0	// SMB_EMAIL if this was e-mail
+			  ,retry
+			  );
+
+if((i=smb_locksmbhdr(retry))!=0) {			// Can't lock status base header
+	printf("smb_locksmbhdr returned %d\n",i);
+	smb_close();
+	return(1); }
+
+if((i=smb_getstatus(&status))!=0) { 		// Can't read status base header
+	smb_unlocksmbhdr();
+	smb_close();
+	printf("smb_getstatus returned %d\n",i);
+	return(1); }
+
+if(status.attr&SMB_HYPERALLOC)
+	storage=SMB_HYPERALLOC;
+else
+	storage=SMB_SELFPACK;
+
+length=strlen(msg_text);					// Get length of message
+length+=sizeof(xlat);						// Add length of xlat string
+
+if(storage==SMB_HYPERALLOC) 				// Allocate space for message text
+	offset=smb_hallocdat();
+else {
+	if((i=smb_open_da(retry))!=0) {
+		smb_unlocksmbhdr();
+		printf("smb_open_da returned %d\n",i);
+		smb_close();
+		return(1); }
+	if(storage==SMB_FASTALLOC)
+		offset=smb_fallocdat(length,copies);
+	else
+		offset=smb_allocdat(length,copies);
+	fclose(sda_fp); }
+
+fseek(sdt_fp,offset,SEEK_SET);				// Seek to beginning of data block
+fwrite(&xlat,sizeof(xlat),1,sdt_fp);		// Write xlat string
+fwrite(msg_text,strlen(msg_text),1,sdt_fp); // Write message text
+fwrite(nul_buf,SDT_BLOCK_LEN-length 		// Write NULLs out to end of block
+	,1,sdt_fp);
+fflush(sdt_fp); 							// Flush output buffer
+smb_unlocksmbhdr(); 						// Unlock status base header
+
+memset(&msg,0,sizeof(smbmsg_t));			// Initialize header to NULL
+memcpy(msg.hdr.id,"SHD\x1a",4);             // Always set to SHD^Z
+msg.hdr.version=SMB_VERSION;
+msg.hdr.when_written.time=time(NULL);
+msg.hdr.when_written.zone=tzone;
+msg.hdr.when_imported.time=time(NULL);
+msg.hdr.when_imported.zone=tzone;
+msg.hdr.offset=offset;
+
+strcpy(str,"All");                          // Send message to "All"
+if((i=smb_hfield(&msg,RECIPIENT,strlen(str),str))!=0) {
+	printf("smb_hfield returned %d\n",i);
+	smb_freemsgdat(offset,length,copies);
+	smb_close();
+	return(1); }
+strlwr(str);								// If this were e-mail, idx.to
+msg.idx.to=crc16(str);						// would be the "to" user number
+
+strcpy(str,"Sysop");                        // Send message from "Sysop"
+if((i=smb_hfield(&msg,SENDER,strlen(str),str))!=0) {
+	printf("smb_hfield returned %d\n",i);
+	smb_freemsgdat(offset,length,copies);
+	smb_freemsgmem(msg);
+	smb_close();
+	return(1); }
+strlwr(str);								// If this were e-mail, idx.from
+msg.idx.from=crc16(str);					// would be the "from" user number
+
+strcpy(str,"This is a test");               // Set the message subject/title
+if((i=smb_hfield(&msg,SUBJECT,strlen(str),str))!=0) {
+	printf("smb_hfield returned %d\n",i);
+	smb_freemsgdat(offset,length,copies);
+	smb_freemsgmem(msg);
+	smb_close();
+	return(1); }
+strlwr(str);
+msg.idx.subj=crc16(str);
+
+if((i=smb_dfield(&msg,TEXT_BODY,length))!=0) {
+	printf("smb_dfield returned %d\n",i);
+	smb_freemsgdat(offset,length,copies);
+	smb_freemsgmem(msg);
+	smb_close();
+	return(1); }
+
+if((i=smb_addmsghdr(&msg,&status,storage,retry))!=0) {
+	printf("smb_addmsghdr returned %d\n",i);
+	smb_freemsgdat(offset,length,copies);
+	smb_freemsgmem(msg);
+	smb_close();
+	return(1); }
+
+smb_freemsgmem(msg);						// Unnecessary if exiting main()
+smb_close();								// Unnecessary if exiting main()
+return(0);
+}
+
+SMBLIB Retrieval Example
+========================
+&&SMBLIB Retrieval Example
+$$SMB_GET_
+
+#include "smblib.h"
+
+int main(void)
+{
+	char		ch; 						// General purpose character
+	int 		i,							// General purpose integer
+				retry=10;					// Retry for opening/locking files
+	ushort		xlat;						// Translation string
+	ulong		l;							// General purpose long integer
+    smbmsg_t    msg;                        // Message structure
+
+strcpy(smb_file,"MSGBASE");                 // We'll use "MSGBASE" for the name
+if((i=smb_open(retry))!=0) {				// Can't open!?!
+	printf("smb_open returned %d\n",i);
+	return(1); }
+
+if(!filelength(fileno(shd_fp))) {			// Message base not created yet
+	printf("Empty\n");
+	smb_close();
+	return(0); }
+
+for(msg.offset=0;!ferror(sid_fp);msg.offset++) {
+
+	fseek(sid_fp,msg.offset*sizeof(idxrec_t),SEEK_SET);
+	if(!fread(&msg.idx,1,sizeof(idxrec_t),sid_fp))
+		break;
+
+	if((i=smb_lockmsghdr(msg,retry))!=0) {
+		printf("smb_lockmsghdr returned %d\n",i);
+		break; }
+	if((i=smb_getmsghdr(&msg))!=0) {
+		smb_unlockmsghdr(msg);
+		printf("smb_getmsghdr returned %d\n",i);
+		break; }
+	if((i=smb_unlockmsghdr(msg))!=0) {
+        smb_freemsgmem(msg);
+        printf("smb_unlockmsghdr returned %d\n",i);
+        break; }
+
+	printf("Subj : %s\n",msg.subj);
+	printf("To   : %s\n",msg.to);
+	printf("From : %s\n",msg.from);
+	printf("Date : %s\n",ctime((time_t *)&msg.hdr.when_written.time));
+
+	for(i=0;i<msg.hdr.total_dfields;i++)
+		switch(msg.dfield[i].type) {
+			case TEXT_BODY: 			// Only show BODY and TAIL data fields
+			case TEXT_TAIL:
+				fseek(sdt_fp,msg.hdr.offset+msg.dfield[i].offset
+					,SEEK_SET);
+				fread(&xlat,sizeof(xlat),1,sdt_fp);
+				if(xlat!=XLAT_NONE) 	// No translations supported
+					continue;
+				for(l=sizeof(xlat);l<msg.dfield[i].length;l++) {
+					ch=fgetc(sdt_fp);
+					if(ch)
+						putchar(ch); }
+				printf("\n");
+				break; }
+	printf("\n");
+
+	smb_freemsgmem(msg); }			// Free memory allocated by smb_getmsghdr()
+
+smb_close();
+return(0);
+}
+
+SMBLIB Performance Issues
+=========================
+&&SMBLIB Performance Issues
+$$PERFORM_
+
+Since importing messages is the usually the most time consuming task likely
+undertaken by an SMB application, it is also the most susceptable to design
+issues that effect performance.
+
+Opening and Closing
+-------------------
+When importing multiple messages for a single message base, it appears logical
+to open the message base, import all the messages, then close it. This indeed
+is preferred over opening and closing the message base for each message.
+
+When importing multiple messages for possibly non-consecutive message bases,
+developers may easily make the mistake of opening and closing the message base
+for each message. This is not necessary and can considerably hinder the
+import performance. The easiest solution is to only close the message base and
+open a new one if the next message to be imported is not for the same message
+base as the previously imported message. Example:
+
+smb_file[0]=0;
+for(i=0;i<total_messages_to_be_imported;i++) {
+	if(stricmp(get_messagebase_for_this_message(i),smb_file)) {
+		if(smb_file[0]) 	/* We've already opened one */
+			smb_close();
+		strcpy(smb_file,get_messagebase_for_this_message(i));
+		smb_open(10); }
+	/* Import this message */
+	}
+if(smb_file[0])
+	smb_close();
+
+A more advanced method is to keep multiple message bases open at the same time.
+Due to the likely limitation of total file handles on the system, it is
+suggested to keep the number of simultaneously open message bases at or below
+3. SMBLIB includes the function smb_stack() to easily "push" and "pop" message
+bases without closing them (push is the equivalent to "save" and pop is the
+equivalent to "restore"). The downside of this function is that you cannot
+access message bases on the stack without actually popping them off (in reverse
+of the order they were pushed). You can however "exchange" the current message
+base with the message base on the top of the stack (most recently pushed).
+To intelligently juggle more than two open message bases, the developer should
+create their own equivalent of the smb_stack() function so they can access the
+message bases on the stack without popping them off. An example of keeping a
+maximum of two message bases open using smb_stack():
+
+	char last_messagebase[128],new_messagebase[128];
+
+smb_file[0]=0;
+last_messagebase[0]=0;
+for(i=0;i<total_messages_to_be_imported;i++) {
+	strcpy(new_messagebase,get_messagebase_for_this_message(i));
+	if(stricmp(new_messagebase,smb_file)) { 	/* Not current message base */
+		if(smb_file[0]) {						/* We've already opened one */
+			if(!stricmp(new_messagebase,last_messagebase)) { /* Same as last */
+				strcpy(last_messagebase,smb_file);
+				smb_stack(SMB_STACK_XCHNG); }		/* Retore previous base */
+			else {
+				if(last_messagebase[0]) {
+					smb_stack(SMB_STACK_XCHNG);
+					smb_close();
+					strcpy(last_messagebase,new_messagebase); }
+				else {
+					strcpy(last_messagebase,smb_file);
+					smb_stack(SMB_STACK_PUSH); }	/* Save current base */
+				strcpy(smb_file,new_messagebase);
+				smb_open(10); } }
+		else {
+			strcpy(smb_file,new_messagebase);
+			smb_open(10); } }
+	/* Import this message */
+	}
+if(smb_file[0])
+    smb_close();
+if(last_messagebase[0]) {
+	smb_stack(SMB_STACK_POP);
+	smb_close(); }
+
+The second example would be of negligible performance gain over the first
+example (6 open operations versus 7) if the messages to import were in the
+following order:
+
+msg[0] --> msgbase[0]		// 0 opened
+msg[1] --> msgbase[1]		// 0 pushed 1 opened
+msg[2] --> msgbase[1]
+msg[3] --> msgbase[2]		// 1 closed 0 popped 0 closed 2 opened
+msg[4] --> msgbase[0]		// 2 pushed 0 opened
+msg[5] --> msgbase[2]		// 0 pushed 2 popped (exchanged)
+msg[6] --> msgbase[3]		// 2 closed 0 popped 0 closed 3 opened
+msg[7] --> msgbase[0]		// 3 pushed 0 opened
+
+The second example would be of significant performance gain over the first
+example (4 open operations versus 8) if the messages to import were in the
+following order:
+
+msg[0] --> msgbase[0]		// 0 opened
+msg[1] --> msgbase[1]		// 0 pushed 1 opened
+msg[2] --> msgbase[0]		// 1 pushed 0 popped (exchanged)
+msg[3] --> msgbase[1]		// 0 pushed 1 popped (exchanged)
+msg[4] --> msgbase[0]		// 1 pushed 0 popped (exchanged)
+msg[5] --> msgbase[2]		// 0 pushed 1 popped (exchanged) 1 closed 2 opened
+msg[6] --> msgbase[3]		// 2 pushed 0 popped (exchanged) 0 closed 3 opened
+msg[7] --> msgbase[2]		// 3 pushed 2 popped (exchanged)
+
+More advanced use of "stack-like" message base file handle storage can easily
+reduce the number of open operations, therefore increasing import performance
+under more adverse message base ordering conditions.
+
+Compression
+-----------
+If any message data compression features are offered by the application, it
+is important the the application not unnecessarily compress data that will
+not save any storage space. While this may seem an obvious statement, please
+review the following pseudo-code example:
+
+if ( message_data_length < SDT_BLOCK_LEN )
+	// Store uncompressed data
+else {
+	// Compress data
+	if ( ( compressed_data_length / SDT_BLOCK_LEN )
+		< ( message_data_length / SDT_BLOCK_LEN ) ) // Saves a block or more
+		// Store compressed data
+	else
+		// Store uncompressed data
+	}
+
+Since the SMB format stores message data in fixed length blocks, there is no
+point in storing a message in compressed format if it requires the same number
+of blocks as the uncompressed format (i.e. a message that is two blocks in
+length in uncompressed format and only a block and a half in length when
+compressed should not be stored in compressed format since it still requires
+two full blocks of storage). It is important to note that in the above example,
+the length of the data translation string was not taken into account in
+determining the number of required blocks. Also, the smb_datblocks() function
+is normally used in determing the number of required blocks to store a given
+data length and it is a little more involved than simply dividing the length of
+the data by SDT_BLOCK_LEN.
+
+
+Bibliography
+============
+&&Bibliography
+$$BIBLIOGR
+
+Title	  : The C Programming Language
+Publisher : Prentice Hall
+Author	  : Brian W. Kernighan and Dennis M. Ritchie
+
+Document  : ARPANET Request for Comments (RFC) #822
+Title	  : Standard for the Format of ARPA Internet text messages
+Publisher : SRI International
+Author	  : David H. Crocker, University of Delaware
+
+Document  : FTS-0001
+Publisher : FSC
+Author	  : Randy Bush, Pacific Systems Group
+
+Document  : FTS-0004
+Title	  : EchoMail Specification
+Publisher : FSC
+Author	  : Bob Hartman
+
+Document  : FTS-0009
+Title	  : A standard for unique message identifiers and reply chain linkage
+Publisher : FSC
+Author	  : Jim Nutt
+
+Document  : FSC-00046
+Title	  : A Product Idenfifier for FidoNet Message Handlers
+Publisher : FSC
+Author	  : Joaquim H. Homrighausen
+
+Document  : FSC-00053
+Title	  : Specifications for the ^aFLAGS field
+Publisher : FSC
+Author	  : Joaquim H. Homrighausen
+
+
+Implementations
+===============
+&&Implementations
+$$IMPLEMEN
+
+Product   : Synchronet Multinode BBS Software
+Developer : Digital Dynamics
+Level	  : III
+Version   : 2.20
+
+Product   : Synchronet/FidoNet Import/Export Utility (SBBSFIDO)
+Developer : Digital Dynamics
+Level	  : III
+Version   : 2.23
+
+Product   : Synchronet UTI (Universal Text Interface) Driver
+Developer : Digital Dynamics
+Level	  : III
+Version   : 2.23
+
+Product   : SBBSecho FidoNet Packet Tosser for Synchronet
+Developer : Digital Dynamics
+Level	  : III
+Version   : 1.11
+
+Product   : NetXpress Internet UUCP for Synchronet
+Developer : Merlin Systems
+Level	  : II
+Version   : 1.50
+
+Product   : InterEcho FidoNet Packet Tosser
+Developer : InterMail Sales Inc
+Level	  : II
+Version   : 1.11
diff --git a/src/sbbs2/smb/docs/smb_read.me b/src/sbbs2/smb/docs/smb_read.me
new file mode 100644
index 0000000000..9cccaf613b
--- /dev/null
+++ b/src/sbbs2/smb/docs/smb_read.me
@@ -0,0 +1,64 @@
+Synchronet Message Base (SMB) Specification v2.01 - READ.ME -
+=============================================================
+
+Files:
+
+        Miscellaneous
+        -------------
+        SMB_READ.ME     This file
+        FILE_ID.DIZ     Description of archive
+        SMB.HST         History of changes made since v1.00
+
+	Technical Specification
+        -----------------------
+        SMB.PRN         Paginated, to be printed at 12cpi (!)
+        SMB.TXT         Not paginated, for online viewing
+
+        Message Base C Library
+        ----------------------
+        SMBDEFS.H       C data types and constants
+        SMBLIB.H        C function prototypes
+        SMBLIB.C        C library functions
+	CRC32.H 	C header file for 32-bit CRC calculations
+        CRC16.C         C source file for 16-bit CRC calculations
+        LZH.H           C header file for LZH compression functions
+        LZH.C           C source file for LZH compression functions
+
+        Message Base Utility
+        --------------------
+        SMBUTIL.H       C header file
+        SMBUTIL.C       C source file
+        SMBUTIL.BOR     C makefile (for Borland C: make -f smbutil.bor)
+	SMBUTIL.WAT     C makefile (for Watcom C: wmake -f smbutil.wat
+                                      32-bit OS2: wmake -f smbutil.wat OS=OS2
+				      32-bit DOS: wmake -f smbutil.wat OS=DOSX)
+        SMBUTIL.EXE     Compiled and linked for 16-bit DOS
+
+        Check Message Base for Corruption
+        ---------------------------------
+        CHKSMB.C        C source file
+        CHKSMB.MAK      C makefile (for Borland C: make -f chksmb)
+        CHKSMB.EXE      Compiled and linked for 16-bit DOS
+
+	Rebuild Message Base Index and Allocation Files
+	-----------------------------------------------
+	FIXSMB.C	C source file
+	FIXSMB.MAK	C makefile (for Borland C: make -f fixsmb)
+	FIXSMB.EXE	Compiled and linked for 16-bit DOS
+
+        Convert from FidoNet (FTSC-1) to SMB
+        ------------------------------------
+        FIDO2SMB.C      C source file
+        FIDO2SMB.MAK    C makefile (for Borland C: make -f fido2smb)
+
+        Convert from Internet (RFC-822) to SMB
+        --------------------------------------
+        INET2SMB.C      C source file
+        INET2SMB.MAK    C makefile (for Borland C: make -f inet2smb)
+
+        Convert from QWK to SMB
+        -----------------------
+        QWK2SMB.C       C source file
+        QWK2SMB.MAK     C makefile (for Borland C: make -f qwk2smb)
+
+/* END of SMB_READ.ME */
-- 
GitLab