// --------------------------------------------------------------------
//        N o d e l i s t . c p p 
//                                                                     
//        Fido messages tracker                            
//        Nodelist.
// --------------------------------------------------------------------
//        Copyright (c) 1998-2001 by Fyodor Ustinov                         
//                                FIDONet 2:5020/79                      
//                                                                     
//        All rights reserved.                                         
// --------------------------------------------------------------------

#ifndef __GNUC__
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#endif                  
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "constant.hpp"
#include "vars.hpp"
#include "configure.hpp"
#include "log.hpp"
#include "nodelist.hpp"
#include "age.hpp"
#include "utils.hpp"

char *NodelistPath;

static int NodelistTurnOff = FALSE;
static int NodelistLineNum = 0;

static char mErrReadIndex[] = "Error reading index file.";
static char mNdlChanged[] = "Some nodelists is changed. Recompile need.";
static char mIndNFound[] = "Index file not found. Create new index file.";
static char mErrNdlMustFull[] = "Error: You must define default Zone for Regional and Network verisons of nodelist.";
static char mErrNdlFormat[] = "Incorrect nodelist format.";

#define ErrReadIndex   Log.Level(LOGE) << mErrReadIndex << EOL
#define IndexChanged   Log.Level(LOGI) << mNdlChanged << EOL
#define IndexNotFound  Log.Level(LOGI) << mIndNFound << EOL


typedef struct _Ntr {
   dword Number;
   dword Attrib;
   struct _Ntr  *Next;
   struct _Ntr  *Sub;
} Ntr;

void ErrNdlFormat(char *m) {
   Log.Level(LOGE) << "Error in line " << NodelistLineNum << ", " << mErrNdlFormat << EOL;
   Log.Level(LOGE) << m << EOL;
}

int FindNodelist(char *Mask, char *Name) {
int maxext;
DIR *dd;
struct dirent *ff;
char NDir[512], Path[512], Fname[512];
char *tmt = NULL;
FILE *ft;

   *Name = '\0';
   Path[1] = '\0';
   maxext = (-1);

   GetFilePath(Path,Mask);
   if (NodelistPath != NULL && *Path != PATHDELIMC
#ifndef UNIX
       && Path[1] != ':'
#endif
   ) {
      strcpy(NDir,NodelistPath);
      strcat(NDir,Path);
      strcpy(Path,NDir);
   }
   GetFileName(Fname,Mask);
   if (strchr(Fname,'*') || strchr(Fname,'?')) {
      if ((dd = opendir(dirslashbug(Path))) != 0) {
         while((ff = readdir(dd)) != NULL) {
            if (fsCompareName(ff->d_name,Fname) != 0 && (tmt = strrchr(ff->d_name,'.')) != NULL) {
	       tmt++;
               if (StrIsNum(tmt) && atoi(tmt) > maxext) {
                  maxext = atoi(tmt);
		  strcpy(Name,Path);
		  strcat(Name,ff->d_name);
               }
            }
         }
         closedir(dd);
      } else {
         yyerror("Nodelist not found.");
         return (-1);
      }
   } else {
      strcpy(Name,Path);
      strcat(Name,Fname);
   }
   if (*Name == '\0') {
      yyerror("Nodelist not found.");
      return (-1);
   }
   if ((ft = fopen(Name,"rt")) == NULL) {
      yyerror("Unable to open Nodelist.");
      return (-1);
   } else {
      fclose (ft);
   }
   return (0);
}

// ------------------- Compile and writing one nodelist  --------------------

static FILE *nh;
static int  CurrentNet = -1;
static int  CurrentZone = -1;
static Ntr  **CurrentHub = NULL;
static Ntr  *_pNodeList = NULL;
static int PointListMode = FALSE;
static Ntr *BossNode = NULL;

int OpenNodelist(char *FName) {
   nh = fopen(FName,"rt");
   if (nh == NULL) {
      Log.Level(LOGE) << "Unable to open nodelist '" << FName << "'" << EOL;
      return FALSE;
   }
   return TRUE;
}

int CloseNodelist(void) {
   return (fclose (nh) == 0);
}

int ReadNdlLine (char *Buff, int Count) {
char *tmt;
   Buff[0] = ';';
   while (Buff[0] == ';') {
      NodelistLineNum++;
      if (fgets(Buff, Count, nh) == NULL) {
         Buff[0] = '\0';
         return (-1);
      }
      tmt = strchr(Buff,'\n');
      if (tmt != NULL) *tmt = '\0';
      tmt = strchr(Buff,'\r');
      if (tmt != NULL) *tmt = '\0';
      tmt = strchr(Buff,(char)0x1a);
      if (tmt != NULL) *tmt = '\0';
      if (strlen(Buff) == 0) {
         Buff[0] = ';';
      }
   }
   return(strlen(Buff));
}

