JAMLIB

                       A JAM subroutine library

                         by Björn Stenberg

                     modifications by Johan Billing

                            version 1.3.2

                              2004-07-10


GENERAL
=======

History
-------
JAMLIB 1.0 was originally released by Björn Stenberg 1996-03-06. Since
the original license did not permit modification of the library,
Johan Billing contacted Björn Stenberg and asked him to change the
license. Björn Stenberg agreed to change the license to the GNU Lesser
General Public License 1999-12-21 (see the accompanying file LICENSE).

  After that, some minor additions and bug fixes were made by Johan
Billing and JAMLIB 1.1 was released under the new license.

Changes in 1.3.2:

 * Updated the Win32-specific parts of the code to make it compatible with
   newer versions of MinGW (tested with 3.1.0-1).

Changes in 1.3.1:

 * Backported the following bugfixes and improvements from JAMLIB 1.4.7 while
   retaining the platform-independence and high portability of the early
   versions of JAMLIB.

   - JAMLIB now uses calloc() instead of malloc() followed by memset()

   - JAM_OpenMB() and JAM_CreateMB() will set (*NewArea_PPS) to NULL if
     calloc() failed

   - JAM_CreateMB() no longer attempts indefinitely to lock the newly created
     messagebase. If the first attempt fails, it will return an error.

   - jam_Lock() now sets Base_PS->Errno under Linux

   - JAM_NewSubPacket() and JAM_PutSubField() would give memory leaks under
     conditions of low memory conditions. Fixed.

   - JAM_ReadMsgHeader() would give memory leaks on failure. Fixed.

   - Added JAM_DeleteMessage()

   Big thanks to Sir Raorn (and others?) for finding and fixing these bugs!

 * JAM_CreateMB() would never unlock or close the newly created messagebase
   upon failure. Fixed.

 * Improved handling of ActiveMsgs counter. JAM_AddMessage() now only
   increases ActiveMsgs if the added message does not have MSG_DELETED set.
   JAM_ChangeMsgHeader() decreases ActiveMsgs if MSG_DELETED is set and the
   message wasn't already deleted. JAM_DeleteMessage() now only decreases
   ActiveMsgs if the message wasn't already deleted.

 * Updated the documentation to reflect the need to free() memory after
   JAM_CloseMB() and failed calls to JAM_OpenMB() and JAM_CreateMB().

 * Eliminated compiler warnings

Changes in 1.3:

 * JAM_AddMessage() would fail when trying to add an empty message
   to the messagebase under Linux. Fixed.

Changes in 1.2:

 * Since JAM_GetSubField() is not reentrant and cannot be used in
   multi-threaded applications, JAM_GetSubField_R() was added as a
   replacement for cases where a reentrant function is needed.

Changes in 1.1:

 * Added support for Win32 and Linux

 * Added JAM_AddEmptyMessage()

 * Rewrote the Makefiles

 * Rewrote the CRC32 routine

 * Fixed broken JAM_FindUser()

 * Fixed broken JAM_GetSubfield()

 * Changed JAM_OpenMB so that files are opened in binary mode. This is
   necessary to use JAMLIB under Windows.

 * Improved JAM_ReadMsgHeader() to give the error JAM_NO_MESSAGE if
   the message no longer exists in the messagebase and JAM_CORRUPT_MSG
   if the subfields of the message have been corrupted.

 * Improved portability by changing JAMLIB so that it no longer reads
   and writes stuctures directly using fread() and fwrite().

 * Improved ANSI-C compatibilty by no longer including the non-ANSI
   header file memory.h and using feof() to check for EOF instead of
   errno == EPASTEOF.

 * Added an #ifdef so that ushort and ulong are no longer defined in
   jam.h when compiling under Linux. These are normally already defined
   in the standard header files.


License
-------
JAMLIB - A JAM subroutine library
Copyright (C) 1999 Björn Stenberg

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  


Description
-----------
These are a collection of subroutines that encapsulate much of the
format-specific and tedious details of the JAM message base format. The
idea is that application programmers by using these routines can
concentrate on the more high-level issues of their programs instead of
worrying about their JAM routines.

  I [Björn Stenberg] wrote these routines primarily because I needed 
them myself. I was trying to implement JAM support in my FrexxLink BBS 
system and was frustrated by the poor level of documentation supplied in 
the JAMAPI archive distributed by the JAM authors. Finally, I dove into 
the JAMAPI source code in a desperate attempt at finding out how to use it. 
To my despair, I discovered that the JAMAPI is targeted at a very low level. 
I would need to implement a lot of JAM-handling code into my own program.

  This library is an attempt to do two things:

  Firstly, provide an, at least sparingly, _documented_ API, allowing
