{$IFDEF WtrGate}{$IFDEF UseOvr}{$O+,F+}{$ENDIF}{$ENDIF}
{ New statistics handling module }
{$I PLATFORM.INC}                  { May actually be needed eventually }
UNIT NewStats;

INTERFACE

USES Database;

TYPE StatDirectionType = (stdInbound, stdOutbound, stdNeither);
     StatEventType = (stComment,             { -- }  { Implemented (In/Out) }
                      stMsgMail,             { ML }  { Yes }
                      stMsgNews,             { MN }  { Yes }
                      stMsgEcho,             { ME }  { Yes }
                      stMsgNet,              { MM }  {## In here only }

                      stPkt,                 { PK }  { Yes }
                      stP2K,                 { P2 }  { ./. }

                      stUUCP,                { CP }  { Yes }
                      stBAG,                 { BA }  { Yes }
                      stSMTP,                { SM }  { Yes }
                      stSOUP,                { SO }  { Yes }
                      stPOP3,                { P3 }  { Yes }

                      stMailingList,         { LS }  { Yes }
                      stMsgsPerSec,          { SP }  { Yes }

                      stUnkown);             { ?? }

CONST
     StatCodesArray : ARRAY [stComment..stUnkown] OF STRING [2] =
                              ('--', 'ML', 'MN', 'ME', 'MM',
                               'PK', 'P2',
                               'CP', 'BA', 'SM', 'SO', 'P3', 
                               'LS', 'SP', 
                               '??');
VAR
     GlobalMsgCount      : LONGINT;
     
PROCEDURE Stats_Init;
PROCEDURE Stats_Close;
PROCEDURE Stats_StartTimer;
PROCEDURE Stats_StopTimer;
PROCEDURE Stats_WriteMsgsPerMin;
PROCEDURE Stats_SeenArea (AreaRecNr: AreaBaseRecordNrType);
FUNCTION  Stats_HaveSeenArea (AreaRecNr: AreaBaseRecordNrType) : BOOLEAN;
PROCEDURE StatEntry_FidoPkt (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsEcho, MsgsNet: LONGINT);
PROCEDURE StatEntry_UUCPJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsNews, MsgsMail: LONGINT);
PROCEDURE StatEntry_BAGJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsNews, MsgsMail: LONGINT);
PROCEDURE StatEntry_SoupJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsNews, MsgsMail: LONGINT);
PROCEDURE StatEntry_SmtpJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsMail: LONGINT);
PROCEDURE StatEntry_Pop3Job (Orig,Dest: STRING; Size, MsgsMail: LONGINT);
PROCEDURE StatEntry_EchoMsg (Direction: StatDirectionType;
                             FromName: STRING; FromAKA: FidoAddrType;
                             ToName: STRING; Subject: STRING;
                             Area: STRING; Size: LONGINT);
PROCEDURE StatEntry_NewsMsg (Direction: StatDirectionType;
                             FromName: STRING; Subject: STRING;
                             Area: STRING; Size: LONGINT);
PROCEDURE StatEntry_MailMsg (Direction: StatDirectionType; 
                             FromName: STRING; ToName: STRING;
                             Subject: STRING; Size: LONGINT);
PROCEDURE StatEntry_MailingList (Sender: STRING; Recipients: LONGINT;
                             Subject: STRING; Size: LONGINT);
PROCEDURE StatEntry_NetMsg  (Direction: StatDirectionType;
                             FromName: STRING; FromAKA: FidoAddrType;
                             ToName: STRING; ToAKA: FidoAddrType;
                             Subject: STRING; Size: LONGINT);

IMPLEMENTATION
USES Dos,                          { Fsplit }
     Ramon,
     Logs,
     Cfg,
     UnixTime,
     Fido;

CONST
     MaxSeenAreasArraySize= 2500;                 { Good for 20,000 areas }
     MaxSeenAreasNum      = (MaxSeenAreasArraySize * 8) - 1;

     AreasSeenPosArray    : ARRAY [1..8] OF BYTE = (1, 2, 4, 8, 16, 32, 64, 128);

TYPE AreasSeenType = ARRAY [1..MaxSeenAreasArraySize] OF BYTE;