Ntr *ExistByNumber(Ntr *Addr, unsigned int Number) {
Ntr *tmt;
   tmt = Addr;
   while (tmt != NULL) {
      if ((tmt->Number & 0xffff) == (Number & 0xffff)) {
         return tmt;
      }
      tmt = tmt->Next;
   }
   return NULL;
}

Ntr *GetCurrentZone(void) {
   return(ExistByNumber(_pNodeList,CurrentZone));
}

Ntr *GetCurrentNet(void) {
Ntr *tmt;
   if ((tmt = GetCurrentZone()) != NULL) {
      if (tmt->Sub != NULL) {
         return (ExistByNumber(tmt->Sub,CurrentNet));
      } else {
        return NULL;
      }
   } else {
      return NULL;
   }
}

int DelDupNode(unsigned int Node) {
// Удаляем ноду из списка, если она там уже есть. Но! При этом проверяем,
// а не в виде-ли хаба она есть? Если в виде хаба, то не удаляем. А если при 
// этом выясняется, что она и сейчас хаб - то устанавливаем указатель
// текущего хаба на это место.
// Return TRUE  - успешно удалили, или этой ноды вообще небыло в списке
//        FALSE - нода была в списке в виде хаба, да и сейчас - хаб.
Ntr *tr;
   tr = GetCurrentNet();
   assert(tr != NULL);
   tr = tr->Sub;
   while (tr != NULL) {
      if ((tr->Number & 0xffff) == (Node & 0xffff)) {
         if ((tr->Number & A_MASK) != A_HUB) {
            tr->Number = (unsigned int)-1;
            return TRUE;
         } else if (((tr->Number & A_MASK) == A_HUB) && ((Node & A_MASK) == A_HUB)) {
//            Log.Level(LOGD) << "Two nodes? " << tr->Number << " " << Node << EOL;
//            Log.Level(LOGD) << "Net: " << CurrentZone << ":" << CurrentNet << EOL;
            CurrentHub = &(tr->Next);
            return FALSE;
         }
      }
      tr = tr->Next;
   }
   return TRUE;
}

void AddElemToList(Ntr **Addr, unsigned int Number) {
Ntr *tmt;
   tmt = (Ntr *) malloc(sizeof(Ntr));
   CheckMem((char *)tmt);
   memset(tmt,0,sizeof(Ntr));
   tmt->Number = Number;
   while ((*Addr) != 0) {
      Addr = &((*Addr)->Next);
   }
   *Addr = tmt;
}

void AddNetToList(void) {
Ntr *tr;
   tr = GetCurrentZone();
   assert(tr != NULL);
   AddElemToList(&(tr->Sub),CurrentNet);
}

void AddZoneToList(void) {
   AddElemToList(&_pNodeList,CurrentZone);
   AddNetToList();
}

void _SetCurrentZone(unsigned int Zone) {
   CurrentZone = Zone;
   CurrentNet = Zone;
   CurrentHub = NULL;
   if (GetCurrentZone() == 0) {
      AddZoneToList();
   }
}

void _AddNode(unsigned int Node) {
// Добавляем ноду в список. Если ее там еще небыло - 
// то просто тупо приписываем в начало списка CurrentHub. Но если он не 
// установлен, то добавляем в список как обычно.
// Если была, то нифига не делаем, за нас уже все сделали в DelDupNode.
Ntr *tr, *tmt;
   
   if (DelDupNode(Node) == TRUE) {
      if (CurrentHub != NULL) {
         tmt = (Ntr *) malloc(sizeof(Ntr));
         CheckMem((char *)tmt);
         memset(tmt,0,sizeof(Ntr));
         tmt->Number = Node;
         tr = (*CurrentHub)->Next;
         (*CurrentHub)->Next = tmt;
         tmt->Next = tr;
      } else {
         tr = GetCurrentNet();
         assert(tr != NULL);
         AddElemToList(&(tr->Sub),Node);
      }
   }
}

void _SetCurrentNet(unsigned int Net) {
   CurrentNet = Net;
   CurrentHub = NULL;
   if (GetCurrentNet() == NULL) {
      AddNetToList();
   }
}

