home *** CD-ROM | disk | FTP | other *** search
/ Beijing Paradise BBS Backup / PARADISE.ISO / software / BBSDOORW / JAMAPI.ZIP / TPAPI.ZIP / JAMMB.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-07-01  |  47.9 KB  |  1,769 lines

  1. (*
  2. **    JAM(mbp) - The Joaquim-Andrew-Mats Message Base Proposal
  3. **
  4. **    Turbo Pascal API
  5. **
  6. **    Written by Joaquim Homrighausen.
  7. **
  8. **    ----------------------------------------------------------------------
  9. **
  10. **    jammb.pas (JAMmb)
  11. **
  12. **    Prototypes and definitions for the JAM message base API
  13. **
  14. **    Copyright 1993 Joaquim Homrighausen, Andrew Milner, Mats Birch, and
  15. **    Mats Wallin. ALL RIGHTS RESERVED.
  16. **
  17. **    93-06-28    JoHo
  18. **    Initial coding
  19. *)
  20. UNIT JAMMB;
  21.  
  22. {$IFNDEF VER55}
  23.     {$A-,B-,E-,D+,L+,F-,I-,N-,O+,R-,S-,V-,X+}
  24. {$ELSE}
  25.     {$A-,B-,E-,D+,L+,F-,I-,N-,O+,R-,S-,V-}
  26. {$ENDIF}
  27.  
  28. INTERFACE
  29.  
  30. USES
  31.     JAM;
  32.  
  33. {-API status codes}
  34.  
  35. CONST
  36.     JAMAPIMSG_NOTHING                =    0;
  37.     JAMAPIMSG_HDRPRINT            =    1;   {Header fingerprint missing or invalid}
  38.     JAMAPIMSG_HDRPASSWORD        =    2;   {Invalid password for header file}
  39.     JAMAPIMSG_MSGPASSWORD        =    3;   {Invalid password for message}
  40.     JAMAPIMSG_ISOPEN                =    4;   {Message base is open}
  41.     JAMAPIMSG_ISNOTOPEN            =    5;   {Message base is not open}
  42.     JAMAPIMSG_ISNOTLOCKED        =    6;   {Message base is not locked}
  43.     JAMAPIMSG_SEEKERROR            =    7;   {Unable to seek in file}
  44.     JAMAPIMSG_CANTMKFILE        =    8;   {Unable to create file}
  45.     JAMAPIMSG_CANTRDFILE        =    9;   {Unable to read file}
  46.     JAMAPIMSG_CANTWRFILE        =    10;  {Unable to write file}
  47.     JAMAPIMSG_CANTRMFILE        =    11;  {Unable to remove file}
  48.     JAMAPIMSG_FIRSTMSG            =    12;  {Already on first message}
  49.     JAMAPIMSG_NEWMODCOUNTER    =    13;  {ModCounter have been updated since read}
  50.     JAMAPIMSG_NOMORETEXT        =    14;  {Read to end of message text}
  51.     JAMAPIMSG_CANTLKFILE        =    15;  {Unable to lock message base}
  52.     JAMAPIMSG_CANTFINDUSER    =    16;  {Unable to locate LastRead record}
  53.     JAMAPIMSG_CANTFINDMSG        =    17;  {Unable to locate message to user}
  54.     JAMAPIMSG_NOMOREMSGS        =    18;  {At end or beginning of file}
  55.     JAMAPIMSG_BADHEADERSIG    =    19;  {Invalid message header signature}
  56.     JAMAPIMSG_BADHEADERREV    = 20;  {Invalid message header revision}
  57.     JAMAPIMSG_INVMSGNUM            =    21;  {Invalid message number specified}
  58.  
  59. {-Values for FROMWHERE parameter to SeekFunc, indentical to those normally
  60.     defined in most C compiler's IO.H as SEEK_SET, SEEK_CUR, and SEEK_END}
  61.  
  62.     JAMSEEK_SET                            =    0;   {To specified position}
  63.     JAMSEEK_CUR                            =    1;   {From current position}
  64.     JAMSEEK_END                            =    2;   {From end of file}
  65.  
  66. {-Return values for user comparison functions}
  67.  
  68.     ScanMsgHdrStop                    =    0;
  69.     ScanMsgHdrDiscard                =    1;
  70.     ScanMsgHdrNextHdr                =    2;
  71.  
  72.     ScanMsgIdxStop                    =    0;
  73.     ScanMsgIdxNextMsg                =    1;
  74.  
  75. {-Basic object definition and user comparison definitions}
  76.  
  77. TYPE
  78.     JAMAPIPTR            =    ^JAMAPI;
  79.  
  80.     ScanMsgHdrFunc=    FUNCTION(TheAPI:JAMAPIPTR):INTEGER;
  81.     ScanMsgIdxFunc=    FUNCTION(TheAPI:JAMAPIPTR):INTEGER;
  82.  
  83.     JAMAPI                =    OBJECT
  84.         BaseName        :    FILENAMETYPE;        {Path for message base}
  85.         WorkBuf            :    JAMBUFPTR;           {Workspace, initialized by user}
  86.         WorkLen            :    LONGINT;             {Length of workspace (above)}
  87.         WorkPos            :    LONGINT;             {Current position, for multi-reads}
  88.         IsOpen            :    BOOLEAN;             {Message base is open}
  89.         HaveLock        :    BOOLEAN;             {.JHR file is locked}
  90.         Errno                :    INTEGER;             {Last DOS error in case of error}
  91.         APImsg            :    INTEGER;             {API status message}
  92.         HdrHandle        :    INTEGER;             {File handle for .JHR file}
  93.         TxtHandle        :    INTEGER;             {File handle for .JDT file}
  94.         IdxHandle        :    INTEGER;             {File handle for .JDX file}
  95.         LrdHandle        :    INTEGER;             {File handle for .JLR file}
  96.         LastMsgNum    :    LONGINT;             {Last message number fetched}
  97.         Idx                    :    JAMIDXREC;           {Message index record}
  98.         Hdr                    :    JAMHDR;              {Message header record}
  99.         HdrInfo            :    JAMHDRINFO;          {Message header info record}
  100.         SubFieldPtr    :    JAMSUBFIELDPTR;      {Subfield record}
  101.         LastLRDnum    :    LONGINT;             {Last LastRead record read (0-based)}
  102.         LastRead        :    JAMLREAD;            {LastRead record}
  103.  
  104.         constructor Init;
  105.         destructor Done; VIRTUAL;
  106.  
  107.         {-DOS file access functions, defined as VIRTUAL to be overriden by an
  108.             object that inherits this object if you feel that these are not
  109.             sufficient.}
  110.  
  111.         function CreateFile(FileName:FILENAMETYPE):INTEGER; virtual;
  112.         function OpenFile(FileName:FILENAMETYPE):INTEGER; virtual;
  113.         function CloseFile(Handle:INTEGER):INTEGER; virtual;
  114.         function UnlinkFile(FileName:FILENAMETYPE):INTEGER; virtual;
  115.         function LockFile(DoLock:BOOLEAN):INTEGER; virtual;
  116.         function SeekFile(Handle:INTEGER; FromWhere:BYTE; FilePos:LONGINT):LONGINT; virtual;
  117.         function ReadFile(Handle:INTEGER; VAR Buffer; DatLen:WORD):WORD; virtual;
  118.         function WriteFile(Handle:INTEGER; VAR Buffer; DatLen:WORD):WORD; virtual;
  119.  
  120.         {-Function to convert uppercase to lowercase}
  121.  
  122.         procedure LowCaseBuf(var Buf; DatLen:WORD);
  123.  
  124.         {-JAM message base access functions}
  125.  
  126.         function OpenMB:BOOLEAN;
  127.         function CreateMB:BOOLEAN;
  128.         function CloseMB:BOOLEAN;
  129.         function UnlinkMB:BOOLEAN;
  130.         function ReIndexMB:BOOLEAN;
  131.  
  132.         function UpdHdrInfo(WriteIt:BOOLEAN):BOOLEAN;
  133.         function LockMB(FetchHdrInfo:BOOLEAN):BOOLEAN;
  134.         function UnlockMB(UpdateHdrInfo:BOOLEAN):BOOLEAN;
  135.  
  136.         function FindField(WhatField:LONGINT; var Position:LONGINT; MaxToScan:LONGINT):BOOLEAN;
  137.         function AddField(WhatField:LONGINT; First:Boolean; DatLen:WORD; var Position:LONGINT; var Data):BOOLEAN;
  138.  
  139.         function FetchMsgIdx(WhatMsg:LONGINT):BOOLEAN;
  140.         function FetchMsgHdr(WhatMsg:LONGINT; WithSubFields:BOOLEAN):BOOLEAN;
  141.         function FetchNextMsgHdr(WithSubFields:BOOLEAN):BOOLEAN;
  142.         function FetchPrevMsgHdr(WithSubFields:BOOLEAN):BOOLEAN;
  143.         function FetchMsgTxt(FirstFetch:BOOLEAN):BOOLEAN;
  144.  
  145.         function ScanForMsgIdx(StartNum:LONGINT; ScanFwd:BOOLEAN; UserCompare:ScanMsgIdxFunc):BOOLEAN;
  146.         function ScanForMsgHdr(StartNum:LONGINT; ScanFwd:BOOLEAN; UserCompare:ScanMsgHdrFunc):BOOLEAN;
  147.  
  148.         function StoreMsgHdr(WhatMsg:LONGINT):BOOLEAN;
  149.         function StoreMsgIdx(WhatMsg:LONGINT):BOOLEAN;
  150.         function StoreMsgTxt:BOOLEAN;
  151.         function StoreMsgTxtBuf(var Buffer; BufLen:WORD; IsFirst:BOOLEAN):BOOLEAN;
  152.  
  153.         function FetchLastRead(UserID:LONGINT):BOOLEAN;
  154.         function StoreLastRead(UpdateHdrInfo:BOOLEAN):BOOLEAN;
  155.     END;{JAMAPI}
  156.  
  157. IMPLEMENTATION
  158.  
  159. USES
  160.     Dos,
  161.     JAMCRC32;
  162.  
  163.     {-Initializes object and its data members.}
  164.     constructor JAMAPI.Init;
  165.     BEGIN
  166.         BaseName:='';
  167.         WorkBuf:=NIL;
  168.         WorkLen:=0;
  169.         WorkPos:=0;
  170.         IsOpen:=FALSE;
  171.         HaveLock:=FALSE;
  172.         Errno:=0;
  173.         APImsg:=JAMAPIMSG_NOTHING;
  174.         HdrHandle:=-1;
  175.         TxtHandle:=-1;
  176.         IdxHandle:=-1;
  177.         LrdHandle:=-1;
  178.         LastMsgNum:=0;
  179.         LastLRDnum:=0;
  180.     END;
  181.  
  182.     {-Deinitializes object and closes message base if it's open}
  183.     destructor JAMAPI.Done;
  184.     BEGIN
  185. {$IFDEF VER55}
  186.         if (not CloseMB) then
  187.             ;
  188. {$ELSE}
  189.         CloseMB;
  190. {$ENDIF}
  191.     END;
  192.  
  193.     {-DOS file access functions, defined as VIRTUAL to be overriden by an
  194.         object that inherits this object if you feel that these are not
  195.         sufficient. -------------------------------------------------------------}
  196.  
  197.     {-Create file, return handle or -1 on error}
  198.     function JAMAPI.CreateFile(FileName:FILENAMETYPE):INTEGER;
  199.     VAR
  200.         az : ASCIIZ;
  201.         r : registers;
  202.     BEGIN
  203.         {NUL terminate filename for DOS}
  204.         move(FileName[1], az[1], length(FileName));
  205.         az[Succ(length(FileName))]:=#0;
  206.  
  207.         r.AH:=$3c;                         {INT 21: Create file}
  208.         r.CX:=0;                           {No special file attribute}
  209.         r.DS:=seg(az);                     {Filename}
  210.         r.DX:=ofs(az);
  211.         msdos(r);                          {Call DOS}
  212.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  213.             BEGIN
  214.                 Errno:=r.AX;                   {Save DOS error}
  215.                 CreateFile:=-1;                {Return error}                
  216.             END
  217.         ELSE
  218.             CreateFile:=r.AX;                {No error, return handle}
  219.     END;
  220.  
  221.     {-Open file, return handle or -1 on error, mode is ReadWrite/DenyNone/
  222.         NoInherit}
  223.     function JAMAPI.OpenFile(FileName:FILENAMETYPE):INTEGER;
  224.     VAR
  225.         az : ASCIIZ;
  226.         r : registers;
  227.     BEGIN
  228.         {NUL terminate filename for DOS}
  229.         move(FileName[1], az[1], length(FileName));
  230.         az[Succ(length(FileName))]:=#0;
  231.  
  232.         r.AH:=$3d;                         {INT 21: Open file}
  233.         r.AL:=194;                         {11000010: ReadWrite/DenyNone/NoInherit}
  234.         r.DS:=seg(az);                     {Filename}
  235.         r.DX:=ofs(az);
  236.         msdos(r);                          {Call DOS}
  237.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  238.             BEGIN
  239.                 Errno:=r.AX;                   {Save DOS error}
  240.                 OpenFile:=-1;                  {Return error}                
  241.             END
  242.         ELSE
  243.             OpenFile:=r.AX;                  {No error, return handle}
  244.     END;
  245.  
  246.     {-Close file pointed to by handle, return 0 or -1 on error}
  247.     function JAMAPI.CloseFile(Handle:INTEGER):INTEGER;
  248.     VAR
  249.         r : registers;
  250.     BEGIN
  251.         r.AH:=$3e;                         {INT 21: Close file}
  252.         r.BX:=Handle;                      {File handle}
  253.         msdos(r);                          {Call DOS}
  254.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  255.             BEGIN
  256.                 Errno:=r.AX;                   {Save DOS error}
  257.                 CloseFile:=-1;                 {Return error}                
  258.             END
  259.         ELSE
  260.             CloseFile:=0;                    {No error}
  261.     END;
  262.  
  263.     {-Unlink (delete) file, return handle or -1 on error}
  264.     function JAMAPI.UnlinkFile(FileName:FILENAMETYPE):INTEGER;
  265.     VAR
  266.         az : ASCIIZ;
  267.         r : registers;
  268.     BEGIN
  269.         {NUL terminate filename for DOS}
  270.         move(FileName[1], az[1], length(FileName));
  271.         az[Succ(length(FileName))]:=#0;
  272.  
  273.         r.AH:=$41;                         {INT 21: Unlink (delete) file}
  274.         r.DS:=seg(az);                     {Filename}
  275.         r.DX:=ofs(az);
  276.         msdos(r);                          {Call DOS}
  277.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  278.             BEGIN
  279.                 Errno:=r.AX;                   {Save DOS error}
  280.                 UnlinkFile:=-1;                {Return error}                
  281.             END
  282.         ELSE
  283.             UnlinkFile:=0;                   {No error}
  284.     END;
  285.  
  286.     {-Lock or unlock header file, return 0 or -1 on error}
  287.     function JAMAPI.LockFile(DoLock:BOOLEAN):INTEGER;
  288.     VAR
  289.         r : registers;
  290.     BEGIN
  291.         r.AH:=$5c;                         {INT 21: Lock/Unlock file region}
  292.         if (DoLock) then
  293.             r.AL:=0                          {Lock file, AL=0}
  294.         ELSE
  295.             r.AL:=1;                         {Unlock file, AL=1}
  296.         r.BX:=HdrHandle;                   {File handle (header file)}
  297.         r.CX:=0;                           {Offset of region, high-order word}
  298.         r.DX:=0;                           {Offset of region, low-order word}
  299.         r.SI:=0;                           {Length of region, high-order word}
  300.         r.DI:=1;                           {Length of region, low-order word}
  301.  
  302.         msdos(r);                          {Call DOS}
  303.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  304.             BEGIN
  305.                 Errno:=r.AX;                   {Save DOS error}
  306.                 LockFile:=-1;                  {Return error}                
  307.             END
  308.         ELSE
  309.             BEGIN
  310.                 LockFile:=0;                   {No error}
  311.                 HaveLock:=DoLock;              {Make sure we know status}
  312.             END;
  313.     END;
  314.  
  315.     {-Seek to specified position (relative to FROMWHERE) in file, returns new
  316.         position or -1 on error}
  317.     function JAMAPI.SeekFile(Handle:INTEGER; FromWhere:BYTE; FilePos:LONGINT):LONGINT;
  318.     VAR
  319.         r : registers;
  320.     BEGIN
  321.         r.AH:=$42;                         {INT 21: Move file pointer (LSEEK)}
  322.         r.AL:=FromWhere;                   {From where to seek}
  323.         r.BX:=Handle;                      {File handle}
  324.         r.CX:=WORD(FilePos shr 16);        {Offset to seek to}
  325.         r.DX:=WORD(FilePos);               {Offset to seek to}
  326.         msdos(r);                          {Call DOS}
  327.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  328.             BEGIN
  329.                 Errno:=r.AX;                   {Save DOS error}
  330.                 SeekFile:=$FFFFFFFF;           {Return error}                
  331.             END
  332.         ELSE
  333.             SeekFile:=(LONGINT(r.DX) shl 16) or LONGINT(r.AX); {Return new position}
  334.     END;
  335.  
  336.     {-Read data from file pointed to by Handle into Buffer. The maximum amount
  337.         of data to read is contained in DatLen. The function returns the number
  338.         of bytes read or $ffff on error.}
  339.     function JAMAPI.ReadFile(Handle:INTEGER; VAR Buffer; DatLen:WORD):WORD;
  340.     VAR
  341.         r : registers;
  342.     BEGIN
  343.         r.AH:=$3f;                         {INT 21: Read from file or device}
  344.         r.BX:=Handle;                      {File handle}
  345.         r.DS:=seg(Buffer);                 {Buffer}
  346.         r.DX:=ofs(Buffer);
  347.         r.CX:=DatLen;                      {Amount to read (bytes)}
  348.         msdos(r);                          {Call DOS}
  349.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  350.             BEGIN
  351.                 Errno:=r.AX;                   {Save DOS error}
  352.                 ReadFile:=$ffff;               {Return error}                
  353.             END
  354.         ELSE
  355.             ReadFile:=r.AX;                  {Return amount read}
  356.     END;
  357.  
  358.     {-Write data from Buffer to file pointed to by Handle. The maximum amount
  359.         of data to write is contained in DatLen. The function returns the number
  360.         of bytes written or $ffff on error.}
  361.     function JAMAPI.WriteFile(Handle:INTEGER; VAR Buffer; DatLen:WORD):WORD;
  362.     VAR
  363.         r : registers;
  364.     BEGIN
  365.         r.AH:=$40;                         {INT 21: Write to file or device}
  366.         r.BX:=Handle;                      {File handle}
  367.         r.DS:=seg(Buffer);                 {Buffer}
  368.         r.DX:=ofs(Buffer);
  369.         r.CX:=DatLen;                      {Amount to write (bytes)}
  370.         msdos(r);                          {Call DOS}
  371.         if (r.Flags and FCarry<>0) then    {Check carry flag (=error)}
  372.             BEGIN
  373.                 Errno:=r.AX;                   {Save DOS error}
  374.                 WriteFile:=$ffff;              {Return error}                
  375.             END
  376.         ELSE
  377.             WriteFile:=r.AX;                 {Return amount written}
  378.     END;
  379.  
  380.     {-Function to convert uppercase to lowercase}
  381.  
  382.     procedure JAMAPI.LowCaseBuf(var Buf; DatLen:WORD);
  383.     VAR
  384.         JunkArray : ARRAY[1..MAXINT] of CHAR ABSOLUTE Buf;
  385.         W : WORD;
  386.     BEGIN
  387.         for w:=1 to DatLen do
  388.             if (JunkArray[w]>='A') and (JunkArray[w]<='Z') then
  389.                 inc(JunkArray[w], 32);
  390.     END;
  391.  
  392.     {-JAM message base access functions. --------------------------------------}
  393.  
  394.     {-Opens message base. Returns TRUE on success or FALSE on error}
  395.     function JAMAPI.OpenMB:BOOLEAN;
  396.     VAR
  397.         FileName : FILENAMETYPE;
  398. {$IFDEF VER55}
  399.         JunkRet : INTEGER;
  400. {$ENDIF}
  401.     BEGIN
  402.         OpenMB:=FALSE;
  403.  
  404.         {Make sure it's not already open}
  405.         if (IsOpen) then
  406.             BEGIN
  407.                 APImsg:=JAMAPIMSG_ISOPEN;
  408.                 OpenMB:=TRUE;
  409.                 exit;
  410.             END
  411.         ELSE
  412.             APImsg:=JAMAPIMSG_NOTHING;
  413.  
  414.  
  415.         {.JHR file}
  416.         FileName:=BaseName+EXT_HDRFILE;
  417.         HdrHandle:=OpenFile(FileName);
  418.         if (HdrHandle<0) then
  419.             exit;
  420.  
  421.         {Read fixed size block from header file}
  422.         if (ReadFile(HdrHandle, HdrInfo, sizeof(JAMHDRINFO))<>sizeof(JAMHDRINFO)) then
  423.             BEGIN
  424. {$IFDEF VER55}
  425.                 JunkRet:=CloseFile(HdrHandle);
  426. {$ELSE}
  427.                 CloseFile(HdrHandle);
  428. {$ENDIF}
  429.                 exit;
  430.             END;
  431.         if (HdrInfo.BaseMsgNum=0) then
  432.             inc(HdrInfo.BaseMsgNum);
  433.  
  434.         {.JDT file}
  435.         FileName:=BaseName+EXT_TXTFILE;
  436.         TxtHandle:=OpenFile(FileName);
  437.         if (TxtHandle<0) then
  438.             BEGIN
  439. {$IFDEF VER55}
  440.                 JunkRet:=CloseFile(HdrHandle);
  441. {$ELSE}
  442.                 CloseFile(HdrHandle);
  443. {$ENDIF}
  444.                 exit;
  445.             END;
  446.  
  447.         {.JDX file}
  448.         FileName:=BaseName+EXT_IDXFILE;
  449.         IdxHandle:=OpenFile(FileName);
  450.         if (IdxHandle<0) then
  451.             BEGIN
  452. {$IFDEF VER55}
  453.                 JunkRet:=CloseFile(HdrHandle);
  454.                 JunkRet:=CloseFile(TxtHandle);
  455. {$ELSE}
  456.                 CloseFile(HdrHandle);
  457.                 CloseFile(TxtHandle);
  458. {$ENDIF}
  459.                 exit;
  460.             END;
  461.  
  462.         {.JLR file}
  463.         FileName:=BaseName+EXT_LRDFILE;
  464.         LrdHandle:=OpenFile(FileName);
  465.         if (LrdHandle<0) then
  466.             BEGIN
  467. {$IFDEF VER55}
  468.                 JunkRet:=CloseFile(HdrHandle);
  469.                 JunkRet:=CloseFile(TxtHandle);
  470.                 JunkRet:=CloseFile(IdxHandle);
  471. {$ELSE}
  472.                 CloseFile(HdrHandle);
  473.                 CloseFile(TxtHandle);
  474.                 CloseFile(IdxHandle);
  475. {$ENDIF}
  476.                 exit;
  477.             END;
  478.  
  479.         {Everything went OK, return with success}
  480.         IsOpen:=TRUE;
  481.         OpenMB:=TRUE;
  482.     END;
  483.  
  484.     {-Creates new message base. Returns TRUE on success or FALSE on error}
  485.     function JAMAPI.CreateMB:BOOLEAN;
  486.     VAR
  487.         FileName : FILENAMETYPE;
  488. {$IFDEF VER55}
  489.         JunkRet : INTEGER;
  490. {$ENDIF}
  491.     BEGIN
  492.         CreateMB:=FALSE;
  493.  
  494.         {Make sure it's not already open}
  495.         if (IsOpen) then
  496.             BEGIN
  497.                 APImsg:=JAMAPIMSG_ISOPEN;
  498.                 CreateMB:=TRUE;
  499.                 exit;
  500.             END
  501.         ELSE
  502.             APImsg:=JAMAPIMSG_NOTHING;
  503.  
  504.  
  505.         {.JHR file}
  506.         FileName:=BaseName+EXT_HDRFILE;
  507.         HdrHandle:=CreateFile(FileName);
  508.         if (HdrHandle<0) then
  509.             exit;
  510.  
  511.         {Write fixed size block to header file}
  512.         fillchar(HdrInfo, sizeof(JAMHDRINFO), 0);
  513.         HdrInfo.PasswordCRC:=$FFFFFFFF;
  514.         move(HEADERSIG, HdrInfo.Signature, sizeof(HdrInfo.Signature));
  515.     HdrInfo.BaseMsgNum:=1;
  516.  
  517.         {-NOTE: The "DateCreated" field is still not filled in properly in the
  518.                         TP API}
  519.  
  520.         if (WriteFile(HdrHandle, HdrInfo, sizeof(JAMHDRINFO))<>sizeof(JAMHDRINFO)) then
  521.             BEGIN
  522. {$IFDEF VER55}
  523.                 JunkRet:=CloseFile(HdrHandle);
  524.                 JunkRet:=UnlinkFile(FileName);
  525. {$ELSE}
  526.                 CloseFile(HdrHandle);
  527.                 UnlinkFile(FileName);
  528. {$ENDIF}
  529.                 exit;
  530.             END;
  531.  
  532.         {.JDT file}
  533.         FileName:=BaseName+EXT_TXTFILE;
  534.         TxtHandle:=CreateFile(FileName);
  535.         if (TxtHandle<0) then
  536.             BEGIN
  537. {$IFDEF VER55}
  538.                 JunkRet:=CloseFile(HdrHandle);
  539. {$ELSE}
  540.                 CloseFile(HdrHandle);
  541. {$ENDIF}
  542.                 exit;
  543.             END;
  544.  
  545.         {.JDX file}
  546.         FileName:=BaseName+EXT_IDXFILE;
  547.         IdxHandle:=CreateFile(FileName);
  548.         if (IdxHandle<0) then
  549.             BEGIN
  550. {$IFDEF VER55}
  551.                 JunkRet:=CloseFile(HdrHandle);
  552.                 JunkRet:=CloseFile(TxtHandle);
  553. {$ELSE}
  554.                 CloseFile(HdrHandle);
  555.                 CloseFile(TxtHandle);
  556. {$ENDIF}
  557.                 exit;
  558.             END;
  559.  
  560.         {.JLR file}
  561.         FileName:=BaseName+EXT_LRDFILE;
  562.         LrdHandle:=CreateFile(FileName);
  563.         if (LrdHandle<0) then
  564.             BEGIN
  565. {$IFDEF VER55}
  566.                 JunkRet:=CloseFile(HdrHandle);
  567.                 JunkRet:=CloseFile(TxtHandle);
  568.                 JunkRet:=CloseFile(IdxHandle);
  569. {$ELSE}
  570.                 CloseFile(HdrHandle);
  571.                 CloseFile(TxtHandle);
  572.                 CloseFile(IdxHandle);
  573. {$ENDIF}
  574.                 exit;
  575.             END;
  576.  
  577.         {Everything went OK, return with success}
  578.         IsOpen:=TRUE;
  579.         CreateMB:=TRUE;
  580.     END;
  581.  
  582.     {-Close open message base. Returns TRUE on success or FALSE on error}
  583.     function JAMAPI.CloseMB:BOOLEAN;
  584. {$IFDEF VER55}
  585.     VAR
  586.         JunkRet : INTEGER;
  587. {$ENDIF}
  588.     BEGIN
  589.         {Make sure it's not already open}
  590.         if (IsOpen) then
  591.             BEGIN
  592.                 APImsg:=JAMAPIMSG_NOTHING;
  593.                 if (HaveLock) then
  594. {$IFDEF VER55}
  595.                     JunkRet:=LockFile(FALSE);
  596.                 JunkRet:=CloseFile(HdrHandle);
  597.                 JunkRet:=CloseFile(TxtHandle);
  598.                 JunkRet:=CloseFile(IdxHandle);
  599.                 JunkRet:=CloseFile(LrdHandle);
  600. {$ELSE}
  601.                     LockFile(FALSE);
  602.                 CloseFile(HdrHandle);
  603.                 CloseFile(TxtHandle);
  604.                 CloseFile(IdxHandle);
  605.                 CloseFile(LrdHandle);
  606. {$ENDIF}
  607.                 IsOpen:=FALSE;
  608.             END
  609.         ELSE
  610.             APImsg:=JAMAPIMSG_ISNOTOPEN;
  611.         CloseMB:=TRUE;
  612.     END;
  613.  
  614.     {-Deletes existing (closed) message base. Returns TRUE on success or FALSE
  615.         on error}
  616.     function JAMAPI.UnlinkMB:BOOLEAN;
  617.     VAR
  618.         FileName : FILENAMETYPE;
  619.         j1, j2, j3, j4 : INTEGER;
  620.     BEGIN
  621.         {Make sure it's not already open}
  622.         if (IsOpen) then
  623.             BEGIN
  624.                 APImsg:=JAMAPIMSG_ISOPEN;
  625.                 UnlinkMB:=FALSE;
  626.                 exit;
  627.             END
  628.         ELSE
  629.             APImsg:=JAMAPIMSG_NOTHING;
  630.  
  631.         {.JDH file}
  632.         FileName:=BaseName+EXT_HDRFILE;
  633.         j1:=UnlinkFile(FileName);
  634.  
  635.         {.JDT file}
  636.         FileName:=BaseName+EXT_TXTFILE;
  637.         j2:=UnlinkFile(FileName);
  638.  
  639.         {.JDX file}
  640.         FileName:=BaseName+EXT_IDXFILE;
  641.         j3:=UnlinkFile(FileName);
  642.  
  643.         {.JLR file}
  644.         FileName:=BaseName+EXT_LRDFILE;
  645.         j4:=UnlinkFile(FileName);
  646.  
  647.         UnlinkMB:=BOOLEAN((j1=0) and (j2=0) and (j3=0) and (j4=0));
  648.     END;
  649.  
  650.     {-Reindex an open message base. The message base must be locked by the
  651.         application prior to calling this function. Returns TRUE on success or
  652.         FALSE on error.}
  653.     function JAMAPI.ReIndexMB:BOOLEAN;
  654.     VAR
  655.         FileName : FILENAMETYPE;
  656.         TempIdxRec, IdxRec : JAMIDXREC;
  657.         JunkBuf : ARRAY[1..110] of BYTE;
  658.         SubPtr : JAMSUBFIELDPTR;
  659.         NextOffset, CurIdxOffset, NextIdxOffset, IdxSize, JunkLong : LONGINT;
  660.         Finished, Error, FoundField : BOOLEAN;
  661.         SubDatLen, Junk : WORD;
  662.     BEGIN
  663.         ReIndexMB:=FALSE;
  664.  
  665.         {Make sure it's open}
  666.         if (not IsOpen) then
  667.             BEGIN
  668.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  669.                 exit;
  670.             END;
  671.  
  672.         {Make sure we have lock}
  673.         if (not HaveLock) then
  674.             BEGIN
  675.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  676.                 exit;
  677.             END;
  678.  
  679.         {Close index file}
  680.         if (IdxHandle>=0) then
  681.             BEGIN
  682. {$IFDEF VER55}
  683.                 IdxHandle:=CloseFile(IdxHandle);
  684. {$ELSE}
  685.                 CloseFile(IdxHandle);
  686. {$ENDIF}
  687.                 IdxHandle:=-1;
  688.             END;
  689.  
  690.         {Create new index file}
  691.         FileName:=BaseName+EXT_IDXFILE;
  692.         IdxHandle:=CreateFile(FileName);
  693.         if (IdxHandle<0) then
  694.             BEGIN
  695.                 APImsg:=JAMAPIMSG_CANTMKFILE;
  696.                 exit;
  697.             END;
  698.  
  699.         {Process header file}
  700.         NextOffset:=LONGINT(sizeof(JAMHDRINFO));
  701.         CurIdxOffset:=0;
  702.         IdxSize:=0;
  703.         Finished:=FALSE;
  704.         Error:=FALSE;
  705.         SubPtr:=@JunkBuf;
  706.  
  707.         WHILE ((not Finished) and (not Error)) DO
  708.             BEGIN
  709.                 {Seek to correct position in header file}
  710.                 if (SeekFile(HdrHandle, JAMSEEK_SET, NextOffset)<>NextOffset) then
  711.                     BEGIN
  712.                         APImsg:=JAMAPIMSG_SEEKERROR;
  713.                         exit;
  714.                     END
  715.                 ELSE
  716.                     BEGIN
  717.                         {Read header}
  718.                         Junk:=ReadFile(HdrHandle, Hdr, sizeof(JAMHDR));
  719.                         if (Junk<>sizeof(JAMHDR)) then
  720.                             BEGIN
  721.                                 if (Junk=0) then
  722.                                     {Nothing left to read}
  723.                                     Finished:=TRUE
  724.                                 ELSE
  725.                                     {Read error}
  726.                                     BEGIN
  727.                                         APImsg:=JAMAPIMSG_CANTRDFILE;
  728.                                         Error:=TRUE;
  729.                                     END;
  730.                             END
  731.                         ELSE
  732.                             BEGIN
  733.                                 {Set values}
  734.                                 IdxRec.HdrOffset:=NextOffset;
  735.  
  736.                                 {-Search subfields if we have any and this header hasn't been
  737.                                     deleted}
  738.                                 if ((Hdr.SubfieldLen>0) and (Hdr.Attribute and MSG_DELETED=0)) then
  739.                                     BEGIN
  740.                                         FoundField:=FALSE;
  741.                                         JunkLong:=0;
  742.  
  743.                                         REPEAT
  744.                                             Junk:=ReadFile(HdrHandle, JunkBuf, 4);
  745.                                             if (Junk<>4) then
  746.                                                 BEGIN
  747.                                                     if (Junk=0) then
  748.                                                         Finished:=TRUE
  749.                                                     ELSE
  750.                                                         BEGIN
  751.                                                             APImsg:=JAMAPIMSG_CANTRDFILE;
  752.                                                             Error:=TRUE;
  753.                                                         END;
  754.                                                 END
  755.                                             ELSE
  756.                                                 BEGIN
  757.                                                     inc(JunkLong, 4);
  758.                                                     SubDatLen:=WORD(SubPtr^.DatLen);
  759.  
  760.                                                     {Is this what we're looking for?}
  761.                                                     if (SubPtr^.LoID=JAMSFLD_RECVRNAME) then
  762.                                                         BEGIN
  763.                                                             {Yes, read username}
  764.                                                             Junk:=ReadFile(HdrHandle, JunkBuf, SubDatLen);
  765.                                                             if (Junk=SubDatLen) then
  766.                                                                 FoundField:=TRUE
  767.                                                             ELSE
  768.                                                                 BEGIN
  769.                                                                     APImsg:=JAMAPIMSG_CANTRDFILE;
  770.                                                                     Error:=TRUE;
  771.                                                                 END;
  772.                                                         END
  773.                                                     ELSE
  774.                                                         {No, skip to next header}
  775.                                                         BEGIN
  776.                                                             inc(JunkLong, SubPtr^.DatLen);
  777.                                                             if (SeekFile(HdrHandle, JAMSEEK_CUR, SubPtr^.DatLen)<0) then
  778.                                                                 BEGIN
  779.                                                                     APImsg:=JAMAPIMSG_SEEKERROR;
  780.                                                                     Error:=TRUE;
  781.                                                                 END;
  782.                                                         END;
  783.                                                 END;
  784.                                         UNTIL (FoundField) or (JunkLong>=Hdr.SubfieldLen) or (Error) or (Finished);
  785.  
  786.                                         if (FoundField) then
  787.                                             BEGIN
  788.                                                 LowCaseBuf(JunkBuf, SubDatLen);
  789.                                                 IdxRec.UserCRC:=crc32(JunkBuf, SubDatLen, $FFFFFFFF);
  790.                                             END
  791.                                         ELSE
  792.                                             IdxRec.UserCRC:=$FFFFFFFF;
  793.                                     END
  794.                                 ELSE
  795.                                     {No subfields}
  796.                                     IdxRec.UserCRC:=$FFFFFFFF;
  797.  
  798.                                 {Skip message if invalid message number}
  799.                                 if (Hdr.MsgNum>=HdrInfo.BaseMsgNum) then
  800.                                     BEGIN
  801.                                         if (not Error) then
  802.                                             BEGIN
  803.                         NextIdxOffset:=(Hdr.MsgNum-HdrInfo.BaseMsgNum)*LONGINT(sizeof(JAMIDXREC));
  804.                                                 if (CurIdxOffset<>NextIdxOffset) then
  805.                                                     BEGIN
  806.                                                         if (NextIdxOffset>IdxSize) then
  807.                                                             BEGIN
  808.                                                                 if (SeekFile(IdxHandle, JAMSEEK_SET, IdxSize)<>IdxSize) then
  809.                                                                     BEGIN
  810.                                                                         APImsg:=JAMAPIMSG_SEEKERROR;
  811.                                                                         Error:=TRUE;
  812.                                                                     END;
  813.  
  814.                                                                 {Extend index and fill with empty index records}
  815.                                                                 TempIdxRec.UserCRC:=$ffffffff;
  816.                                                                 TempIdxRec.HdrOffset:=$ffffffff;
  817.                                                                 WHILE (not Error) and (IdxSize<>NextIdxOffset) DO
  818.                                                                     BEGIN
  819.                                                                         if (WriteFile(IdxHandle, TempIdxRec, sizeof(JAMIDXREC))<>sizeof(JAMIDXREC)) then
  820.                                                                             BEGIN
  821.                                         APImsg:=JAMAPIMSG_CANTWRFILE;
  822.                                         Error:=TRUE;
  823.                                                                             END
  824.                                                                         ELSE
  825.                                                                             inc(IdxSize, LONGINT(sizeof(JAMIDXREC)));
  826.                                                                     END;
  827.                                                             END
  828.                                                         ELSE
  829.                                                             if (SeekFile(IdxHandle, JAMSEEK_SET, NextIdxOffset)<>NextIdxOffset) then
  830.                                                                 BEGIN
  831.                                                                     APImsg:=JAMAPIMSG_SEEKERROR;
  832.                                                                     Error:=TRUE;
  833.                                                                 END;
  834.                                                     END
  835.                                             END;
  836.  
  837.                                         if (not Error) then
  838.                                             BEGIN
  839.                                                 {Write index record}
  840.                                                 if (WriteFile(IdxHandle, IdxRec, sizeof(JAMIDXREC))<>sizeof(JAMIDXREC)) then
  841.                                                     BEGIN
  842.                                                         {Write error}
  843.                                                         APImsg:=JAMAPIMSG_CANTWRFILE;
  844.                                                         Error:=TRUE;
  845.                                                     END
  846.                                                 ELSE
  847.                                                     BEGIN
  848.                             CurIdxOffset:=NextIdxOffset+LONGINT(sizeof(JAMIDXREC));
  849.                             if (CurIdxOffset>IdxSize) then
  850.                                 IdxSize:=CurIdxOffset;
  851.                                                         inc(NextOffset, LONGINT(sizeof(JAMHDR))+LONGINT(Hdr.SubfieldLen));
  852.                                                     END;
  853.                                             END;
  854.                                     END;
  855.                             END;{ReadOK}
  856.                     END;{SeekOK}
  857.             END;{while}
  858.         ReIndexMB:=(not Error);
  859.     END;
  860.  
  861.     {-Fetch or update message header info block. Returns TRUE on success and
  862.         FALSE on failure.}
  863.     function JAMAPI.UpdHdrInfo(WriteIt:BOOLEAN):BOOLEAN;
  864.     BEGIN
  865.         UpdHdrInfo:=FALSE;
  866.  
  867.         {Make sure it's open}
  868.         if (not IsOpen) then
  869.             BEGIN
  870.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  871.                 exit;
  872.             END;
  873.  
  874.         {Make sure we have lock if we need it}
  875.         if (WriteIt) and (not HaveLock) then
  876.             BEGIN
  877.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  878.                 exit;
  879.             END;
  880.  
  881.         {Seek to beginning of file}
  882.         if (SeekFile(HdrHandle, JAMSEEK_SET, 0)<>0) then
  883.             BEGIN
  884.                 APImsg:=JAMAPIMSG_SEEKERROR;
  885.                 exit;
  886.             END;
  887.  
  888.         if (WriteIt) then
  889.             BEGIN
  890.                 {Update}
  891.                 inc(HdrInfo.ModCounter);
  892.  
  893.                 if (HdrInfo.BaseMsgNum=0) then
  894.                     inc(HdrInfo.BaseMsgNum);
  895.  
  896.                 if (WriteFile(HdrHandle, HdrInfo, sizeof(JAMHDRINFO))<>sizeof(JAMHDRINFO)) then
  897.                     BEGIN
  898.                         dec(HdrInfo.ModCounter);
  899.                         APImsg:=JAMAPIMSG_CANTWRFILE;
  900.                         exit;
  901.                     END;
  902.             END
  903.         ELSE
  904.             {Read}
  905.             BEGIN
  906.                 if (ReadFile(HdrHandle, HdrInfo, sizeof(JAMHDRINFO))<>sizeof(JAMHDRINFO)) then
  907.                     BEGIN
  908.                         APImsg:=JAMAPIMSG_CANTRDFILE;
  909.                         exit;
  910.                     END;
  911.                 if (HdrInfo.BaseMsgNum=0) then
  912.                     inc(HdrInfo.BaseMsgNum);
  913.             END;
  914.  
  915.         APImsg:=JAMAPIMSG_NOTHING;
  916.         UpdHdrInfo:=TRUE;
  917.     END;
  918.  
  919.     {-Locks message base and if successful, optionally reads the header info
  920.         block at the beginning of the header file. Returns TRUE on success or
  921.         FALSE on failure.}
  922.     function JAMAPI.LockMB(FetchHdrInfo:BOOLEAN):BOOLEAN;
  923.     BEGIN
  924.         LockMB:=FALSE;
  925.  
  926.         {Make sure it's open}
  927.         if (not IsOpen) then
  928.             BEGIN
  929.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  930.                 exit;
  931.             END;
  932.  
  933.         {Attempt to lock it if we don't already have a lock}
  934.         if (not HaveLock) and (LockFile(TRUE)<>0) then
  935.             BEGIN
  936.                 APImsg:=JAMAPIMSG_CANTLKFILE;
  937.                 exit;
  938.             END;
  939.  
  940.         {Read header info block if told to}
  941.         if (FetchHdrInfo) then
  942.             BEGIN
  943.                 if (not UpdHdrInfo(FALSE)) then
  944.                     exit;
  945.             END;
  946.  
  947.         APImsg:=JAMAPIMSG_NOTHING;
  948.         LockMB:=TRUE;
  949.     END;
  950.  
  951.  
  952.     {-Unlocks message base and if successful, optionally writes the header
  953.         info block to the beginning of the header file. Returns TRUE upon
  954.         success and FALSE upon failure.}
  955.     function JAMAPI.UnlockMB(UpdateHdrInfo:BOOLEAN):BOOLEAN;
  956.     BEGIN
  957.         UnlockMB:=FALSE;
  958.  
  959.         {Make sure it's open}
  960.         if (not IsOpen) then
  961.             BEGIN
  962.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  963.                 exit;
  964.             END;
  965.  
  966.         {Make sure we have a lock}
  967.         if (not HaveLock) then
  968.             BEGIN
  969.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  970.                 exit;
  971.             END;
  972.  
  973.         {Update header info if told to before unlocking}
  974.         if (UpdateHdrInfo) then
  975.             BEGIN
  976.                 if (not UpdHdrInfo(TRUE)) then
  977.                     exit;
  978.             END;
  979.  
  980.         {Unlock the file}
  981. {$IFDEF VER55}
  982.         APImsg:=LockFile(FALSE);
  983. {$ELSE}
  984.         LockFile(FALSE);
  985. {$ENDIF}
  986.  
  987.         APImsg:=JAMAPIMSG_NOTHING;
  988.         UnlockMB:=TRUE;
  989.     END;
  990.  
  991.     {-Find specified field in WorkBuf. Returns TRUE if the field is found and
  992.         FALSE if the field is not found. The POSITION parameter is updated to
  993.         point to the beginning of the found field.}
  994.     function JAMAPI.FindField(WhatField:LONGINT; var Position:LONGINT; MaxToScan:LONGINT):BOOLEAN;
  995.     BEGIN
  996.         WHILE (Position<MaxToScan) DO
  997.             BEGIN
  998.                 SubFieldPtr:=@WorkBuf^[Position];
  999.                 if (SubFieldPtr^.LoID=WORD(WhatField)) then
  1000.                     BEGIN
  1001.                         FindField:=TRUE;
  1002.                         exit;
  1003.                     END;
  1004.                 inc(Position, SubFieldPtr^.DatLen + sizeof(JAMBINSUBFIELD));
  1005.             END;{while}
  1006.         FindField:=FALSE;
  1007.     END;
  1008.  
  1009.     {-Add specified field to WorkBuf. It requires that POSITION has been reset
  1010.         to one (1) prior to the first call. The POSITION parameter is updated to
  1011.         point to the first position after the newly added field. The FIRST
  1012.         parameter determines if the DatLen/ID fields should be copied. This
  1013.         allows multiple calls to this function to add more than 64kb to a
  1014.         subfield. Returns TRUE on success or FALSE if the requested field does
  1015.         not fit into WorkBuf}
  1016.     function JAMAPI.AddField(WhatField:LONGINT; First:Boolean; DatLen:WORD; var Position:LONGINT; var Data):BOOLEAN;
  1017.     VAR
  1018.         DummyField : JAMBINSUBFIELDPTR;
  1019.     BEGIN
  1020.         AddField:=TRUE;
  1021.         if (First) then
  1022.             BEGIN
  1023.                 if ((LONGINT(sizeof(JAMBINSUBFIELD))+Pred(Position)+DatLen)>WorkLen) then
  1024.                     BEGIN
  1025.                         AddField:=FALSE;
  1026.                         exit;
  1027.                     END;
  1028.                 DummyField:=@WorkBuf^[Position];
  1029.                 DummyField^.LoID:=WORD(WhatField);
  1030.                 DummyField^.HiID:=0;
  1031.                 DummyField^.DatLen:=DatLen;
  1032.                 inc(Position, sizeof(JAMBINSUBFIELD));
  1033.             END
  1034.         ELSE
  1035.             if ((DatLen+Pred(Position))>WorkLen) then
  1036.                 BEGIN
  1037.                     AddField:=FALSE;
  1038.                     exit;
  1039.                 END;
  1040.         move(Data, WorkBuf^[Position], DatLen);
  1041.         inc(Position, LONGINT(DatLen));
  1042.     END;
  1043.  
  1044.     {-Scan message index records starting at the specified number (STARTNUM).
  1045.         For each record found, the function calls USERCOMPARE. The Forward
  1046.         parameter determines the direction of the scan. The Idx record will
  1047.         contain the newly read message index record and. Returns TRUE if user
  1048.         function returned ScanMsgIdxStop and FALSE otherwise.}
  1049.     function JAMAPI.ScanForMsgIdx(StartNum:LONGINT; ScanFwd:BOOLEAN; UserCompare:ScanMsgIdxFunc):BOOLEAN;
  1050.     VAR
  1051.         WhatOffset : LONGINT;
  1052.         UserResult : INTEGER;
  1053.         ReadCount : WORD;
  1054.     BEGIN
  1055.         ScanForMsgIdx:=FALSE;
  1056.  
  1057.         {Make sure it's open}
  1058.         if (not IsOpen) then
  1059.             BEGIN
  1060.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1061.                 exit;
  1062.             END;
  1063.  
  1064.     {Make sure the message number is valid}
  1065.         if (StartNum<HdrInfo.BaseMsgNum) then
  1066.             BEGIN
  1067.                 APImsg:=JAMAPIMSG_INVMSGNUM;
  1068.                 exit;
  1069.             END;
  1070.  
  1071.         {Position of first index record}
  1072.         WhatOffset:=LONGINT((StartNum-HdrInfo.BaseMsgNum) * LONGINT(sizeof(JAMIDXREC)));
  1073.  
  1074.         {Seek to correct position if we're going forward}
  1075.         if (ScanFwd) then
  1076.             BEGIN
  1077.                 if (SeekFile(IdxHandle, JAMSEEK_SET, WhatOffset)<>WhatOffset) then
  1078.                     BEGIN
  1079.                         APImsg:=JAMAPIMSG_SEEKERROR;
  1080.                         exit;
  1081.                     END;
  1082.             END;
  1083.  
  1084.         {Scan index records}
  1085.         WHILE TRUE DO
  1086.             BEGIN
  1087.                 {Seek to correct position if we're going backwards}
  1088.                 if (not ScanFwd) then
  1089.                     BEGIN
  1090.                         if (SeekFile(IdxHandle, JAMSEEK_SET, WhatOffset)<>WhatOffset) then
  1091.                             BEGIN
  1092.                                 APImsg:=JAMAPIMSG_SEEKERROR;
  1093.                                 exit;
  1094.                             END;
  1095.                     END;
  1096.  
  1097.                 ReadCount:=ReadFile(IdxHandle, Idx, sizeof(JAMIDXREC));
  1098.                 if (ReadCount=sizeof(JAMIDXREC)) then
  1099.                     BEGIN
  1100.                         LastMsgNum:=StartNum;
  1101.                         
  1102.                         {Call user function and process result}
  1103.                         UserResult:=UserCompare(@Self);
  1104.                         if (UserResult=ScanMsgIdxStop) then
  1105.                             BEGIN
  1106.                                 ScanForMsgIdx:=TRUE;
  1107.                                 exit;
  1108.                             END
  1109.                         ELSE
  1110.                             BEGIN
  1111.                                 if (ScanFwd) then
  1112.                                     inc(StartNum)
  1113.                                 ELSE
  1114.                                     BEGIN
  1115.                                         if (StartNum=HdrInfo.BaseMsgNum) then
  1116.                                             BEGIN
  1117.                                                 APImsg:=JAMAPIMSG_NOMOREMSGS;
  1118.                                                 exit;
  1119.                                             END
  1120.                                         ELSE
  1121.                                             BEGIN
  1122.                                                 dec(StartNum);
  1123.                                                 dec(WhatOffset, LONGINT(sizeof(JAMIDXREC)));
  1124.                                             END;
  1125.                                     END;
  1126.                             END;
  1127.                     END
  1128.                 ELSE
  1129.                     {Check for end of file, otherwise error}
  1130.                     BEGIN
  1131.                         if (ReadCount=0) and (ScanFwd) then
  1132.                             APImsg:=JAMAPIMSG_NOMOREMSGS
  1133.                         ELSE
  1134.                             APImsg:=JAMAPIMSG_CANTRDFILE;
  1135.                         exit;
  1136.                     END;
  1137.             END;{while}
  1138.     END;
  1139.  
  1140.     {-Fetch specified message number. If WITHSUBFIELDS=TRUE, the function will
  1141.         attempt to read as much of the message's subfield data into the internal
  1142.         work buffer as possible. Returns TRUE on success and FALSE on failure.}
  1143.     function JAMAPI.FetchMsgHdr(WhatMsg:LONGINT; WithSubFields:BOOLEAN):BOOLEAN;
  1144.     VAR
  1145.         ReadCount : WORD;
  1146.     BEGIN
  1147.         FetchMsgHdr:=FALSE;
  1148.  
  1149.         {Fetch index record, checks for IsOpen}
  1150.         if (not FetchMsgIdx(WhatMsg)) then
  1151.             exit;
  1152.  
  1153.         {Fetch header}
  1154.         if (SeekFile(HdrHandle, JAMSEEK_SET, Idx.HdrOffset)<>Idx.HdrOffset) then
  1155.             BEGIN
  1156.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1157.                 exit;
  1158.             END;
  1159.         if (ReadFile(HdrHandle, Hdr, sizeof(JAMHDR))<>sizeof(JAMHDR)) then
  1160.             BEGIN
  1161.                 APImsg:=JAMAPIMSG_CANTRDFILE;
  1162.                 exit;
  1163.             END;
  1164.  
  1165.         {Check header}
  1166.         if (Hdr.Signature<>HEADERSIG) then
  1167.             BEGIN
  1168.                 APImsg:=JAMAPIMSG_BADHEADERSIG;
  1169.                 exit;
  1170.             END;
  1171.         if (Hdr.Revision<>CurrentRevLev) then
  1172.             BEGIN
  1173.                 APImsg:=JAMAPIMSG_BADHEADERREV;
  1174.                 exit;
  1175.             END;
  1176.  
  1177.         {Fetch subfields if told to}
  1178.         if (WithSubFields) then
  1179.             BEGIN
  1180.                 if (Hdr.SubfieldLen>WorkLen) then
  1181.                     ReadCount:=WORD(WorkLen)
  1182.                 ELSE
  1183.                     ReadCount:=WORD(Hdr.SubfieldLen);
  1184.             END
  1185.         ELSE
  1186.             ReadCount:=0;
  1187.  
  1188.         if (ReadCount<>0) then
  1189.             BEGIN
  1190.                 if (ReadFile(HdrHandle, WorkBuf^, ReadCount)<>ReadCount) then
  1191.                     BEGIN
  1192.                         APImsg:=JAMAPIMSG_CANTRDFILE;
  1193.                         exit;
  1194.                     END;
  1195.             END;
  1196.  
  1197.         {Got it OK}
  1198.         APImsg:=JAMAPIMSG_NOTHING;
  1199.         FetchMsgHdr:=TRUE;
  1200.     END;
  1201.  
  1202.     {-Fetch index record with specified message number. Returns TRUE on success
  1203.         and FALSE on failure.}
  1204.     function JAMAPI.FetchMsgIdx(WhatMsg:LONGINT):BOOLEAN;
  1205.     VAR
  1206.         WhatOffset : LONGINT;
  1207.     BEGIN
  1208.         FetchMsgIdx:=FALSE;
  1209.  
  1210.         {Make sure it's open}
  1211.         if (not IsOpen) then
  1212.             BEGIN
  1213.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1214.                 exit;
  1215.             END;
  1216.  
  1217.     {Make sure the message number is valid}
  1218.     if (WhatMsg<HdrInfo.BaseMsgNum) then
  1219.             BEGIN
  1220.                 APImsg:=JAMAPIMSG_INVMSGNUM;
  1221.                 exit;
  1222.             END;
  1223.  
  1224.         {Fetch index record}
  1225.         WhatOffset:=LONGINT((WhatMsg-HdrInfo.BaseMsgNum) * LONGINT(sizeof(JAMIDXREC)));
  1226.         if (SeekFile(IdxHandle, JAMSEEK_SET, WhatOffset)<>WhatOffset) then
  1227.             BEGIN
  1228.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1229.                 exit;
  1230.             END;
  1231.         if (ReadFile(IdxHandle, Idx, sizeof(JAMIDXREC))<>sizeof(JAMIDXREC)) then
  1232.             BEGIN
  1233.                 APImsg:=JAMAPIMSG_CANTRDFILE;
  1234.                 exit;
  1235.             END;
  1236.  
  1237.         {Update structure}
  1238.         LastMsgNum:=WhatMsg;
  1239.         APImsg:=JAMAPIMSG_NOTHING;
  1240.         FetchMsgIdx:=TRUE;
  1241.     END;
  1242.  
  1243.     {-Fetch header for message number following LASTMSGNUM. Returns TRUE on
  1244.         success and FALSE on failure.}
  1245.     function JAMAPI.FetchNextMsgHdr(WithSubFields:BOOLEAN):BOOLEAN;
  1246.     BEGIN
  1247.         FetchNextMsgHdr:=FetchMsgHdr(Succ(LastMsgNum), WithSubFields);
  1248.     END;
  1249.  
  1250.     {-Fetch header for message number preceding LASTMSGNUM. Returns TRUE on
  1251.         success and FALSE on failure.}
  1252.     function JAMAPI.FetchPrevMsgHdr(WithSubFields:BOOLEAN):BOOLEAN;
  1253.     BEGIN
  1254.         if (LastMsgNum>1) then
  1255.             FetchPrevMsgHdr:=FetchMsgHdr(Pred(LastMsgNum), WithSubFields)
  1256.         ELSE
  1257.             BEGIN
  1258.                 APImsg:=JAMAPIMSG_FIRSTMSG;
  1259.                 FetchPrevMsgHdr:=FALSE;
  1260.             END;
  1261.     END;
  1262.  
  1263.     {-Fetch text for specified message number. FIRSTFETCH determines if the
  1264.         function seeks to the actual text position or simply keeps reading.
  1265.         Returns TRUE on success and FALSE on failure.}
  1266.     function JAMAPI.FetchMsgTxt(FirstFetch:BOOLEAN):BOOLEAN;
  1267.     VAR
  1268.         RemainToRead : LONGINT;
  1269.         ReadCount : WORD;
  1270.     BEGIN
  1271.         FetchMsgTxt:=FALSE;
  1272.  
  1273.         {Make sure it's open}
  1274.         if (not IsOpen) then
  1275.             BEGIN
  1276.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1277.                 exit;
  1278.             END;
  1279.  
  1280.         {Seek to appropriate text position if this is first fetch}
  1281.         if (FirstFetch) then
  1282.             BEGIN
  1283.                 if (SeekFile(TxtHandle, JAMSEEK_SET, Hdr.TxtOffset)<>Hdr.TxtOffset) then
  1284.                     BEGIN
  1285.                         APImsg:=JAMAPIMSG_SEEKERROR;
  1286.                         exit;
  1287.                     END
  1288.                 ELSE
  1289.                     {Haven't read anything yet}
  1290.                     WorkPos:=0;
  1291.             END;
  1292.  
  1293.         {Make sure we don't read more than we have}
  1294.         if (WorkPos>=Hdr.TxtLen) then
  1295.             BEGIN
  1296.                 APImsg:=JAMAPIMSG_NOMORETEXT;
  1297.                 FetchMsgTxt:=TRUE;
  1298.                 exit;
  1299.             END;
  1300.  
  1301.         {Figure out how much to get}
  1302.         RemainToRead:=(Hdr.TxtLen-WorkPos);
  1303.         if (RemainToRead>WorkLen) then
  1304.             BEGIN
  1305.                 ReadCount:=WORD(WorkLen);
  1306.                 if (WorkLen>$0000fffe) then
  1307.                     ReadCount:=$fffe;
  1308.             END
  1309.         ELSE
  1310.             BEGIN
  1311.                 ReadCount:=WORD(RemainToRead);
  1312.                 if (RemainToRead>$0000fffe) then
  1313.                     ReadCount:=$fffe;
  1314.             END;
  1315.  
  1316.         {Get it from disk}
  1317.         if (ReadFile(TxtHandle, WorkBuf^, ReadCount)<>ReadCount) then
  1318.             BEGIN
  1319.                 APImsg:=JAMAPIMSG_CANTRDFILE;
  1320.                 exit;
  1321.             END;
  1322.  
  1323.         {Got it OK}
  1324.         inc(WorkPos, LONGINT(ReadCount));
  1325.         APImsg:=JAMAPIMSG_NOTHING;
  1326.         FetchMsgTxt:=TRUE;
  1327.     END;
  1328.  
  1329.     {-Scan message headers starting at the specified number (STARTNUM). For
  1330.         each header found, the function calls USERCOMPARE. The Forward parameter
  1331.         determines the direction of the scan. The Hdr record will contain the
  1332.         newly read message header and if the header had any subfields, WorkBuf
  1333.         will contain as much subfield data as will fit. If all of the subfield
  1334.         data didn't fit and the USERCOMPARE function returns ScanMsgHdrDiscard,
  1335.         the function will read the remaining subfield data and call the user
  1336.         function again. Returns TRUE if user function returned ScanMsgHdrStop and
  1337.         FALSE otherwise.}
  1338.     function JAMAPI.ScanForMsgHdr(StartNum:LONGINT; ScanFwd:BOOLEAN; UserCompare:ScanMsgHdrFunc):BOOLEAN;
  1339.     VAR
  1340.         UserResult : INTEGER;
  1341.         MaxBlockToRead, JunkW : WORD;
  1342.         ReadBytes : LONGINT;
  1343.     BEGIN
  1344.         ScanForMsgHdr:=FALSE;
  1345.  
  1346.     {Make sure the message number is valid}
  1347.         if (StartNum<HdrInfo.BaseMsgNum) then
  1348.             BEGIN
  1349.                 APImsg:=JAMAPIMSG_INVMSGNUM;
  1350.                 exit;
  1351.             END;
  1352.  
  1353.         {Scan headers}
  1354.         WHILE TRUE DO
  1355.             BEGIN
  1356.                 {Seek to correct position for current message}
  1357.                 if (not FetchMsgIdx(StartNum)) then
  1358.                         exit;
  1359.                 if (SeekFile(HdrHandle, JAMSEEK_SET, Idx.HdrOffset)<>Idx.HdrOffset) then
  1360.                     BEGIN
  1361.                         APImsg:=JAMAPIMSG_SEEKERROR;
  1362.                         exit;
  1363.                     END;
  1364.  
  1365.                 JunkW:=ReadFile(HdrHandle, Hdr, sizeof(JAMHDR));
  1366.                 if (JunkW=sizeof(JAMHDR)) then
  1367.                     BEGIN
  1368.                         LastMsgNum:=StartNum;
  1369.                         
  1370.                         {If all subfield data will fit, read it and call user function}
  1371.                         if (Hdr.SubfieldLen<=WorkLen) then
  1372.                             BEGIN
  1373.                                 if (Hdr.SubfieldLen>0) then
  1374.                                     BEGIN
  1375.                                         if (ReadFile(HdrHandle, WorkBuf^, WORD(Hdr.SubfieldLen))<>WORD(Hdr.SubFieldLen)) then
  1376.                                             BEGIN
  1377.                                                 APImsg:=JAMAPIMSG_CANTRDFILE;
  1378.                                                 exit;
  1379.                                             END;
  1380.                                     END;
  1381.  
  1382.                                 {Call user function and process result}
  1383.                                 UserResult:=UserCompare(@Self);
  1384.                                 if (UserResult=ScanMsgHdrStop) then
  1385.                                     BEGIN
  1386.                                         ScanForMsgHdr:=TRUE;
  1387.                                         exit;
  1388.                                     END
  1389.                                 ELSE
  1390.                                     BEGIN
  1391.                                         if (ScanFwd) then
  1392.                                             inc(StartNum)
  1393.                                         ELSE
  1394.                                             BEGIN
  1395.                                                 if (StartNum=HdrInfo.BaseMsgNum) then
  1396.                                                     BEGIN
  1397.                                                         APImsg:=JAMAPIMSG_NOMOREMSGS;
  1398.                                                         exit;
  1399.                                                     END
  1400.                                                 ELSE
  1401.                                                     dec(StartNum);
  1402.                                             END;
  1403.                                     END;
  1404.                             END
  1405.                         ELSE
  1406.                             {All subfield data won't fit, do segmented read/call}
  1407.                             BEGIN
  1408.                                 ReadBytes:=0;
  1409.                                 if (WorkLen>$0000fffe) then
  1410.                                     MaxBlockToRead:=$fffe
  1411.                                 ELSE
  1412.                                     MaxBlockToRead:=WORD(WorkLen);
  1413.  
  1414.                                 REPEAT
  1415.                                     if (ReadFile(HdrHandle, WorkBuf^, MaxBlockToRead)<>MaxBlockToRead) then
  1416.                                         BEGIN
  1417.                                             APImsg:=JAMAPIMSG_CANTRDFILE;
  1418.                                             exit;
  1419.                                         END;
  1420.  
  1421.                                     if (MaxBlockToRead>0) then
  1422.                                         UserResult:=UserCompare(@Self);
  1423.  
  1424.                                     inc(ReadBytes, LONGINT(MaxBlockToRead));
  1425.                                     if (Hdr.SubfieldLen-ReadBytes<LONGINT(MaxBlockToRead)) then
  1426.                                         MaxBlockToRead:=WORD(Hdr.SubfieldLen-ReadBytes);
  1427.  
  1428.                                 UNTIL (ReadBytes=Hdr.SubfieldLen) or
  1429.                                                 (MaxBlockToRead=0) or
  1430.                                                     (UserResult<>ScanMsgHdrDiscard);
  1431.  
  1432.                                 {We've read all subfield data or got told to do something else}
  1433.  
  1434.                                 if (UserResult=ScanMsgHdrStop) then
  1435.                                     BEGIN
  1436.                                         ScanForMsgHdr:=TRUE;
  1437.                                         exit;
  1438.                                     END
  1439.                                 ELSE
  1440.                                     BEGIN
  1441.                                         if (ScanFwd) then
  1442.                                             BEGIN
  1443.                                                 {If we didn't read all data, seek to next header}
  1444.                                                 if (MaxBlockToRead<>0) then
  1445.                                                     BEGIN
  1446.                                                         ReadBytes:=(Hdr.SubfieldLen-ReadBytes);
  1447.                                                         if (SeekFile(HdrHandle, JAMSEEK_CUR, ReadBytes)<0) then
  1448.                                                             BEGIN
  1449.                                                                 APImsg:=JAMAPIMSG_SEEKERROR;
  1450.                                                                 exit;
  1451.                                                             END;
  1452.                                                     END;
  1453.                                                 inc(StartNum);
  1454.                                             END
  1455.                                         ELSE
  1456.                                             BEGIN
  1457.                                                 if (StartNum=HdrInfo.BaseMsgNum) then
  1458.                                                     BEGIN
  1459.                                                         APImsg:=JAMAPIMSG_NOMOREMSGS;
  1460.                                                         exit;
  1461.                                                     END
  1462.                                                 ELSE
  1463.                                                     dec(StartNum);
  1464.                                             END;
  1465.                                     END;
  1466.                             END;
  1467.                     END
  1468.                 ELSE
  1469.                     {Check for end of file, otherwise error}
  1470.                     BEGIN
  1471.                         if (JunkW=0) and (ScanFwd) then
  1472.                             APImsg:=JAMAPIMSG_NOMOREMSGS
  1473.                         ELSE
  1474.                             APImsg:=JAMAPIMSG_CANTRDFILE;
  1475.                         exit;
  1476.                     END;
  1477.             END;{while}
  1478.     END;
  1479.  
  1480.     {-Store message header with specified number. The HdrHandle's file offset
  1481.         will point to the end of the fixed-length header record when the function
  1482.         returns (if successful) so the application can write any subfields
  1483.         directly to the file. Returns TRUE on success and FALSE on failure.}
  1484.     function JAMAPI.StoreMsgHdr(WhatMsg:LONGINT):BOOLEAN;
  1485.     BEGIN
  1486.         StoreMsgHdr:=FALSE;
  1487.  
  1488.         {Make sure it's open}
  1489.         if (not IsOpen) then
  1490.             BEGIN
  1491.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1492.                 exit;
  1493.             END;
  1494.  
  1495.         {Make sure it's locked}
  1496.         if (not HaveLock) then
  1497.             BEGIN
  1498.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  1499.                 exit;
  1500.             END;
  1501.  
  1502.         {Fetch index record}
  1503.         if (not FetchMsgIdx(WhatMsg)) then
  1504.             exit;
  1505.  
  1506.         {Update structure, even if below fails}
  1507.         Hdr.MsgNum:=WhatMsg;
  1508.  
  1509.         {Make sure header signature and revision is OK}
  1510.         move(HEADERSIG, Hdr.Signature, sizeof(Hdr.Signature));
  1511.         Hdr.Revision:=CurrentRevLev;
  1512.  
  1513.         {Write header}
  1514.         if (SeekFile(HdrHandle, JAMSEEK_SET, Idx.HdrOffset)<>Idx.HdrOffset) then
  1515.             BEGIN
  1516.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1517.                 exit;
  1518.             END;
  1519.         if (WriteFile(HdrHandle, Hdr, sizeof(JAMHDR))<>sizeof(JAMHDR)) then
  1520.             BEGIN
  1521.                 APImsg:=JAMAPIMSG_CANTWRFILE;
  1522.                 exit;
  1523.             END;
  1524.  
  1525.         {Wrote it OK}
  1526.         APImsg:=JAMAPIMSG_NOTHING;
  1527.         StoreMsgHdr:=TRUE;
  1528.     END;
  1529.  
  1530.     {-Store message index record with specified number. The IdxHandle's file
  1531.         offset will point to the end of the fixed-length index record when the
  1532.         function returns (if successful). Returns TRUE on success and FALSE on
  1533.         failure.}
  1534.     function JAMAPI.StoreMsgIdx(WhatMsg:LONGINT):BOOLEAN;
  1535.     VAR
  1536.         WhatOffset : LONGINT;
  1537.     BEGIN
  1538.         StoreMsgIdx:=FALSE;
  1539.  
  1540.         {Make sure it's open}
  1541.         if (not IsOpen) then
  1542.             BEGIN
  1543.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1544.                 exit;
  1545.             END;
  1546.  
  1547.         {Make sure it's locked}
  1548.         if (not HaveLock) then
  1549.             BEGIN
  1550.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  1551.                 exit;
  1552.             END;
  1553.  
  1554.         {Make sure the message number is valid}
  1555.     if (WhatMsg<HdrInfo.BaseMsgNum) then
  1556.             BEGIN
  1557.                 APImsg:=JAMAPIMSG_INVMSGNUM;
  1558.                 exit;
  1559.             END;
  1560.  
  1561.         {Write index record}
  1562.         WhatOffset:=LONGINT((WhatMsg-HdrInfo.BaseMsgNum) * LONGINT(sizeof(JAMIDXREC)));
  1563.         if (SeekFile(IdxHandle, JAMSEEK_SET, WhatOffset)<>WhatOffset) then
  1564.             BEGIN
  1565.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1566.                 exit;
  1567.             END;
  1568.         if (WriteFile(IdxHandle, Idx, sizeof(JAMIDXREC))<>sizeof(JAMIDXREC)) then
  1569.             BEGIN
  1570.                 APImsg:=JAMAPIMSG_CANTWRFILE;
  1571.                 exit;
  1572.             END;
  1573.  
  1574.         {Wrote it OK}
  1575.         APImsg:=JAMAPIMSG_NOTHING;
  1576.         StoreMsgIdx:=TRUE;
  1577.     END;
  1578.  
  1579.     {-Store message text at current header's text position. The TxtHandle's
  1580.         file offset will point to the end of the written text block when the
  1581.         function returns (if successful). This assumes that the entire
  1582.         message text is stored in the internal buffer. See STOREMSGTXTBUF()
  1583.         for a function that takes an external parameter and length specifier.
  1584.         Returns TRUE on success and FALSE on failure.}
  1585.     function JAMAPI.StoreMsgTxt:BOOLEAN;
  1586.     BEGIN
  1587.         StoreMsgTxt:=FALSE;
  1588.  
  1589.         {Make sure it's open}
  1590.         if (not IsOpen) then
  1591.             BEGIN
  1592.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1593.                 exit;
  1594.             END;
  1595.  
  1596.         {Make sure it's locked}
  1597.         if (not HaveLock) then
  1598.             BEGIN
  1599.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  1600.                 exit;
  1601.             END;
  1602.  
  1603.         {Write text}
  1604.         if (SeekFile(TxtHandle, JAMSEEK_SET, Hdr.TxtOffset)<>Hdr.TxtOffset) then
  1605.             BEGIN
  1606.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1607.                 exit;
  1608.             END;
  1609.         if (WriteFile(TxtHandle, WorkBuf^, WORD(Hdr.TxtLen))<>WORD(Hdr.TxtLen)) then
  1610.             BEGIN
  1611.                 APImsg:=JAMAPIMSG_CANTWRFILE;
  1612.                 exit;
  1613.             END;
  1614.  
  1615.         {Wrote it OK}
  1616.         APImsg:=JAMAPIMSG_NOTHING;
  1617.         StoreMsgTxt:=TRUE;
  1618.     END;
  1619.  
  1620.     {-Store message text at current header's text position. The TxtHandle's
  1621.         file offset will point to the end of the written text block when the
  1622.         function returns (if successful). This assumes that the entire
  1623.         message text is stored in the internal buffer. The ISFIRST parameter
  1624.         determines if the function should seek to the position specified in
  1625.         the header before writing. This allows multiple calls to write large
  1626.         message texts. Returns TRUE on success and FALSE on failure.}
  1627.     function JAMAPI.StoreMsgTxtBuf(var Buffer; BufLen:WORD; IsFirst:BOOLEAN):BOOLEAN;
  1628.     BEGIN
  1629.         StoreMsgTxtBuf:=FALSE;
  1630.  
  1631.         {Make sure it's open}
  1632.         if (not IsOpen) then
  1633.             BEGIN
  1634.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1635.                 exit;
  1636.             END;
  1637.  
  1638.         {Make sure it's locked}
  1639.         if (not HaveLock) then
  1640.             BEGIN
  1641.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  1642.                 exit;
  1643.             END;
  1644.  
  1645.         {Seek if told to}
  1646.         if (IsFirst) then
  1647.             BEGIN
  1648.                 if (SeekFile(TxtHandle, JAMSEEK_SET, Hdr.TxtOffset)<>Hdr.TxtOffset) then
  1649.                     BEGIN
  1650.                         APImsg:=JAMAPIMSG_SEEKERROR;
  1651.                         exit;
  1652.                     END;
  1653.             END;
  1654.  
  1655.         {Write text}
  1656.         if (WriteFile(TxtHandle, Buffer, BufLen)<>BufLen) then
  1657.             BEGIN
  1658.                 APImsg:=JAMAPIMSG_CANTWRFILE;
  1659.                 exit;
  1660.             END;
  1661.  
  1662.         {Wrote it OK}
  1663.         APImsg:=JAMAPIMSG_NOTHING;
  1664.         StoreMsgTxtBuf:=TRUE;
  1665.     END;
  1666.  
  1667.     {-Fetch LastRead for passed UserID. Returns TRUE on success and FALSE on
  1668.         failure.}
  1669.     function JAMAPI.FetchLastRead(UserID:LONGINT):BOOLEAN;
  1670.     VAR
  1671.         ReadCount : WORD;
  1672.         LastReadRec : LONGINT;
  1673.     BEGIN
  1674.         FetchLastRead:=FALSE;
  1675.  
  1676.         {Make sure it's open}
  1677.         if (not IsOpen) then
  1678.             BEGIN
  1679.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1680.                 exit;
  1681.             END;
  1682.  
  1683.         {Seek to beginning of file}
  1684.         if (SeekFile(LrdHandle, JAMSEEK_SET, 0)<>0) then
  1685.             BEGIN
  1686.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1687.                 exit;
  1688.             END;
  1689.  
  1690.         {Read file from top to bottom}
  1691.         LastReadRec:=0;
  1692.         WHILE (TRUE) DO
  1693.             BEGIN
  1694.                 ReadCount:=ReadFile(LrdHandle, LastRead, sizeof(JAMLREAD));
  1695.                 if (ReadCount<>sizeof(JAMLREAD)) then
  1696.                     BEGIN
  1697.                         if (ReadCount=0) then
  1698.                             {End of file}
  1699.                             APImsg:=JAMAPIMSG_CANTFINDUSER
  1700.                         ELSE
  1701.                             {Read error}
  1702.                             APImsg:=JAMAPIMSG_CANTRDFILE;
  1703.                         exit;
  1704.                     END;
  1705.  
  1706.                 {See if it matches what we want}
  1707.                 if (LastRead.UserID=UserID) then
  1708.                     BEGIN
  1709.                         LastLRDnum:=LastReadRec;
  1710.                         APImsg:=JAMAPIMSG_NOTHING;
  1711.                         FetchLastRead:=TRUE;
  1712.                         exit;
  1713.                     END;
  1714.  
  1715.                 {Next record number}
  1716.                 inc(LastReadRec);
  1717.             END;{while}
  1718.     END;
  1719.  
  1720.     {-Store LastRead record and if successful, optionall updates the header
  1721.         info block (and its ModCounter) at the beginning of the header file.
  1722.         Returns TRUE upon success and FALSE upon failure.}
  1723.     function JAMAPI.StoreLastRead(UpdateHdrInfo:BOOLEAN):BOOLEAN;
  1724.     VAR
  1725.         UserOffset : LONGINT;
  1726.     BEGIN
  1727.         StoreLastRead:=FALSE;
  1728.  
  1729.         {Make sure it's open}
  1730.         if (not IsOpen) then
  1731.             BEGIN
  1732.                 APImsg:=JAMAPIMSG_ISNOTOPEN;
  1733.                 exit;
  1734.             END;
  1735.  
  1736.         {Make sure it's locked}
  1737.         if (not HaveLock) then
  1738.             BEGIN
  1739.                 APImsg:=JAMAPIMSG_ISNOTLOCKED;
  1740.                 exit;
  1741.             END;
  1742.  
  1743.         {Seek to the appropriate position}
  1744.         UserOffset:=LONGINT(LastLRDnum * LONGINT(sizeof(JAMLREAD)));
  1745.         if (SeekFile(LrdHandle, JAMSEEK_SET, UserOffset)<>UserOffset) then
  1746.             BEGIN
  1747.                 APImsg:=JAMAPIMSG_SEEKERROR;
  1748.                 exit;
  1749.             END;
  1750.  
  1751.         {Write record}
  1752.         if (WriteFile(LrdHandle, LastRead, sizeof(JAMLREAD))<>sizeof(JAMLREAD)) then
  1753.             BEGIN
  1754.                 APImsg:=JAMAPIMSG_CANTWRFILE;
  1755.                 exit;
  1756.             END;
  1757.  
  1758.         {Update header info if told to}
  1759.         if (UpdateHdrInfo and not UpdHdrInfo(TRUE)) then
  1760.             exit;
  1761.  
  1762.         APImsg:=JAMAPIMSG_NOTHING;
  1763.         StoreLastRead:=TRUE;
  1764.     END;
  1765.  
  1766. END.
  1767.  
  1768. (* end of file "jammb.pas" *)
  1769.