application programmers to easily implement JAM into their programs.

  Secondly, raise the level of functionality above that of the original
JAMAPI package, so that the application programmer does not have to learn
and understand all the internals of the JAM message base format to
implement support for it.

  I have not succeded completely on any of the two points, of course.
Documentation can never be too good, and there are still a few things about
JAM you must know in order to use it. But I think I have made it somewhat
easier than perhaps was the case before.


References
----------
If you are extra curious about the JAM message format, I suggest you get
a hold of an archive called JAMAPI.ARJ. That archive contains a file called
JAM.DOC which is the file I have used as reference for the development of
these routines.


Credits
-------
All original code except for the CRC32 routine was written by Björn
Stenberg. The CRC32 code was rewritten by Johan Billing for JAMLIB 1.1
to replace the original CRC32 code whose origin and copyright was unclear.
The jam.h header file is a compilation of the best from the various header
files in the JAMAPI package with some of additions by Björn Stenberg as well.
Additions and modifications by Johan Billing.

  The JAM message base proposal is:

  JAM(mbp) - Copyright 1993 Joaquim Homrighausen, Andrew Milner,
                            Mats Birch, Mats Wallin.
                            ALL RIGHTS RESERVED


Contact Information
-------------------
For questions about JAMLIB, please contact:

   Johan Billing

   E-mail: billing@df.lth.se

  If you wish to contact Björn Stenberg, his current e-mail address (as of
1999-12-21) is bjorn@haxx.nu.


THE LIBRARY
===========

The Source Code
---------------
  I made a point of making this library as system independant as I could.
Only one function needs to be altered when porting this to another system:
The file locking. ANSI C does not include file locking so there is not much
I can do about it.
  The choice of C over C++ is a part of this philosophy aswell. More
systems have C compilers than C++ compilers, and more people know C than
C++. Also, converting this library to a C++ class should be fairly simple.
If you do, send me a copy.

  I use some naming conventions throughout the code and in the examples.
These are invented by myself as a reaction to the stunningly ugly and
hard-to-read Hungarian Notation promoted by some people. The rules of my
notation are simple:

  * All library-global identifiers are prefixed with 'JAM_'. All
    file-global identifiers are prefixed with 'jam_'. Local identifiers do
    not have prefixes.

  * All variables have a suffix describing their basic type. Suffixes used
    in this library are:
    _I - integer                        (int      Example_I)
    _C - character                      (char     Example_C)
    _S - struct                         (struct   Example_S)
    _P - pointer                        (void*    Example_P)
    _A - array

    Suffixes are then combined, to show the correct type:
    _PI  - pointer to integer           (int*     Example_PI)
    _PC  - pointer to char              (char*    Example_PC)
    _AC  - array of char                (char     Example_AC[x])
    _PPS - pointer to pointer to struct (struct** Example_PPS)

  * Functions do not have suffixes

  The whole idea is that it is quicker to read and comprehend a variable
called 'Text_PC' than one called 'pszText'. We read from left to right, and
thus the most important information - the name - should be the leftmost
data in the word. The variable type is additional information and is
therefore added to the end where it does not disturb the reader.


The Functions
-------------
  The library is divided into five groups:

  * Message base functions
  * Message functions
  * Subfield functions
  * LastRead functions
  * Miscellanous functions


--------------------------------------------------------------------------

                      Message base functions
                      ----------------------

  These functions handle JAM message bases, by opening, locking, scanning
etc the contents of a message base. These are fairly straight-forward and
simple routines that you should have little, if any, trouble with.

  A message base is identified by a message base handle, which is obtained
from either JAM_OpenMB() och JAM_CreateMB(). All functions that read or
write from the message base take this handle as parameter, to know which
message base to use.

================================
JAM_OpenMB - Open a message base
================================

Syntax
        int JAM_OpenMB( uchar* Basename_PC, t_JamBase** NewBase_PPS );

Description
        Opens a message base. Only one message base can be open at a time.

Parameters
        Basename_PC     The path and base filename of the message base.
                        "Base filename" means the filename without the
                        JAM-specific extension.

        NewBase_PPS     A pointer to a message base handle where the new
                        message base handle will be written. On error you
                        must free() this memory if (*NewBase_PPS) not NULL.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_BAD_PARAM   if NewBas_PPS is NULL

Example
        {
          int Result_I;

          Result_I = JAM_OpenMB( "c:\\jam\\mybase", &Base_PS );
          if ( Result_I )
            printf("JAM_OpenMB returned %d.\n", Result_I );
        }