int _SetCurrentBoss(char *tmt) {
FA f;
Ntr *tp;

   BossNode = NULL;
   f.Parse(tmt);
   if (f.Masked()) return FALSE;
   if ((f.Point() & 0xffff) != 0) return FALSE;
   tp = ExistByNumber(_pNodeList,f.Zone());
   if (tp == NULL) return TRUE;
   if (tp->Sub == NULL) return TRUE;
   tp = ExistByNumber(tp->Sub,f.Net());
   if (tp == NULL) return TRUE;
   if (tp->Sub == NULL) return TRUE;
   tp = ExistByNumber(tp->Sub,f.Node());
   if (tp == NULL) return TRUE;
   BossNode = tp;
   return TRUE;
}

void _AddPoint(int pnt) {

   if (BossNode != NULL) {
      AddElemToList(&(BossNode->Sub),pnt);
   }
}

int ParseNodeLine(char *tmt) {
unsigned int tmp;

// DumpNdl();
// fprintf(stderr,"%s\n",tmt);
//   printf("'%s'\n",tmt);
   switch(*tmt) {
      case 'B': // Boss record. Pointlist?
      case 'b':
         PointListMode = TRUE;
         if (_SetCurrentBoss(tmt+5) != TRUE) {
            ErrNdlFormat("Bad BOSS record");
            return FALSE;
         }
         break;
      case 'Z': // Zone record
         PointListMode = FALSE;
         _SetCurrentZone(atoi(tmt+5));
         break;
      case 'R': // Region record
         PointListMode = FALSE;
         if (CurrentZone == 0) {
            Log.Level(LOGE) << mErrNdlMustFull << EOL;
            return FALSE;
         }
         tmp = atoi(tmt+7);
         tmp |= A_REGION;
         _SetCurrentNet(tmp);  // Region is a strange type of net.
         break;
      case 'H': // Host, Hub, or Hold record
         switch(*(tmt+2)) {
            case 's': // Host
               PointListMode = FALSE;
               if (CurrentZone == 0) {
                  Log.Level(LOGE) << mErrNdlMustFull << EOL;
                  return FALSE;
               }
               _SetCurrentNet(atoi(tmt+5));
               break;
            case 'b': // Hub
               if (PointListMode == TRUE) {
                  ErrNdlFormat("HUB record in pointlist!");
                  return FALSE;
               }
               if (CurrentZone == 0) {
                  ErrNdlFormat("HUB record without Zone record!");
                  return FALSE;
               }
               tmp = atoi(tmt+4);
               tmp |= A_HUB;
               _AddNode(tmp);
               break;
            case 'l': // Hold
               if (PointListMode == TRUE) {
                  ErrNdlFormat("Node or point with HOLD flag!");
                  return FALSE;
               }
               if (CurrentZone == 0) {
                  ErrNdlFormat("HOLD record without Zone record!");
                  return FALSE;
               }
               tmp = atoi(tmt+5);
               tmp |= A_HOLD;
               _AddNode(tmp);
               break;
            default: 
               ErrNdlFormat("Unknown format!");
               return FALSE;
         }
         break;
      case ',': // Simple node
         if (PointListMode == TRUE) {
            _AddPoint(atoi(tmt+1));
         } else {
            if (CurrentZone == 0) {
               ErrNdlFormat("Node record without HOST!");
               return FALSE;
            }
            _AddNode(atoi(tmt+1));
         }
         break;
      case 'D': // Down
         if (PointListMode == TRUE) {
            ErrNdlFormat("DOWN record in pointlist!");
            return FALSE;
         }
         if (CurrentZone == 0) {
            ErrNdlFormat("Node record without HOST!");
            return FALSE;
         }
         tmp = atoi(tmt+5);
         tmp |= A_DOWN;
         _AddNode(tmp);
         break;
      case 'P': // Pvt or Point
      case 'p': 
         switch(*(tmt+1)) {
            case 'v': // Pvt
               if (PointListMode == TRUE) {
                  ErrNdlFormat("PVT record in pointlist!");
                  return FALSE;
               }
               if (CurrentZone == 0) {
                  ErrNdlFormat("Node record without HOST!");
                  return FALSE;
               }
               tmp = atoi(tmt+4);
               tmp |= A_PVT;
               _AddNode(tmp);
               break;
            case 'o': // Point
               if (PointListMode == FALSE) {
                  ErrNdlFormat("POINT record not in pointlist!");
                  return FALSE;
               }
               tmp = atoi(tmt+6);
               _AddPoint(tmp);
               break;
            default: 
               ErrNdlFormat("Unknown format!");
               return FALSE;
         }
         break;
      default: // No one? Hmm...
         ErrNdlFormat("Unknown format!");
         return FALSE;
   }
   return TRUE;
}

