Project: Webinterface II - Fidonet Messagebase Structure: SQUISH SDK
Squish Developers Kit
Version 2.00
Created May 23rd, 1994
Documentation produced by Scott Dudley
Copyright 1991-1994 by SCI Communications. All rights reserved.
Maximus and Squish are trademarks of SCI Communications.
TABLE OF CONTENTS
INTRODUCTION . . . . . . . . . . . . . . . . . . . . . . . . 1
COPYRIGHT AND DISTRIBUTION RESTRICTIONS . . . . . . . . . . . 2
Distribution Policy . . . . . . . . . . . . . . . . . . 2
No Warranty . . . . . . . . . . . . . . . . . . . . . . 2
Author Contact Information . . . . . . . . . . . . . . . 3
MSGAPI FUNCTION REFERENCE . . . . . . . . . . . . . . . . . . 4
Initialization/Termination Functions . . . . . . . . . . 4
MsgOpenApi . . . . . . . . . . . . . . . . . . . . 4
MsgCloseApi . . . . . . . . . . . . . . . . . . . . 6
Area-Oriented Functions . . . . . . . . . . . . . . . . 6
MsgOpenArea . . . . . . . . . . . . . . . . . . . . 6
MsgCloseArea . . . . . . . . . . . . . . . . . . . 7
MsgValidate . . . . . . . . . . . . . . . . . . . . 8
MsgGetHighWater . . . . . . . . . . . . . . . . . . 8
MsgSetHighWater . . . . . . . . . . . . . . . . . . 9
MsgGetCurMsg . . . . . . . . . . . . . . . . . . . 9
MsgGetNumMsg . . . . . . . . . . . . . . . . . . . 9
MsgGetHighMsg . . . . . . . . . . . . . . . . . . . 9
MsgLock . . . . . . . . . . . . . . . . . . . . . . 10
MsgUnlock . . . . . . . . . . . . . . . . . . . . . 10
MsgInvalidHarea . . . . . . . . . . . . . . . . . . 10
Message-Oriented Functions . . . . . . . . . . . . . . . 11
MsgOpenMsg . . . . . . . . . . . . . . . . . . . . 11
MsgCloseMsg . . . . . . . . . . . . . . . . . . . . 12
MsgReadMsg . . . . . . . . . . . . . . . . . . . . 12
MsgWriteMsg . . . . . . . . . . . . . . . . . . . . 15
MsgKillMsg . . . . . . . . . . . . . . . . . . . . 17
MsgGetCurPos . . . . . . . . . . . . . . . . . . . 18
MsgSetCurPos . . . . . . . . . . . . . . . . . . . 18
MsgGetTextLen . . . . . . . . . . . . . . . . . . . 18
MsgGetCtrlLen . . . . . . . . . . . . . . . . . . . 19
MsgInvalidHmsg . . . . . . . . . . . . . . . . . . 19
UMSGID Translation Functions . . . . . . . . . . . . . . 19
MsgMsgnToUid . . . . . . . . . . . . . . . . . . . 19
MsgUidToMsgn . . . . . . . . . . . . . . . . . . . 20
CtrlInfo Manipulation Functions . . . . . . . . . . . . 20
MsgGetCtrlToken . . . . . . . . . . . . . . . . . . 20
MsgFreeCtrlToken . . . . . . . . . . . . . . . . . 21
MsgRemoveToken . . . . . . . . . . . . . . . . . . 21
MsgGetNumKludges . . . . . . . . . . . . . . . . . 21
MsgCvt4D . . . . . . . . . . . . . . . . . . . . . 22
MsgCreateCtrlBuf . . . . . . . . . . . . . . . . . 23
MsgCvtCtrlToKludge . . . . . . . . . . . . . . . . 24
MsgFreeCtrlBuf . . . . . . . . . . . . . . . . . . 24
Squish-Specific Functions . . . . . . . . . . . . . . . 24
SquishHash . . . . . . . . . . . . . . . . . . . . 24
SquishSetMaxMsg . . . . . . . . . . . . . . . . . . 25
API Changes from MsgAPI Version 0 . . . . . . . . . . . 25
BUILDING MSGAPI APPLICATIONS . . . . . . . . . . . . . . . . 26
Source File Requirements . . . . . . . . . . . . . . . . 26
Compiling Source Files . . . . . . . . . . . . . . . . . 26
Linking Your Application . . . . . . . . . . . . . . . . 27
Building the Sample Applications . . . . . . . . . . . . 28
BUILDING THE MSGAPI SOURCE . . . . . . . . . . . . . . . . . 29
OS/2 DLL DEVELOPMENT . . . . . . . . . . . . . . . . . . . . 30
Sample Feature DLLs . . . . . . . . . . . . . . . . . . 30
KILLRCAT.DLL . . . . . . . . . . . . . . . . . . . 30
MSGTRACK.DLL . . . . . . . . . . . . . . . . . . . 31
Feature DLL Programming Interface . . . . . . . . . . . 31
Building Feature DLLs . . . . . . . . . . . . . . . . . 37
SQUISH FILE FORMAT SPECIFICATION . . . . . . . . . . . . . . 38
Squish Philosophy . . . . . . . . . . . . . . . . . . . 38
Squish Data Types . . . . . . . . . . . . . . . . . . . 39
Data File Format . . . . . . . . . . . . . . . . . . . . 41
Index File Format . . . . . . . . . . . . . . . . . . . 48
Message Links . . . . . . . . . . . . . . . . . . . . . 50
Reading Messages . . . . . . . . . . . . . . . . . . . . 51
Writing Messages . . . . . . . . . . . . . . . . . . . . 52
Deleting Messages . . . . . . . . . . . . . . . . . . . 53
Concurrency Considerations . . . . . . . . . . . . . . . 53
INTRODUCTION
This document serves as a reference guide for version 2.0 of the
Squish Developers Kit. The primary purpose of this document is
to describe the C Application Programming Interface (API) for
Squish bases, also known as MsgAPI.
Information is included on rebuilding the MsgAPI source,
compiling applications that use MsgAPI, and a description of each
function within the MsgAPI. The standard MsgAPI distribution
supports Turbo C, Borland C, WATCOM C, and Microsoft C.
This document does not provide a tutorial on using the MsgAPI,
but those with a reasonable amount of programming experience
should have little trouble in using MsgAPI.
In addition, this document includes information on the "feature
DLL" interface that is supported by the OS/2 version of the
Squish tosser/scanner program. These hooks allow third-party
developers to manipulate messages when they are being processed
by Squish. These hooks are called when Squish is tossing and
packing messages.
Finally, this document also describes the physical layout of the
Squish file format. Although use of the MsgAPI is recommended,
this information should allow third-party developers to access
the Squish base directly, without going through a set of API
calls.
WARNING! THE SQUISH DEVELOPER'S KIT IS NOT A SUPPORTED PRODUCT.
We would be interested in hearing of any problems that you have
with this product, but we cannot provide technical support for
MsgAPI users.
While we have tried to ensure that the material provided in this
kit is correct, we also cannot guarantee that the code and
documentation will function correctly on all systems.
Please read the following section for information on distribution
terms and the product warranty.
Squish Developers Kit Version 2.0 - Page 1
COPYRIGHT AND DISTRIBUTION RESTRICTIONS
All of the source code, header files and documentation within the
Squish Developers Kit is copyright 1991-1994 by SCI
Communications. All rights reserved.
Distribution Policy
Although the MsgAPI code is copyrighted, you are granted a
limited license to modify or usr MsgAPI in your own applications.
1) You may use the MsgAPI code as part of any type of
application, including freeware, shareware, or commercial
programs. No royalties or licensing fees are required.
2) This code must not be sold on its own. While it is
permissible to sell an application that uses or contains the
MsgAPI code, you may not charge any extra fee (above the
base cost of the product) for just providing the user with a
copy of the Squish Developers kit.
Also, you may not charge any extra fee (above the base cost
of the product) for providing Squish base support within
your program.
3) If you use this code in your application, you must give
credit for the MsgAPI code and indicate that "Squish" is a
trademark of SCI Communications.
4) If you wish to identify your application as being compatible
with the Squish message format, you must ensure that the
original MsgAPI code is capable of reading and writing to
the message bases that are created by your program. If the
original MsgAPI code is unable to read the files created by
your program, you may not label your program as Squish-
compatible.
AS LONG AS THE ABOVE FOUR CONDITIONS ARE FOLLOWED, MSGAPI MAY BE
INCORPORATED INTO ANY TYPE OF APPLICATION WITHOUT CHARGE,
REGARDLESS OF THE NATURE OF THE APPLICATION (COMMERCIAL,
SHAREWARE, OR FREEWARE).
No Warranty
BECAUSE THE SQUISH DEVELOPERS KIT (SDK) IS LICENSED FREE OF
CHARGE, WE PROVIDE ABSOLUTELY NO WARRANTY. EXCEPT WHEN OTHERWISE
STATED IN WRITING, SCI COMMUNICATIONS AND/OR OTHER PARTIES
PROVIDE THE SDK "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF
SQUISH, AND THE ACCURACY OF ITS ASSOCIATED DOCUMENTATION, IS WITH
Squish Developers Kit Version 2.0 - Page 2
YOU. SHOULD THE SDK OR ITS ASSOCIATED DOCUMENTATION PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR
OR CORRECTION.
IN NO EVENT WILL SCI COMMUNICATIONS BE RESPONSIBLE IN ANY WAY FOR
THE BEHAVIOUR OF MODIFIED VERSIONS OF THE SDK. IN NO EVENT WILL
SCI COMMUNICATIONS AND/OR ANY OTHER PARTY WHO MAY MODIFY AND
REDISTRIBUTE SDK AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
OTHER PROGRAMS) THE SDK, EVEN IF SCI COMMUNICATIONS HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY
ANY OTHER PARTY.
Author Contact Information
If you make any modifications to this code, and if you feel that
the author would be interested in the changes, please forward the
source code to the author.
You can contact the author at any of the addresses listed below:
FidoNet: 1:249/106
Internet: sjd@f106.n249.z1.fidonet.org
CompuServe: >INTERNET:sjd@f106.n249.z1.fidonet.org
BBS: (613) 634-3058 (V.32bis)
Surface mail:
777 Downing St.
Kingston, Ont.
Canada K7M 5N3
The author can also be reached through the FidoNet EchoMail
conferences called MUFFIN (Maximus support) and TUB (Squish
support).
Sending correspondence via electronic mail is strongly preferred,
and the use of surface mail is discouraged. However, if you
really have to send paper mail (and expect to receive a reply),
please enclose a self-addressed, stamped envelope. (Users
outside of Canada should include an international postal reply
coupon instead of a stamp.)
DO NOT ATTEMPT TO CONTACT THE AUTHOR BY TELEPHONE! VOICE SUPPORT
WILL NOT BE PROVIDED FOR THIS PRODUCT!
Squish Developers Kit Version 2.0 - Page 3
MSGAPI FUNCTION REFERENCE
This section describes all of the functions in the MsgAPI that
can be used by application programs.
Initialization/Termination Functions
MsgOpenApi
sword MsgOpenApi(struct _minf *minf);
The MsgOpenApi function is used to initialize the MsgAPI.
This function must be called before any of the other API
functions are called, or else the results are undefined.
This function serves to initialize any needed structures,
and to prepare the message bases for use.
This function accepts one argument: a pointer to a
structure containing information about the application.
This structure contains the following information:
struct _minf
{
/* The following fields are required for all
* MsgAPI clients:
*/
word req_version;
word def_zone;
word haveshare;
/* The following fields are required when
* req_version >= 1:
*/
void OS2FAR * (MAPIENTRY *palloc)(size_t size);
void (MAPIENTRY *pfree)(void OS2FAR *ptr);
void OS2FAR * (MAPIENTRY *repalloc)(void OS2FAR *ptr,
size_t size);
void far * (MAPIENTRY *farpalloc)(size_t size);
void (MAPIENTRY *farpfree)(void far *ptr);
void far * (MAPIENTRY *farrepalloc)(void far *ptr,
size_t size);
};
'req_version' indicates the MsgAPI revision level that the
application is requesting. The compile-time revision level
can always be accessed using the constant 'MSGAPI_VERSION'.
'def_zone' should contain a default FidoNet zone number.
Squish Developers Kit Version 2.0 - Page 4
Certain message systems, such as the FTSC-0001 *.MSG format,
do not store zone information with each message. When the
API encounters such a message and no zone is present, the
specified zone will be used instead. A 'def_zone' of 0
indicates that nothing is to be inferred about the zone
number of a message, and in that case, the API functions
will return 0 as the zone number for any message with an
unknown zone.
'haveshare' is automatically filled in by the internal API
routines, and this flag indicates whether or not the DOS
"SHARE.EXE" program is currently loaded. Note that SHARE
must always be loaded to access Squish-format bases in a
multitasking or network environment. This field is not used
in the OS/2 version of the MsgAPI.
If 'req_version' is more than or equal to 1, the final six
fields in the _minf structure must be provided. These
fields are memory allocation hooks that MsgAPI will call
whenever it needs to allocate memory. (If req_version is 0,
or if one of the function pointers in this structure is
NULL, then MsgAPI will use its own memory allocation
routines.)
'palloc' is called to allocate a block of near memory. This
function should behave in the same manner as the ANSI
malloc() function. If this field is NULL, the MsgAPI will
use its own malloc() function.
'pfree' is called to free a block of near memory. This
function should behave in the same manner as the ANSI free()
function. If this field is NULL, the MsgAPI will use its
own free() function.
'repalloc' is called to reallocate a block of near memory.
This function should behave in the same manner as the ANSI
realloc() function. If this field is NULL, the MsgAPI will
use its own realloc() function.
'farpalloc' is called to allocate a block of far memory.
This function should behave in the same manner as the ANSI
malloc() function, except that a far pointer should be
returned. If this field is NULL, MsgAPI will use its own
malloc() function.
'farpfree' is called to free a block of far memory. This
function should behave in the same manner as the ANSI free()
function, except that a far pointer should be accepted. If
this field is NULL, MsgAPI will use its own free() function.
'farrepalloc' is called to reallocate a block of far memory.
This function should behave in the same manner as the ANSI
Squish Developers Kit Version 2.0 - Page 5
realloc() function, except that a far pointer should be
accepted and returned. If this field is NULL, MsgAPI will
use its own realloc() function.
MsgOpenApi() returns a value of 0 if the initialization was
performed successfully, and -1 if a problem was encountered.
MsgCloseApi
sword MsgCloseApi(void);
This function is used to deinitialize the MsgAPI. This
function performs any clean-up actions which may be
necessary, including the closing of files and releasing
allocated memory. This function should be called before the
application terminates.
MsgCloseApi returns a value of 0 if the API was successfully
deinitialized, and -1 otherwise.
Area-Oriented Functions
MsgOpenArea
HAREA MsgOpenArea(byte *name, word mode, word type);
This function is used to open or create a message area.
This function accepts three parameters:
'name' is the name of the message area to open. The
contents of this string are implementation-defined. (See
'type' for more information.)
'mode' is the mode with which the area should be opened.
Values for 'mode' are as follows:
MSGAREA_NORMAL Open the message area in a normal access
mode. If the area does not exist, this
function fails.
MSGAREA_CRIFNEC Open the message area in a normal access
mode. If the area does not exist, the
MsgAPI attempts to create the area. If
the area cannot be created, this
function fails.
MSGAREA_CREATE Create the message area. If the area
already exists, it is truncated or
started anew with no messages. If the
area cannot be created, this function
Squish Developers Kit Version 2.0 - Page 6
fails.
'type' specifies the type of message area to open. 'type'
can have any of the following values:
MSGTYPE_SDM Star Dot MSG (SDM). This specifies a
FTSC-0001 compatible access mode, and it
instructs the MsgAPI to create and read
Fido-compatible messages for this area. If
MSGTYPE_SDM is specified, 'name' should
contain the path to the *.MSG directory.
MSGTYPE_SQUISH Squish (*.SQ?) format. This specifies that
the proprietary Squish message format is to
be used for this area. 'name' should give
the path and root name (eight characters) for
the message area.
In addition, if the mask 'MSGTYPE_ECHO' is bitwise 'OR'ed
with the 'MSGTYPE_SDM' value, the area in question will be
treated as a FidoNet-style echomail area. This instructs
the MsgAPI to keep high-water mark information in the 1.MSG
file, and to stop the normal MsgAPI functions from writing
to the first message in each area. Other message formats
have a cleaner way of storing the high-water mark, so this
mask is only required for *.MSG areas.
Other values for 'type' are currently reserved.
If this function succeeds in opening the message area, an
HAREA handle is returned. This handle does not contain any
information which can be used directly by the caller; all
interaction should be performed through the MsgAPI functions
only.
If this function fails, NULL is returned, and the global
'msgapierr' variable is set to one of the following values:
MERR_NOMEM Not enough memory for requested task
MERR_NOENT The area did not exist or could not be
created.
MERR_BADF The message area is structurally damaged.
MsgCloseArea
sword MsgCloseArea(HAREA ha);
This function serves to "close" a message area. This
function performs all clean-up actions necessary, such as
Squish Developers Kit Version 2.0 - Page 7
closing files, changing directories, and so on.
The function accepts one argument, which must be an HAREA
handle returned by the MsgOpenArea function. The
MsgCloseArea function should be called for each area opened
by MsgOpenArea.
If the area was successfully closed, MsgCloseArea returns 0.
Otherwise, a value of -1 is returned and msgapierr is set to
one of the following:
MERR_BADH An invalid handle was passed to the function.
MERR_EOPEN Messages are still "open" in this area, so
the area could not be closed.
MsgValidate
sword MsgValidate(word type, byte *name);
The MsgValidate function validates a particular message
area, determining whether or not the area exists and if it
is valid.
'type' is the type of the message area, using the same
constants as specified for MsgOpenARea.
'name' is the name of the message area, using the same
format as specified for MsgOpenArea.
MsgValidate returns the value 1 if the area exists and is
valid. Otherwise. MsgValidate returns 0.
MsgGetHighWater
dword MsgGetHighWater(HAREA ha);
The MsgGetHighWater function returns the 'high water marker'
for the current area. This number represents the highest
message number that was processed by a message export or
import utility.
'ha' is a message area handle, as returned by MsgOpenArea.
The high water marker is automatically adjusted when
messages are killed.
The MsgGetHighWater function returns the high water mark
number on success, or 0 on error and sets msgapierr to:
Squish Developers Kit Version 2.0 - Page 8
MERR_BADH
MsgSetHighWater
sword MsgSetHighWater(HAREA mh, dword hwm);
The MsgSetHighWater function sets the 'high water marker'
for the current area.
'ha' is a message area handle, as returned by MsgOpenArea.
'hwm' is the new high water marker to use for the specified
area.
The MsgGetHighWater function returns 0 on success, or -1 on
error and sets msgapierr to:
MERR_BADH
MsgGetCurMsg
dword MsgGetCurMsg(HAREA ha);
The MsgGetCurMsg function returns the number of the last
message accessed with MsgOpenMsg.
'ha' is a message area handle, as returned by MsgOpenArea.
MsgGetCurMsg returns the current message number on success,
or 0 if there is no current message.
MsgGetNumMsg
dword MsgGetNumMsg(HAREA ha);
The MsgGetNumMsg function returns the number of messages in
the current message area. On error, MsgGetNumMsg returns
(dword)-1 and sets msgapierr to:
MERR_BADH
MsgGetHighMsg
dword MsgGetHighMsg(HAREA mh);
The MsgGetHighMsg function returns the number of the highest
message in the specified area. On error, MsgGetHighMsg
returns (dword)-1 and sets msgapierr to:
Squish Developers Kit Version 2.0 - Page 9
MERR_BADH
MsgLock
sword MsgLock(HAREA ha);
The MsgLock function 'locks' a message area for exclusive
access. This function may enable buffering or otherwise
improve performance, so it is advised that MsgLock be called
if speed is a concern when accessing the area.
All of the MsgAPI functions automatically perform file
locking and sharing internally, so this function is only
required when high performance is necessary.
'ha' is a message area handle, as returned by the
MsgOpenArea function.
MsgLock returns 0 on success. On error, MsgLock returns -1
and sets msgapierr to one of the following:
MERR_BADH
MsgUnlock
sword MsgUnlock(HAREA ha);
The MsgUnlock function unlocks a previously-locked message
area.
'ha' is a message area handle, as returned by MsgOpenMsg.
MsgUnlock returns 0 on success, or it returns -1 on error
and sets msgapierr to:
MERR_BADH
MsgInvalidHarea
sword MsgInvalidHarea(HAREA ha);
The MsgInvalidHarea function tests the given message area
handle for validity. If the message area handle is invalid,
this function returns TRUE and sets msgapierr to MERR_BADH.
Otherwise, a value of FALSE will be returned. returned.
Squish Developers Kit Version 2.0 - Page 10
Message-Oriented Functions
MsgOpenMsg
HMSG MsgOpenMsg(HAREA mh, word mode, dword msgn);
This function "opens" a message for access, and it must be
used to read from or write to a given message.
The function accepts three arguments:
'mh' is a message area handle, as returned by the
MsgOpenArea function.
'mode' is an access flag, containing one of the following
manifest constants:
MOPEN_CREATE Create a new message. This mode should only
be used for creating new messages.
MOPEN_READ Open an existing message for reading ONLY.
MOPEN_WRITE Open an existing message for writing ONLY.
MOPEN_RW Open an existing message for reading AND
writing.
'msgn' is the specified message number to open. If mode is
either MOPEN_READ, MOPEN_WRITE or MOPEN_RW, the message
number must currently exist in the specified area. If mode
is set to MOPEN_CREATE, a value of 0 for 'msgn' indicates
that a new message should be created, and assigned a number
one higher than the current highest message. If 'msgn' is
non-zero, but MOPEN_CREATE is set to the number of a
currently-existing message, the specified message will be
truncated and the new message will take its place.
For MOPEN_READ or MOPEN_RW, the following constants can also
be passed in place of 'msgn':
MSGNUM_CUR Open the last message which was accessed by
MsgOpenMsg.
MSGNUM_PREV Open the message prior to the last message
accessed by MsgOpenMsg.
MSGNUM_NEXT Open the message after the last message
accessed by MsgOpenMsg.
The MsgAPI maintains the number of the last message opened
by MsgOpenMsg, which is used when processing these
constants. (See also MsgGetCurMsg.)
Squish Developers Kit Version 2.0 - Page 11
If the message was successfully opened, the MsgOpenMsg
function will return a HMSG handle. Otherwise, a value of
NULL is returned, and msgapierr will be set to one of the
following:
MERR_NOENT
MERR_NOMEM
MERR_BADF
MERR_BADA
MERR_BADH
MsgCloseMsg
sword MsgCloseMsg(HMSG hmsg);
The MsgCloseMsg function serves to "close" a message which
has been previously opened by MsgOpenMsg. All messages
should be closed after use, or else data loss may result.
This function accepts a single argument, which is the
message handle that was returned by MsgOpenMsg.
If the message was successfully closed, this function
returns 0. Otherwise, a value of -1 is returned, and
msgapierr is set to:
MERR_BADH
MsgReadMsg
dword MsgReadMsg(HMSG hmsg, dword ofs, dword bytes,
byte *text, dword cbyt, byte *ctxt);
The MsgReadMsg function is used to read a message from disk.
This function can be used to read all parts of a message,
including the message header, message body, and control
information.
'hmsg' is a message handle, as returned by the MsgOpenMsg
function. The message in question must have been opened
with a mode of either MOPEN_READ or MOPEN_RW.
'msg' is a pointer to an XMSG (extended message) structure.
The format of this structure is detailed in the "Squish File
Format Specification" section, but it contains all of the
message information that is found in the message header,
including the to/from/subject fields, origination and
arrival dates, 4D origination and destination addresses, and
so forth. (See the appendices for specific information on
the XMSG structure itself.) If the application wishes to
Squish Developers Kit Version 2.0 - Page 12
read the header of a given message, this argument should
point to an XMSG structure. Otherwise, this argument should
be NULL, which informs the API that the message header does
not need to be read.
'ofs' is used for reading message text in a multiple-pass
environment. This defines the offset in the message body
from which the API should start reading. To start reading
from the beginning of the message, a value of 0L should be
given. Otherwise, the offset into the message (in bytes)
should be given for this argument. If the application does
not wish to read the message body, this argument should be
set to 0L.
'bytes' represents the maximum number of bytes to read from
the message. Fewer bytes may be read, but the API will read
no more than 'bytes' during this call. (See 'text', and
also this function's return value.) If the application does
not wish to read the message body, this argument should be
set to 0L.
'text' is a pointer to a block of memory, into which the API
will place the message body. The message body will be read
from the position specified by 'ofs', up to a maximum of
'bytes' bytes. If the application does not wish to read the
message body, this argument should be set to NULL.
'cbyt' represents the maximum number of bytes of control
information to read from the message.
'ctxt' is a pointer to a block of memory, into which the API
will place the message control information. No more than
'cbyt' bytes of control information will be placed into the
buffer. NOTE: unlike the message text functions, control
information can only be read in one pass.
The text read by this function is free-form. The message
body may or may not contain control characters, NULs, or any
other sequence of characters. Messages are simply treated
as a block of bytes, with no interpretation whatsoever.
In FidoNet areas, the message body consists of one or more
paragraphs of text. Each paragraph is delimited by a hard
carriage return, '\r', or ASCII 13. Each paragraph can be
of any length, so the text should be wordwrapped onto
logical lines before being displayed. If created by older
applications, paragraphs may also contain linefeeds ('\n')
and soft returns ('\x8d') at the end of each line, but these
are optional and should always be ignored.
As an example, assume that the following stream of text was
returned by MsgReadMsg():
Squish Developers Kit Version 2.0 - Page 13
"Hi!\r\rHow's it going? I got the new MsgAPI kit
today!\r\rAnyhow, gotta run!"
The "\r" marks are carriage returns, so they indicate the
end of a paragraph. Notice that the second paragraph is
fairly long, so it might have to be wordwrapped, depending
on the screen width. Your application might wordwrap the
text to make it look like this, if using a window 40
characters wide:
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Hi! ³
³ ³
³ How's it going? I got the new MsgAPI ³
³ kit today! ³
³ ³
³ Anyhow, gotta run! ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Paragraphs should always be wordwrapped by the application,
regardless of the screen/window size. When parsing the
message text, linefeeds and soft carriage returns should be
simply skipped.
The 'message control information' has a somewhat more
restricted format. The control information is passed to the
application in the form of an ASCIIZ string. The control
information is a variable-length string of text which
contains information not found in the (fixed-size) message
header.
The format of control information is given by the following
regular expression:
(group)+<NUL>
A 'group' consists of a <SOH> and a control item.
<SOH> is the Start Of Header character, or ASCII 01. All
control information strings must begin with an SOH, whether
or not control items are present.
Following the <SOH> is a control item. A control item
consists of a string which describes the type of control
item, or it may consist of nothing.
At least one group must be present in each message. If a
message has no extra control information, this field should
consists of a <SOH> followed by a single <NUL>.
Although the control items are free-form, the following
format is suggested:
Squish Developers Kit Version 2.0 - Page 14
<SOH>tag: value
where 'tag' is a descriptive identifier, describing the type
of field that the item represents. 'value' is simply
free-form text, which continues up until the next SOH or
<NUL>.
The character set for the tag and value consists of those
characters in the range 2-255, inclusive.
As an example, a message might have the following control
information:
<SOH>CHARSET: LATIN1<SOH>REALNAME: Mark Twain<NUL>
The trailing <NUL> byte must be included in the read count
given by 'cbyt'.
The return value for this function is the number of bytes
read from the message body. If no characters were
requested, this function returns 0.
On error, the function returns -1 and sets msgapierr to one
of the following:
MERR_BADH
MERR_BADF
MERR_NOMEM
MsgWriteMsg
sword MsgWriteMsg(HMSG hmsg, word fAppend, PXMSG msg,
byte *text, dword textlen, dword totlen,
dword clen, byte *ctxt);
The MsgWriteMsg function is used to write the message
header, body, and control information to a message.
'hmsg' is a message handle, as returned by the MsgOpenMsg
function. The message must have been opened with a mode of
MOPEN_CREATE, MOPEN_WRITE or MOPEN_RW.
'fAppend' is a boolean flag, indicating the state of the
message body. If 'append' is zero, then the API will write
the message body starting at offset zero. Otherwise, if
'append' is non-zero, the API will continue writing from the
offset used by the last MsgWriteMsg call. This flag applies
to the message body only; if no text is to be written to the
body, this argument should be set to 0.
'msg' is a pointer to an XMSG structure. If this pointer is
Squish Developers Kit Version 2.0 - Page 15
non-NULL, then MsgWriteMsg will place the XMSG structure
information into the message's physical header. To leave
the header unmodified, NULL should be passed for 'msg'.
THIS 'msg' PARAMETER MUST BE PASSED THE **FIRST** TIME THAT
MSGWRITEMSG() IS USED WITH A JUST-OPENED MESSAGE HANDLE!
'text' points to an array of bytes to be written to the
message body. If no text is to be written, this argument
should be NULL.
'textlen' indicates the number of bytes to be written to the
message body in this pass of the MsgWriteMsg function. The
text is free-format, and it can consist of any characters,
including NULs and control characters. If the application
does not wish to update the message body, a value of 0L
should be passed for this argument.
'totlen' indicates the total length of the message to be
written. This differs from 'textlen' in that the message
may be written a piece at a time (using small 'textlen'
values), but the total length of the message will not exceed
'totlen'. This parameter can be somewhat restrictive for
the application; however, this value is required for optimal
use of some message base types. The 'totlen' value does not
have to be the exact length of the message to write;
however, space may be wasted if this value is not reasonably
close to the actual length of the message. The rationale
behind this argument is that it gives the API writer the
most flexibility, in terms of supporting future message base
formats. If the application can provide this information to
the API, then almost any message base format can be
supported by simply dropping in a new API module or DLL.
To write text by making multiple passes, the FIRST pass
should call MsgWriteMsg with 'append' set to 0, with the
total length of the message in 'totlen', and the length of
'text' in textlen. Second and subsequent passes should set
'append' to 1, with the length of 'text' in textlen.
If the application does not wish to update the message body
of an existing message, a value of 0L should be passed for
this argument.
This argument MUST be specified during the first call to the
MsgWriteMsg when using a mode of MOPEN_CREATE, even if the
first call is not requesting any text to be written.
However, this value will be stored internally, and ignored
on the second and later calls.
When operating on a preexisting message (opened with
MOPEN_WRITE or MOPEN_RW), it is an error to specify a length
Squish Developers Kit Version 2.0 - Page 16
in 'totlen' which is greater than the original length of the
message.
'clen' specifies the total length of the control
information, including the trailing NUL byte. To write no
control information, a value of 0L should be passed for this
argument.
'ctxt' is a pointer to the control information string. To
write no control information, a value of 0L should be passed
for this argument.
N.B. Several restrictions apply to writing control
information:
First and foremost, control information can only be written
once. If the control information is to be changed, the
message must be read and copied to another message.
Secondly, control information MUST be written during or
before MsgWriteMsg is called with information about the
message body.
MsgWriteMsg returns a value of 0 on success, or -1 on error.
If an error occurred, msgapierr will be set to one of the
following values:
MERR_BADH
MERR_BADF
MERR_NOMEM
MERR_NODS
MsgKillMsg
sword MsgKillMsg(HAREA ha, dword msgnum);
The MsgKillMsg function is used to delete a message from the
specified message area.
'ha' is a message area handle, as returned by MsgOpenArea.
'msgnum' specifies the message number to kill.
It is an error to kill a message which is currently open.
MsgKillMsg returns a value of 0 if the message was
successfully killed, or it returns -1 on error and sets
msgapierr to one of the following:
MERR_BADH
MERR_NOENT
Squish Developers Kit Version 2.0 - Page 17
MERR_BADF
MERR_NOMEM
MsgGetCurPos
dword MsgGetCurPos(HMSG hmsg);
The MsgGetCurPos function retrieves the 'current position'
of a message handle. This position is where the MsgReadMsg
would read text from the message body next.
'hmsg' is a message handle, as returned by MsgOpenMsg.
MsgGetCurPos returns the offset into the message on success,
or (dword)-1 on error and sets msgapierr to:
MERR_BADH
MsgSetCurPos
sword MsgSetCurPos(HMSG hmsg, dword pos);
The MsgSetCurPos function sets the 'current position' in a
message handle. This position is used by MsgReadMsg to read
text from the message body.
'hmsg' is a message handle, as returned by MsgOpenMsg.
'pos' is the number of bytes into the message from which
MsgReadMsg should start reading.
MsgSetCurPos returns 0 on success, or -1 on error and sets
msgapierr to:
MERR_BADH
MsgGetTextLen
dword MsgGetTextLen(HMSG hmsg);
The MsgGetTextLen function retrieves the length of the
message body for the specified message.
'hmsg' is a message handle, as returned by MsgOpenMsg.
MsgGetTextLen returns the length of the body on success. On
error, it returns (dword)-1 and sets msgapierr to:
MERR_BADH
Squish Developers Kit Version 2.0 - Page 18
MsgGetCtrlLen
dword MsgGetCtrlLen(HMSG hmsg);
The MsgGetCtrlLen function retrieves the length of the
control information for the specified message.
'hmsg' is a message handle, as returned by MsgOpenMsg.
MsgGetCtrlLen returns the length of the control information
on success. On error, it returns (dword)-1 and sets
msgapierr to:
MERR_BADH
MsgInvalidHmsg
sword MsgInvalidHmsg(HMSG hmsg);
The MsgInvalidHmsg function tests the given message handle
for validity. If the message area handle is invalid, this
function returns TRUE and sets msgapierr to MERR_BADH.
Otherwise, a value of FALSE will be returned. returned.
UMSGID Translation Functions
MsgMsgnToUid
UMSGID MsgMsgnToUid(HAREA ha, dword msgnum);
The MsgMsgnToUid function converts a message number to a
'unique message ID', or UMSGID. This function can be used
to maintain pointers to an 'absolute' message number,
regardless of whether or not the area is renumbered or
packed. The MsgMsgnToUid function converts a message number
to a UMSGID, and the MsgUidToMsgn function converts that
UMSGID back to a message number.
'mh' is the message area handle, as returned by MsgOpenArea.
'msgnum' is the message number to convert.
MsgMsgnToUid returns a UMSGID on success; otherwise, it
returns 0 and sets msgapierr to:
MERR_BADH
MERR_BADF
MERR_NOENT
Squish Developers Kit Version 2.0 - Page 19
MsgUidToMsgn
dword MsgUidToMsgn(HAREA ha, UMSGID umsgid, word type);
The MsgUidToMsgn function converts a UMSGID to a message
number.
'ha' is the message area handle, as returned by MsgOpenArea.
'umsgid' is the UMSGID, as returned by a prior call to
MsgMsgnToUid.
'type' is the type of conversion to perform. 'type' can be
any of the following values:
UID_EXACT Return the message number represented by the
UMSGID, or 0 if the message no longer exists.
UID_PREV Return the message number represented by the
UMSGID. If the message no longer exists, the
number of the preceding message will be
returned.
UID_NEXT Return the message number represented by the
UMSGID. If the message no longer exists, the
number of the following message will be
returned.
If no valid message could be found, MsgUidToMsgn returns 0
and sets msgapierr to one of the following:
MERR_BADH
MERR_NOENT
CtrlInfo Manipulation Functions
MsgGetCtrlToken
byte *MsgGetCtrlToken(char *szCtrlInfo, char *szTag);
The MsgGetCtrlToken function finds a specified control
information tag in a message's control information buffer.
'szCtrlInfo' is the control information buffer for a
message, using the format described for MsgReadMsg.
'szTag' is the name of the tag to be extracted from the
control information buffer. (For example, tags such as
"FMPT ", "MSGID:" or "REPLY:" can be specified.)
Squish Developers Kit Version 2.0 - Page 20
If the tag is found in szCtrlInfo, the function allocates a
new block of memory and copies the tag to that new block.
The address of the new block is returned to the caller.
(The returned address should be freed later with a call to
MsgFreeCtrlToken.)
If the tag is not found in szCtrlInfo, this function returns
NULL.
For example, given a szCtrlInfo buffer that initially
contains the following text:
<SOH>REALNAME: John Doe<SOH>FMPT 24<SOH>TOPT 6<SOH>
A call to MsgGetCtrlToken(szBuf, "FMPT ") would return the
string "FMPT 24". After the call, szCtrlInfo would contain:
<SOH>REALNAME: John Doe<SOH>TOPT 6<SOH>
MsgFreeCtrlToken
void MsgFreeCtrlToken(byte *szCtrlToken);
The MsgFreeCtrlToken function frees a control token that was
returned by a call to MsgGetCtrlToken. This function must
be called to release memory that is allocated by the
MsgGetCtrlToken function.
'szCtrlToken' is the return value of a prior call to
MsgGetCtrlToken.
MsgRemoveToken
void MsgRemoveToken(byte *szCtrlInfo, byte *szTag);
The MsgRemoveToken function removes a specified control
token from a control information buffer. The existing text
in the buffer is shifted over such that no gaps are left in
the buffer.
'szCtrlInfo' is a control information buffer, using the
format specified in the description for MsgReadMsg.
MsgGetNumKludges
word MsgGetNumKludges(byte *szCtrlInfo);
The MsgGetNumKludges function returns the number of tags
contained in the control information buffer.
Squish Developers Kit Version 2.0 - Page 21
'szCtrlInfo' is a control information buffer, using the
format specified in the description for MsgReadMsg.
For example, given a control information buffer of the
following format:
<SOH>TAG1: ABC<SOH>TAG2: DEF<SOH>TAG3: GHI<SOH>
MsgGetNumKludges would return a value of 3, since three
separate tags exist within the control information buffer.
MsgCvt4D
void MsgCvt4D(byte *szCtrlInfo, NETADDR *orig,
NETADDR *dest);
The MsgCvt4D function converts FidoNet-style control
information (namely, the FMPT, TOPT and INTL tags) into a
proper 4D address. Messages are also un-gaterouted, if
necessary. After each tag is converted, it is removed from
the control information.
Most normal applications will not need to use this function.
MsgCvt4D is usually only used by tosser/scanner programs.
(The MsgAPI automatically handles 4D address conversions
when writing to *.MSG areas.)
'szCtrlInfo' specifies the buffer containing the original
control information for the message.
'orig' points to a NETADDR structure containing the two-
dimensional origination address of the message. (This
information is normally extracted from the 2D header of a
FTS-0001 packet.)
'dest' points to a NETADDR structure containing the two-
dimensional destination address of the message. (This
information is normally extracted from the 2D header of a
FTS-0001 packet.)
On output, the 'orig' and 'dest' parameters are updated with
zone and point information from the control information, if
necessary.
For example, if the following parameters were given as
input:
orig = {0, 249, 106, 0} /* zone and point fields are 0 */
dest = {0, 254, 1, 0} /* zone and point fields are 0 */
szCtrlInfo = <SOH>FMPT 4<SOH>TOPT 6<SOH>INTL: 2:254/1
Squish Developers Kit Version 2.0 - Page 22
1:249/106<SOH>REALNAME: John Doe<SOH>
A call to MsgCvt4D would result in the parameters being
updated as follows:
orig = {1, 249, 106, 4}
dest = {2, 254, 1, 6}
szCtrlInfo = <SOH>REALNAME: John Doe<SOH>
MsgCreateCtrlBuf
char *MsgCreateCtrlBuf(char *szKludgeText,
char **pszEndText,
unsigned *puiLength);
The MsgCreateCtrlBuf function is used to convert an array of
FidoNet-style "kludge lines" (as per FTS-0001) into a
control information buffer. Most application programs will
not need to use this function.
MsgCreateCtrlBuf is normally used by tosser/scanner programs
that need to directly manipulate FTS-0001 *.PKT files. Most
applications will not need to use this function.
'szKludgeText' points to the buffer containing the beginning
of an FTS-0001 style message body. MsgCreateCtrlBuf will
extract all of the control information from this buffer, up
until the first non-blank and non-kludge line is
encountered.
'pszEndText' should be a pointer to a 'char *' variable.
When MsgCreateCtrlBuf returns, the given variable will point
to the first byte of the text following the kludge
information. (In most cases, this will be the beginning of
the FTS-0001 style message body.)
'puiLength' is a pointer to a variable that contains the
length of the message buffer pointed to by 'szKludgeText'.
On return, this variable will be updated to reflect the real
length of the message body, not counting the kludge lines at
the beginning of the text.
This function returns a pointer to the newly-created control
information buffer. This buffer should be freed with a call
to MsgFreeCtrlBuf.
Squish Developers Kit Version 2.0 - Page 23
MsgCvtCtrlToKludge
char *MsgCvtCtrlToKludge(char *szCtrlInfo);
The MsgCvtCtrlToKludge function converts a body of control
information lines to an equivalent set of FTS-0001 kludge
lines.
MsgCvtCtrlToKludge is normally used by tosser/scanner
programs that need to directly manipulate FTS-0001 *.PKT
files. Most applications will not need to use this
function.
'szCtrlInfo' is a buffer of control information, using the
format given in the MsgReadMsg description.
This function returns a pointer to a new block of memory
containing the FTS-0001 style kludge lines. This buffer
should be freed with a call to MsgFreeCtrlBuf.
MsgFreeCtrlBuf
void MsgFreeCtrlBuf(byte *szCtrlBuf);
The MsgFreeCtrlBuf function is used to free the control
buffers which are allocated by the MsgCvtCtrlToKludge and
MsgCreateCtrlBuf functions.
'szCtrlBuf' is the return value of a prior call to
MsgCvtCtrlToKludge or MsgCreateCtrlBuf.
Squish-Specific Functions
SquishHash
dword SquishHash(byte *szTxt);
The SquishHash function is used to calculate a hash for a
"To:" field in a message.
'szTxt' is a pointer to the To: field of a message.
The algorithm used to calculate this hash is described in
the "Index File Format" subsection of the Squish File Format
Specification (contained later in this document).
Squish Developers Kit Version 2.0 - Page 24
SquishSetMaxMsg
dword SquishSetMaxMsg(HAREA ha, dword dwMaxMsg,
dword dwSkipMsg, dword dwMaxDays);
The SquishSetMaxMsg function is used to set the "maximum
messages", "skip messages" and "maximum age" parameters in a
Squish-format base.
'ha' is a Squish area handle, as returned by MsgOpenMsg.
'ha' must be a handle for a Squish-format message area.
'dwMaxMsg' is the maximum number of messages to allow in
this area.
'dwSkipMsg' is the number of messages to skip (at the
beginning of the base) before automatically deleting
messages.
'dwMaxDays' is the maximum age (in days) of messages to be
kept in the message base.
API Changes from MsgAPI Version 0
1) Renamed the msg.ftsc_date field to msg.__ftsc_date. Most of
the developers whoa re currently using this field should
actually be using the binary dates. See the comments for
the __ftsc_date field in MSGAPI.H for more information.
2) For compatibility with Windows, the MSG and MSGH names have
been changed. The old-style ones will still be there for
compatibility, but the API defaults to using HAREA instead
of MSG*, and HMSG instead of MSGH*. If you wish to turn off
the old definitions because they conflict with your source
files or the Windows headers, define MSGAPI_NO_OLD_TYPES
before including msgapi.h.
3) Renamed the InvalidMsgh and InvalidMsg functions to
MsgInvalidHarea and MsgInvalidHmsg, making them more
consistent with the other API names. Also fixed the
documented return codes for these functions.
4) A few slight changes were made to the XMSG structure.
Notably, now only 9 reply links are supported, and the field
that used to hold the tenth reply link is now used for
storing a message's UMSGID.
Squish Developers Kit Version 2.0 - Page 25
BUILDING MSGAPI APPLICATIONS
Source File Requirements
Any code which uses the MsgAPI must include the following line at
the top of each source file:
#include "msgapi.h"
If the MsgAPI source is not installed in the current directory,
you should add the MsgAPI directory to your compiler's "include
path" before trying to compile an application.
Programmers who are using MsgAPI with Windows should make sure to
define the "MSGAPI_NO_OLD_TYPES" constant before including the
msgapi.h header. Older versions of the MsgAPI used typedefs
which were incompatible with Windows, and this macro must be
defined to stop MsgAPI from using the old typedef names.
For information on using the MsgAPI function calls, please see
the section entitled "MsgAPI Function Reference". This section
contains a complete list of function calls in the API, including
formal parameters, notes on operation, and return codes.
If you are using TC++ or BC++, make sure that your application is
compiled in "C mode". MsgAPI was not designed for use with C++.
(In the TC++ IDE, set Options/Compiler/C++ to "CPP extension
only". For the command-line compiler, make sure that you are NOT
using the "-P" switch.)
Also, the distribution TC++/BC++ and WC libraries have been
compiled without overlay support. If you wish to use the WATCOM
or Borland overlay managers, you will have to recompile the
MsgAPI source with overlay support.
Compiling Source Files
No special requirements are necessary for compiling source files
for the DOS version of MsgAPI. However, make sure to select the
LARGE memory model when compiling your application. If you wish
to create an application using a different memory model, you will
have to recompile the MsgAPI code.
For compiling OS/2 applications that use MsgAPI, make sure that
the "OS_2" macro is defined on the compiler command-line. The
MsgAPI uses a different set of calling conventions under OS/2, so
all of your code must be compiled with the OS_2 macro defined.
Squish Developers Kit Version 2.0 - Page 26
Linking Your Application
This package already includes pre-made libraries for a number of
compilers, all built using the large memory model.
Under DOS, the MsgAPI library is statically linked with each
application. To link with the MsgAPI routines, simply specify
the required library file when linking. For example:
TLINK (Borland C, DOS):
tlink \bc\lib\c0l myapp,myapp,nul,bc_dos_l \bc\lib\cl
TLINK (Turbo C, DOS):
tlink \tc\lib\c0l myapp,myapp,nul,tc_dos_l \tc\lib\cl
WLINK (16-bit DOS):
wlink file myapp lib wc_dos_l name myapp
WLINK (32-bit DOS):
wlink file myapp lib wc_dos_f name myapp
LINK (DOS):
link myapp,myapp,nul,mc_dos_l;
If using the TC++ or BC++ IDE, simply declare the required
library as part of your project file. If you are using TC++ or
BC++ from the command line, you can specify the name of the
library after your source module, like this:
tcc -ml myapp.c tc_dos_l.lib
Under OS/2, the MsgAPI code is distributed as a DLL (dynamic link
library). To use this code in your application, simply link with
the DLL16.LIB import library (for 16-bit code) or the DLL32.LIB
import library (for 32-bit code). 16-bit OS/2 applications
should also be compiled in the large memory model.
After ensuring that MSGAPI.DLL and/or MSGAPI32.DLL are on your
LIBPATH, your application should be ready to run.
If you wish to use the default libraries and memory models, you
do not need any of the *.c files in the source directory.
However, you must still retain all *.h header files, since they
are required when compiling your application.
Squish Developers Kit Version 2.0 - Page 27
Building the Sample Applications
The SAMPLES subdirectory contains a number of sample programs
that use the MsgAPI. The commands given below can be used to
make the samples using the supported compilers:
Compiler OS Command
WATCOM C/16 DOS wmake -u -f makefile.wcd
WATCOM C/16 OS/2 wmake -u -f makefile.wco
WATCOM C/32 DOS wmake -u -f makefile.w3d
WATCOM C/32 OS/2 wmake -u -f makefile.w3o
Microsoft C DOS nmake -f makefile.mcd
Microsoft C OS/2 nmake -f makefile.mco
Turbo C DOS make -fmakefile.tcd
Borland C DOS make -fmakefile.bcd
Squish Developers Kit Version 2.0 - Page 28
BUILDING THE MSGAPI SOURCE
The MsgAPI sources have been tested with the following compilers:
WATCOM C/16 9.5 (DOS and OS/2)
WATCOM C/32 9.5 (DOS and OS/2)
Microsoft C 6.0 (DOS and OS/2)
Turbo C 2.0 (DOS only)
Borland C++ 3.1 (DOS only)
In addition, either the Microsoft Macro Assembler or the Borland
Turbo Assembler is required to build the MsgAPI source.
Other versions of these compilers (or compilers made by different
vendors) may also work, but only the compilers above have been
tested. The code works with both 16-bit and 32-bit compilers.
(However, if you add support for a compiler from a different
vendor, you will probably have to modify h\compiler.h.)
To recompile the MsgAPI source, simply edit the MAKEFILE within
the SRC directory. Sample definitions have been provided for all
of the supported compilers.
The makefile included in this package should work with almost any
make utility. This makefile has been successfully used under
Borland's MAKE, Microsoft's NMAKE, WATCOM's WMAKE, and Vadura's
DMAKE.
Warning! WMAKE users must use the "-u" command-line option when
running WMAKE.EXE. By default, WMAKE uses a non-standard line
continuation character, so the makefile will not be processed
correctly unless the "-u" option is used.
By default, the makefile is set up to compile for WATCOM C/16
9.5. To select another compiler, comment out all of the WC/16
definitions and uncomment those that pertain to your compiler.
Having made the appropriate modifications, simply type "make" to
rebuild the appropriate msgapi*.lib or msgapi*.dll file.
Under DOS, the "MODEL=" directive at the beginning of the
makefile can also be used to instruct the compiler to build a
MsgAPI library for one of the following memory models. MODEL
should be set to one of the following values:
s Small
m Medium
c Compact
l Large
Squish Developers Kit Version 2.0 - Page 29
OS/2 DLL DEVELOPMENT
The files in the FEATURES subdirectory contains everything that
is required to write third-party extensions for Squish.
This developers kit includes the source for two sample feature
DLLs. These DLLs are not part of the standard Squish
distribution, nor are they supported in any manner. They are
simply included to provide examples and to give a demonstration
of feature DLL programming.
Sample Feature DLLs
KILLRCAT.DLL
The first sample DLL is "KillrCat", a message filtering program
that selectively deletes messages based on a set of search
criteria. The following can be added to the top of your
SQUISH.CFG for a demonstration of KillrCat's capabilities:
; The Feature and/or Feature32 keywords tell Squish to load
; KILLRCAT and/or KILLRC32.
Feature KillrCat
Feature32 KillrC32
; Keyword:
;
; KillrCat <where> <echo_tag> <text>
;
; <where>: t = to
; f = from
; s = subject
; b = body
;
; Any combination of the above flags are permitted.
;
; <echo_tag> is the tag of the echo area.
;
; <text> is the text to find.
KillrCat b sysop249 Node Diff announcement
KillrCat s sysop249 Nodediff announcement
KillrCat sb users249 Latest Versions'n Stuff
KillrCat sb users249 Fidonews!
KillrCat b netmail File request generated by AMAX
When KillrCat find a message that matches the specified criteria,
it diverts the message from its usual area and places it in
BAD_MSGS. If you don't want to see these messages at all, fix
KILLRCAT.C and use the "delete message" bitmask in the return
code.
Squish Developers Kit Version 2.0 - Page 30
MSGTRACK.DLL
The other sample DLL is a message bouncer. This bouncer requires
a version 7 nodelist to work properly. Try adding the following
to your SQUISH.CFG for a demonstration:
Feature Msgtrack
Feature32 Msgtra32
; Path and name of nodelist
Msgtrack Nodelist <filename>
; Delete bounced messages
;Msgtrack Kill
When packing netmail, this bouncer will automatically return
messages to the originating node if the destination is not listed
in the nodelist.
<filename> should be the name of the NODEX.DAT file from your
version 7 nodelist.
The optional "Msgtrack Kill" line instructs the message bouncer
to delete messages after they have been bounced. (By default,
Squish just marks a bounced message as "Sent" and "Orphan", but
the message is left in the netmail area.)
Feature DLL Programming Interface
FEATURES.LZH includes a sample TEMPLATE.C file that can be used
as a template for writing feature DLLs.
A feature DLL must start with the following lines:
#define INCL_DOS
#include <os2.h>
#include "sqfeat.h"
os2.h is the standard OS/2 include file, as distributed in the
OS/2 toolkit. (For 16-bit feature DLLs, the OS/2 1.x toolkit is
required. For 32-bit feature DLLs, an OS/2 2.x toolkit is
required.)
sqfeat.h is the feature DLL include file. This contains
definitions and typedefs necessary to write a Squish feature DLL.
Each feature DLL must contain a number of functions. These
functions are used as hooks when Squish is performing certain
actions.
Squish Developers Kit Version 2.0 - Page 31
All of the hook functions must be exported by name, and they all
must use the pascal calling convention. The "FEATENTRY" keyword
automatically defines all of these attributes.)
The following functions must be present in every feature DLL:
FeatureInit
This function is responsible for performing feature-specific
initializations. FeatureInit() is called as soon as the
Feature or Feature32 keyword is encountered in SQUISH.CFG.
This function is passed a pointer to a _feat_init structure.
The structure contains the following fields:
struct_len The length of the _feat_init structure
szConfigName The name of the DLL. This should be filled
in by the FeatureInit() function. This name
is be used for adding feature-specific
configuration options to SQUISH.CFG.
When Squish encounters an unknown
configuration line, it first checks to see if
the first word matches any of the feature
DLLs, as given in the szConfigName field. If
a match is found, the configuration line is
passed to the DLL's FeatConfig function.
pfnLogMsg A pointer to the Squish logging routine.
This field is filled in by Squish itself. If
the feature DLL needs to add lines to the
Squish log, this function pointer can be used
to access the system log function.
The FeatInit() function should save a copy of
the pfnLogMsg field in a global variable,
such that the other Feat...() functions can
create log entries when necessary.
ulFlag This flag contains a bitmask that Squish uses
to optimize calls made to the feature DLL.
FeatInit() should initialize this field
before returning to the caller.
Any or all of the following flags can be
combined using the bitwise OR operator ("|"):
FFLAG_NETSENT Only call FeatNetMsg when
packing a message that
does not have the SENT
bit set.
Squish Developers Kit Version 2.0 - Page 32
FFLAG_NETTOUS Only call FeatNetMsg when
packing a message which
is addressed to one of
our addresses, as given
in SQUISH.CFG.
FFLAG_NETRECD Only call FeatNetMsg when
packing a message that
does not have the
RECEIVED bit set.
FFLAG_NETNOTTOUS Only call FeatNetMsg when
packing a message that is
NOT addressed to one of
our addresses, as given
in SQUISH.CFG.
If this field is given a value of 0, the
FeatNetMsg function will be called before
packing every netmail message.
FeatureConfig
This function is called whenever a feature-specific
configuration line is encountered. Feature DLLs can use
this function to process application-specific configuration
information from SQUISH.CFG.
This function is passed a pointer to a _feat_config
structure. The structure contains the following fields:
struct_len This field contains the length of the
_feat_config structure.
szConfigLine This field contains the line just read from
SQUISH.CFG. The first word on this line
matches the szConfigName value that was
placed in the _feat_init structure by
FeatInit.
ppszArgs This field contains a standard argv-style
variable, pointing to a tokenized version of
szConfigLine. The first word on szConfigLine
can be accessed as ppszArgs[0]; the second
word can be accessed as ppszArgs[1]; and so
on. If there are 'n' arguments, ppszArgs[0]
through ppszArgs[n-1] will contain pointers
to each word on the line, and ppszArgs[n]
will be NULL.
The FeatureConfig function should extract any relevant
Squish Developers Kit Version 2.0 - Page 33
information and store it in a global variable that can be
accessed by other functions in the DLL.
FeatureNetMsg
This function is called whenever a NetMail message is about
to be packed. This function is only used on non-
ArcmailAttach systems.
This function is passed a pointer to the _feat_netmsg
structure. The structure contains the following fields:
struct_len This field contains the length of the
_feat_netmsg structure.
ulAction This field controls the action taken by
Squish when FeatureNetMsg returns. The
contents of this field should be filled out
by the feature DLL.
This field can be set to a combination of any
of the following values, using the bitwise OR
operator ("|"):
FACT_NONE No action is taken
FACT_KILL The message is deleted upon
returning to Squish, before
the message is packed.
FACT_SKIP The message is left alone and
is not packed.
FACT_HIDE Do not pass this message to
other installed features. (By
default, all installed
features are called with a
pointer to the current
message. However, if the
FACT_HIDE flag is set, other
DLLs will not be called with
this message during the
current run.)
FACT_RWMSG Rewrite the current message.
This flag should be used if
the DLL modifies the message
header in pMsg.
pszAreaTag This field contains the name of the current
Squish Developers Kit Version 2.0 - Page 34
netmail area.
ha This is a MsgAPI HAREA handle for the current
netmail area.
hmsg This is a MsgAPI HMSG handle for the current
message.
ulMsgNum This contains the message number of the
current message.
pMsg Pointer to the message header of the current
message.
pszCtrl Pointer to the control information for the
current message.
pszMsgTxt Pointer to the message text for the current
message.
us A NETADDR-type structure giving Squish's
primary address.
FeatureTossMsg
This function is called whenever an EchoMail message is
about to be tossed.
This function is passed a pointer to the _feat_toss
structure. The structure contains the following fields:
struct_len Length of the _feat_toss structure.
ulTossAction This field specifies the action to be taken
when Squish returns from FeatureTossMsg.
This field can contain any of the following
options, combined using the bitwise OR
operator ("|"):
FTACT_NONE No action is taken.
FTACT_KILL Delete the message without
attempting to toss it.
FTACT_AREA Toss to the new area tag
specified in szArea.
FTACT_HIDE Do not pass this message to
any other installed features.
(See the comments for
Squish Developers Kit Version 2.0 - Page 35
FACT_HIDE in FeatureNetMsg for
more information.)
FTACT_NSCN Do not scan this message to
anyone else (if performing a
one-pass toss/scan).
szArea Area tag for this message. This field can be
changed to cause the message to be redirected
to another area, but to ensure that Squish
recognizes the change, the FTACT_AREA flag
should be set (above).
szPktName Name of the packet file that contains the
current message.
pMsg Pointer to the message header for the current
message.
pszCtrl Control information for the current message.
pszMsgTxt Message text of the current message.
FeatureScanMsg
This function is currently not supported by Squish.
However, it must be present for Squish to load the DLL.
FeatureTerm
This function is called when Squish is about to finish
execution. This function should perform feature-specific
clean-up operations.
Feature16Bit
This function is never actually called by Squish, but it
must be present in all 16-bit feature DLLs.
Feature32Bit
This function is never actually called by Squish, but it
must be present in all 32-bit feature DLLs.
Squish Developers Kit Version 2.0 - Page 36
Building Feature DLLs
The following commands can be used to build a sample 16-bit DLL
using WATCOM C/16 9.5 or above, where "template" is the name of
the source file for the feature:
[C:\] wcc /bd /dOS_2 /ml /zu template.c
[C:\] wlink sys os2 dll initi debug all name template.dll
file template.obj lib os2 opt manyauto, protmode, verb,
modname=template
The following commands can be used to build a sample 32-bit DLL
using WATCOM C/32 9.5 or above:
[C:\] wcc386 /bd /dOS_2 /fo=templa32 template.c
[C:\] wlink sys os2v2 dll initi debug all name templa32.dll
file templa32.obj lib os2386 opt verb, manyauto,
modname=templa32
Squish Developers Kit Version 2.0 - Page 37
SQUISH FILE FORMAT SPECIFICATION
This section describes the physical file layout of a Squish
message base. This is intended as a reference for developers who
are writing their own Squish-compatible programs. For an
overview of the Squish message base, see the section of
SQUISH.PRN entitled "Using Squish-Format Message Areas".
While the Squish MsgAPI library provides a standardized interface
to Squish and *.MSG bases for C programmers, authors who use
other languages may need to access Squish bases directly. This
section describes the implementation details of the Squish file
format.
Squish Philosophy
A standard Squish base consists of two files: a message data
file and a message index file. Both files have the same prefix,
but the data file has an extension of ".sqd", while the index
file has an extension of ".sqi".
From an overall point of view, the Squish data file consists of
messages stored in a doubly-linked list. The Squish data file
includes a header that contains pointers to the first and last
frames in the area, in addition to other area-specific
information.
In the data file, a "frame" is used to hold an individual
message. A frame consists of a frame header (which contains
links to the prior and next messages), followed by the optional
message header, control information and message body fields.
This "linked list of frames" approach is ideal for a BBS message
base. Almost all message base access is sequential, starting
from a particular offset, and reading or writing until the end of
the message base is reached. Since each frame header contains
the offset of the prior and next messages, no disk accesses are
required to find the preceding or following messages.
The index file is a flat array of Squish Index (SQIDX) records.
The index file is used primarily for performing random access
look-ups by message number.
Unlike other message base formats, the Squish base is only
loosely based on the concept of "message numbers". While all
messages have a message number, these numbers can change at any
time. By definition, the message numbers in a Squish base always
range from 1 to the highest message in the area. Consequently,
there are no "gaps" in message numbers, so a Squish message area
never needs to be renumbered.
Squish Developers Kit Version 2.0 - Page 38
While this makes it easy to scan through all of the messages in
an area, this also makes it difficult to find one specific
message. Consequently, the concept of a "unique message
identifier" or (UMSGID) is introduced.
When a message is created, it is assigned a 32-bit UMSGID. These
identifiers are unique and NEVER CHANGE. Unique message numbers
are never "renumbered", so once a UMSGID of a message is
obtained, it can always be used to find the current message
number of the given message, no matter how many messages have
been added or deleted in the interim.
Squish Data Types
The following integral types are used in the Squish file format
definitions:
Type Size Description
char 1 byte A one-byte unsigned character.
word 2 bytes A two-byte unsigned integer.
sword 2 bytes A two-byte signed integer.
dword 4 bytes A four-byte unsigned integer.
FOFS 4 bytes A four-byte unsigned integer. This type is
used to store offsets of frames within the
Squish data file.
UMSGID 4 bytes A four-byte unsigned integer. This type is
used to store unique message identifiers for
Squish messages.
The types above are stored in the standard Intel "backwords"
format, with the least significant byte being stored first, and
the most significant byte being stored last.
A two-byte integer containing 0x1234 would be stored as follows:
Offset Value
0 0x34
1 0x12
A four-byte integer containing 0x12345678 would be stored as
follows:
Offset Value
0 0x78
1 0x56
2 0x34
Squish Developers Kit Version 2.0 - Page 39
3 0x12
The Squish file format also uses a number of abstract data types:
The SCOMBO type is used for describing a message date/time stamp.
This structure has the following format:
Name Type Ofs Description
date word 0 DOS bitmapped date value. This field is
used to store a message date.
The first five bits represent the day of
the month. (A value of 1 represents the
first of the month.)
The next four bits indicate the month of
the year. (1=January; 12=December.)
The remaining seven bits indicate the
year (relative to 1980).
time word 2 DOS bitmapped time value. This field
used to store a message time.
The first five bits indicate the seconds
value, divided by two. This implies
that all message dates and times get
rounded to a multiple of two seconds.
(0 seconds = 0; 16 seconds = 8; 58
seconds = 29.)
The next six bits represent the minutes
value.
The remaining five bits represent the
hour value, using a 24-hour clock.
Total: 4 bytes
Squish Developers Kit Version 2.0 - Page 40
The NETADDR type is used for describing a FidoNet network
address. This structure has the following format:
Name Type Offset Description
zone word 0 FidoNet zone number.
net word 2 FidoNet net number.
node word 4 FidoNet node number.
point word 6 FidoNet point number. If the
system is not a point, this field
should be assigned a value of zero.
Total: 8 bytes
In addition, to describe an array of a given type, the "type[n]"
notation is used. For example, "char[6]" represents an array of
six contiguous characters. Likewise, "UMSGID[12]" represents an
array of twelve UMSGID types.
Data File Format
The Squish data file consists of two major sections:
1) A fixed-length area header, stored at the beginning of the
file.
2) A variable-length heap that comprises the rest of the file.
This part of the file is used for storing message text.
The area header stores pointers to the head and tail of two major
"chains" of messages; the message chain and the free chain. The
message chain is used to find all active messages in an area.
The free chain is used for storing the locations of deleted
messages, such that space can be reused at a later point in time.
The Squish data file always contains a copy of the following
_sqbase structure at file offset 0:
Name Type Offset Description
len word 0 Length of the _sqbase
structure.
reserved word 2 Reserved for future use.
num_msg dword 4 Number of messages in
this Squish base. This
should always be equal to
the value of the high_msg
field.
Squish Developers Kit Version 2.0 - Page 41
high_msg dword 8 Highest message number in
this Squish base. This
should always be equal to
the value of the num_msg
field.
skip_msg dword 12 When automatically
deleting messages, this
field indicates that the
first skip_msg messages
in the area should not be
deleted. (If max_msg=50
and skip_msg=2, this
means that the writing
program should start
deleting from the third
message whenever the
total message count
exceeds 50 messages.)
high_water dword 16 The high water marker for
this area, stored as a
UMSGID. This field is
used in EchoMail areas
only. This contains the
UMSGID of the highest
message that was scanned
by EchoMail processing
software.
uid dword 20 This field contains the
UMSGID to be assigned to
the next message created
in this area.
base char[80] 24 Name and path of the
Squish base, as an ASCIIZ
string, not including the
extension. This field is
optional and will not
necessarily be filled out
by all applications. (If
this field is not
supported, it should be
initialized to ASCII 0.)
begin_frame FOFS 104 Offset of the first frame
in the message chain.
last_frame FOFS 108 Offset of the last frame
in the message chain.
Squish Developers Kit Version 2.0 - Page 42
free_frame FOFS 112 Offset of the first frame
in the free chain.
last_free_frame FOFS 116 Offset of the last
message in the free
chain.
end_frame FOFS 120 Offset of end-of-file.
Applications will append
messages to the Squish
file from this point.
max_msg dword 124 Maximum number of
messages to store in this
area. When writing
messages, applications
should dynamically delete
messages to make sure
that no more than
max_msgs exist in this
area.
keep_days word 128 Maximum age (in days) of
messages in this area.
This field is not
normally used by
applications. However,
it is used by SQPACK when
performing a message area
pack.
sz_sqhdr word 130 Size of the SQHDR
structure. For
compatibility with future
versions of the Squish
file format, applications
should use this value as
the size of the SQHDR
structure, instead of
using a hardcoded
"sizeof(SQHDR)" value.
reserved char[124] 132 Reserved for future use.
Total: 256 bytes
To examine the messages in a Squish base, the application needs
to follow the message chain. To do this, start with the
begin_frame and/or end_frame fields. These fields contain the
offsets of the first and last frames (respectively) in the
message base.
Squish Developers Kit Version 2.0 - Page 43
A frame in the message chain consists of a Squish Frame Header
structure (SQHDR), followed by the XMSG message header, message
control information, and message body.
A frame in the free chain consists of a SQHDR structure only. A
free frame does not necessarily contain a message.
A SQHDR structure always has the following format:
Name Type Ofs Description
id dword 0 The frame identifier signature.
This field must always be set to a
value of 0xAFAE4453.
next_frame FOFS 4 Frame offset of the next frame, or
0 if this is the last frame.
prev_frame FOFS 8 Frame offset of the prior frame, or
0 if this is the first frame.
frame_length dword 12 Amount of space ALLOCATED for the
frame, not including the space used
by the SQHDR itself.
msg_length dword 16 Amount of space USED in this frame,
including the size of the XMSG
header, the control information,
and the message text. This field
does NOT include the size of the
SQHDR itself.
clen dword 20 Length of the control information
field in this frame.
frame_type word 24 This field can contain one of the
following frame type values:
0 Normal frame. This frame
contains an XMSG header,
followed by the message
control information and
the message body. Normal
frames should only be
encountered when
processing the normal
message chain.
1 Free frame. This frame
has been deleted, but it
can be reused. The
amount of available space
in the frame is given by
Squish Developers Kit Version 2.0 - Page 44
the frame_length field.
Free frames should only
be encountered when
processing the free
chain.
2 LZSS frame. This frame
type is reserved for
future use.
3 Frame update. The frame
is being updated by
another task. This is
only a transient frame
type; it indicates that
the frame should not be
manipulated by another
task.
All other frame types are
reserved for future use.
reserved word 26 Reserved for future use.
Total: 28 bytes
For a normal frame type, the XMSG header immediately follows the
Squish frame header. The XMSG structure has the following
format:
Name Type Ofs Description
attr dword 0 Message attributes. This is a
combination of any of the MSG*
attributes. (See below.)
from char[36] 4 Name of the user who originated
this message.
to char[36] 40 Name of the user to whom this
message is addressed.
subject char[72] 76 Message subject.
orig NETADDR 148 Originating network address of this
message.
dest NETADDR 156 Destination network address of this
message. (Used for netmail areas
only.)
Squish Developers Kit Version 2.0 - Page 45
date_written SCOMBO 164 Date that the message was written.
date_arrived SCOMBO 168 Date that the message was placed in
this Squish area.
utc_ofs sword 172 The message writer's offset from
UTC, in minutes. Currently, this
field is not used.
replyto UMSGID 174 If this message is a reply, this
field gives the UMSGID of the
original message. Otherwise, this
field is given a value of 0.
replies UMSGID[9] 178 If any replies for this message are
present, this array lists the
UMSGIDs of up to nine reply
messages.
umsgid UMSGID 214 The UMSGID of this message. THIS
FIELD IS ONLY VALID IF THE MSGUID
BIT IS SET IN THE "ATTR" FIELD.
Older Squish programs do not always
set this field, so its contents can
only be trusted if the MSGUID bit
is set.
__ftsc_date char[20] 218 FTS-0001 compatible date. Squish
applications should not access this
field directly. This field is used
exclusively by tossers and scanners
for preserving the original ASCII
message date. Squish applications
should use the binary dates in
date_written and date_arrived to
retrieve the message date.
Total: 238 bytes
Any of the following bitmasks can be used in the XMSG "attr"
field:
Attribute Value Description
MSGPRIVATE 0x00000001 The message is private.
MSGCRASH 0x00000002 The message is given a "crash"
flavour when packed. When both
MSGCRASH and MSGHOLD are both
enabled, the message is given a
"direct" flavour.
MSGREAD 0x00000004 The message has been read by the
addressee.
Squish Developers Kit Version 2.0 - Page 46
MSGSENT 0x00000008 The message has been packed and
prepared for transmission to a
remote system.
MSGFILE 0x00000010 The message has a file attached.
The filename is given in the "subj"
field.
MSGFWD 0x00000020 The message is in-transit; it is
not addressed to one of our primary
addresses.
MSGORPHAN 0x00000040 The message is orphaned. The
message destination address could
not be found in the nodelist.
MSGKILL 0x00000080 The message should be deleted from
the local message base when it is
packed.
MSGLOCAL 0x00000100 The message originated on this
system. This flag must be present
on all locally-generated netmail
for Squish to function properly.
MSGHOLD 0x00000200 The message should be given a
"hold" flavour when packed. When
combined with the MSGCRASH flag,
the message is given a "direct"
flavour.
MSGXX2 0x00000400 Reserved for future use.
MSGFRQ 0x00000800 The message is a file request. The
filename is given in the "subj"
field.
MSGRRQ 0x00001000 A receipt is requested. (Not
supported by Squish.)
MSGCPT 0x00002000 This message is a receipt for an
earlier MSGRRQ request.
MSGARQ 0x00004000 An audit trail is requested. (Not
supported by Squish.)
MSGURQ 0x00008000 This message is an update request.
The filename is given in the "subj"
field.
MSGSCANNED 0x00010000 This echomail message has been
scanned out to other systems.
Squish Developers Kit Version 2.0 - Page 47
MSGUID 0x00020000 The "uid" field contains a valid
UMSGID for this message.
Index File Format
The index file provides random access capability for a Squish
base. Given a message number, the index file can be used to
quickly find the frame offset for that message.
Similarly, given a UMSGID, the index file can also be used to
find the message number and/or the frame offset for the message.
The Squish index file is an array of Squish Index (SQIDX)
structures. Each SQIDX structure corresponds to an active
message. For a base containing 'n' messages, there are at least
'n' SQIDX structures. (There may also be extra SQIDX frames at
the end of the index file, but these will be initialized with
invalid values, as described below.)
The SQIDX for the first message is stored at offset 0.
The SQIDX for the second message is stored at offset 12.
The SQIDX for the third message is stored at offset 24.
(and so on)
The Squish Index structure (SQIDX) has the following format:
Name Type Ofs Description
ofs FOFS 0 Offset of the frame for this message. A
value of 0 is used to indicate an
invalid message.
umsgid UMSGID 4 Unique message ID for this message. A
value of 0xffffffff is used to indicate
an invalid message.
The umsgid field must always be greater
than the umsgid field of the preceding
SQIDX structure. UMSGIDs are assigned
serially, so this will normally be the
case. (A binary search is performed on
the index file to translate UMSGIDs, so
the umsgid field of the SQIDX headers
must always be stored in ascending
order.)
hash dword 8 The low 31 bits of this field contain a
hash the "To:" field for this message.
(See below for the hash function.) The
high bit is set to 1 if the MSGREAD flag
is enabled in the corresponding XMSG
Squish Developers Kit Version 2.0 - Page 48
header.
Total: 12 bytes
The following hash function is used to calculate the "hash" field
of the SQIDX structure. All variables are 32-bit unless
otherwise noted:
Set "hash" to a value of 0
For each 8-bit character "ch" in the To: field, repeat:
- Shift "hash" left by four bytes.
- Convert "ch" to lowercase
- Increment the hash by the ASCII value of "ch"
- Set "g" to the value of "hash"
- Perform a bitwise AND on "g", using a mask of
0xf0000000.
- If "g" is non-zero:
- Perform a bitwise OR on "hash" with the value
of "g".
- Shift "g" right by 24 bits.
- Perform a bitwise OR on "hash" with the value
of "g".
Perform a bitwise AND on "hash" with a value of 0x7fffffff.
The following C function can be used to calculate such a hash:
#include <ctype.h>
unsigned long SquishHash(unsigned char *f)
{
unsigned long hash=0;
unsigned long g;
char *p;
for (p=f; *p; p++)
{
hash=(hash << 4) + (unsigned long)tolower(*p);
if ((g=(hash & 0xf0000000L)) != 0L)
{
hash |= g >> 24;
hash |= g;
}
}
Squish Developers Kit Version 2.0 - Page 49
/* Strip off high bit */
return (hash & 0x7fffffffLu);
}
The SquishHash function is derived from the hashpjw() function by
P.J. Weinberger.
The following procedure can be used to find the frame offset or
UMSGID of a particular message number:
- Read the Squish data header and ensure that the message
number is less than or equal to the value given by
"high_msg".
- Subtract one from the message number.
- Multiply the result by 12 (the size of the SQIDX structure).
- The product is the required offset in the SQIDX file. Seek
to that offset and read the SQIDX header.
- The "fofs" field of the SQIDX structure indicates the offset
of the message frame. The "umsgid" field indicates the
UMSGID for the message in question.
To find a message number and/or frame offset of a particular
UMSGID, a simple binary search can be performed on the index
file. Since the SQIDX structures are (by definition) stored in
ascending order by the "umsgid" field. Conventional binary
search techniques can be used to find the SQIDX structure for a
particular UMSGID value.
Having found the SQIDX structure, the offset of the message can
be obtained from the SQIDX "fofs" field. In addition, the
message number can be determined from the location of the SQIDX
within the index file. (The SQIDX at offset 0 is for message 1;
the SQIDX at offset 12 is for message 2; and so on.)
Message Links
Like with any doubly-linked list, inserting a message into a
Squish message chain requires more work than just writing a
single header.
First of all, when writing the SQHDR for the new message, the
"prev_frame" and the "next_frame" fields must be set to the frame
offsets of the previous and next messages in the chain.
In addition, the "next_frame" field of the PREVIOUS message must
be set to the offset of the message being inserted. Likewise,
Squish Developers Kit Version 2.0 - Page 50
the "prev_frame" field of the NEXT message must be set to the
offset of the message being inserted.
Similar arguments apply to deleting a message. The prior and
next messages must be linked together to remove the current
message from the chain.
Beyond that, if the message is being inserted or deleted is at
the beginning or end of the chain, the begin_frame/last_frame or
free_frame/last_free_frame pointers must be updated (for the
message and free chains, respectively).
Reading Messages
Once the offset of a message frame is known, the application can
follow these steps to read the message:
- Seek to the specified frame offset.
- Read the SQHDR for the frame, making sure to read "sz_sqhdr"
bytes (as given in the _sqdata base header).
- Validate the "id" field. If the contents of this field are
not equal to the predefined constant (see "id", above), the
Squish base is damaged.
- Validate the "frame_type" field. If the type is equal to
"normal frame", proceed. If the type is equal to "frame
update", another application is processing the base, so the
message should be skipped. Any other value is an error.
- Read the XMSG header.
- Use the "clen" field of the SQHDR to determine the length of
the control information. Read this many bytes into an
array.
- Use the "msg_length" field of the SQHDR to determine the
total length of the frame. Subtract the size of the XMSG
header and the "clen" value, and use this result as the
length of the message text. Read this many bytes into an
array.
To read the next or prior message in a chain, simply use the
"next_frame" or "prev_frame" fields (respectively) to obtain the
frame offset (FOFS) of the indicated frame. A FOFS of zero
indicates that the end of the chain has been reached.
Squish Developers Kit Version 2.0 - Page 51
Writing Messages
To write a new message to a Squish base, an application should
follow this procedure:
- Obtain exclusive access to the Squish base. (See the
section entitled "Concurrency" for more information.)
- Re-read a copy of the Squish base header.
- Scan the messages in the "free_frame" chain to see if any
frame is large enough to accommodate the new message.
Ensure that the message header has a type of "free frame".
If so, unlink the message from the free chain, adjusting the
free_frame and last_free_frame pointers if necessary. (See
"Message Links", above.)
- If no free frame was found, allocate a frame at the end of
the file, using the "end_frame" offset from the base header.
Increment the "end_frame" value by the amount of space
allocated.
- Link the new SQHDR frame into the end of the message chain,
adjusting the begin_frame and last_frame pointers in the
base header (if necessary).
- Set the "frame_type" field of the new SQHDR to "frame
update" and write at the appropriate offset.
- Copy the "uid" field from the base header into the XMSG
header of the message to be written. Also set the MSGUID
flag in the "attr" field of the XMSG header. Then increment
the "uid" field in the base header.
- Increment the num_msg and high_msg values in the base
header.
- Rewrite the Squish base header.
- Relinquish exclusive access to the Squish base.
- Take the offset of the new SQHDR, and add the "sz_sqhdr"
value from the base header. Seek to that offset and write
the XMSG header.
- Write the message's control information.
- Write the message body.
- Set the frame type of the new message to "normal".
- Seek to the appropriate offset in the Squish index file, and
Squish Developers Kit Version 2.0 - Page 52
write a SQIDX header for the new message. Make sure
- Check the "high_msg" count of the message area to ensure
that it is less than or equal to max_msg. If not, delete
messages (while skipping the first skip_msg messages) until
the total message count is less than high_msg.
Deleting Messages
An application should follow this procedure to delete an existing
message:
- Obtain the frame offset of the message to be deleted. (If
given a message number, the frame offset for a message can
be found in the n'th record of the index file.)
- Read the frame header into memory.
- Obtain exclusive access to the message base.
- Update the header pointed to by the next_frame link to skip
over the message being deleted.
- Update the header pointed to by the next_frame link to skip
over the message being deleted.
- Shift the index file to remove the just-deleted message from
the message area. The global pointers in the Squish header
(num_msg, high_msg, and so on) should also be updated.
- Append the just-deleted header to the chain of "free"
messages. The frame should be inserted at the end of the
free list. The procedure used for inserting a free frame is
similar to that used in "Writing a message", except that the
free list is manipulated (instead of the message list).
- Relinquish exclusive access to the message base.
Concurrency Considerations
For Squish applications to operate properly in a multitasking or
networked environment, certain precautions must be taken when
manipulating a Squish base. While there are few restrictions on
reading message information, a certain set of conventions must be
followed when writing to a Squish base.
The procedure for obtaining exclusive access to the message base
(as used in the descriptions above) is as follows:
- Attempt to lock the first byte of the Squish data file. If
the lock fails, wait for one second and try again. If all
Squish Developers Kit Version 2.0 - Page 53
ten locks fail, the base is locked by another process and
cannot be modified.
- Re-read the Squish base header. Old copies of the header
may no longer be current, so this step must always be
performed when obtaining exclusive access to the Squish
base.
An application should not write to the Squish base until
performing the steps above.
Once exclusive access has been obtained, the application can
perform any necessary modifications. When the application no
longer needs to write to the message base, the following steps
can be performed to relinquish exclusive access:
- Write the updated Squish base header. This revised header
should include any modifications which were made while
exclusive access was in effect.
- Unlock the first byte of the Squish data file.
As long as all Squish applications follow these conventions for
accessing the Squish base, concurrency should not be a problem.
###
Squish Developers Kit Version 2.0 - Page 54