================================
JAM_CloseMB - Close message base
================================

Syntax
        int JAM_CloseMB( Base_PS );

Description
        Unlocks (if locked) and closes the currently open message base.

Parameters
        Base_PS         The message base to close. Note, that you must
                        free() this memory by yourself.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_LOCK_FAILED if the message base could not be unlocked

Example
        {
          int Result_I;

          Result_I = JAM_CloseMB( Base_PS );
          if ( Result_I )
            printf("JAM_CloseMB returned %d.\n", Result_I );
        }



========================================
JAM_CreateMB - Create a new message base
========================================

Syntax
        int JAM_CreateMB( uchar*      Basename_PC,
                          ulong       BaseMsg_I,
                          s_JamBase** NewBase_PPS );

Description
        Creates the necessary files for a new message base and writes a
        new message base header.
        If the message base already exists, its contents are destroyed.

Parameters
        Basename_PC     The path and base filename of the new message base.

        BaseMsg_I       The base message number (first message #) for the
                        new message base. This number is used when
                        calculating new messages' unique message number. It
                        should not be set to 0.

        NewBase_PPS     A pointer to a message base handle where the new
                        message base handle will be written. On error you
                        must free() this memory if (*NewBase_PPS) not NULL.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_BAD_PARAM   if BaseMsg_I is 0 or NewBase_PPS is NULL

Example
        {
          int Result_I;

          Result_I = JAM_CreateMB( "c:\\jam\\mybase", 1, &Base_PS );
          if ( Result_I )
            printf("JAM_CreateMB returned %d.\n", Result_I );
        }



====================================
JAM_RemoveMB - Remove a message base
====================================

Syntax
        int JAM_RemoveMB( ErrorBase_PS, uchar* Basename_PC );

Description
        Deletes all files associated with a message base. No checking is
        done as to whether the message base is currently open or not.

Parameters
        ErrorBase_PS    The message base in which to store the I/O error,
                        if any. This parameter does *NOT* specify the
                        message to be removed, it is only used for error
                        tracking purposes. If an i/o error occurs when
                        removing the message base files, this message base
                        handler will simply hold the error code.

        Basename_PC     The path and base filename of the message base to
                        remove.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_BAD_PARAM   if ErrorBase_PS is NULL

Example
        {
          int        Result_I;

          Result_I = JAM_RemoveMB( Base_PS, "c:\\jam\\mybase" );
          if ( Result_I ) {
            printf("JAM_RemoveMB returned %d.\n", Result_I );
            if ( Result_I == JAM_IO_ERROR )
              printf( "i/o error %d\n", JAM_Errno( ErrorBase_PS ) );
          }
        }



===================================================
JAM_LockMB - Lock message base for exclusive access
===================================================

Syntax
        int JAM_LockMB( t_JamBase* Base_PS );

Description
        Locks the currently open message base so that no other programs may
        modify it. The message base should be locked for only small periods
        of time, or the performance of tossers and other software may be
        affected.

Parameters
        Base_PS         The message base to lock

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_LOCK_FAILED if the message base is currently locked by another
                        process
        JAM_BAD_PARAM   if Base_PS is NULL

Example
        {
            int        Result_I;

            while ( 1 ) {
                Result_I = JAM_LockMB( Base_PS );
                if ( Result_I ) {

                   if ( Result_I == JAM_LOCK_FAILED )
                       /* base locked by someone else, wait for unlock */
                       sleep( 1 );

                   else {
                       /* error */
                       printf("JAM_LockMB returned %d.\n", Result_I );
                       return -1;
                   }
                }
            }
        }



==================================
JAM_UnlockMB - Unlock message base
==================================

Syntax
        int JAM_UnlockMB( s_JamBase* Base_PS );

Description
        Unlocks message base, allowing other programs to modify it.

Parameters
        Base_PS         The message base to unlock

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_BAD_PARAM   if Base_PS is NULL

Example
        {
          int Result_I;

          Result_I = JAM_UnlockMB( Base_PS );
          if ( Result_I )
            printf("JAM_UnlockMB returned %d.\n", Result_I );
        }



===========================================
JAM_ReadMBHeader - Read message base header
===========================================

Syntax
        int JAM_ReadMBHeader( s_JamBase*       Base_PS,
                              s_JamBaseHeader* Header_PS );

Description
        Reads the message base header from the start of the JAM header
        file.

Parameters
        Base_PS         The message base to use

        Header_PS       A pointer to a base header structure where the base
                        header will be stored.

Returns
        0               if successful
        JAM_BAD_PARAM   if Base_PS or Header_PS is NULL
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()

Example
        {
          s_JamBaseHeader BaseHeader_S;
          int             Result_I;

          Result_I = JAM_ReadMBHeader( Base_PS, &BaseHeader_S );
          if ( Result_I )
            printf("JAM_ReadMBHeader returned %d.\n", Result_I );
        }



=============================================
JAM_WriteMBHeader - Write message base header
=============================================

Syntax
        int JAM_WriteMBHeader( s_JamBase*       Base_PS,
                               s_JamBaseHeader* Header_PS );

Description
        Increases the ModCounter field by one, resets the header signature
        and writes the message base header to the start of the JAM header
        file.

Parameters
        Base_PS         The message base to use

        Header_PS       A pointer to the base header to be stored

Returns
        0               if successful
        JAM_BAD_PARAM   if Base_PS or Header_PS is NULL
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NOT_LOCKED  if the message base is not locked

Example
        {
          s_JamBaseHeader BaseHeader_S;
          int             Result_I;

          /* modify header here */

          Result_I = JAM_WriteMBHeader( &BaseHeader_S );
          if ( Result_I )
            printf("JAM_WriteMBHeader returned %d.\n", Result_I );
        }



=====================================
JAM_FindUser - Find message to a user
=====================================

Syntax
        int JAM_FindUser( s_JamBase* Base_PS,
                          ulong      UserCrc_I,
                          ulong      StartMsg_I,
                          ulong*     MsgNo_PI );

Description
        Scans the message base looking for a message written to a specific
        user.

Parameters
        Base_PS         The message base to use

        UserCrc_I       The CRC32 value for the searched name

        StartMsg_I      The first message number to look at. This value is
                        not the message's unique number, but rather the
                        absolute position of the message in the message
                        base. Message 0 therefore means the first message.

        MsgNo_PI        A pointer to a variable where the message number
                        for the found message will be stored. This number
                        is the absolute message position in the message
                        base. Message 0 means the first message.

Returns
        0               if a message was found
        JAM_NO_USER     if no message was found
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()

Example
        {
          uchar Name_AC[32];
          int   Result_I;
          ulong Crc_I;
          ulong Msg_I;

          strcpy( Name_AC, "Bjorn Stenberg" );

          Crc_I = JAM_Crc32( Name_AC, strlen( Name_AC ) );

          Result_I = JAM_FindUser( Base_PS, Crc_I, 0, &Msg_I );

          switch ( Result_I ) {
            case JAM_NO_USER:
              printf("No message for me.\n");
              break;

            case JAM_IO_ERROR:
              printf("IO error %d\n", JAM_Errno() );
              break;
          }
        }



==========================================================
JAM_GetMBSize - Get the number of messages in message base
==========================================================

Syntax
        int JAM_GetMBSize( s_JamBase* Base_PS,
                           ulong*     Messages_PI );

Description
        Finds out the number of messages (deleted and undeleted) in the
        message base.

Parameters
        Base_PS         The message base to use

        Messages_PI     A pointer to a variable where the number of
                        messages will be stored.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()

Example
        {
          int   Result_I;
          ulong Size_I;

          Result_I = JAM_GetMBSize( Base_PS, &Size_I );
          if ( Result_I )
            printf("JAM_GetMBSize returned %d.\n", Result_I );
         }





--------------------------------------------------------------------------

                         Message functions
                         -----------------

  These functions handle individual JAM messages. A JAM message contains of
three parts:

  * Message Header
  * Message Header Subfields
  * Message Text

  The message header is a simple C structure and the message text is a
simple text buffer.
  The subfields, however, are a bit more tricky. These contain everything
that is not covered by the header, including the TO, FROM, SUBJECT fields,
origin and destination network adresses etc. There can be an unlimited
number of subfields to a message.
  In this routine library the subfields are encapsulated by a 'subfield
packet', which is handled by its own set of routines. See a later section
of this document for an explanation of those.

=============================================================
JAM_ReadMsgHeader - Read a message's header and its subfields
=============================================================

Syntax
        int JAM_ReadMsgHeader( s_JamBase*       Base_PS,
                               ulong            MsgNo_I,
                               s_JamMsgHeader*  Header_PS,
                               s_JamSubPacket** Subfields_PPS );

Description
        Reads a message header and (optionally) the message header
        subfields.

Parameters
        Base_PS         The message base to use

        MsgNo_I         The message number, i.e. the absolute position of
                        the message in the message base. Message 0 is the
                        first message.

        Header_PS       A pointer to a message header structure where the
                        message header will be stored.

        Subfields_PPS   A pointer to a subpacket pointer, where the
                        subfield packet handle will be stored.
                        If this parameter is NULL, no subfields are read.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NO_MEMORY   if a memory allocation failed
        JAM_NO_MESSAGE  if message has been removed
        JAM_CORRUPT_MSG if message subfields are corrupted

Example
        {
          s_JamMsgHeader  Header_S;
          s_JamSubPacket* SubPack_PS
          int             Result_I;

          Result_I = JAM_ReadMsgHeader( 0, &Header_S, &SubPack_PS );
          if ( Result_I )
            printf("JAM_ReadMsgHeader returned %d.\n", Result_I );
        }



=======================================
JAM_ReadMsgText - Read a message's text
=======================================

Syntax
        int JAM_ReadMsgText( s_JamBase* Base_PS,
                             ulong      Offset_I,
                             ulong      Length_I,
                             uchar*     Buffer_PC );

Description
        Reads the body text associated with a message.

Parameters
        Base_PS         The message base to use

        Offset_I        The text position in the text file. This
                        information is stored in the message header.

        Length_I        The text length. This information is stored in the
                        message header.

        Buffer_PC       A pointer to where the text should be stored.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()

Example
        {
          s_JamMsgHeader Header_S;
          uchar*         Buffer_PC;
          int            Result_I;

          /* read msg header */
          Result_I = JAM_ReadMsgHeader( Base_PS, 0, &Header_S, &SubPack_PS );
          if ( Result_I ) {
            printf("JAM_ReadMsgHeader returned %d.\n", Result_I );
            return;
          }

          /* allocate buffer text */
          Buffer_PC = (uchar*) malloc( Header_S.TxtLen );
          if ( !Buffer_PC ) {
            printf("malloc failed.\n");
            return;
          }

          /* read text */
          Result_I = JAM_ReadMsgText( Base_PS,
                                      Header_S.TxtOffset,
                                      Header_S.TxtLen,
                                      Buffer_PC );
          if ( Result_I )
            printf("JAM_ReadMsgText returned %d.\n", Result_I );

          free( Buffer_PC );
        }



==============================================
JAM_AddMessage - Add a message to message base
==============================================

Syntax
        int JAM_AddMessage( s_JamBase*      Base_PS,
                            s_JamMsgHeader* Header_PS,
                            s_JamSubPacket* SubPack_PS,
                            uchar*          Text_PC,
                            ulong           TextLen_I );

Description
        Adds a message to the message base. Fully automatic.

Parameters
        Base_PS         The message base to use

        Header_PS       A pointer to the message header struct. The
                        function will set the following header fields:
                        Signature, Revision, TxtOffset, TxtLen, SubfieldLen
                        and MsgNum. Whatever you set these fields to will
                        be overwritten.

        SubPack_PS      A subfield packet handler, containing all subfields
                        for the message.

        Text_PC         A pointer to the first byte of the message text.

        TextLen_I       The length of the message text, excluding any zero
                        termination characters.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NOT_LOCKED  if the message base is not locked

Example
        {
          s_JamSubPacket*   SubPacket_PS;
          s_JamSubfield     Subfield_S;
          s_JamMsgHeader    Header_S;
          uchar             Text_AC[64];
          uchar             Field_AC[64];

          /*
          **  Fix message header
          */

          JAM_ClearMsgHeader( &Header_S );
          Header_S.DateWritten = time(NULL);

          /*
          **  Create subfield packet
          */

          SubPacket_PS = JAM_NewSubPacket();
          if ( !SubPacket_PS ) {
              printf("JAM_NewSubPacket returned NULL.\n" );
              return;
          }

          /* set up subfield 1 */
          strcpy( Field_AC, "This is field #1" );
          Subfield_S.LoID   = JAMSFLD_SENDERNAME;
          Subfield_S.HiID   = 0;
          Subfield_S.DatLen = strlen( Field_AC );
          Subfield_S.Buffer = Field_AC;
          JAM_PutSubfield( SubPacket_PS, &Subfield_S );

          /* set up subfield 2 */
          strcpy( Field_AC, "This is field #2" );
          Subfield_S.LoID   = JAMSFLD_RECVRNAME;
          Subfield_S.HiID   = 0;
          Subfield_S.DatLen = strlen( Field_AC );
          Subfield_S.Buffer = Field_AC;
          JAM_PutSubfield( SubPacket_PS, &Subfield_S );


          /*
          **  Add message
          */

          strcpy( Text_AC, "Hello world!\nThis is a test.");

          /* [lock the message base] */

          Result_I = JAM_AddMessage( Base_PS, &Header_S, SubPacket_PS,
                                     Text_AC, strlen( Text_AC ) );
          if ( Result_I  ) {
            printf("JAM_AddMessage returned %d.\n", Result_I );
            return;
          }

          /* [unlock the message base] */

          JAM_DelSubPacket( SubPacket_PS );
        }



=================================================================
JAM_AddEmptyMessage - Add a empty message entry to a message base
=================================================================
Syntax
        int JAM_AddEmptyMessage( s_JamBase*      Base_PS);

Description
        Adds an empty message header to the message base. Useful
        when writing a messagebase maintenance utility.

Parameters
        Base_PS         The message base to use

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NOT_LOCKED  if the message base is not locked

Example
        none



===============================================
JAM_ChangeMsgHeader - Change a message's header
===============================================

Syntax
        int JAM_ChangeMsgHeader( s_JamBase*      Base_PS,
                                 ulong           MsgNo_I,
                                 s_JamMsgHeader* Header_PS );

Description
        Writes over an old message header with a new one. Only the header -
        not the subfields - can be changed due to the subfields' dynamic
        size.
        NOTE! Use this function with caution. It is easy to corrupt a
        message by giving it an incorrect header.

Parameters
        Base_PS         The message base to use

        MsgNo_I         The absolute message number. Message #0 is the
                        first in the message base.

        Header_PS       A pointer to the header structure to write.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NOT_LOCKED  if the message base is not locked

Example
        {
          s_JamMsgHeader  Header_S;
          int             Result_I;

          /* [lock the message base] */

          Result_I = JAM_ReadMsgHeader( Base_PS, 0, &Header_S, NULL );
          if ( Result_I )
            printf("JAM_ReadMsgHeader returned %d.\n", Result_I );

          Header_S.TimesRead++;

          Result_I = JAM_ChangeMsgHeader( Base_PS, 0, &Header_S );
          if ( Result_I )
            printf("JAM_ChangeMsgHeader returned %d.\n", Result_I );

          /* [unlock the message base] */
        }



=====================================================
JAM_ClearMsgHeader - Clear a message header structure
=====================================================

Syntax
        int JAM_ClearMsgHeader( s_JamMsgHeader* Header_PS );

Description
        Clears a message header structure and prepares it for use. This
        includes setting the Signature field and the Revision field to
        their correct values, and setting the CRC fields to JAM_NO_CRC.

Parameters
        Header_PS       A pointer to the structure to prepare.

Returns
        0               if successful
        JAM_BAD_PARAM   if Header_PS is NULL



===================================================
JAM_DeleteMessage - Delete message from messagebase
===================================================

Syntax
        int JAM_DeleteMessage( s_JamBase*        Base_PS,
                               ulong             MsgNo_I );


Description
        Deletes message from messagebase by setting HdrOffset and UserCRC
        in index to 0xFFFFFFFF. ActiveMsgs in base header also updated.

Parameters
        Base_PS         The message base to use

        MsgNo_I         The absolute message number. Message #0 is the
                        first in the message base.

Returns
        0               if successful
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NOT_LOCKED  if the message base is not locked

Example
        none



--------------------------------------------------------------------------

                      Subfield packet functions
                      -------------------------

  As described earlier, a subfield is a part of the message header. Due to
the complexity of the different network types in use, it is not feasible to
try and cram all data into one header struct. Therefore, JAM uses a fairly
small header struct and instead marks all additional data fields as
'subfields'.
  In order to make life a little more easy, I have used the concept of a
container for all subfields. I call it a 'Subfield Packet'. It is
identified by a struct pointer, and should be looked upon as a file or a
list that you manipulate via the following four functions:


===============================================
JAM_NewSubPacket - Create a new subfield packet
===============================================

Syntax
        s_JamSubPacket* JAM_NewSubPacket( void );

Description
        Creates a new, empty, subfield packet.

Parameters
        None

Returns
        The subpacket handle, if successful, or NULL if a memory allocation
        failed.

Example
        {
          s_JamSubPacket*   SubPacket_PS;

          SubPacket_PS = JAM_NewSubPacket();
          if ( !SubPacket_PS ) {
              printf("JAM_NewSubPacket returned NULL.\n" );
              return;
          }
        }



===========================================
JAM_DelSubPacket - Delete a subfield packet
===========================================

Syntax
        int JAM_DelSubPacket( s_JamSubPacket* SubPack_PS );

Description
        Frees all memory used by a subfield packet. All subfields in the
        packet will be lost and the packet handle will not be valid any
        more.

Parameters
        SubPack_PS      The subfield packet to delete

Returns
        0               if successful
        JAM_BAD_PARAM   if SubPack_PS is NULL.

Example
        {
          s_JamSubPacket*   SubPacket_PS;

          SubPacket_PS = JAM_NewSubPacket();
          if ( !SubPacket_PS ) {
              printf("JAM_NewSubPacket returned NULL.\n" );
              return;
          }

          SubPacket_PS = JAM_DelSubPacket();
          if ( !SubPacket_PS ) {
              printf("JAM_DelSubPacket returned NULL.\n" );
              return;
          }
        }



=======================================================================
JAM_GetSubfield - Get a subfield from a subfield packet (not reentrant)
=======================================================================

Syntax
        s_JamSubfield* JAM_GetSubfield( s_JamSubPacket* SubPack_PS );

Description
        Returns a pointer to the first/next subfield struct in the subfield
        packet.

        WARNING: This function is not reentrant and should not be used in
        multi-threaded applications unless you know what you are doing.

        Use JAM_GetSubfield_R instead when a reentrant function is needed.

Parameter
        SubPack_PS      The subfield packet to use. If this parameter is
                        NULL, the next subfield from the subfield packet
                        previously scanned will be returned.

Returns
        A pointer to a subfield, if successful, or NULL if there are no
        more subfields in the packet.

Example
        {
          s_JamSubPacket* SubPack_PS;
          s_JamSubfield*  Subfield_PS;
          s_JamMsgHeader  Header_S;
          int             Result_I;

          Result_I = JAM_ReadMsgHeader( 0, &Header_S, &SubPack_PS );
          if ( Result_I )
            printf("JAM_ReadMsgHeader returned %d.\n", Result_I );

          for ( Subfield_PS = JAM_GetSubfield( SubPack_PS ); Subfield_PS;
                Subfield_PS = JAM_GetSubfield( NULL ) )
            printf("Subfield id %d\n", Subfield_PS->LoID );

          JAM_DelSubPacket( SubPack_PS );
        }



=====================================================================
JAM_GetSubfield_R - Get a subfield from a subfield packet (reentrant)
=====================================================================

Syntax
        s_JamSubfield* JAM_GetSubfield( s_JamSubPacket* SubPack_PS,
                                        ulong*          Count_PI );

Description
        Returns a pointer to the first/next subfield struct in the subfield
        packet.

        This function is a reentrant replacement for JAM_GetSubfield.

Parameter
        SubPack_PS      The subfield packet to use.

        Count_PI        Pointer to a variable that contains the number of
                        the subfield to retrieve. The variable should be
                        set to zero the first time the function is called
                        and is then automatically increased by the function
                        for any subsequent calls.

Returns
        A pointer to a subfield, if successful, or NULL if there are no
        more subfields in the packet.

Example
        {
          s_JamSubPacket* SubPack_PS;
          s_JamSubfield*  Subfield_PS;
          s_JamMsgHeader  Header_S;
          ulong           Count_I;
          int             Result_I;

          Result_I = JAM_ReadMsgHeader( 0, &Header_S, &SubPack_PS );
          if ( Result_I )
            printf("JAM_ReadMsgHeader returned %d.\n", Result_I );

          Count_I = 0;

          while( ( Subfield_PS = JAM_GetSubfield_R( SubPack_PS , &Count_I ) ) )
            printf("Subfield id %d\n", Subfield_PS->LoID );

          JAM_DelSubPacket( SubPack_PS );
        }

=======================================================
JAM_PutSubfield - Put a subfield into a subfield packet
=======================================================

Syntax
        int JAM_PutSubfield( s_JamSubPacket* SubPack_PS,
                             s_JamSubfield*  Subfield_PS );

Description
        Puts a subfield into a subfield packet. The subfield is copied
        before being put into the subfield packet.

Parameters
        SubPack_PS      The subfield packet to add to

        Subfield_PS     The subfield to put in the packet

Returns
        0               if successful
        JAM_NO_MEMORY   if a memory allocation failed

Example
        {
          s_JamSubPacket*   SubPacket_PS;
          s_JamSubfield     Subfield_S;
          uchar             Field_AC[64];

          SubPacket_PS = JAM_NewSubPacket();
          if ( !SubPacket_PS ) {
              printf("JAM_NewSubPacket returned NULL.\n" );
              return;
          }

          /* set up subfield 1 */
          strcpy( Field_AC, "This is field #1" );
          Subfield_S.LoID   = JAMSFLD_SENDERNAME;
          Subfield_S.HiID   = 0;
          Subfield_S.DatLen = strlen( Field_AC );
          Subfield_S.Buffer = Field_AC;
          JAM_PutSubfield( SubPacket_PS, &Subfield_S );

          /* set up subfield 2 */
          strcpy( Field_AC, "This is field #2" );
          Subfield_S.LoID   = JAMSFLD_RECVRNAME;
          Subfield_S.HiID   = 0;
          Subfield_S.DatLen = strlen( Field_AC );
          Subfield_S.Buffer = Field_AC;
          JAM_PutSubfield( SubPacket_PS, &Subfield_S );

          JAM_DelSubPacket( SubPacket_PS );
        }





--------------------------------------------------------------------------

                       LastRead functions
                       ------------------

  JAM implements the often-used concept of high water marking for
remembering which user read how many messages in each area.
  Personally I think this concept stinks, since it does not store *which*
messages a user has read, only the number of the highest message he has
read. But since it's a part of JAM and it's fairly straightforward and
easy, I've implemented two support functions for it.
  I would, however, strongly recommend all BBS programmers to use proper
message mapping systems instead, so your users can read their messages in
whatever order they wish.


=========================================
JAM_ReadLastRead - Read a lastread record
=========================================

Syntax
        int JAM_ReadLastRead( s_JamBase*     Base_PS,
                              ulong          User_I,
                              s_JamLastRead* Record_PS );

Description
        Reads a lastread record from the lastread file.

Parameter
        Base_PS         The message base to use

        User_I          A system-unique user number.

        Record_PS       A pointer to the lastread struct where the record
                        will be stored.

Returns
        0               if successful
        JAM_BAD_PARAM   if Record_PS is NULL
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()
        JAM_NO_USER     if the user number was not found

Example
        {
          int           Result_I;
          s_JamLastRead LastRead_S;

          Result_I = JAM_ReadLastRead( Base_PS, 4711, &LastRead_S );
          if ( Result_I )
            printf("JAM_ReadLastRead returned %d\n", Result_I );
        }



===========================================
JAM_WriteLastRead - Write a lastread record
===========================================

Syntax
        int JAM_WriteLastRead( s_JamBase*     Base_PS,
                               ulong          User_I,
                               s_JamLastRead* Record_PS );

Description
        Writes a lastread record to the lastread file. If the user number
        could not be found, the record will be appended to the end of the
        file.

Parameter
        Base_PS         The message base to use

        User_I          A system-unique user number

        Record_PS       A pointer to the lastread struct to be written

Returns
        0               if successful
        JAM_BAD_PARAM   if Record_PS is NULL
        JAM_IO_ERROR    if an I/O error occured. see JAM_Errno()

Example
        {
          int           Result_I;
          s_JamLastRead LastRead_S;

          Result_I = JAM_WriteLastRead( Base_PS, 4711, &LastRead_S );
          if ( Result_I )
            printf("JAM_WriteLastRead returned %d\n", Result_I );
        }




--------------------------------------------------------------------------

                      Miscellanous functions
                      ----------------------


==============================================
JAM_Crc32 - Calculate CRC32 on a block of data
==============================================

Syntax
        ulong JAM_Crc32( uchar* Buffer_PC,
                         ulong  Length_I );

Description
        Calculates the Crc32 value for a block of data. All ASCII
        characters are converted to lowercase before calculating
        the CRC (the input data is unchanged).

Parameters
        Buffer_PC       A pointer to the first byte of the data block

        Length_I        The number of bytes in the data block

Returns
        The Crc32 value

Example
        {
          ulong Crc_I;
          uchar Text_AC[32];

          strcpy( Text_AC, "Hello world!\n");

          Crc_I = JAM_Crc32( Text_AC, strlen( Text_AC ) );
        }



=============================
JAM_Errno - Specify I/O error
=============================

Syntax
        int JAM_Errno( s_JamBase* Base_PS );

Description
        When any of these library routines return JAM_IO_ERROR, you can
        call this function to find out exactly what went wrong.

Parameters
        Base_PS         The message base to use

Returns
        Standard 'errno' values, as the C compiler generated them, or if
        the I/O error was system specific, the return code is (10000 +
        system status code).

Examples
        {
          int   Result_I;
          uchar Text_AC[10];

          /* generate an I/O error */
          Result_I = JAM_ReadMsgText( 0xffffffff, 10, Text_AC );
          if ( Result_I ) {
              errno = JAM_Errno( Base_PS );
              perror("JAM i/o error");
          }
        }