int ParseOneNodelist (NodeListElem *Elem) {
char Buff[512];
   Log.Level(LOGI) << "Compile nodelist '" << Elem->Name << "'..." << EOL;
   if (Elem->StartZone != 0) {
      _SetCurrentZone(Elem->StartZone);
   } else {
      CurrentZone = 0;
   }
   CurrentNet = 0;

   if (!OpenNodelist(Elem->Name)) {
      return FALSE;
   }

   NodelistLineNum = 0;
   while(ReadNdlLine(Buff,510) > 0) {
      if (!ParseNodeLine(Buff)) {
         CloseNodelist();
         return FALSE;
      }
   }
   CloseNodelist();
   Log.Level(LOGI) << "Done" << EOL;
   return TRUE;
}

// ------------------------------------------------------
// Flush compiled nodelist to disk
// ------------------------------------------------------

#if 0
void PrintNtr(Ntr *tmt, char *Buff) {
int len;
Ntr *tmt2;
   len = strlen(Buff);
   tmt2= tmt;
   while (tmt != NULL) {
      sprintf(Buff+len,"%d ",tmt->Number & 0xffff);
      PrintNtr(tmt->Sub,Buff);
      tmt = tmt->Next;
   }
   if (tmt2 == NULL) printf("%s\n",Buff);
}
#endif

int ElementsInList (Ntr *Addr) {
Ntr *tmt;
int Elems;

   tmt = Addr;
   Elems = 0;
   while (tmt != NULL) {
      if (tmt->Number != (unsigned int)-1) {
         Elems++;
      }
      tmt = tmt->Next;
   }
   return Elems;
}

int SaveElements(FILE *fh, Ntr *Addr) {
Ntr *tmt;
   tmt = Addr;
   while (tmt != NULL) {
      if (tmt->Number != (unsigned int)-1) {
          if (fwrite(&tmt->Number,sizeof(int),1,fh) != 1) {
             Log.Level(LOGE) << "Unable to write body of Elems" << EOL;
             return FALSE;
          }
      }
      tmt = tmt->Next;
   }
   return TRUE;
}

int FlushElements(FILE *fh, Ntr *tmt) {
int Elems;

   Elems = ElementsInList(tmt);
   if (fwrite(&Elems,sizeof(Elems),1,fh) != 1) {
      Log.Level(LOGE) << "Unable to write Elems" << EOL;
      return FALSE;
   }
   if (!SaveElements(fh,tmt)) return FALSE;
   return TRUE;
}

int FlushSubElements(FILE *fh, Ntr *tmt) {
   if (FlushElements(fh,tmt) == FALSE) return FALSE;
   while (tmt != NULL) {
      if (tmt->Number != (unsigned int)-1) {
         if (FlushSubElements(fh,tmt->Sub) == FALSE) return FALSE;
      }
      tmt = tmt->Next;
   }
   return TRUE;   
}

int FlushElementsTree(FILE *fh) {
   return (FlushSubElements(fh, _pNodeList));
}   

// ------------------------------------------------------
// Free parser memory
// ------------------------------------------------------

void FreeSubElements(Ntr *tmt) {
Ntr *tmt2;

   if (tmt == NULL) return;
   while (tmt != NULL) {
      FreeSubElements(tmt->Sub);
      tmt2 = tmt->Next;
      free(tmt);
      tmt = tmt2;
   }
}

void FreeParserMem(void) {
   FreeSubElements(_pNodeList);
   _pNodeList = NULL;
}

// ------------------------------------------------------

int FlushNodelist (FILE *fh) {
#if 0
char Buff[1024];
   PrintNtr(_pNodeList,Buff);
#endif
   if (FlushElementsTree(fh) == FALSE) return FALSE;
   FreeParserMem();
   return TRUE;
}



// ------------------------- class NodeLists -----------------------

NodeLists::NodeLists() {
   NList = NULL;
   Lists = 0;
   StartZone = 0;
   IndexName = strdup(DefaultIndex);
}

NodeLists::~NodeLists() {
   if (NList != NULL) {
      free(NList);
      NList= NULL;
   }
   Lists = 0;
   if (IndexName != NULL) {
      free(IndexName);
      IndexName = NULL;
   }
}

#if 0
void PrintNch(Nch *tmt, char *Buff) {
int i;
int len;
   len = strlen(Buff);
   i = 0;
   while (tmt[i].Number != -1) {
      sprintf(Buff+len,"%d ",tmt[i].Number & 0xffff);
      PrintNch(tmt[i].Sub,Buff);
      i++;
   }
   if (tmt[0].Number == -1) printf("%s\n",Buff);
}
#endif

