{$IFDEF WtrGate}{$IFDEF UseOvr}{$O+,F+}{$ENDIF}{$ENDIF}
UNIT NodeList;

{$i platform.inc}

INTERFACE

{$IFDEF WtrUtil}
PROCEDURE NodeList_RebuildIndex;
{$ENDIF}

FUNCTION NodeList_Verify (ZoneNr,NetNr,NodeNr : WORD) : BYTE;
PROCEDURE NodeList_ListNodelistFilenames;


IMPLEMENTATION

USES Ramon,
     Logs,
     ReadRout,
     Dos,
     Cfg,
     Msgs,
     Globals;

TYPE FilenameEntry = RECORD
                           Filename : ARRAY[1..8+1+3] OF CHAR;
                           Length   : LONGINT;
                     END;

TYPE ZoneEntry = RECORD
                       Zone   : WORD;
                       Length : WORD;
                 END;

TYPE NetEntry = RECORD
                      Net       : WORD;
                      ByteNodes : BYTE;
                      WordNodes : BYTE;
                END;

VAR NodeListTdb : FILE;

{$IFDEF WtrUtil}
{--------------------------------------------------------------------------}
{ NodeList_AddFileToIndex                                                  }
{                                                                          }
{ This routine reads a nodelist file and adds it to the index.             }
{                                                                          }
PROCEDURE NodeList_AddFileToIndex (Path : STRING);

VAR Net      : NetEntry;
    BNodes   : ARRAY[1..255] OF BYTE;
    WNodes   : ARRAY[1..255] OF WORD;

    PROCEDURE WriteNetPlusNodes;
    BEGIN
         BlockWrite (NodeListTdb,Net,SizeOf (NetEntry));
         BlockWrite (NodeListTdb,BNodes,Net.ByteNodes);
         BlockWrite (NodeListTdb,WNodes,Net.WordNodes*2);

         Net.ByteNodes:=0;
         Net.WordNodes:=0;
    END;

VAR InFile   : TEXT;
    IORes    : BYTE;
    Regel    : STRING;
    P        : BYTE;
    Soort    : STRING[20];
    NrStr    : STRING[5];
    Nr       : WORD;
    Nop      : ValNop;

    FNamePos : LONGINT;
    FName    : FilenameEntry;

    ZonePos  : LONGINT;
    Zone     : ZoneEntry;

