home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / ECO30603.ZIP / ECO30603.LZH / ECOLIBBS / eco_hdsn.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-04-12  |  36.9 KB  |  1,397 lines

  1. (*
  2.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  3.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  4.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  5.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  6.     ▓▓▓▓▓▓▓▓·──                                              ──·▓▓▓▓▓▓▓▓▓▓▓
  7.     ▓▓▓▓▓▓▓▓│                                                  │░░▓▓▓▓▓▓▓▓▓
  8.     ▓▓▓▓▓▓▓▓   The ECO_HDSN Toolkit was conceived, designed     ░░▓▓▓▓▓▓▓▓▓
  9.     ▓▓▓▓▓▓▓▓   and written by Floor A.C. Naaijkens for          ░░▓▓▓▓▓▓▓▓▓
  10.     ▓▓▓▓▓▓▓▓   UltiHouse Software / The ECO Group.              ░░▓▓▓▓▓▓▓▓▓
  11.     ▓▓▓▓▓▓▓▓                                                    ░░▓▓▓▓▓▓▓▓▓
  12.     ▓▓▓▓▓▓▓▓   (C) MCMXCII by EUROCON PANATIONAL CORPORATION.   ░░▓▓▓▓▓▓▓▓▓
  13.     ▓▓▓▓▓▓▓▓   All Rights Reserved for The ECO Group.           ░░▓▓▓▓▓▓▓▓▓
  14.     ▓▓▓▓▓▓▓▓                                                    ░░▓▓▓▓▓▓▓▓▓
  15.     ▓▓▓▓▓▓▓▓   Interface to RA/Hudson message database.         ░░▓▓▓▓▓▓▓▓▓
  16.     ▓▓▓▓▓▓▓▓│                                                  │░░▓▓▓▓▓▓▓▓▓
  17.     ▓▓▓▓▓▓▓▓·──                                              ──·░░▓▓▓▓▓▓▓▓▓
  18.     ▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓
  19.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  20.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  21.     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  22. *)
  23. {$A-,B-,D-,E-,F-,I-,L-,N-,O-,R-,S-,V-}
  24. unit eco_hdsn;
  25.  
  26.  
  27. interface
  28.  
  29. uses
  30.   dos, eco_text;
  31.  
  32.  
  33. {$I rastruct.inc}
  34.  
  35.  
  36. type
  37.   msgattrs = set of (
  38.     m_del, m_transit, m_netm, m_priv, m_rcvd, m_echotr, m_local, m_r1
  39.   );
  40.   netattrs = set of (
  41.     n_kill, n_sent, n_file, n_crash, n_reqrec, n_audreq, n_retrec, n_r2
  42.   );
  43.   namestring = string[35];
  44.   subjstring = string[72];
  45.  
  46.  
  47. type
  48.   lastcall = record
  49.     line   :            byte;
  50.     name   :  msgtoidxrecord;
  51.     city   :      string[25];
  52.     baud   :            word;
  53.     times  :         longint;
  54.     logon  :       string[5];
  55.     logoff :       string[5];
  56.   end;
  57.  
  58.  
  59.   msgboardsarray = array[1..200] of messagerecord;
  60.   msgboardsarrayptr = ^msgboardsarray;
  61.  
  62.  
  63.  
  64.  
  65. const
  66.   boardlim = 200;       {maximum number of boards}
  67.   ok = 0;               {ioresult value}
  68.  
  69.   defaultmsgattr: array[msgtype] of msgattrs=(          {may be modified}
  70.         [m_transit, m_netm],     {netmail}
  71.         [m_echotr],             {echomail}
  72.         [m_local]);             {local mail}
  73.   defaultnetattr: array[msgtype] of netattrs=(
  74.         [],                     {netmail}
  75.         [],                     {echomail}
  76.         []);                    {local mail}
  77.  
  78.   {- set the location of the *.bbs files, default is current directory -}
  79.   procedure setbbspath(path: pathstr);
  80.  
  81.   {- set the location of the users*.bbs files, default is current directory -}
  82.   procedure setuserspath(path: pathstr);
  83.  
  84.   {- set the user related files support -}
  85.   procedure setusersupport(name: namestring;  lastread, current: boolean);
  86.  
  87.   {-
  88.     open all the files associated with the quickbbs message base.
  89.     this function looks for the files in the bbspath directory and 
  90.     returns 0 iff everything went ok.
  91.   -}
  92.  
  93.   function openmsgbase: integer;
  94.   function flushmsgbase: integer;
  95.   function closemsgbase: integer;
  96.  
  97.  
  98.   {- create a header for a new message -}
  99.   procedure createnewheader(var hdr: msghdrrecord;  whofrom, whoto: namestring;
  100.                             subj: subjstring;  brd: byte;  typ: msgtype);
  101.  
  102.   {- update a header before calling writemessage after changing a message -}
  103.   procedure changeheader(var hdr: msghdrrecord);
  104.  
  105.   {- create a new message or modify an old message -}
  106.   function writemessage(var hdr: msghdrrecord;  var t: textbuffer): integer;
  107.  
  108.   {- read a message -}
  109.   function readmessage(msgno: integer; var hdr: msghdrrecord;  var t: textbuffer): integer;
  110.  
  111.   {- get information about messages from index files -}
  112.   function firstmsg(brd: byte): integer;
  113.   function lastmsg(brd: byte): integer;
  114.   function countmsg(brd: byte): integer;
  115.   function lastreadmsg(brd: byte): integer;
  116.   function curmsg(brd: byte): integer;
  117.   function boardmsg(brd: byte;  cur: integer): integer;
  118.  
  119.   {- set message pointers -}
  120.   procedure setlastread(brd: byte;  msgno: integer);
  121.   procedure setcur(brd: byte;  msgno: integer);
  122.  
  123.   {- get next/previous message, returns 0 if empty, -1 if not found -}
  124.   function msgnext(brd: byte;  cur: integer): integer;
  125.   function msgprev(brd: byte;  cur: integer): integer;
  126.  
  127.   {- return true iff there is a message at 'msgno' in board 'brd' -}
  128.   function msgat(brd: byte;  msgno: integer): boolean;
  129.  
  130.   {- read and write a message header, return 0 iff ok -}
  131.   function readheader(msgno: integer;  var hdr: msghdrrecord): integer;
  132.   function writeheader(var hdr: msghdrrecord): integer;
  133.  
  134.   {- read message text, return 0 iff ok.  return empty buffer on error. -}
  135.   function readtext(var hdr: msghdrrecord; var t: textbuffer): integer;
  136.  
  137.   {- search for a message to a person -}
  138.   function searchto(
  139.     board: byte; var msgnum: integer; name: namestring
  140.   ): integer;
  141.  
  142.   {- delete a message -}
  143.   function msgdelete(msgnum: integer): integer;
  144.  
  145.  
  146.  
  147.  
  148. implementation
  149.  
  150.  
  151. const
  152.   filenotfound = 2;             {ioresult value}
  153.  
  154.   maxsize = 65520;              {max size for a structure}
  155.   maxidx = maxsize div sizeof(msgidxrecord);
  156.   maxtoss = maxsize div sizeof(integer);
  157.  
  158. type
  159.   timestr = string[5];
  160.   datestr = string[8];
  161.   tossarray = array[0..maxtoss] of integer;
  162.   tossarrayptr = ^tossarray;
  163.  
  164.   idxarray = array[0..maxidx] of msgidxrecord;
  165.   idxarrayptr = ^idxarray;
  166.   qfrange = (qidx, qinfo, qhdr, qtxt, qtoidx, qcurrent, qlastread{, qboards});
  167.  
  168. const
  169.   qfname: array[qfrange] of string[8+1+3]=(
  170.     'msgidx.bbs', 'msginfo.bbs', 'msghdr.bbs', 'msgtxt.bbs', 
  171.     'msgtoidx.bbs', 'current.bbs', 'lastread.bbs'{, 'messages.ra'}
  172.   );
  173.   qfsize: array[qfrange] of integer=(sizeof(msgidxrecord), 
  174.     sizeof(msginforecord), sizeof(msghdrrecord), sizeof(msgtxtrecord), 
  175.     sizeof(msgtoidxrecord), sizeof(lastreadrecord), sizeof(lastreadrecord){, 
  176.     sizeof(msgboardsarray)}
  177.   );
  178.  
  179.   currentdirty :  boolean = false;  {dirty bits 4 current.bbs and lastread.bbs}
  180.   lastreaddirty:  boolean = false;
  181.   msgbaseopened:  boolean = false;
  182.  
  183. var
  184.   userspath    : pathstr;       {- path for *.bbs files, '\' terminated -}
  185.   bbspath      : pathstr;       {- path for *.bbs files, '\' terminated -}
  186.   usernum      : integer;       {current user number for lastread.bbs}
  187.   username     : namestring;    {for checking msgtoidx.bbs}
  188.   highmsgread  : integer;       {for current user, only if username<>''}
  189.   uselastread  : boolean;       {must be set before openmsgbase}
  190.   usecurrent   : boolean;
  191.  
  192.   qf           : array[qfrange] of file;
  193.   msginfo      : msginforecord;
  194.  
  195.   msgidx       : record
  196.     len        : integer;       {length of msgidx}
  197.     alloc      : integer;       {current allocated length in elements}
  198.     idx        : idxarrayptr;   {may be partially allocated}
  199.   end;
  200.  
  201.   lastread     : lastreadrecord;
  202.   current      : lastreadrecord;
  203.  
  204. {- set the size of the msgidx dynamic array.  doesn't actually modify the
  205.    values in the array, only the size. -}
  206.  
  207.  
  208.  
  209.   procedure setmsgidxsize(size: integer);
  210.   const overalloc = 16; {overallocate this much to decrease fragmentation}
  211.   var
  212.     newidx: idxarrayptr;
  213.     newalloc: integer;
  214.   begin
  215.     if (size > msgidx.alloc) then begin         {allocate more space}
  216.       newalloc := size+overalloc;
  217.       getmem(newidx, word(newalloc*sizeof(newidx^[0])));        {allocate new}
  218.       fillchar(newidx^, word(newalloc*sizeof(newidx^[0])), 0);  {and clear it}
  219.       if (msgidx.idx <> nil) then begin                         {copy old part}
  220.         move(msgidx.idx^, newidx^, word(msgidx.len*sizeof(newidx^[0])));
  221.         freemem(msgidx.idx, word(msgidx.alloc*sizeof(newidx^[0])));  {free old}
  222.       end; {if}
  223.       msgidx.alloc := newalloc;                                 {set new}
  224.       msgidx.idx := newidx;                                     {new pointer}
  225.     end; {if}
  226.  
  227.     msgidx.len := size;
  228.   end; {setmsgidxsize}
  229.  
  230. {- update the directory entry of a file, return 0 iff ok -}
  231.  
  232.  
  233.  
  234.   function flushfile(var f: file): integer;
  235.   var reg: registers;
  236.   begin
  237.     reg.ah := $45;      {dup handle}
  238.     reg.bx := filerec(f).handle;
  239.     msdos(reg);
  240.     flushfile := reg.ax;
  241.     if odd(reg.flags) then exit;        {cf set -> error}
  242.  
  243.     reg.bx := reg.ax;                   {new handle}
  244.     reg.ah := $3e;      {close file}
  245.     msdos(reg);
  246.     flushfile := reg.ax;
  247.     if odd(reg.flags) then exit;        {error}
  248.  
  249.     flushfile := ok;
  250.   end; {flushfile}
  251.  
  252. {- open a file, optionally creating it.  return 0 iff ok -}
  253.  
  254.  
  255.  
  256.   function open(
  257.     var f: file; name: pathstr; recsize: integer; create, bbs: boolean
  258.   ): integer;
  259.   var result: integer;
  260.   begin
  261.     if bbs then assign(f, bbspath + name) else assign(f, userspath + name);
  262.     {$i-} reset(f, recsize); {$i+}
  263.     result := ioresult;
  264.     if create and (result=filenotfound) then begin
  265.       {$i-} rewrite(f, recsize); {$i+}
  266.       result := ioresult;
  267.     end; {if}
  268.     open := result;
  269.   end; {open}
  270.  
  271.  
  272.  
  273.   function upper(s: string): string;
  274.   var p: byte;
  275.   begin
  276.     for p := 1 to length(s) do
  277.       s[p] := upcase(s[p]);
  278.     upper := s;
  279.   end; {upper}
  280.  
  281.  
  282.  
  283.   function openmsgbase: integer;
  284.   var
  285.     qfstep: qfrange;
  286.     result: integer;
  287.     offset: longint;
  288.  
  289.   procedure forceintorange(var a: lastreadrecord);
  290.     var
  291.       c: byte;
  292.       temp: integer;
  293.     begin
  294.       for c := 1 to boardlim do begin
  295.         temp := lastmsg(c);
  296.         if (a[c] > temp) then
  297.           a[c] := temp
  298.         else begin
  299.           temp := firstmsg(c);
  300.           if (a[c] < temp) then
  301.             a[c] := temp;
  302.         end; {else}
  303.       end; {for}
  304.     end; {forceintorange}
  305.  
  306.  
  307.  
  308.  
  309.   {- search for a user in users.bbs, returns ioresult.  offset is record
  310.      number of user in users.bbs, or -1 if not found -}
  311.  
  312.     function searchuser(name: namestring;  var offset: longint;
  313.                       var high: integer): integer;
  314.     label error;
  315.     var
  316.       result: integer;
  317.       users: file;
  318.       user: usersrecord;
  319.       found: boolean;
  320.  
  321.     begin
  322.       offset := -1; {not found}
  323.       high := 0;
  324.       result := open(users, 'users.bbs', sizeof(usersrecord), false, false);
  325.       if result <> ok then begin
  326.         searchuser := result;
  327.         exit;
  328.       end; {if}
  329.  
  330.       found := false;
  331.       name := upper(name);
  332. {$I-} while not eof(users) and not found do begin
  333.         blockread(users, user, 1);
  334. {$I+}   result := ioresult;
  335.         if result <> ok then goto error;
  336.         found := (name = upper(user.name));
  337.       end; {while}
  338.       result := ioresult;   {for eof, will be ok if ok}
  339.  
  340.       if found then begin
  341.         offset := filepos(users)-1; {we read past, remember?}
  342.         high := user.lastread;
  343.       end; {if}
  344.  
  345.   error:
  346.       close(users);
  347.       searchuser := result;
  348.     end; {searchuser}
  349.  
  350.   begin {openmsgbase}
  351.     if (username <> '') then begin
  352.       result := searchuser(username, offset, highmsgread);
  353.       if (result <> ok) then begin
  354.         openmsgbase := result;
  355.         exit;
  356.       end; {if}
  357.       if (offset = -1) then begin
  358.         openmsgbase := -2;  {user not found}
  359.         exit;
  360.       end; {if}
  361.       usernum := offset;
  362.     end else usernum := 0;
  363.  
  364.     for qfstep := qidx to qtoidx do begin
  365.       result := open(qf[qfstep], qfname[qfstep], qfsize[qfstep], true, false);
  366.       if (result <> ok) then begin
  367.         openmsgbase := result;
  368.         exit;
  369.       end; {if}
  370.     end; {for}
  371.  
  372.     if uselastread then begin
  373.       result := open(
  374.         qf[qlastread], qfname[qlastread], qfsize[qlastread], true, false
  375.       );
  376.       if (result <> ok) then begin
  377.         openmsgbase := result;
  378.         exit;                   {fail}
  379.       end; {if}
  380.     end; {if}
  381.  
  382.     if usecurrent then begin
  383.       result := open(
  384.         qf[qcurrent], qfname[qcurrent], qfsize[qcurrent], true, true
  385.       );
  386.       if (result <> 0) then begin
  387.         openmsgbase := result;
  388.         exit;                   {fail}
  389.       end; {if}
  390.     end; {if}
  391.  
  392.     fillchar(msgidx, sizeof(msgidx), 0);
  393.     setmsgidxsize(filesize(qf[qidx]));
  394.     {$i-} blockread(qf[qidx], msgidx.idx^, msgidx.len); {$i+}
  395.     result := ioresult;
  396.     if (result <> ok) then begin
  397.       openmsgbase := result;
  398.       exit;                     {fail}
  399.     end; {if}
  400.  
  401.     fillchar(msginfo, sizeof(msginfo), 0);
  402.     blockread(qf[qinfo], msginfo, 1, result);      {ignore short read}
  403.  
  404.     fillchar(lastread, sizeof(lastread), 0);
  405.     if uselastread then begin
  406.       seek(qf[qlastread], usernum);              {current user}
  407.       blockread(qf[qlastread], lastread, 1, result);       {ignore short read}
  408.       forceintorange(lastread);
  409.     end; {if}
  410.  
  411.     fillchar(current, sizeof(current), 0);
  412.     if usecurrent then begin
  413.       seek(qf[qcurrent], usernum);              {current user}
  414.       blockread(qf[qcurrent], current, 1, result); {ignore short read}
  415.       forceintorange(current);
  416.     end; {if}
  417.  
  418.     msgbaseopened := true;
  419.     openmsgbase := ok;
  420.   end; {openmsgbase}
  421.  
  422. {- update lastread.bbs, current.bbs and users.bbs -}
  423.  
  424.  
  425.  
  426.  
  427.   function updatecurlast: integer;
  428.   var
  429.     result: integer;
  430.     users: file;
  431.     user: usersrecord;
  432.   begin
  433.     updatecurlast := ok;
  434.  
  435.     if (username <> '') then begin
  436.       result := open(users, 'users.bbs', sizeof(usersrecord), false, false);
  437.       seek(users, usernum);
  438.       blockread(users, user, 1);
  439.       if (highmsgread > user.lastread) then begin
  440.         user.lastread := highmsgread;
  441.         seek(users, usernum);
  442.         blockwrite(users, user, 1);
  443.       end; {if}
  444.       close(users);
  445.     end; {if}
  446.  
  447.     if (usecurrent and currentdirty) then begin
  448.       {$i-} seek(qf[qcurrent], usernum); {$i+}
  449.       {$i-} blockwrite(qf[qcurrent], current, 1); {$i+}
  450.       updatecurlast := ioresult;
  451.     end; {if}
  452.  
  453.     if (uselastread and lastreaddirty) then begin
  454.       for result := 1 to boardlim do
  455.         if (lastreadmsg(result) > lastmsg(result)) then
  456.           setlastread(result, lastmsg(result));
  457.       {$i-} seek(qf[qlastread], usernum); {$i+}
  458.       {$i-} blockwrite(qf[qlastread], lastread, 1); {$i+}
  459.       updatecurlast := ioresult;
  460.     end; {if}
  461.   end; {updatecurlast}
  462.  
  463.  
  464.  
  465.   function closemsgbase: integer;
  466.   var
  467.     qfstep: qfrange;
  468.     result: integer;
  469.     isopen: set of qfrange;
  470.   begin
  471.     closemsgbase := updatecurlast;
  472.  
  473.     isopen := [qidx..qtoidx];
  474.     if usecurrent then isopen := isopen+[qcurrent];
  475.     if uselastread then isopen := isopen+[qlastread];
  476.     for qfstep := qidx to qlastread do
  477.       if qfstep in isopen then begin
  478.         {$i-} close(qf[qfstep]); {$i+}
  479.         result := ioresult;
  480.         if (result <> ok) then
  481.           closemsgbase := result;
  482.       end; {for}
  483.  
  484.     if (msgidx.idx <> nil) then
  485.       freemem(msgidx.idx, word(msgidx.alloc*sizeof(msgidx.idx^[0])));
  486.     fillchar(msgidx, sizeof(msgidx), 0);
  487.  
  488.     msgbaseopened := false;
  489.   end; {closemsgbase}
  490.  
  491.  
  492.  
  493.  
  494.   function flushmsgbase: integer;
  495.   var
  496.     qfstep: qfrange;
  497.     result: integer;
  498.   begin
  499.     flushmsgbase := updatecurlast;
  500.  
  501.     for qfstep := qidx to qlastread do begin
  502.       result := flushfile(qf[qfstep]);
  503.       if (result <> ok) then
  504.         flushmsgbase := result;
  505.     end; {for}
  506.   end; {flushmsgbase}
  507.  
  508. {- find the first and last message in a board -}
  509.  
  510.  
  511.  
  512.  
  513.   function firstmsg(brd: byte): integer;
  514.   var
  515.     step: integer;
  516.   begin
  517.     firstmsg := 0;
  518.     if (countmsg(brd) = 0) then exit;
  519.  
  520.     for step := 0 to msgidx.len-1 do
  521.       with msgidx.idx^[step] do
  522.         if (board = brd) and (msgnum <> -1) then begin
  523.           firstmsg := msgnum;
  524.           exit;
  525.         end; {if}
  526.  
  527.     {return 0 if not found}
  528.   end; {firstmsg}
  529.  
  530.  
  531.  
  532.  
  533.   function lastmsg(brd: byte): integer;
  534.   var
  535.     step: integer;
  536.   begin
  537.     lastmsg := 0;
  538.     if (countmsg(brd) = 0) then exit;
  539.  
  540.     for step := msgidx.len-1 downto 0 do
  541.       with msgidx.idx^[step] do
  542.         if (board = brd) and (msgnum <> -1) then begin
  543.           lastmsg := msgnum;
  544.           exit;
  545.         end; {if}
  546.  
  547.     {return 0 if not found}
  548.   end; {lastmsg}
  549.  
  550.  
  551.  
  552.  
  553.   function countmsg(brd: byte): integer;
  554.   begin
  555.     countmsg := msginfo.totalonboard[brd]; (* msginfo.activemsgs[brd]; *)
  556.   end; {countmsg}
  557.  
  558. {-
  559.   work out the relative position of a message in it's board
  560.   return 0 if the message is not found
  561.   this is a slow linear search which is used by msged/q to display the
  562.   relative message number in the current message board.  it is not useful
  563.   for any other purpose.
  564. -}
  565.  
  566.  
  567.  
  568.  
  569.   function boardmsg(brd: byte;  cur: integer): integer;
  570.   var step, count: integer;
  571.   begin
  572.     count := 0;
  573.     for step := 0 to msgidx.len-1 do
  574.       with msgidx.idx^[step] do
  575.         if (board = brd) and (msgnum <> -1) then
  576.           if (msgnum = cur) then begin
  577.             boardmsg := count+1;
  578.             exit;
  579.           end else begin
  580.             inc(count);
  581.           end; {else}
  582.     boardmsg := 0;
  583.   end; {boardmsg}
  584.  
  585.  
  586.  
  587.  
  588.   function curmsg(brd: byte): integer;
  589.   begin
  590.     curmsg := current[brd];
  591.   end; {curmsg}
  592.  
  593.  
  594.  
  595.   procedure setcur(brd: byte;  msgno: integer);
  596.   begin
  597.     currentdirty := true;
  598.     current[brd] := msgno;
  599.   end; {setcur}
  600.  
  601.  
  602.  
  603.  
  604.   procedure setlastread(brd: byte;  msgno: integer);
  605.   begin
  606.     lastreaddirty := true;
  607.     lastread[brd] := msgno;
  608.   end; {setlastread}
  609.  
  610.  
  611.  
  612.   function lastreadmsg(brd: byte): integer;
  613.   begin
  614.     lastreadmsg := lastread[brd];
  615.   end; {lastreadmsg}
  616.  
  617.  
  618.  
  619.   function maximum(a, b: longint): longint;
  620.   begin
  621.     if (a > b) then
  622.       maximum := a
  623.     else
  624.       maximum := b;
  625.   end; {maximum}
  626.  
  627. {-
  628.  binary search in msgidx
  629.  handles deleted entries (out of sort) by skipping alternatingly
  630.  left and right from the middle until a non-deleted entry is found.
  631.  may become slower when there are a lot of deleted entries.
  632. -}
  633.  
  634.  
  635.  
  636.  
  637.   function binidx(num: integer;  var found: boolean): integer;
  638.   var left, right, mid, stab, skip, skipend: integer;
  639.   begin
  640.     left := 0;  right := msgidx.len-1;
  641.     found := true;
  642.  
  643.     while (left <= right) do begin
  644.       skip := 1;
  645.       mid := (left+right) div 2;
  646.       skipend := maximum(mid-left, right-mid)*2 + 1;
  647.       repeat
  648.         if (mid >= left) and (mid <= right) then
  649.           stab := msgidx.idx^[mid].msgnum
  650.         else stab := -1;
  651.  
  652.         if (stab = -1) then begin       {deleted or out of range}
  653.           inc(mid, skip);                {step sideways}
  654.           if (skip < 0) then
  655.             skip := -skip+1
  656.           else skip := -(skip+1);       { +1 -2 +3 -4 +5 -6 ... }
  657.         end; {if}
  658.       until (stab <> -1) or (abs(skip) > skipend);
  659.  
  660.       if (stab = -1) then begin
  661.         found := false;  binidx := left;  exit;
  662.       end; {if}
  663.  
  664.       if (num = stab) then begin
  665.         binidx := mid;  exit;
  666.       end; {if}
  667.  
  668.       if (num < stab) then
  669.         right := mid-1
  670.       else
  671.         left := mid+1;
  672.     end; {while}
  673.  
  674.     found := false;  binidx := left;  exit;
  675.   end; {binidx}
  676.  
  677.  
  678.  
  679.  
  680.   function msgnext(brd: byte;  cur: integer): integer;
  681.   var
  682.     step: integer;
  683.     found: boolean;
  684.   begin
  685.     msgnext := 0;
  686.     if (countmsg(brd) = 0) then exit;
  687.  
  688.     step := binidx(cur, found)+1;
  689.  
  690.     while (step < msgidx.len) do begin
  691.       with msgidx.idx^[step] do
  692.         if (brd = board) and (msgnum > cur) then begin
  693.           msgnext := msgnum;
  694.           exit;
  695.         end; {if}
  696.       inc(step);
  697.     end; {while}
  698.  
  699.     (* msgnext := lastmsg(brd); *)
  700.     msgnext := -1;
  701.   end; {msgnext}
  702.  
  703.  
  704.  
  705.  
  706.   function msgprev(brd: byte;  cur: integer): integer;
  707.   var
  708.     step: integer;
  709.     found: boolean;
  710.   begin
  711.     msgprev := 0;
  712.     if (countmsg(brd) = 0) then exit;
  713.  
  714.     step := binidx(cur, found)-1;
  715.  
  716.     while (step >= 0) do begin
  717.       with msgidx.idx^[step] do
  718.         if (brd = board) and (msgnum <> -1) and (msgnum < cur) then begin
  719.           msgprev := msgnum;
  720.           exit;
  721.         end; {if}
  722.       dec(step);
  723.     end; {while}
  724.  
  725.     msgprev := -1;
  726.   end; {msgprev}
  727.  
  728. {- return header file record number from message number, -1 on error -}
  729.  
  730.  
  731.  
  732.   function filemsg(msgno: integer): integer;
  733.   var
  734.     step: integer;
  735.     found: boolean;
  736.   begin
  737.     filemsg := -1;
  738.     if (msgno > msginfo.highmsg) or (msgno < msginfo.lowmsg) then exit;
  739.     step := binidx(msgno, found);
  740.     if found then filemsg := step;
  741.   end; {filemsg}
  742.  
  743. {- return true iff there is a message at 'msgno' in board 'brd' -}
  744.  
  745.  
  746.  
  747.  
  748.   function msgat(brd: byte;  msgno: integer): boolean;
  749.   var fmsg: integer;
  750.   begin
  751.     msgat := false;
  752.     if (msgno = -1) then exit;
  753.     fmsg := filemsg(msgno);
  754.     if (fmsg = -1) then exit;
  755.     msgat := (msgidx.idx^[fmsg].board = brd);
  756.   end; {msgat}
  757.  
  758. {- read a message header -}
  759.  
  760.  
  761.  
  762.  
  763.   function readheader(msgno: integer;  var hdr: msghdrrecord): integer;
  764.   var
  765.     n: integer;
  766.   begin
  767.     readheader := -1;
  768.     n := filemsg(msgno);
  769.     if (n = -1) then exit;
  770.     {$i-} seek(qf[qhdr], n); {$i+}
  771.     {$i-} blockread(qf[qhdr], hdr, 1); {$i+}
  772.     readheader := ioresult;
  773.   end; {readheader}
  774.  
  775.  
  776.  
  777.  
  778.   function writeheader(var hdr: msghdrrecord): integer;
  779.   var
  780.     n: integer;
  781.   begin
  782.     writeheader := -1;
  783.     n := filemsg(hdr.msgnum);
  784.     if (n = -1) then exit;
  785.     {$i-} seek(qf[qhdr], n); {$i+}
  786.     {$i-} blockwrite(qf[qhdr], hdr, 1); {$i+}
  787.     writeheader := ioresult;
  788.   end; {writeheader}
  789.  
  790. {- add or modify a record of msgtoidx.bbs -}
  791.  
  792.  
  793.  
  794.  
  795.   function writetoidx(fmsg: integer;  whoto: msgtoidxrecord): integer;
  796.   begin
  797.     {$i-} seek(qf[qtoidx], fmsg); {$i+}
  798.     {$i-} blockwrite(qf[qtoidx], whoto, 1); {$i+}
  799.     writetoidx := ioresult;
  800.   end; {writetoidx}
  801.  
  802. {- increment message counters in msginfo.bbs -}
  803.  
  804.  
  805.  
  806.  
  807.   function incmsginfo(brd: byte): integer;
  808.   begin
  809.     inc(msginfo.highmsg);
  810.     inc(msginfo.totalmsgs);  (* inc(msginfo.totalactive); *)
  811.     inc(msginfo.totalonboard[brd]); (* inc(msginfo.activemsgs[brd]); *)
  812.  
  813.     {$i-} seek(qf[qinfo], 0); {$i+}
  814.     {$i-} blockwrite(qf[qinfo], msginfo, 1); {$i+}
  815.     incmsginfo := ioresult;
  816.   end; {incmsginfo}
  817.  
  818. {- add or modify a record of msgidx.bbs -}
  819.  
  820.  
  821.  
  822.   function writemsgidx(fmsg, msgno: integer;  brd: byte): integer;
  823.   begin
  824.     if (fmsg >= msgidx.len) then
  825.       setmsgidxsize(fmsg+1);
  826.  
  827.     with msgidx.idx^[fmsg] do begin
  828.       msgnum := msgno;
  829.       board := brd;
  830.     end; {with}
  831.  
  832.     {$i-} seek(qf[qidx], fmsg); {$i+}
  833.     {$i-} blockwrite(qf[qidx], msgidx.idx^[fmsg], 1); {$i+}
  834.  
  835.     writemsgidx := ioresult;
  836.   end; {writemsgidx}
  837.  
  838.  
  839.  
  840.  
  841.   function writetext(var hdr: msghdrrecord;  var t: textbuffer;  old: boolean): integer;
  842.   var
  843.     buf: textbuffer;
  844.     result: integer;
  845.     oldhdr: msghdrrecord;
  846.     step: textnodeptr;
  847.     line: string;
  848.  
  849.   begin
  850.     unwrapbuffer(t, buf);
  851.     hdr.numblocks := bufferlength(buf); (* hdr.numrecs := bufferlength(buf); *)
  852.     if old then begin
  853.       result := readheader(hdr.msgnum, oldhdr);
  854.       if (result <> ok) then begin
  855.         writetext := result;
  856.         exit;
  857.       end; {if}
  858.     end else oldhdr.numblocks := 0;
  859.  
  860.     {$i-}
  861.     if (not old) or (hdr.numblocks > oldhdr.numblocks) then
  862.       hdr.startblock := filesize(qf[qtxt])      {need more, was numrecs }
  863.     else
  864.       hdr.startblock := oldhdr.startblock;   {rec := block}
  865.     {$i+}
  866.  
  867.     {$i-} seek(qf[qtxt], hdr.startblock); {$i+}
  868.     result := ioresult;
  869.     if (result <> ok) then begin
  870.       writetext := result;
  871.       exit;
  872.     end; {if}
  873.  
  874.     step := buf.first;
  875.     while (step <> nil) do begin
  876.       fillchar(line, sizeof(line), 0);
  877.       line := step^.line^;
  878.       {$i-} blockwrite(qf[qtxt], line, 1); {$i+}
  879.       result := ioresult;
  880.       if (result <> ok) then begin
  881.         writetext := result;
  882.         exit;
  883.       end; {if}
  884.       step := step^.next;
  885.     end; {while}
  886.  
  887.     deletebuffer(buf);
  888.  
  889.     writetext := ok;
  890.   end; {writetext}
  891.  
  892. {- write to echomail.bbs or netmail.bbs -}
  893.  
  894.  
  895.  
  896.   function updatetosslog(num: integer;  echom, delete: boolean): integer;
  897.   const name: array[boolean] of string[12]=('netmail.bbs', 'echomail.bbs');
  898.   var
  899.     result, pos: integer;
  900.     f: file;
  901.     toss: tossarrayptr;
  902.     size, alloc: word;
  903.   begin
  904.     result := open(f, name[echom], 1, true, true);
  905.     if (result <> ok) then begin
  906.       updatetosslog := result;
  907.       exit;
  908.     end; {if}
  909.  
  910.     size := filesize(f);
  911.     alloc := size;
  912.     if alloc <> 0 then
  913.       getmem(toss, alloc)
  914.     else
  915.       toss := nil;
  916.  
  917.     if (toss <> nil) then begin
  918.       {$i-} blockread(f, toss^, size); {$i+}
  919.       pos := size div 2 - 1;
  920.       while (pos >= 0) and (toss^[pos] <> num) do
  921.         dec(pos);
  922.     end else pos := -1;
  923.  
  924.     if delete then begin
  925.       if (pos >= 0) then begin
  926.         if (size-pos*2 > 2) then
  927.           move(toss^[pos+1], toss^[pos], size-pos*2-2);   {delete}
  928.         {$i-} seek(f, 0); {$i+}
  929.         dec(size, 2);
  930.         {$i-} blockwrite(f, toss^, size); {$i+}
  931.         {$i-} truncate(f); {$i+}
  932.       end; {if}
  933.     end else begin
  934.       if (pos < 0) then begin
  935.         {$i-} seek(f, filesize(f)); {$i+}        {to end}
  936.         {$i-} blockwrite(f, num, 2); {$i+}
  937.         inc(size, 2);
  938.       end; {if}
  939.     end; {if}
  940.  
  941.     if (toss <> nil) then freemem(toss, alloc);
  942.  
  943.     result := ioresult;
  944.     {$i-} close(f); {$i+}
  945.     {$i-} if (size = 0) then erase(f); {$i+}
  946.     updatetosslog := ioresult;
  947.   end; {updatetosslog}
  948.  
  949.  
  950.  
  951.   procedure asctime(var time: timestr;  var date: datestr);
  952.   type numstr = string[8];
  953.   var
  954.     hour, min, sec, sec100, year, month, day, dayofweek: word;
  955.  
  956.   function padnum(num: word): numstr;
  957.     var temp: numstr;
  958.     begin
  959.       str(num:2, temp);
  960.       if temp[1]=' ' then temp[1] := '0';
  961.       padnum := temp;
  962.     end; {padnum}
  963.  
  964.   begin
  965.     gettime(hour, min, sec, sec100);
  966.     getdate(year, month, day, dayofweek);
  967.     time := padnum(hour)+':'+padnum(min);
  968.     date := padnum(month)+'-'+padnum(day)+'-'+padnum(year mod 100);
  969.   end; {asctime}
  970.  
  971.  
  972.  
  973.  
  974.   procedure createnewheader(
  975.     var hdr: msghdrrecord;  whofrom, whoto: namestring;
  976.     subj: subjstring;  brd: byte;  typ: msgtype
  977.   );
  978.   begin
  979.     fillchar(hdr, sizeof(hdr), 0);
  980.     hdr.msgnum := msginfo.highmsg+1;
  981.     hdr.whofrom := whofrom;
  982.     hdr.whoto := whoto;
  983.     hdr.subject := subj; { subject ipv subj }
  984.     asctime(hdr.posttime, hdr.postdate);
  985.     hdr.board := brd;
  986.     hdr.msgattr := byte(defaultmsgattr[typ]);
  987.     hdr.netattr := byte(defaultnetattr[typ]);
  988.   end; {createnewheader}
  989.  
  990. {- update a header before re-writing (changing) a message -}
  991.  
  992. procedure changeheader(var hdr: msghdrrecord);
  993.   begin
  994.     asctime(hdr.posttime, hdr.postdate);
  995.  
  996.     if m_netm in msgattrs(hdr.msgattr) then begin               {is netmail}
  997.       msgattrs(hdr.msgattr) := msgattrs(hdr.msgattr) + [m_transit];
  998.     end else
  999.       if not (m_local in msgattrs(hdr.msgattr)) then begin      {not local}
  1000.         msgattrs(hdr.msgattr) := msgattrs(hdr.msgattr) + [m_echotr]; {is echom}
  1001.       end; {else}
  1002.  
  1003.     netattrs(hdr.netattr) := netattrs(hdr.netattr) - [n_sent];
  1004.   end; {changeheader}
  1005.  
  1006.  
  1007.  
  1008.  
  1009.   function writemessage(var hdr: msghdrrecord;  var t: textbuffer): integer;
  1010.   var
  1011.     result: integer;
  1012.     fmsg: integer;
  1013.     oldmsg: boolean;
  1014.   begin
  1015.     fmsg := filemsg(hdr.msgnum);
  1016.  
  1017.     oldmsg := (fmsg <> -1);
  1018.     if not oldmsg then
  1019.       fmsg := msgidx.len;               {next message index}
  1020.  
  1021.     result := writetoidx(fmsg, hdr.whoto);
  1022.     if (result <> ok) then begin
  1023.       writemessage := result;
  1024.       exit;
  1025.     end; {if}
  1026.  
  1027.     if not oldmsg then begin
  1028.       result := incmsginfo(hdr.board);
  1029.       if (result <> ok) then begin
  1030.         writemessage := result;
  1031.         exit;
  1032.       end; {if}
  1033.     end; {if}
  1034.  
  1035.     result := writemsgidx(fmsg, hdr.msgnum, hdr.board);
  1036.     if (result <> ok) then begin
  1037.       writemessage := result;
  1038.       exit;
  1039.     end; {if}
  1040.  
  1041.     result := writetext(hdr, t, oldmsg);
  1042.     if (result <> ok) then begin
  1043.       writemessage := result;
  1044.       exit;
  1045.     end; {if}
  1046.  
  1047.     result := writeheader(hdr);
  1048.     if (result <> ok) then begin
  1049.       writemessage := result;
  1050.       exit;
  1051.     end; {if}
  1052.  
  1053.     if m_netm in msgattrs(hdr.msgattr) then begin               {is netmail}
  1054.       result := updatetosslog(fmsg, false, false);
  1055.     end else
  1056.       if not (m_local in msgattrs(hdr.msgattr)) then begin      {not local}
  1057.         result := updatetosslog(fmsg, true, false);               {is echom}
  1058.       end; {else}
  1059.     if (result <> ok) then begin
  1060.       writemessage := result;
  1061.       exit;
  1062.     end; {if}
  1063.  
  1064.     writemessage := ok;
  1065.   end; {writemessage}
  1066.  
  1067. function readtext(var hdr: msghdrrecord; var t: textbuffer): integer;
  1068.   var
  1069.     line: msgtxtrecord;
  1070.     count: word;
  1071.     result: integer;
  1072.   begin
  1073.     newbuffer(t);
  1074.  
  1075.     {$i-} seek(qf[qtxt], hdr.startblock); {$i+}  { block ipv rec }
  1076.     result := ioresult;
  1077.     if (result <> ok) then begin
  1078.       readtext := result;
  1079.       exit;
  1080.     end; {if}
  1081.  
  1082.     for count := 1 to hdr.numblocks do begin  { numblocks ipv numrecs }
  1083.       {$i-} blockread(qf[qtxt], line, 1); {$i+}
  1084.       result := ioresult;
  1085.       if (result <> ok) then begin
  1086.         deletebuffer(t);
  1087.         readtext := result;
  1088.         exit;
  1089.       end; {if}
  1090.       addtoend(t, line);
  1091.     end; {for}
  1092.  
  1093.     readtext := ok;
  1094.   end; {readtext}
  1095.  
  1096. {- update msgtoidx.bbs if the message is received -}
  1097.  
  1098. function checkrcvd(var hdr: msghdrrecord): integer;
  1099.   var
  1100.     fmsg, result: integer;
  1101.     s, t: namestring;
  1102.  
  1103.   begin
  1104.     checkrcvd := ok;
  1105.     if (username='') or (m_rcvd in msgattrs(hdr.msgattr)) then exit;
  1106.                                         {already received or n/a}
  1107.     checkrcvd := -1;
  1108.     fmsg := filemsg(hdr.msgnum);
  1109.     if (fmsg = -1) then exit;
  1110.  
  1111.     s := upper(username);
  1112.     t := upper(hdr.whoto);
  1113.     t[0] := s[0];               {truncate t to s's length}
  1114.  
  1115.     if (s = t) then begin       {is addressed to current user, mark rcvd}
  1116.       msgattrs(hdr.msgattr) := msgattrs(hdr.msgattr) + [m_rcvd];
  1117.       result := writeheader(hdr);
  1118.       if (result <> ok) then begin
  1119.         checkrcvd := result;
  1120.         exit;
  1121.       end; {if}
  1122.  
  1123.       result := writetoidx(fmsg, '* Received *');        {flag it for quickbbs}
  1124.       if (result <> ok) then begin
  1125.         checkrcvd := result;
  1126.         exit;
  1127.       end; {if}
  1128.     end; {if}
  1129.  
  1130.     checkrcvd := ok;
  1131.   end; {checkrcvd}
  1132.  
  1133.  
  1134.  
  1135. {- read a message -}
  1136.  
  1137.   function readmessage(
  1138.   msgno: integer; var hdr: msghdrrecord; var t: textbuffer
  1139. ): integer;
  1140.   var
  1141.     result: integer;
  1142.   begin
  1143.     result := readheader(msgno, hdr);
  1144.     if (result <> ok) then begin
  1145.       readmessage := result;
  1146.       exit;
  1147.     end; {if}
  1148.  
  1149.     result := readtext(hdr, t);
  1150.     if (result <> ok) then begin
  1151.       readmessage := result;
  1152.       exit;
  1153.     end; {if}
  1154.  
  1155.     result := checkrcvd(hdr);
  1156.     if (result <> ok) then begin
  1157.       readmessage := result;
  1158.       exit;
  1159.     end; {if}
  1160.  
  1161.     if (msgno > highmsgread) then
  1162.       msgno := highmsgread;
  1163.  
  1164.     readmessage := ok;
  1165.   end; {readmessage}
  1166.  
  1167. {-
  1168.   search through a board in msgtoidx.bbs for a name
  1169.   starts at msgnum and stops at the last message in the board
  1170.   returns zero result if there was no error and -1 or an ioresult if there was
  1171.   an error.
  1172.   if there was no error (i.e. 0 was returned) then msgnum can be examined to
  1173.   determine if a message to the named person was found.  if msgnum = -1 then
  1174.   no message was found, else a message was found and msgnum is the number of
  1175.   the found message.
  1176. -}
  1177.  
  1178.  
  1179.   function searchto(
  1180.     board: byte;  var msgnum: integer;  name: namestring
  1181.   ): integer;
  1182.   var
  1183.     fmsg, result: integer;
  1184.     get: namestring;
  1185.  
  1186.   {- read from msgtoidx.bbs, can be used to find mail addressed to a user
  1187.      note:  uses a header record number, not a message number -}
  1188.  
  1189.   function readtoidx(fmsg: integer;  var msgto: msgtoidxrecord): integer;
  1190.     var result: integer;
  1191.     begin
  1192.       {$i-} seek(qf[qtoidx], fmsg); {$i+}
  1193.       result := ioresult;
  1194.       if (result <> ok) then begin
  1195.         readtoidx := result;
  1196.         exit;
  1197.       end; {if}
  1198.  
  1199.       {$i-} blockread(qf[qtoidx], msgto, 1); {$i+}
  1200.       readtoidx := ioresult;
  1201.  
  1202.     end; {readtoidx}
  1203.  
  1204.   begin
  1205.     name := upper(name);
  1206.     repeat
  1207.       searchto := 0;
  1208.       if (msgnum = -1) then exit;       {return "no error" and "not found"}
  1209.       searchto := -1;
  1210.       fmsg := filemsg(msgnum);
  1211.       if fmsg = -1 then exit;           {return "error -1"}
  1212.       result := readtoidx(fmsg, get);
  1213.       if (result <> ok) then begin      {return "error IOresult"}
  1214.         searchto := result;
  1215.         exit;
  1216.       end; {if}
  1217.       get := upper(get);
  1218.       get[0] := name[0];                {truncate get to name's length}
  1219.  
  1220.       searchto := 0;                    {return "no error" and "found"}
  1221.       if (get = name) then exit;        {found it, msgnum is number}
  1222.  
  1223.       msgnum := msgnext(board, msgnum);
  1224.     until false;
  1225.   end; {searchto}
  1226.  
  1227. {- delete a message -}
  1228.  
  1229. function msgdelete(msgnum: integer): integer;
  1230.   label error;
  1231.   var
  1232.     result, fmsg: integer;
  1233.     board: byte;
  1234.     hdr: msghdrrecord;
  1235.  
  1236. {- set the deleted bit in msghdr.bbs.  return false iff error, result is set -}
  1237.  
  1238.   function setdeletedbit(var hdr: msghdrrecord): boolean;
  1239.     begin
  1240.       setdeletedbit := false;
  1241.       result := readheader(msgnum, hdr);
  1242.       if (result <> ok) then exit;
  1243.  
  1244.       if not (m_del in msgattrs(hdr.msgattr)) then begin
  1245.         msgattrs(hdr.msgattr) := msgattrs(hdr.msgattr)+[m_del];
  1246.         result := writeheader(hdr);
  1247.         if (result <> ok) then exit;
  1248.       end; {if}
  1249.  
  1250.       setdeletedbit := true;
  1251.     end; {setdeletedbit}
  1252.  
  1253.   function updatelinks(var hdr: msghdrrecord): boolean;
  1254.   {- this code could be bettered, it creates weird links sometimes -}
  1255.     var
  1256.       nto, nfrom:  word;
  1257.       tohdr, fromhdr: msghdrrecord;
  1258.  
  1259.     begin
  1260.       updatelinks := false;
  1261.       nto := hdr.nextreply; (* nto := hdr.replyto; *)
  1262.       nfrom := hdr.prevreply;
  1263.  
  1264.       if (nto <> 0) then
  1265.         if (readheader(nto, tohdr) <> ok) then
  1266.           nto := 0;
  1267.  
  1268.       if (nfrom <> 0) then
  1269.         if (readheader(nfrom, fromhdr) <> ok) then
  1270.           nfrom := 0;
  1271.  
  1272.       fromhdr.nextreply := nto;
  1273.       tohdr.prevreply := nfrom;
  1274.  
  1275.       if (nto <> 0) then begin
  1276.         result := writeheader(tohdr);
  1277.         if (result <> ok) then exit;
  1278.       end; {if}
  1279.  
  1280.       if (nfrom <> 0) then begin
  1281.         result := writeheader(fromhdr);
  1282.         if (result <> ok) then exit;
  1283.       end; {if}
  1284.  
  1285.       updatelinks := true;
  1286.     end; {updatelinks}
  1287.  
  1288.   function updatemsginfo: boolean;
  1289.     begin
  1290.       updatemsginfo := false;
  1291.       dec(msginfo.totalmsgs);  (* totalactive);  *)
  1292.       dec(msginfo.totalonboard[board]); (* activemsgs[board]); *)
  1293.       if (msgnum = msginfo.lowmsg) then
  1294.         inc(msginfo.lowmsg);            {not too good}
  1295.  
  1296.       {$i-} seek(qf[qinfo], 0); {$i+}
  1297.       {$i-} blockwrite(qf[qinfo], msginfo, 1); {$i+}
  1298.       result := ioresult;
  1299.       updatemsginfo := (result = ok);
  1300.     end; {updatemsginfo}
  1301.  
  1302.   begin
  1303.     fmsg := filemsg(msgnum);
  1304.     result := -1;
  1305.     if (fmsg = -1) then goto error;
  1306.  
  1307.     board := msgidx.idx^[fmsg].board;
  1308.  
  1309.     if not setdeletedbit(hdr) then goto error;  {result is set inside}
  1310.     if not updatelinks(hdr) then goto error;
  1311.  
  1312.     result := writemsgidx(fmsg, -1, 0);
  1313.     if (result <> ok) then goto error;
  1314.  
  1315.     if not updatemsginfo then goto error;
  1316.  
  1317.     result := writetoidx(fmsg, '* Deleted *');
  1318.     if (result <> ok) then goto error;
  1319.  
  1320.     if m_netm in msgattrs(hdr.msgattr) then begin               {is netmail}
  1321.       result := updatetosslog(fmsg, false, true);
  1322.     end else
  1323.       if not (m_local in msgattrs(hdr.msgattr)) then begin      {not local}
  1324.         result := updatetosslog(fmsg, true, true);                {is echom}
  1325.       end; {else}
  1326.     {result falls through}
  1327.  
  1328.   error:
  1329.     msgdelete := result;
  1330.  
  1331.   end; {msgdelete}
  1332.  
  1333.  
  1334.  
  1335.  
  1336.  
  1337.   procedure setbbspath(path: pathstr);
  1338.   var dir: string;
  1339.   begin
  1340.     if (path='') then begin getdir(0, dir); path := dir end; {if}
  1341.     bbspath := fexpand(path);
  1342.     if (bbspath[length(bbspath)] <> '\') then bbspath := bbspath + '\';
  1343.   end; {setbbspath}
  1344.  
  1345.  
  1346.  
  1347.   procedure setuserspath(path: pathstr);
  1348.   var dir: string;
  1349.   begin
  1350.     if (path='') then begin getdir(0, dir); path := dir end; {if}
  1351.     userspath := fexpand(path);
  1352.     if (userspath[length(userspath)] <> '\') then userspath := userspath + '\';
  1353.   end; {setbbspath}
  1354.  
  1355.  
  1356.  
  1357.   procedure setusersupport(name: namestring;  lastread, current: boolean);
  1358.   begin
  1359.     username := name;
  1360.     usernum := 0;
  1361.     highmsgread := 0;
  1362.     uselastread := lastread;
  1363.     usecurrent := current;
  1364.   end; {setusersupport}
  1365.  
  1366.  
  1367.  
  1368.   procedure setdefaults;
  1369.   begin
  1370.     setbbspath('');           {default to current directory}
  1371.     setuserspath('');         {default to current directory}
  1372.     setusersupport('', false, false); {users.bbs off, lastread and current off}
  1373.   end; {setdefaults}
  1374.  
  1375.  
  1376.  
  1377. var
  1378.   oldexitproc: pointer;
  1379.  
  1380.  
  1381.  
  1382. {$F+}
  1383.   procedure cleanup;
  1384.   var dummy: integer;
  1385.   begin
  1386.     exitproc := oldexitproc;
  1387.     if msgbaseopened then
  1388.       dummy := closemsgbase;
  1389.   end; {cleanup}
  1390.  
  1391.  
  1392.  
  1393. begin
  1394.   setdefaults; oldexitproc := exitproc; exitproc := @cleanup;
  1395. end.
  1396.  
  1397.