void NodeLists::Print(void) {
#if 0
int tmc;
char Buff[1024];
NodeListElem *tmt;
   printf("Index file: '%s'\n",IndexName);
   printf("Total nodelists: '%d'\n",Lists);
   tmt = NList;
   tmc = Lists;
   while (tmc != 0) {
      printf("Nodelist name: '%s'\n",tmt->Name);
      printf("Nodelist Date: '%d'\n",tmt->Time);
      tmc--;
      tmt++;
   }
   Buff[0] = '\0';
   PrintNch(Index,Buff);
#endif
}

void NodeLists::IndexFile(char *File) {
   if (IndexName != NULL) {
      free(IndexName);
      IndexName = NULL;
   }
   IndexName = strdup(File);
}

int NodeLists::CompileNeed(void) {
FILE *fh;
unsigned int tmp;
NodeListElem Elem;

// 1 - index file exist?
   if (access(IndexName,F_OK) != 0) {
      IndexNotFound;
      return TRUE;
   }

// 2 - We can open him?
   fh = fopen(IndexName,"r+b");
   if (fh == NULL) {
      Log.Level(LOGE) << "Unable to open index file." << EOL;
      return TRUE;
   }

// 3 - We can read signature from him?
   if (fread(&tmp,sizeof(tmp),1,fh) != 1) {
      ErrReadIndex;
      fclose(fh);
      return TRUE;
   }

// 4 - Signature is correct?
   if (tmp != NdlSign) {
      Log.Level(LOGE) << "Index file from old version of FTrack. Recompile need." << EOL;
      fclose(fh);
      return TRUE;
   }

// 5 - We can read nodelist count?
   if (fread(&tmp,sizeof(tmp),1,fh) != 1) {
      ErrReadIndex;
      fclose(fh);
      return TRUE;
   }

// 6 - count of nodelists the same?
   if (tmp != (unsigned int) Lists) {
      IndexChanged;
      fclose(fh);
      return TRUE;
   }

// 7 - Node lists is no changed?
   for (unsigned int i = 0; i < tmp; i++) {
      if (fread(&Elem,sizeof(Elem),1,fh) != 1) {
         ErrReadIndex;
         fclose(fh);
         return TRUE;
      }
      if ((MaxNodelistAge != (time_t)-1) && (NodelistTurnOff != TRUE)) {
         if ((time(NULL) - (NList+i)->Time) > MaxNodelistAge) {
            Log.Level(LOGI) << "Nodelist '" << (NList+i)->Name << "' too old." << EOL;
            Log.Level(LOGI) << "Checking existance in nodelists turned off." << EOL;
            NodelistTurnOff = TRUE;
            Log.Level(LOGD) << "Time  : " << (int) (time(NULL)) << EOL;
            Log.Level(LOGD) << "NTime : " << (int) (NList+i)->Time << EOL;
            Log.Level(LOGD) << "Age   : " << (int) (time(NULL) - (NList+i)->Time) << EOL;
            Log.Level(LOGD) << "MaxAge: " << (int) MaxNodelistAge << EOL;
         }
      }
      if (memcmp(&Elem,NList+i,sizeof(Elem)) != 0) {
         Log.Level(LOGD) << "NodeLists::CompileNeed. memcmp failed. Nodelist changed." << EOL;
         Log.Level(LOGD) << "--Information should be:" << EOL;
         Log.Level(LOGD) << "NName : " << (NList+i)->Name << EOL;
         Log.Level(LOGD) << "NTime : " << (int) (NList+i)->Time << EOL;
         Log.Level(LOGD) << "Zone  : " << (int) (NList+i)->StartZone << EOL;
         Log.Level(LOGD) << "--Information in index file:" << EOL;
         Log.Level(LOGD) << "NName : " << Elem.Name << EOL;
         Log.Level(LOGD) << "NTime : " << Elem.Time << EOL;
         Log.Level(LOGD) << "Zone  : " << Elem.StartZone << EOL;
         IndexChanged;
         fclose(fh);
         return TRUE;
      }
   }
   fclose (fh);
   return FALSE;
}

char *NodeLists::Names(char *Buff) {
NodeListElem *Elem;
int i;
char *tmt;

   Buff[0] = '\0';
   for (Elem = NList, i=0; i < Lists; Elem++, i++) {
      tmt = strrchr(Elem->Name,PATHDELIMC);
      if (tmt != NULL) {
         tmt++;
      } else {
         tmt = Elem->Name;
      }
      strcat(Buff,tmt);
      if (i+1 < Lists) strcat(Buff,", ");
   }
   return Buff;
}