BEGIN
     Assign (InFile,Path);
     {$I-} Reset (InFile); {$I+} IORes:=IOResult;
     IF (IORes <> 0) THEN
     BEGIN
          LogDiskIOError (IORes,'Error opening '+Path);
          Exit;
     END;

     {$IFDEF LogFileIO}PostOpenT (InFile);{$ENDIF}

     LogMessage (liTrivial,'Adding '+Path);

     REPEAT
           P:=Pos ('\',Path);
           IF (P > 0) THEN
              Delete (Path,1,P);
     UNTIL (P = 0);

     Path:=AddUpWithSpaces (8+1+3,Path);

     Move (Path[1],FName.Filename,8+1+3);
     FName.Length:=0;

     FNamePos:=FileSize (NodeListTdb);
     Seek (NodeListTdb,FNamePos);
     BlockWrite (NodeListTdb,FName,SizeOf (FilenameEntry));

     ZonePos:=-1;
     Net.Net:=65535;

     WHILE (NOT Eof (InFile)) DO
     BEGIN
          ReadLn (InFile,Regel);

          { skip comments }
          IF (Regel[1] = ';') THEN
             Continue;

          { if first part contains "Zone","Region", etc., then store that }
          P:=Pos (',',Regel);
          IF (P = 0) THEN
             Soort:=''
          ELSE
              Soort:=Copy (Regel,1,P-1);

          IF (Soort = 'Down') OR (Soort = 'Hold') OR (Soort = 'Point') THEN
             Continue;

          { then store the number after the first comma }
          NrStr:=Copy (Regel,P+1,5);

          { cut off at second comma }
          P:=Pos (',',NrStr);
          IF (P > 0) THEN
             NrStr:=Copy (NrStr,1,P-1);

          Val (NrStr,Nr,Nop);
          IF (Nop <> 0) THEN
          BEGIN
               LogMessage (liConfig,'Error in '+Regel);
               Continue;
          END;

          IF (Soort = 'Zone') THEN
          BEGIN
               { Zone:=Nr }
               IF (ZonePos <> -1) THEN
               BEGIN
                    Zone.Length:=FileSize (NodeListTdb)-ZonePos-SizeOf (ZoneEntry);
                    Seek (NodeListTdb,ZonePos);
                    BlockWrite (NodeListTdb,Zone,SizeOf (ZoneEntry));
               END;

               { start a new zone entry }
               Zone.Zone:=Nr;

               ZonePos:=FileSize (NodeListTdb);
               Seek (NodeListTdb,ZonePos);

               { pre-occupy space }
               BlockWrite (NodeListTdb,Zone,SizeOf (ZoneEntry));

               Net.Net:=Nr;
               Net.ByteNodes:=0;
               Net.WordNodes:=0;

               Nr:=0; { is added below as a node -> Z:Z/0 }
          END;

          IF (Soort = 'Host') OR (Soort = 'Region') THEN
          BEGIN
               WriteNetPlusNodes;

               { start a new net entry }
               Net.Net:=Nr;

               Nr:=0; { Node 0 }
          END;

          IF ((Nr < 256) AND (Net.ByteNodes = 255)) OR
             ((Nr > 255) AND (Net.WordNodes = 255)) THEN
          BEGIN
               { full, so update on disk and start new entry with same net }
               WriteNetPlusNodes;

               { start a new net entry with the same net number }
               Net.ByteNodes:=0;
               Net.WordNodes:=0;
          END;

          IF (Nr < 256) THEN
          BEGIN
               Inc (Net.ByteNodes);
               BNodes[Net.ByteNodes]:=Nr;
          END ELSE
          BEGIN
               Inc (Net.WordNodes);
               WNodes[Net.WordNodes]:=Nr;
          END;

     END; { while }

     { laatste net wegschrijven }
     WriteNetPlusNodes;

     { laatste zone updaten }
     Zone.Length:=FileSize (NodeListTdb)-ZonePos-SizeOf (ZoneEntry);
     Seek (NodeListTdb,ZonePos);
     BlockWrite (NodeListTdb,Zone,SizeOf (ZoneEntry));

     {$IFDEF LogFileIO}PreCloseT (InFile);{$ENDIF}
     Close (InFile);

     FName.Length:=FileSize (NodeListTdb)-FNamePos-SizeOf (FilenameEntry);
     Seek (NodeListTdb,FNamePos);
     BlockWrite (NodeListTdb,FName,SizeOf (FilenameEntry));
END;


{--------------------------------------------------------------------------}
{ NodeList_FindMostRecent                                                  }
{                                                                          }
{ Deze routine kijkt in de opgeven directory met de opgegeven naam (zonder }
{ extensie) naar de meest recente nodelist (als er meerder zijn). Die      }
{ wordt daarna aan de nodelist toegevoegd.                                 }
{                                                                          }
PROCEDURE NodeList_FindMostRecent (Path : STRING);

VAR Search     : SearchRec;
    OldestTime : LONGINT;
    OldestPath : STRING;
    Nr         : WORD;
    Nop        : WordLong;

BEGIN
     OldestPath:='';

     IF (Pos ('.*',Path) = 0) THEN
        FindFirst (Path+'.*',saJustFiles,Search)
     ELSE
         FindFirst (Path,saJustFiles,Search);

     WHILE (DosError = 0) DO
     BEGIN
          IF (Pos ('.',Search.Name) > 0) THEN
          BEGIN
               Val (Copy (Search.Name,Pos ('.',Search.Name)+1,3),Nr,Nop);
               IF (Nop = 0) THEN
                  IF (OldestPath = '') OR (Search.Time < OldestTime) THEN
                  BEGIN
                       OldestTime:=Search.Time;
                       OldestPath:=Path;
                       WHILE (OldestPath[Length (OldestPath)] <> '\') DO
                             Delete (OldestPath,Length (OldestPath),1);
                       OldestPath:=OldestPath+Search.Name;
                  END;
          END;

          FindNext (Search);
     END; { while }

     FindClose (Search);

     IF (OldestPath = '') THEN
     BEGIN
          LogMessage (liFatal,'Nothing found matching '+Path+'.*');
          Exit;
     END;

     NodeList_AddFileToIndex (OldestPath);
END;


{--------------------------------------------------------------------------}
{ NodeList_RebuildIndex                                                    }
{                                                                          }
{ This routine rebuilds the WaterGate nodelist index by reading the node-  }
{ lists pointed to by the ROUTE.TDB file and stores these in NODELIST.TDB. }
{                                                                          }
PROCEDURE NodeList_RebuildIndex;

VAR Zoek  : ^NodeListRecord;
    IORes : BYTE;

BEGIN
     LogMessage (liTrivial,'Building NODELIST.TDB');

     Assign (NodeListTdb,'NODELIST.TDB');
     {$I-} ReWrite (NodeListTdb,1); {$I+} IORes:=IOResult;
     IF (IORes <> 0) THEN
     BEGIN
          LogDiskIOError (IORes,'Error creating NODELIST.TDB');
          Exit;
     END;

     {$IFDEF LogFileIO}PostOpenF (NodeListTdb);{$ENDIF}

     { browse through all the stored paths and compile each list }
     Zoek:=NodeListsList.GetFirstItem;
     IF (Zoek = NIL) THEN
     BEGIN
          LogMessage (liFatal,'No NODELIST statements in ROUTE.TDB found!!');
     END ELSE
     BEGIN
          WHILE (Zoek <> NIL) DO
          BEGIN
               NodeList_FindMostRecent (Zoek^.Path);
               Zoek:=NodeListsList.GetNextItem;
          END;
     END;

     {$IFDEF LogFileIO}PreCloseF (NodeListTdb);{$ENDIF}
     Close (NodeListTdb);

     LogMessage (liTrivial,'Completed building NODELIST.TDB');
END;
{$ENDIF (WtrUtil)}


{--------------------------------------------------------------------------}
{ NodeList_Verify                                                          }
{                                                                          }
{ This routine can be called to verify if an AKA is on the nodelist. If    }
{ the nodelist file cannot be accessed, then the address is OK.            }
{ Result code:                                                             }
{ 0 - nodelist not accesable (=found)                                      }
{ 1 - found                                                                }
{ 2 - not found                                                            }
{                                                                          }
FUNCTION NodeList_Verify (ZoneNr,NetNr,NodeNr : WORD) : BYTE;

VAR InFile   : FILE;
    IORes    : BYTE;

    FName    : FilenameEntry;
    Zone     : ZoneEntry;
    Net      : NetEntry;

    BNodes   : ARRAY[1..255] OF BYTE;
    WNodes   : ARRAY[1..255] OF WORD;
    Lp       : BYTE;

    OldFMode : BYTE;

LABEL Einde,
      Abort;

BEGIN
     OldFMode:=FileMode;
     FileMode:=fmReadOnly+fmDenyNone;

     Assign (InFile,Config.SystemDir+'NODELIST.TDB');
     {$I-} Reset (InFile,1); {$I+} IORes:=IOResult;
     IF (IORes <> 0) THEN
     BEGIN
          { add logging? - might just not exist }
          NodeList_Verify:=0; { OK, but nodelist not found }
          GOTO Abort;
     END;

     {$IFDEF LogFileIO}PostOpenF (InFile);{$ENDIF}

     NodeList_Verify:=1; { assume OK }

     WHILE (FilePos (InFile) < FileSize (InFile)) DO
     BEGIN
          BlockRead (InFile,FName,SizeOf (FilenameEntry));

          WHILE (FName.Length > 0) DO
          BEGIN
               BlockRead (InFile,Zone,SizeOf (ZoneEntry));
               Dec (FName.Length,SizeOf (ZoneEntry)+Zone.Length);

               IF (Zone.Zone <> ZoneNr) THEN
               BEGIN
                    Seek (InFile,FilePos (InFile)+Zone.Length);
                    Continue; { next zone }
               END;

               WHILE (Zone.Length > 0) DO
               BEGIN
                    BlockRead (InFile,Net,SizeOf (NetEntry));
                    Dec (Zone.Length,SizeOf (NetEntry)+Net.ByteNodes+Net.WordNodes*2);

                    IF (Net.Net <> NetNr) THEN
                    BEGIN
                         Seek (InFile,FilePos (InFile)+Net.ByteNodes+Net.WordNodes*2);
                         Continue; { next net }
                    END;

                    IF (Net.ByteNodes > 0) THEN
                    BEGIN
                         BlockRead (InFile,BNodes,Net.ByteNodes);
                         IF (NodeNr < 256) THEN
                            FOR Lp:=1 TO Net.ByteNodes DO
                                IF (BNodes[Lp] = NodeNr) THEN
                                   GOTO Einde;
                    END;

                    IF (Net.WordNodes > 0) THEN
                    BEGIN
                         BlockRead (InFile,WNodes,Net.WordNodes*2);
                         FOR Lp:=1 TO Net.WordNodes DO
                             IF (WNodes[Lp] = NodeNr) THEN
                                GOTO Einde;
                    END;

                    { cannot stop searching here. there might be another }
                    { net entry with the same net number.                }
               END; { while }
          END; { while }
     END; { while }

     NodeList_Verify:=2; { not found }

Einde:
     {$IFDEF LogFileIO}PreCloseF (InFile);{$ENDIF}
     Close (InFile);

Abort:
     FileMode:=OldFMode;
END;


{--------------------------------------------------------------------------}
{ NodeList_ListNodelistFilenames                                           }
{                                                                          }
{ This routine adds the nodelist filenames stored in NODELIST.TDB to the   }
{ Body of the current message.                                             }
{                                                                          }
PROCEDURE NodeList_ListNodelistFilenames;

VAR InFile   : FILE;
    IORes    : BYTE;
    OldFMode : BYTE;
    FName    : FilenameEntry;
    FNameStr : STRING[20];

LABEL Abort;

BEGIN
     OldFMode:=FileMode;
     FileMode:=fmReadOnly+fmDenyNone;

     Assign (InFile,Config.SystemDir+'NODELIST.TDB');
     {$I-} Reset (InFile,1); {$I+} IORes:=IOResult;
     IF (IORes <> 0) THEN
     BEGIN
          { should never get here }
          LogDiskIOError (IORes,'Error accessing NODELIST.TDB for adding nodelist filenames to bounce message');
          MsgsAddLineTo (Body,'(could not access information)');
          GOTO Abort;
     END;

     {$IFDEF LogFileIO}PostOpenF (InFile);{$ENDIF}

     WHILE (FilePos (InFile) < FileSize (InFile)) DO
     BEGIN
          BlockRead (InFile,FName,SizeOf (FilenameEntry));

          Move (FName.Filename,FNameStr[1],8+1+3);
          FNameStr[0]:=Char (8+1+3);

          MsgsAddLineTo (Body,DeleteBackSpaces (FNameStr));

          { jump to the next fname block }
          Seek (InFile,FilePos (InFile)+FName.Length);
     END; { while }

     {$IFDEF LogFileIO}PreCloseF (InFile);{$ENDIF}
     Close (InFile);

Abort:
     FileMode:=OldFMode;
END;


END.