VAR
     StatsFile:     TEXT;
     StatsOpened:   BOOLEAN;
     WroteTitle:    BOOLEAN;       { Have we written '.. for toss on X at Y' }

     SpeedCntStart: LONGINT;       { Time at which speed counting was started }
     SpeedCntStop:  LONGINT;       { Time at which speed counting was stopped }

     AreasSeenPtr  : ^AreasSeenType;

PROCEDURE Stats_WriteTitle; Forward;
PROCEDURE StatEntry_MsgsPerSec (S: STRING); Forward;

{ ------------------------------------------------------------------------ }
{ Stats_Init                                                               }
{                                                                          }
{ Called at startup to open the statistics file.  If an error occurs, this }
{ is reported in the log file, and statistics counting is suspended for    }
{ this run.                                                                }
{                                                                          }
{ ## Writing a header may be too long?                                     }
{                                                                          }
PROCEDURE Stats_Init;
VAR
     Dir       : DirStr;
     Name      : NameStr;
     Ext       : ExtStr;
     StaPath   : FilePathStr;
     IORes     : BYTE;
BEGIN
     {$IFDEF WTRGATE}
     FSplit (UNC_FExpand (Config.LogFilePath), Dir, Name, Ext);
     StaPath := Dir+Name+'.STX';

     Assign (StatsFile, StaPath);
     {$I-} Append (StatsFile); {$I+} IORes := IOResult;
     IF (IORes = 2) THEN
     BEGIN
          {$I-} ReWrite (StatsFile); {$I+} IORes := IOResult;
          IF (IORes <> 0) THEN
               LogDiskIOError (IORes, 'Error creating statistics file '+StaPath);
     END ELSE
          IF (IORes <> 0) THEN
               LogDiskIOError (IORes, 'Error appending statistics file '+StaPath);

     IF (IORes = 0) THEN
          StatsOpened := True
     ELSE
          LogMessage (liFatal, 'NOTICE: Statistics will not be recorded during this run!');

     GetMem (AreasSeenPtr,SizeOf (AreasSeenType));
     FillChar (AreasSeenPtr^, SizeOf (AreasSeenType), 0);

     { Don't write anything until something is added.. }
     {$ENDIF}
END;


{ ------------------------------------------------------------------------ }
{ Stats_Close                                                              }
{                                                                          }
{ Called at shutdown to close the statistics file, if it was opened.       }
{                                                                          }
PROCEDURE Stats_Close;
BEGIN
     IF (StatsOpened) THEN
     BEGIN
          Close (StatsFile);
          StatsOpened := False;
     END;

     IF (AreasSeenPtr <> NIL) THEN
     BEGIN
          FreeMem (AreasSeenPtr,SizeOf (AreasSeenType));
          AreasSeenPtr:=NIL;
     END;
END;

{ ------------------------------------------------------------------------ }
{ Stats_StartTimer                                                         }
{                                                                          }
{ Starts the statistics timer, used in msgs/min calculations.  Should be   }
{ called right before Watergate starts working.                            }
{                                                                          }
PROCEDURE Stats_StartTimer;
BEGIN
     SpeedCntStart := GetCurrentUnixTime;
END;

{ ------------------------------------------------------------------------ }
{ Stats_StopTimer                                                          }
{                                                                          }
{ Stops the statistics timer, used in msgs/min calculations.  Should be    }
{ called right before Watergate starts packing.                            }
{                                                                          }
PROCEDURE Stats_StopTimer;
BEGIN
     IF (SpeedCntStart = 0) THEN
     BEGIN
          { This should never occur! }
          LogMessage (liReport, '[Stats_StopTimer] Called before StartTimer!');
          Exit;
     END;

     SpeedCntStop := GetCurrentUnixTime;
END;

{ ------------------------------------------------------------------------ }
{ Stats_AddEntry                                                           }
{                                                                          }
{ This routine is called to add a new entry to the statistics file.  You   }
{ pass the direction (see stDirectionType above), the type (see stStatType }
{ above), and the line to be added.  The result will be a line in the      }
{ format:                                                                  }
{                                                                          }
{ YYYYMMDDHHMMSS <DIRECTION><TYPE> <LINE>                             }
{ example:                                                                 }
{ 19990823112014 -SP 402.0
{                                                                          }
PROCEDURE Stats_AddEntry (Direction: StatDirectionType; Event: StatEventType; Line: STRING);
VAR
     Dt: DateTime;
     Nop: WordLong;

BEGIN
     IF (StatsOpened) THEN
     BEGIN
          Stats_WriteTitle;

          GetDate (Dt.Year, Dt.Month, Dt.Day, Nop);
          GetTime (Dt.Hour, Dt.Min, Dt.Sec, Nop);

          { Date/time stamp as YYYYMMDDHHMMSS }
          Write (StatsFile, Integer2String (Dt.Year) +
                            AddUpWithPre0s (2, Integer2String (Dt.Month))+
                            AddUpWithPre0s (2, Integer2String (Dt.Day))+
                            AddUpWithPre0s (2, Integer2String (Dt.Hour))+
                            AddUpWithPre0s (2, Integer2String (Dt.Min))+
                            AddUpWithPre0s (2, Integer2String (Dt.Sec)) + ' ');

          { Direction }
          CASE Direction OF
               stdInbound:    Write (StatsFile, '<');
               stdOutbound:   Write (StatsFile, '>');
               stdNeither:    Write (StatsFile, '-');
          END;

          { Code and line }
          Writeln (StatsFile, StatCodesArray [Event] + ' ' + Line);
     END;
END;

{ ------------------------------------------------------------------------ }
{ Stats_WriteMsgsPerMin                                                    }
{                                                                          }
{ Calculates the number of msgs/min that we probably achieved.  Writes     }
{ this to the statistics file, as well as to the log file.                 }
{                                                                          }
PROCEDURE Stats_WriteMsgsPerMin;
VAR
     Result: REAL;
     TempStr: STRING;

BEGIN
     IF (SpeedCntStart = 0) OR (GlobalMsgCount = 0) THEN
          Exit;

     IF (SpeedCntStop = 0) THEN
     BEGIN
          LogMessage (liReport, '[Stats_GetMsgsPerMin] Warning: Timer wasn''t stopped -- check this');
          Stats_StopTimer;
     END;

     { We can't divide by 0, so force it to look like one second }
     IF (SpeedCntStop - SpeedCntStart = 0) THEN
          Inc (SpeedCntStop);

     Result := (LONGINT(60) * GlobalMsgCount) DIV (SpeedCntStop - SpeedCntStart);

     TempStr := Real2String (Result, 4, 1);

     { Update the log file }
     LogMessage (liTrivial, 'Processed ' + Longint2String (GlobalMsgCount) + ' messages in ' +
                         Longint2String (SpeedCntStop - SpeedCntStart) + ' seconds (' +
                         TempStr + ' msgs/min)');

     { If the stats file is open, write this out. }
     IF (NOT StatsOpened) THEN
          Exit;

     Stats_WriteTitle;
     StatEntry_MsgsPerSec (TempStr);
END;

{ ------------------------------------------------------------------------ }
{ Stats_WriteTitle                                                         }
{                                                                          }
{ This routine should be called at the beginning of each and every update  }
{ routine.  If this is the first entry going to the STA file, we write a   }
{ small header (--- Statistics report for run... blah blah)                }
{                                                                          }
PROCEDURE Stats_WriteTitle;
BEGIN
     IF (StatsOpened) AND (NOT WroteTitle) THEN
     BEGIN
          WroteTitle := True;
          Writeln (StatsFile);
     END;
END;


{ ------------------------------------------------------------------------ }
{ Stats_SeenArea                                                           }
{                                                                          }
{ This should be called from DeliverEchomail and DeliverNews to record the }
{ fact that a particular area has been used.  This information can then be }
{ used to write an ECHOTOSS.LOG file and for auto-linking purposes.        }
{                                                                          }
PROCEDURE Stats_SeenArea (AreaRecNr: AreaBaseRecordNrType);

VAR ArrayCode : WORD;
    BitNr     : BYTE;

BEGIN
     { Divide by 8 to get byte index into array }
     ArrayCode:=1+(AreaRecNr SHR 3);
     BitNr:=1 SHL (AreaRecNr AND 7);
     AreasSeenPtr^[ArrayCode]:=AreasSeenPtr^[ArrayCode] OR BitNr;
END;

{ ------------------------------------------------------------------------ }
{ Stats_HaveSeenArea                                                       }
{                                                                          }
{ Returns TRUE if the specified AreaRecNr has been used in this run.       }
{                                                                          }
FUNCTION Stats_HaveSeenArea (AreaRecNr: AreaBaseRecordNrType) : BOOLEAN;

VAR ArrayCode : WORD;
    BitNr     : BYTE;

BEGIN
     { Divide by 8 to get byte index into array }
     ArrayCode:=1+(AreaRecNr SHR 3);
     BitNr:=1 SHL (AreaRecNr AND 7);
     Stats_HaveSeenArea:=(AreasSeenPtr^[ArrayCode] AND BitNr) <> 0;
END;

{ ------------------------------------------------------------------------ }
{ Stats_CleanField                                                         }
{                                                                          }
{ Replaces double-quotes with single quotes, and trims strings.  This is   }
{ needed for the names and subject lines.                                  }
{                                                                          }
FUNCTION Stats_CleanField (Original: STRING): STRING;
VAR
     I: INTEGER;

BEGIN
     FOR I := 1 TO Length (Original) DO
          IF (Original [I] = '"') THEN
               Original [I] := '''';

     Stats_CleanField := DeleteFrontAndBackSpaces (Original);
END;

{ ====================== ENTRY FUNCTIONS ================================= }
{ All of the functions below are called to add a specific entry to the     }
{ stats file.  A brief description of the parameters for each is provided  }
{ before each function.  If a function is marked *PRIVATE*, it is not      }
{ available outside of the unit (e.g. MsgsPerSec)                          }

{ ------------------------------------------------------------------------ }
{ FidoPkt (Direction: StatDirectionType; Orig,Dest: STRING;                }
{          Size: LONGINT; MsgsEcho, MsgsNet: LONGINT);                     }
{                                                                          }
{ Direction: stdInbound or stdOutbound                                     }
{ Orig,Dest: Origin and target fido addr.                                  }
{ Size:      Number of bytes processed in packet, excluding header         }
{ *Echo:     Number of echo/net msgs processed in packet                   }
{                                                                          }
{ Format:    <from> <to> <bytes> <echo#> <net#>                            }
{                                                                          }
PROCEDURE StatEntry_FidoPkt (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsEcho, MsgsNet: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Orig + '" "' + Dest + '" ' + Longint2String (Size)
                  + ' ' + Longint2String (MsgsEcho)
                  + ' ' + Longint2String (MsgsNet);

     Stats_AddEntry (Direction, stPkt, Line);
END;

{ ------------------------------------------------------------------------ }
{ UUCPJob (Direction: StatDirectionType; Orig,Dest: STRING;                }
{          Size: LONGINT; MsgsNews, MsgsMail: LONGINT);                    }
{                                                                          }
{ Direction: stdInbound or stdOutbound                                     }
{ Orig,Dest: Origin and target system names.                               }
{ Size:      Number of bytes processed in packet, excluding header         }
{ Msgs*:     Number of mail/news msgs processed in packet                  }
{                                                                          }
{ Format:    <from> <to> <bytes> <news#> <mail#>                           }
{                                                                          }
PROCEDURE StatEntry_UUCPJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsNews, MsgsMail: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Orig + '" "' + Dest + '" ' + Longint2String (Size)
                  + ' ' + Longint2String (MsgsNews)
                  + ' ' + Longint2String (MsgsMail);

     Stats_AddEntry (Direction, stUUCP, Line);
END;

{ ------------------------------------------------------------------------ }
{ BAGJob (Direction: StatDirectionType; Orig,Dest: STRING;                 }
{          Size: LONGINT; MsgsNews, MsgsMail: LONGINT);                    }
{                                                                          }
{ Direction: stdInbound or stdOutbound                                     }
{ Orig,Dest: Origin and target system names.                               }
{ Size:      Number of bytes processed in packet, excluding header         }
{ Msgs*:     Number of mail/news msgs processed in packet                  }
{                                                                          }
{ Format:    <from> <to> <bytes> <news#> <mail#>                           }
{                                                                          }
PROCEDURE StatEntry_BAGJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsNews, MsgsMail: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Orig + '" "' + Dest + '" ' + Longint2String (Size)
                  + ' ' + Longint2String (MsgsNews)
                  + ' ' + Longint2String (MsgsMail);

     Stats_AddEntry (Direction, stBAG, Line);
END;

{ ------------------------------------------------------------------------ }
{ SoupJob (Direction: StatDirectionType; Orig,Dest: STRING;                }
{          Size: LONGINT; MsgsNews, MsgsMail: LONGINT);                    }
{                                                                          }
{ Direction: stdInbound or stdOutbound                                     }
{ Orig,Dest: Origin and target system names (one will be LOCAL)            }
{ Size:      Number of bytes processed in packet                           }
{ Msgs*:     Number of mail/news msgs processed in packet                  }
{                                                                          }
{ Format:    <from> <to> <bytes> <news#> <mail#>                           }
{                                                                          }
PROCEDURE StatEntry_SoupJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsNews, MsgsMail: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Orig + '" "' + Dest + '" ' + Longint2String (Size)
                  + ' ' + Longint2String (MsgsNews)
                  + ' ' + Longint2String (MsgsMail);

     Stats_AddEntry (Direction, stSOUP, Line);
END;

{ ------------------------------------------------------------------------ }
{ SmtpJob (Direction: StatDirectionType; Orig,Dest: STRING;                }
{          Size: LONGINT; MsgsMail: LONGINT);                              }
{                                                                          }
{ Direction: stdInbound or stdOutbound                                     }
{ Orig,Dest: Origin and target system names (one will be LOCAL)            }
{ Size:      Number of bytes processed in packet                           }
{ Msgs*:     Number of mail msgs processed in packet                       }
{                                                                          }
{ Format:    <from> <to> <bytes> <mail#>                                   }
{                                                                          }
PROCEDURE StatEntry_SmtpJob (Direction: StatDirectionType; Orig,Dest: STRING;
                             Size, MsgsMail: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Orig + '" "' + Dest + '" ' + Longint2String (Size)
                  + ' ' + Longint2String (MsgsMail);

     Stats_AddEntry (Direction, stSMTP, Line);
END;


{ ------------------------------------------------------------------------ }
{ Pop3Job (Orig,Dest: STRING; Size: LONGINT; MsgsMail: LONGINT);           }    
{                                                                          }
{ Orig,Dest: Origin and target system names (one will be LOCAL)            }
{ Size:      Number of bytes processed in packet                           }
{ Msgs*:     Number of mail msgs processed in packet                       }
{                                                                          }
{ Format:    <from> <to> <bytes> <mail#>                                   }
{                                                                          }
PROCEDURE StatEntry_Pop3Job (Orig,Dest: STRING; Size, MsgsMail: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Orig + '" "' + Dest + '" ' + Longint2String (Size)
                  + ' ' + Longint2String (MsgsMail);

     Stats_AddEntry (stdInbound, stPOP3, Line);
END;

{ ------------------------------------------------------------------------ }
{ EchoMsg (Direction: StatDirectionType; FromName: STRING;                 }
{                     FromAKA: FidoAddrType; ToName: STRING; Area: STRING; }
{                     Size: LONGINT);                                      }
{                                                                          }
{ FromName:  Name from From field in message                               }
{ FromAKA:   System from which this message *origininated*                 }
{ Area:      FTN Area tag for this message                                 }
{ Size:      Number of bytes in message                                    }
{                                                                          }
{ Format:    "<fromName>" <fromaka> "<toname>" "<subject>" <area> <size>   }
{                                                                          }
PROCEDURE StatEntry_EchoMsg (Direction: StatDirectionType;
                             FromName: STRING; FromAKA: FidoAddrType;
                             ToName: STRING; Subject: STRING;
                             Area: STRING; Size: LONGINT);
VAR
     Line: STRING;

BEGIN
     IF (Area = '') THEN
     BEGIN
          LogMessage (liReport, '[StatEntry_EchoMsg] Blank area, using ???!');
          Area := '???';
     END;

     Line := '"' + Stats_CleanField (FromName) + '" '+ Fido2Str (FromAKA)
                  + ' "' + Stats_CleanField (ToName) + '" "' +
                  Stats_CleanField (Subject) + '" ' +
                  Stats_CleanField (Area) + ' ' + Longint2String (Size);

     Stats_AddEntry (Direction, stMsgEcho, Line);
END;

{ ------------------------------------------------------------------------ }
{ NewsMsg (Direction: StatDirectionType; FromName: STRING;                 }
{                     STRING; Area: STRING;                                }
{                     Size: LONGINT);                                      }
{                                                                          }
{ FromName:  Name from From field in message                               }
{ Area:      FTN Area tag for this message                                 }
{ Size:      Number of bytes in message                                    }
{                                                                          }
{ Format:    "<fromname>" "<subject>" <area> <size>                        }
{                                                                          }
PROCEDURE StatEntry_NewsMsg (Direction: StatDirectionType;
                             FromName: STRING; Subject: STRING; Area: STRING; Size: LONGINT);
VAR
     Line: STRING;

BEGIN
     IF (Area = '') THEN
     BEGIN
          LogMessage (liReport, '[StatEntry_NewsMsg] Blank area, using ???!');
          Area := '???';
     END;

     Line := '"' + Stats_CleanField (FromName) + '" "' +
                   Stats_CleanField (Subject) + '" ' +
                   Stats_CleanField (Area) + ' ' + Longint2String (Size);

     Stats_AddEntry (Direction, stMsgNews, Line);
END;

{ ------------------------------------------------------------------------ }
{ MailMsg (Direction: StatDirectionType; FromName: STRING;                 }
{                     ToName: STRING; Subject: STRING; Size: LONGINT;      }
{                                                                          }
{ Format:    "<fromname>" "<toname>" "<subject>" <size>                    }
{                                                                          }
PROCEDURE StatEntry_MailMsg (Direction: StatDirectionType; 
                             FromName: STRING; ToName: STRING;
                             Subject: STRING; Size: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Stats_CleanField (FromName) +
             '" "' + Stats_CleanField (ToName) + '" "'+
                     Stats_CleanField (Subject) +'" '
                    +Longint2String (Size);

     Stats_AddEntry (Direction, stMsgMail, Line);
END;


{ ------------------------------------------------------------------------ }
{ MailingList (Sender: STRING; Recipients: LONGINT; Subject: STRING;       }
{                     Size: LONGINT);                                      }
{                                                                          }
{ Format:    "<sender>" <recipients> "<subject>" <size>                    }
{                                                                          }
PROCEDURE StatEntry_MailingList (Sender: STRING; Recipients: LONGINT;
                             Subject: STRING; Size: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"' + Stats_CleanField (Sender) +
             '" ' + Longint2String (Recipients) +
             ' "' + Stats_CleanField (Subject) + '" '+
                    +Longint2String (Size);

     Stats_AddEntry (stdOutbound, stMailingList, Line);
END;

{ ------------------------------------------------------------------------ }
{ NetMsg (Direction: StatDirectionType; FromName: STRING; FromAKA: Fid...  }
{                    ToName: STRING; ToAKA: FidoAddrType; Subject: STRING; }
{                    Size: LONGINT;                                        }
{                                                                          }
{ Format:    "<fromname>"%<fromaka> "<toname>"%<toaka> "<subject>" <size>  }
{                                                                          }
PROCEDURE StatEntry_NetMsg  (Direction: StatDirectionType; 
                             FromName: STRING; FromAKA: FidoAddrType;
                             ToName: STRING; ToAKA: FidoAddrType;
                             Subject: STRING; Size: LONGINT);
VAR
     Line: STRING;

BEGIN
     Line := '"'+Stats_CleanField (FromName)+'" '+Fido2Str(FromAKA)+' '+
             '"'+Stats_CleanField (ToName)+'" '+Fido2Str(ToAKA)+' '+
             '"'+Stats_CleanField (Subject)+'" '+Longint2String (Size);

     Stats_AddEntry (Direction, stMsgNet, Line);
END;


{ ------------------------------------------------------------------------ }
{ MsgsPerSec (S: STRING)                                        *PRIVATE*  }
{                                                                          }
{ S: String representation of the msgs/sec entry to be added (number only) }
{                                                                          }
PROCEDURE StatEntry_MsgsPerSec (S: STRING);
VAR
     Line: STRING;

BEGIN
     Line := S;

     Stats_AddEntry (stdNeither, stMsgsPerSec, Line);
END;


BEGIN
     GlobalMsgCount := 0;
     StatsOpened := False;
     WroteTitle := False;
     SpeedCntStart := 0;
     SpeedCntStop := 0;
     AreasSeenPtr:=NIL;
END.