int NodeLists::Compile(void) {
FILE *fh;
char *tmt;
int tmp;
int i;
// 1  - Write new _empty_ header.
   fh = fopen(IndexName,"wb");
   if (fh == NULL) {
      Log.Level(LOGE) << "Unable to create index file." << EOL;
      return FALSE;
   }
   tmt = (char *) malloc(sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem)*Lists);
   CheckMem(tmt);
   memset(tmt,'\0',sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem)*Lists);
   if (fwrite(tmt,sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem)*Lists,1,fh) != 1) {
      Log.Level(LOGE) << "Unable to write temporary header to index file." << EOL;
      fclose(fh);
      free(tmt);
      return FALSE;
   }
   free(tmt);

// 2  - Write compiled nodelists.

   for (i = 0; i < Lists; i++) {
      if (!ParseOneNodelist(NList + i)) {
         fclose (fh);
         return FALSE;
      }
   }
   if (!FlushNodelist(fh)) {
      fclose(fh);
      return FALSE;
   }

// 3  - Write new, true header.

   if (fseek(fh,0,SEEK_SET) != 0) {
      Log.Level(LOGE) << "Unable to set pointer to begin of index file." << EOL;
      fclose(fh);
      return FALSE;
   }
   tmp = NdlSign;
   if (fwrite(&tmp,sizeof(tmp),1,fh) != 1) {
      Log.Level(LOGE) << "Unable to write signature to index file." << EOL;
      fclose(fh);
      return FALSE;
   }
   if (fwrite(&Lists,sizeof(Lists),1,fh) != 1) {
      Log.Level(LOGE) << "Unable to write Nodelist counter to index file." << EOL;
      fclose(fh);
      return FALSE;
   }
   for (i=0; i < Lists; i++) {
      if ((MaxNodelistAge != (time_t)-1) && (NodelistTurnOff != TRUE)) {
         if ((time(NULL) - (NList+i)->Time) > MaxNodelistAge) {
            Log.Level(LOGI) << "Nodelist '" << (NList+i)->Name << "' too old." << EOL;
            Log.Level(LOGI) << "Checking existance in nodelists turned off." << EOL;
            NodelistTurnOff = TRUE;
            Log.Level(LOGD) << "Age   : " << (int) (time(NULL) - (NList+i)->Time) << EOL;
            Log.Level(LOGD) << "MaxAge: " << (int) MaxNodelistAge << EOL;
         }
      }
      if (fwrite(NList+i,sizeof(NodeListElem),1,fh) != 1) {
         Log.Level(LOGE) << "Unable to write Nodelist information to index file." << EOL;
         fclose(fh);
         return FALSE;
      }
   }
   fclose(fh);
   return TRUE;
}
#ifdef _DEBUG

void IPrint(Nch *Ind) {
   if (Ind != NULL) {
      fprintf(stderr,"---%p>",Ind);
      while (Ind->Number != -1) {
         fprintf(stderr," %u",Ind->Number);
         Ind++;
      }
      fprintf(stderr,"\n");
   }
}
#endif
int NodeLists::LoadOneIndex(FILE *fh, Nch *&Ind) {
unsigned int tmp;
Nch *tmt;
unsigned int i;

   if (fread(&tmp,sizeof(tmp),1,fh) != 1) return FALSE;
   tmt = (Nch *) malloc((tmp+1)*sizeof(Nch));
   CheckMem((char *)tmt);
   memset(tmt,0,(tmp+1)*sizeof(Nch));
   tmt[tmp].Number = (unsigned int) -1;
   for (i = 0; i < tmp; i++) {
      if (fread(&tmt[i].Number,sizeof(int),1,fh) != 1) return FALSE;
   }
   Ind = tmt;
   for (i = 0; i < tmp; i++) {
      if (LoadOneIndex(fh,Ind[i].Sub) == FALSE) return FALSE;
   }
   return TRUE;
}

int NodeLists::Load(void) {
FILE *fh;
   if (!Enabled()) {
      return TRUE;
   }
   if (CompileNeed()) {
      if (!Compile()) {
         return FALSE;
      }
   }
   fh = fopen(IndexName,"rb");
   if (fh == NULL) {
      Log.Level(LOGE) << "Unable to open index file." << EOL;
      return FALSE;
   }
   if (fseek(fh,sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem)*Lists,SEEK_SET) != 0) {
      Log.Level(LOGE) << "Unable to seek in index file." << EOL;
      return FALSE;
   }
   if (!LoadOneIndex(fh,Index)) {
      Log.Level(LOGE) << "Unable to load index file." << EOL;
      return FALSE;
   }
   fclose(fh);
   return TRUE;
}

Nch *NodeLists::Srch(Nch *Addr, unsigned int Number) {
   if (Addr == NULL) return NULL;
   while (Addr->Number != (unsigned int)-1) {
      if ((Addr->Number & 0xffff) == (Number & 0xffff)) {
         return Addr;
      }
      Addr++;
   }
   return NULL;
}

unsigned int NodeLists::ExistInNodelist(FA const &f) {
Nch *tmt;
// if nodelists turned off or need say that node exist then return to any 
// adress - A_HOST
// in another part of this programm compare return of this
// function only with (-1).
   if (NodelistTurnOff) {
      return (A_HOST);
   }
   tmt = Srch(Index,f.Zone() & 0xffff);
   if (tmt == NULL) return ((SoftCheckInNodelists == FALSE) ? (unsigned int)-1 : A_HOST);

   tmt = tmt->Sub;
   tmt = Srch(tmt,f.Net() & 0xffff);
   if (tmt == NULL) return ((SoftCheckInNodelists == FALSE) ? (unsigned int)-1 : A_HOST);
   if (f.Node() == 0) {
      return A_HOST;
   }
   tmt = tmt->Sub;
   tmt = Srch(tmt,f.Node() & 0xffff);
   if (tmt == NULL) {
      return (unsigned int)-1;
   }
   if ((f.Point() & 0xffff) != 0) {
      switch (CheckPoints) {
         case CHECKPNT_HARD:
            if (Srch(tmt->Sub,f.Point() & 0xffff) == NULL) {
               return (unsigned int)-1;
            }
            break;
         case CHECKPNT_SOFT:
            if (tmt->Sub == NULL) break;
            if (tmt->Sub->Number == (unsigned int)-1) break;
            if (Srch(tmt->Sub,f.Point() & 0xffff) == NULL) return (unsigned int)-1;
            break;
         case CHECKPNT_NEVER:
            break;
      }
   }
   return tmt->Number;
}

unsigned int NodeLists::GetFlags(FA const &f) {
Nch *tmt;
   if (NodelistTurnOff) {
      return (A_NONE);
   }
   tmt = Srch(Index,f.Zone() & 0xffff);
   if (tmt == NULL) return A_NONODE;

   tmt = tmt->Sub;
   tmt = Srch(tmt,f.Net() & 0xffff);
   if (tmt == NULL) return A_NONODE;
   if (f.Node() == 0) {
      return A_HOST;
   }
   tmt = tmt->Sub;
   tmt = Srch(tmt,f.Node() & 0xffff);
   if (tmt == NULL) {
      return A_NONODE;
   }
   return tmt->Number;
}

void SayNodelistFlags(FA const &f) {
unsigned int i;
   i = Ndl.GetFlags(f);
   if (i == A_NONODE) {
      Log.Level(LOGD) << "NONODE";
      return;
   }
   i &= A_MASK;
   if (i == A_DOWN) Log.Level(LOGD) << "DOWN";
   if (i == A_HOLD) Log.Level(LOGD) << "HOLD";
   if (i == A_HUB) Log.Level(LOGD) << "HUB";
   if (i == A_HOST) Log.Level(LOGD) << "HOST";
   if (i == A_PVT) Log.Level(LOGD) << "PVT";
   if (i == A_REGION) Log.Level(LOGD) << "REGION";
}

int NodeLists::InSubHubs(FA const &Addr, FA const &Mask) {
Nch *tmt;
int Existing;
// Если узел используемый в качестве маски есть в нодлисте и он хаб или хост
// то проверяем адрес на наличие у него в подхабнике. Иначе - проверяем поинт.
// Ну и еще некоторые заморочки с регионами. Будь они трижды неладны в месте
// с теми, кто придумывал фидошные стандарты.

   Existing = (ExistInNodelist(Addr) == (unsigned int) -1 ? FALSE : TRUE);
// Node is equal Mask?
   if ((Addr.Zone() & 0xffff) == (Mask.Zone() & 0xffff) &&
       (Addr.Net() & 0xffff) == (Mask.Net() & 0xffff) &&
       (Addr.Node() & 0xffff) == (Mask.Node() & 0xffff)) {
      if ((Addr.Point() & 0xffff) != 0) {
         return (Existing);
      }
      return TRUE;
   }
   if (Existing == FALSE) return FALSE;

// Node in the same zone?
   if ((Addr.Zone() & 0xffff) != (Mask.Zone() & 0xffff)) return FALSE;

// Search zone of mask.
   tmt = Srch(Index,Mask.Zone() & 0xffff);
   if (tmt == NULL) {
//      Log.Level(LOGD) << "Zone " << (Mask.Zone() & 0xffff) << " missing" << EOL;
      return FALSE;
   }
   tmt = tmt->Sub;
   if (tmt == NULL) {
//      Log.Level(LOGD) << "Zone " << (Mask.Zone() & 0xffff) << " Empty" << EOL;
      return FALSE;
   }

// Search Net of mask.
   tmt = Srch(tmt,Mask.Net() & 0xffff);
   if (tmt == NULL) {
//      Log.Level(LOGD) << "Net " << (Mask.Zone() & 0xffff) << ":" << (Mask.Net() & 0xffff) << " missing" << EOL;
      return FALSE;
   }
   if ((tmt->Number & A_MASK) == A_REGION) {
      if ((Mask.Node() & 0xffff) == 0) {
         // Mask to region. Search net.
         while (tmt->Number != (unsigned int) -1) {
            if ((tmt->Number & 0xffff) == (Addr.Net() & 0xffff)) {
               // Net is Found. Search node.
               tmt = tmt->Sub;
               if (tmt == NULL) return FALSE;
               if ((Addr.Node() & 0xffff) == 0) return TRUE;
               if (Srch(tmt,Addr.Node() & 0xffff) == NULL) {
                  return FALSE;
               } else {
                  return TRUE;
               }
            }
            tmt++;
            if ((tmt->Number & A_MASK) == A_REGION) {
               // Next region started, but net not found...
               return FALSE;
            }
         }
         return FALSE;
      }
   }
   if ((Addr.Net() & 0xffff) != (Mask.Net() & 0xffff)) {
//      Log.Level(LOGD) << "Node " << Addr << " not equal to mask " << Mask << " and mask is not a regional mask." << EOL;
      return FALSE;
   }
   tmt = tmt->Sub;
   if (tmt == NULL) {
//      Log.Level(LOGD) << "Node " << Addr << " not equal to mask " << Mask << " and no nodes in mask net." << EOL;
      return FALSE;
   }
   // if mask is a HOST, then start already founded. Continue search otherwise.   
   if ((Mask.Node() & 0xffff) != 0) {
      tmt = Srch(tmt,Mask.Node() & 0xffff);
      if (tmt == NULL) return FALSE;
      // if Mask is not a hub - leave.
      if ((tmt->Number & A_MASK) != A_HUB) return FALSE;
      tmt++;
   }
   // Ok. We hawe start of list. Search node to hub or end of list.
   while (tmt->Number != (unsigned int) -1 && (tmt->Number & A_MASK) != A_HUB) {
      if ((tmt->Number & 0xffff) == (Addr.Node() & 0xffff)) {
         return Existing;
      }
      tmt++;
   }
   return FALSE;
}      

int NodeLists::AddNodelist(char *tmt, int TempZone) {
char Buff[512];
struct stat NdlStat;
NodeListElem *Elem;

   memset(Buff,0,512);
   if (strlen(tmt) == 0) {
      yyerror("Missed parameter: nodelist name or mask.");
      return (-1);
   }
   if ((TempZone <= 0) && (TempZone != -3)) {
      yyerror("Zone number must be between 1 and 65535");
      return (-1);
   }
   if (TempZone != -3) {
      StartZone = TempZone;
   } else {
      StartZone = 0;
   }

   if (FindNodelist(tmt,Buff) != 0) {
      return (-1);
   }
   if (stat(Buff,&NdlStat) != 0) {
      Log.Level(LOGE) << "Unable to get information about nodelist '" << Buff << "'." << EOL;
      return (-1);
   }
   Elem = (NodeListElem *) malloc(sizeof(NodeListElem)+100);
   CheckMem((char *)Elem);
   memcpy(Elem->Name,Buff,512);
   Elem->Time = NdlStat.st_mtime;
   Elem->StartZone = StartZone;
   Log.Level(LOGD) << "NTime : " << (int) Elem->Time << EOL;
   if (NList == NULL) {
      NList = Elem;
      Lists = 1;
   } else {
      tmt = (char *) realloc(NList, (Lists + 1) * sizeof(NodeListElem));
      CheckMem((char *)tmt);
      NList = (NodeListElem *)tmt;
      memcpy(NList + Lists,Elem,sizeof(NodeListElem));
      Lists++;
      free(Elem);
   }
   return (0);
}

// --------------------------------------------------------------------

int SetMaxNodelistAge(int tmt) {

   if (MaxNodelistAge != (time_t)-1) {
      yyerror("Max nodelists age already set.");
      return(-1);
   }
   if (tmt < 1) {
      yyerror("Parameter must be a number great than 0.");
      return (-1);
   }
   MaxNodelistAge = tmt * 24 * 60 * 60;
   return 0;
}

// --------------------------------------------------------------------

int SetNodelist(char *tmt, int TempZone) {
   return Ndl.AddNodelist(tmt,TempZone);
}


syntax highlighted by Code2HTML, v. 0.9.1