1 /*
2 * RNtrack - FTN message tracker/router
3 *
4 * nodelist.cpp - Work with nodelists
5 *
6 * Copyright (c) 2003-2005 Alex Soukhotine, 2:5030/1157
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * $Id: nodelist.cpp 257 2020-01-30 18:19:33Z dukelsky $
14 */
15
16 #ifndef __GNUC__
17 #include <io.h>
18 #include <direct.h>
19 #ifdef _MSC_VER
20 #include "dirent/dirent.h"
21 #endif
22 #else
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <dirent.h>
26 #include <sys/stat.h>
27 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32
33 #include "compiler.h"
34 #include "constant.hpp"
35 #include "vars.hpp"
36 #include "configure.hpp"
37 #include "log.hpp"
38 #include "nodelist.hpp"
39 #include "age.hpp"
40 #include "utils.hpp"
41
42 char * NodelistPath;
43
44 static bool NodelistTurnOff = FALSE;
45 static int NodelistLineNum = 0;
46
47 static const char mErrReadIndex[] = "Error reading index file.";
48 static const char mNdlChanged[] =
49 "Some nodelists have changed. Recompilation is necessary.";
50 static const char mIndNFound[] =
51 "Index file not found. Create new index file.";
52 static const char mErrNdlMustFull[] =
53 "Error: You must define default Zone for Regional and Network versions of nodelist.";
54 static const char mErrNdlFormat[] = "Incorrect nodelist format.";
55
56 #define ErrReadIndex Log.Level(LOGE) << mErrReadIndex << EOL
57 #define IndexChanged Log.Level(LOGI) << mNdlChanged << EOL
58 #define IndexNotFound Log.Level(LOGI) << mIndNFound << EOL
59
60
61 typedef struct _Ntr
62 {
63 dword Number;
64 dword Attrib;
65 struct _Ntr * Next;
66 struct _Ntr * Sub;
67 } Ntr;
68
ErrNdlFormat(const char * m)69 void ErrNdlFormat(const char * m)
70 {
71 Log.Level(LOGE) << "Error in line " << NodelistLineNum << ", " <<
72 mErrNdlFormat << EOL;
73 Log.Level(LOGE) << m << EOL;
74 NodelistTurnOff = TRUE;
75 }
76
FindNodelist(char * Mask,char * Name)77 int FindNodelist(char * Mask, char * Name)
78 {
79 int maxext;
80 DIR * dd;
81 struct dirent * ff;
82 char NDir[512], Path[512], Fname[512];
83 char * tmt = NULL;
84 FILE * ft;
85
86 *Name = '\0';
87 Path[1] = '\0';
88 maxext = (-1);
89
90 GetFilePath(Path, Mask);
91
92 if(NodelistPath != NULL && *Path != PATHDELIMC
93 #ifndef __unix__
94 && Path[1] != ':'
95 #endif
96 )
97 {
98 strcpy(NDir, NodelistPath);
99 strcat(NDir, Path);
100 strcpy(Path, NDir);
101 }
102
103 GetFileName(Fname, Mask);
104
105 if(strchr(Fname, '*') || strchr(Fname, '?'))
106 {
107 if((dd = opendir(dirslashbug(Path))) != 0)
108 {
109 while((ff = readdir(dd)) != NULL)
110 {
111 if(fsCompareName(ff->d_name, Fname) != 0 &&
112 (tmt = strrchr(ff->d_name, '.')) != NULL)
113 {
114 tmt++;
115
116 if(StrIsNum(tmt) && atoi(tmt) > maxext)
117 {
118 maxext = atoi(tmt);
119 strcpy(Name, Path);
120 strcat(Name, ff->d_name);
121 }
122 }
123 }
124 closedir(dd);
125 }
126 else
127 {
128 yyerror("Nodelist not found.");
129 return -1;
130 }
131 }
132 else
133 {
134 strcpy(Name, Path);
135 strcat(Name, Fname);
136 }
137
138 if(*Name == '\0')
139 {
140 yyerror("Nodelist not found.");
141 return -1;
142 }
143
144 if((ft = fopen(Name, "rt")) == NULL)
145 {
146 yyerror("Unable to open Nodelist.");
147 return -1;
148 }
149 else
150 {
151 fclose(ft);
152 }
153
154 return 0;
155 } // FindNodelist
156
157 // ------------------- Compile and write one nodelist --------------------
158
159 static FILE * nh;
160 static int CurrentNet = -1;
161 static int CurrentZone = -1;
162 static Ntr ** CurrentHub = NULL;
163 static Ntr * _pNodeList = NULL;
164 static bool PointListMode = FALSE;
165 static Ntr * BossNode = NULL;
166
OpenNodelist(char * FName)167 bool OpenNodelist(char * FName)
168 {
169 nh = fopen(FName, "rt");
170
171 if(nh == NULL)
172 {
173 Log.Level(LOGE) << "Unable to open nodelist '" << FName << "'" << EOL;
174 return FALSE;
175 }
176
177 return TRUE;
178 }
179
CloseNodelist(void)180 bool CloseNodelist(void)
181 {
182 return fclose(nh) == 0;
183 }
184
ReadNdlLine(char * Buff,int Count)185 int ReadNdlLine(char * Buff, int Count)
186 {
187 char * tmt;
188
189 Buff[0] = ';';
190
191 while(Buff[0] == ';')
192 {
193 NodelistLineNum++;
194
195 if(fgets(Buff, Count, nh) == NULL)
196 {
197 Buff[0] = '\0';
198 return -1;
199 }
200
201 tmt = strchr(Buff, '\n');
202
203 if(tmt != NULL)
204 {
205 *tmt = '\0';
206 }
207
208 tmt = strchr(Buff, '\r');
209
210 if(tmt != NULL)
211 {
212 *tmt = '\0';
213 }
214
215 tmt = strchr(Buff, (char)0x1a);
216
217 if(tmt != NULL)
218 {
219 *tmt = '\0';
220 }
221
222 if(strlen(Buff) == 0)
223 {
224 Buff[0] = ';';
225 }
226 }
227 return (int)strlen(Buff);
228 } // ReadNdlLine
229
ExistByNumber(Ntr * Addr,unsigned int Number)230 Ntr * ExistByNumber(Ntr * Addr, unsigned int Number)
231 {
232 Ntr * tmt;
233
234 tmt = Addr;
235
236 while(tmt != NULL)
237 {
238 if((tmt->Number & 0xffff) == (Number & 0xffff))
239 {
240 return tmt;
241 }
242
243 tmt = tmt->Next;
244 }
245 return NULL;
246 }
247
GetCurrentZone(void)248 Ntr * GetCurrentZone(void)
249 {
250 return ExistByNumber(_pNodeList, CurrentZone);
251 }
252
GetCurrentNet(void)253 Ntr * GetCurrentNet(void)
254 {
255 Ntr * tmt;
256
257 if((tmt = GetCurrentZone()) != NULL)
258 {
259 if(tmt->Sub != NULL)
260 {
261 return ExistByNumber(tmt->Sub, CurrentNet);
262 }
263 else
264 {
265 return NULL;
266 }
267 }
268 else
269 {
270 return NULL;
271 }
272 }
273
DelDupNode(unsigned int Node)274 bool DelDupNode(unsigned int Node)
275 {
276 // Remove a node from the list if it is already present there but check
277 // whether it is a hub. If it is a hub then do not remove it. If the node
278 // is also a hub now then we set the current hub pointer to the hub.
279 // Return TRUE if the node was successfully removed
280 // or it was absent in the list
281 // FALSE if the node was in the list as a hub and it is a hub now.
282 Ntr * tr;
283
284 tr = GetCurrentNet();
285 assert(tr != NULL);
286 tr = tr->Sub;
287
288 while(tr != NULL)
289 {
290 if((tr->Number & 0xffff) == (Node & 0xffff))
291 {
292 if((tr->Number & A_MASK) != A_HUB)
293 {
294 tr->Number = (unsigned int)-1;
295 return TRUE;
296 }
297 else if(((tr->Number & A_MASK) == A_HUB) &&
298 ((Node & A_MASK) == A_HUB))
299 {
300 // Log.Level(LOGD) << "Two nodes? " << tr->Number << " " << Node <<
301 // EOL;
302 // Log.Level(LOGD) << "Net: " << CurrentZone << ":" << CurrentNet <<
303 // EOL;
304 CurrentHub = &(tr->Next);
305 return FALSE;
306 }
307 }
308
309 tr = tr->Next;
310 }
311 return TRUE;
312 } // DelDupNode
313
AddElemToList(Ntr ** Addr,unsigned int Number)314 void AddElemToList(Ntr ** Addr, unsigned int Number)
315 {
316 Ntr * tmt;
317
318 tmt = (Ntr *)malloc(sizeof(Ntr));
319 CheckMem((char *)tmt);
320 memset(tmt, 0, sizeof(Ntr));
321 tmt->Number = Number;
322
323 while((*Addr) != 0)
324 {
325 Addr = &((*Addr)->Next);
326 }
327 *Addr = tmt;
328 }
329
AddNetToList(void)330 void AddNetToList(void)
331 {
332 Ntr * tr;
333
334 tr = GetCurrentZone();
335 assert(tr != NULL);
336 AddElemToList(&(tr->Sub), CurrentNet);
337 }
338
AddZoneToList(void)339 void AddZoneToList(void)
340 {
341 AddElemToList(&_pNodeList, CurrentZone);
342 AddNetToList();
343 }
344
_SetCurrentZone(unsigned int Zone)345 void _SetCurrentZone(unsigned int Zone)
346 {
347 CurrentZone = Zone;
348 CurrentNet = Zone;
349 CurrentHub = NULL;
350
351 if(GetCurrentZone() == 0)
352 {
353 AddZoneToList();
354 }
355 }
356
_AddNode(unsigned int Node)357 void _AddNode(unsigned int Node)
358 {
359 // Add a node to the list. If it is absent there then just prepend it
360 // to the CurrentHub list. If there is no CurrentHub list then we add
361 // the node as usual. If the node is already present in the list
362 // then do nothing.
363 Ntr * tr, * tmt;
364
365 if(DelDupNode(Node) == TRUE)
366 {
367 if(CurrentHub != NULL)
368 {
369 tmt = (Ntr *)malloc(sizeof(Ntr));
370 CheckMem((char *)tmt);
371 memset(tmt, 0, sizeof(Ntr));
372 tmt->Number = Node;
373 tr = (*CurrentHub)->Next;
374 (*CurrentHub)->Next = tmt;
375 tmt->Next = tr;
376 }
377 else
378 {
379 tr = GetCurrentNet();
380 assert(tr != NULL);
381 AddElemToList(&(tr->Sub), Node);
382 }
383 }
384 } // _AddNode
385
_SetCurrentNet(unsigned int Net)386 void _SetCurrentNet(unsigned int Net)
387 {
388 CurrentNet = Net;
389 CurrentHub = NULL;
390
391 if(GetCurrentNet() == NULL)
392 {
393 AddNetToList();
394 }
395 }
396
_SetCurrentBoss(char * tmt)397 bool _SetCurrentBoss(char * tmt)
398 {
399 FA f;
400 Ntr * tp;
401
402 BossNode = NULL;
403 f.Parse((const char * &)tmt);
404
405 if(f.Masked())
406 {
407 return FALSE;
408 }
409
410 if((f.Point() & 0xffff) != 0)
411 {
412 return FALSE;
413 }
414
415 tp = ExistByNumber(_pNodeList, f.Zone());
416
417 if(tp == NULL)
418 {
419 return TRUE;
420 }
421
422 if(tp->Sub == NULL)
423 {
424 return TRUE;
425 }
426
427 tp = ExistByNumber(tp->Sub, f.Net());
428
429 if(tp == NULL)
430 {
431 return TRUE;
432 }
433
434 if(tp->Sub == NULL)
435 {
436 return TRUE;
437 }
438
439 tp = ExistByNumber(tp->Sub, f.Node());
440
441 if(tp == NULL)
442 {
443 return TRUE;
444 }
445
446 BossNode = tp;
447 return TRUE;
448 } // _SetCurrentBoss
449
_AddPoint(int pnt)450 void _AddPoint(int pnt)
451 {
452 if(BossNode != NULL)
453 {
454 AddElemToList(&(BossNode->Sub), pnt);
455 }
456 }
457
ParseNodeLine(char * tmt)458 bool ParseNodeLine(char * tmt)
459 {
460 unsigned int tmp;
461
462 // DumpNdl();
463 // fprintf(stderr,"%s\n",tmt);
464 // printf("'%s'\n",tmt);
465 switch(*tmt)
466 {
467 case 'B': // Boss record. Pointlist?
468 case 'b':
469 PointListMode = TRUE;
470
471 if(_SetCurrentBoss(tmt + 5) != TRUE)
472 {
473 ErrNdlFormat("Bad BOSS record");
474 return FALSE;
475 }
476
477 break;
478
479 case 'Z': // Zone record
480 PointListMode = FALSE;
481 _SetCurrentZone(atoi(tmt + 5));
482 break;
483
484 case 'R': // Region record
485 PointListMode = FALSE;
486
487 if(CurrentZone == 0)
488 {
489 Log.Level(LOGE) << mErrNdlMustFull << EOL;
490 return FALSE;
491 }
492
493 tmp = atoi(tmt + 7);
494 tmp |= A_REGION;
495 _SetCurrentNet(tmp); // Region is a strange type of net.
496 break;
497
498 case 'H': // Host, Hub, or Hold record
499
500 switch(*(tmt + 2))
501 {
502 case 's': // Host
503 PointListMode = FALSE;
504
505 if(CurrentZone == 0)
506 {
507 Log.Level(LOGE) << mErrNdlMustFull << EOL;
508 return FALSE;
509 }
510
511 _SetCurrentNet(atoi(tmt + 5));
512 break;
513
514 case 'b': // Hub
515
516 if(PointListMode == TRUE)
517 {
518 ErrNdlFormat("HUB record in pointlist!");
519 return FALSE;
520 }
521
522 if(CurrentZone == 0)
523 {
524 ErrNdlFormat("HUB record without Zone record!");
525 return FALSE;
526 }
527
528 tmp = atoi(tmt + 4);
529 tmp |= A_HUB;
530 _AddNode(tmp);
531 break;
532
533 case 'l': // Hold
534
535 if(PointListMode == TRUE)
536 {
537 ErrNdlFormat("Node or point with HOLD flag!");
538 return FALSE;
539 }
540
541 if(CurrentZone == 0)
542 {
543 ErrNdlFormat("HOLD record without Zone record!");
544 return FALSE;
545 }
546
547 tmp = atoi(tmt + 5);
548 tmp |= A_HOLD;
549 _AddNode(tmp);
550 break;
551
552 default:
553 ErrNdlFormat("Unknown format!");
554 return FALSE;
555 } // switch
556 break;
557
558 case ',': // Simple node
559
560 if(PointListMode == TRUE)
561 {
562 _AddPoint(atoi(tmt + 1));
563 }
564 else
565 {
566 if(CurrentZone == 0)
567 {
568 ErrNdlFormat("Node record without Zone record!");
569 return FALSE;
570 }
571
572 if(CurrentNet == 0)
573 {
574 ErrNdlFormat("Node record without Host/Region record!");
575 return FALSE;
576 }
577
578 _AddNode(atoi(tmt + 1));
579 }
580
581 break;
582
583 case 'D': // Down
584
585 if(PointListMode == TRUE)
586 {
587 ErrNdlFormat("DOWN record in pointlist!");
588 return FALSE;
589 }
590
591 if(CurrentZone == 0)
592 {
593 ErrNdlFormat("Node Down record without Zone record!");
594 return FALSE;
595 }
596
597 if(CurrentNet == 0)
598 {
599 ErrNdlFormat("Node Down record without Host/Region record!");
600 return FALSE;
601 }
602
603 tmp = atoi(tmt + 5);
604 tmp |= A_DOWN;
605 _AddNode(tmp);
606 break;
607
608 case 'P': // Pvt or Point
609 case 'p':
610
611 switch(*(tmt + 1))
612 {
613 case 'v': // Pvt
614
615 if(PointListMode == TRUE)
616 {
617 ErrNdlFormat("PVT record in pointlist!");
618 return FALSE;
619 }
620
621 if(CurrentZone == 0)
622 {
623 ErrNdlFormat("Node Pvt record without Zone record!");
624 return FALSE;
625 }
626
627 if(CurrentNet == 0)
628 {
629 ErrNdlFormat("Node Pvt record without Host record!");
630 return FALSE;
631 }
632
633 tmp = atoi(tmt + 4);
634 tmp |= A_PVT;
635 _AddNode(tmp);
636 break;
637
638 case 'o': // Point
639
640 if(PointListMode == FALSE)
641 {
642 ErrNdlFormat("POINT record not in pointlist!");
643 return FALSE;
644 }
645
646 tmp = atoi(tmt + 6);
647 _AddPoint(tmp);
648 break;
649
650 default:
651 ErrNdlFormat("Unknown format!");
652 return FALSE;
653 } // switch
654 break;
655
656 default: // No one? Hmm...
657 ErrNdlFormat("Unknown format!");
658 return FALSE;
659 } // switch
660 return TRUE;
661 } // ParseNodeLine
662
ParseOneNodelist(NodeListElem * Elem)663 bool ParseOneNodelist(NodeListElem * Elem)
664 {
665 char Buff[512];
666
667 Log.Level(LOGI) << "Compile nodelist '" << Elem->Name << "'";
668
669 if(Elem->StartZone != 0)
670 {
671 _SetCurrentZone(Elem->StartZone); // Set address to Zone:Zone/0; 1st
672 // line of the nodelist should be
673 // "Host" or "Region"
674 Log.Level(LOGI) << ", start zone number is " << Elem->StartZone << EOL;
675 }
676 else
677 {
678 CurrentZone = 0;
679 }
680
681 CurrentNet = 0;
682 Log.Level(LOGI) << " ..." << EOL;
683
684 if(!OpenNodelist(Elem->Name))
685 {
686 return FALSE;
687 }
688
689 NodelistLineNum = 0;
690
691 while(ReadNdlLine(Buff, 510) > 0)
692 {
693 if(!ParseNodeLine(Buff))
694 {
695 CloseNodelist();
696 return FALSE;
697 }
698 }
699 CloseNodelist();
700 Log.Level(LOGI) << "Done" << EOL;
701 return TRUE;
702 } // ParseOneNodelist
703
704 // ------------------------------------------------------
705 // Flush compiled nodelist to disk
706 // ------------------------------------------------------
707
708 #if 0
709 void PrintNtr(Ntr * tmt, char * Buff)
710 {
711 int len;
712 Ntr * tmt2;
713
714 len = strlen(Buff);
715 tmt2 = tmt;
716
717 while(tmt != NULL)
718 {
719 sprintf(Buff + len, "%d ", tmt->Number & 0xffff);
720 PrintNtr(tmt->Sub, Buff);
721 tmt = tmt->Next;
722 }
723
724 if(tmt2 == NULL)
725 {
726 printf("%s\n", Buff);
727 }
728 }
729 #endif
730
ElementsInList(Ntr * Addr)731 int ElementsInList(Ntr * Addr)
732 {
733 Ntr * tmt;
734 int Elems;
735
736 tmt = Addr;
737 Elems = 0;
738
739 while(tmt != NULL)
740 {
741 if(tmt->Number != (unsigned int)-1)
742 {
743 Elems++;
744 }
745
746 tmt = tmt->Next;
747 }
748 return Elems;
749 }
750
SaveElements(FILE * fh,Ntr * Addr)751 bool SaveElements(FILE * fh, Ntr * Addr)
752 {
753 Ntr * tmt;
754
755 tmt = Addr;
756
757 while(tmt != NULL)
758 {
759 if(tmt->Number != (unsigned int)-1)
760 {
761 if(fwrite(&tmt->Number, sizeof(int), 1, fh) != 1)
762 {
763 Log.Level(LOGE) << "Unable to write body of Elems" << EOL;
764 return FALSE;
765 }
766 }
767
768 tmt = tmt->Next;
769 }
770 return TRUE;
771 }
772
FlushElements(FILE * fh,Ntr * tmt)773 bool FlushElements(FILE * fh, Ntr * tmt)
774 {
775 int Elems;
776
777 Elems = ElementsInList(tmt);
778
779 if(fwrite(&Elems, sizeof(Elems), 1, fh) != 1)
780 {
781 Log.Level(LOGE) << "Unable to write Elems" << EOL;
782 return FALSE;
783 }
784
785 if(!SaveElements(fh, tmt))
786 {
787 return FALSE;
788 }
789
790 return TRUE;
791 }
792
FlushSubElements(FILE * fh,Ntr * tmt)793 bool FlushSubElements(FILE * fh, Ntr * tmt)
794 {
795 if(FlushElements(fh, tmt) == FALSE)
796 {
797 return FALSE;
798 }
799
800 while(tmt != NULL)
801 {
802 if(tmt->Number != (unsigned int)-1)
803 {
804 if(FlushSubElements(fh, tmt->Sub) == FALSE)
805 {
806 return FALSE;
807 }
808 }
809
810 tmt = tmt->Next;
811 }
812 return TRUE;
813 }
814
FlushElementsTree(FILE * fh)815 bool FlushElementsTree(FILE * fh)
816 {
817 return FlushSubElements(fh, _pNodeList);
818 }
819
820 // ------------------------------------------------------
821 // Free parser memory
822 // ------------------------------------------------------
823
FreeSubElements(Ntr * tmt)824 void FreeSubElements(Ntr * tmt)
825 {
826 Ntr * tmt2;
827
828 if(tmt == NULL)
829 {
830 return;
831 }
832
833 while(tmt != NULL)
834 {
835 FreeSubElements(tmt->Sub);
836 tmt2 = tmt->Next;
837 free(tmt);
838 tmt = tmt2;
839 }
840 }
841
FreeParserMem(void)842 void FreeParserMem(void)
843 {
844 FreeSubElements(_pNodeList);
845 _pNodeList = NULL;
846 }
847
848 // ------------------------------------------------------
849
FlushNodelist(FILE * fh)850 bool FlushNodelist(FILE * fh)
851 {
852 #if 0
853 char Buff[1024];
854 PrintNtr(_pNodeList, Buff);
855 #endif
856
857 if(FlushElementsTree(fh) == FALSE)
858 {
859 return FALSE;
860 }
861
862 FreeParserMem();
863 return TRUE;
864 }
865
866 // ------------------------- class NodeLists -----------------------
867
NodeLists()868 NodeLists::NodeLists()
869 {
870 NList = NULL;
871 Lists = 0;
872 StartZone = 0;
873 IndexName = strdup(DefaultIndex);
874 }
875
~NodeLists()876 NodeLists::~NodeLists()
877 {
878 if(NList != NULL)
879 {
880 free(NList);
881 NList = NULL;
882 }
883
884 Lists = 0;
885
886 if(IndexName != NULL)
887 {
888 free(IndexName);
889 IndexName = NULL;
890 }
891 }
892
893 #if 0
894 void PrintNch(Nch * tmt, char * Buff)
895 {
896 int i;
897 int len;
898
899 len = strlen(Buff);
900 i = 0;
901
902 while(tmt[i].Number != -1)
903 {
904 sprintf(Buff + len, "%d ", tmt[i].Number & 0xffff);
905 PrintNch(tmt[i].Sub, Buff);
906 i++;
907 }
908
909 if(tmt[0].Number == -1)
910 {
911 printf("%s\n", Buff);
912 }
913 }
914 #endif
915
Print(void)916 void NodeLists::Print(void)
917 {
918 #if 0
919 int tmc;
920 char Buff[1024];
921 NodeListElem * tmt;
922 printf("Index file: '%s'\n", IndexName);
923 printf("Total nodelists: '%d'\n", Lists);
924 tmt = NList;
925 tmc = Lists;
926
927 while(tmc != 0)
928 {
929 printf("Nodelist name: '%s'\n", tmt->Name);
930 printf("Nodelist Date: '%d'\n", tmt->Time);
931 tmc--;
932 tmt++;
933 }
934 Buff[0] = '\0';
935 PrintNch(Index, Buff);
936 #endif
937 }
938
IndexFile(char * File)939 void NodeLists::IndexFile(char * File)
940 {
941 if(IndexName != NULL)
942 {
943 free(IndexName);
944 IndexName = NULL;
945 }
946
947 IndexName = strdup(File);
948 }
949
CompileNeed(void)950 bool NodeLists::CompileNeed(void)
951 {
952 FILE * fh;
953 unsigned int tmp;
954 NodeListElem Elem;
955
956 // 1 - does index file exist?
957 if(access(IndexName, F_OK) != 0)
958 {
959 IndexNotFound;
960 return TRUE;
961 }
962
963 // 2 - can we open it?
964 fh = fopen(IndexName, "r+b");
965
966 if(fh == NULL)
967 {
968 Log.Level(LOGE) << "Unable to open index file." << EOL;
969 return TRUE;
970 }
971
972 // 3 - can we read the signature from it?
973 if(fread(&tmp, sizeof(tmp), 1, fh) != 1)
974 {
975 ErrReadIndex;
976 fclose(fh);
977 return TRUE;
978 }
979
980 // 4 - is signature correct?
981 if(tmp != NdlSign)
982 {
983 Log.Level(LOGE) <<
984 "Index file from the old version of RNtrack. Recompilation is necessary."
985 <<
986 EOL;
987 fclose(fh);
988 return TRUE;
989 }
990
991 // 5 - can we read nodelist count?
992 if(fread(&tmp, sizeof(tmp), 1, fh) != 1)
993 {
994 ErrReadIndex;
995 fclose(fh);
996 return TRUE;
997 }
998
999 // 6 - is the count of nodelists the same?
1000 if(tmp != (unsigned int)Lists)
1001 {
1002 IndexChanged;
1003 fclose(fh);
1004 return TRUE;
1005 }
1006
1007 // 7 - have nodelists not changed?
1008 for(unsigned int i = 0; i < tmp; i++)
1009 {
1010 if(fread(&Elem, sizeof(Elem), 1, fh) != 1)
1011 {
1012 ErrReadIndex;
1013 fclose(fh);
1014 return TRUE;
1015 }
1016
1017 if((MaxNodelistAge != (time_t)-1) && (NodelistTurnOff != TRUE))
1018 {
1019 if((time(NULL) - (NList + i)->Time) > MaxNodelistAge)
1020 {
1021 Log.Level(LOGW) << "Nodelist '" <<
1022 (NList + i)->Name << "' too old." << EOL;
1023 Log.Level(LOGI) <<
1024 "Checking existance in nodelists turned off." << EOL;
1025 NodelistTurnOff = TRUE;
1026 Log.Level(LOGD) << "Time : " << (int)(time(NULL)) << EOL;
1027 Log.Level(LOGD) << "NTime : " << (int)(NList + i)->Time << EOL;
1028 Log.Level(LOGD) << "Age : " <<
1029 (int)(time(NULL) - (NList + i)->Time) << EOL;
1030 Log.Level(LOGD) << "MaxAge: " << (int)MaxNodelistAge << EOL;
1031 }
1032 }
1033
1034 if(memcmp(&Elem, NList + i, sizeof(Elem)) != 0)
1035 {
1036 Log.Level(LOGD) <<
1037 "NodeLists::CompileNeed. memcmp failed. Nodelist changed." << EOL;
1038 Log.Level(LOGD) << "--Information should be:" << EOL;
1039 Log.Level(LOGD) << "NName : " << (NList + i)->Name << EOL;
1040 Log.Level(LOGD) << "NTime : " << (int)(NList + i)->Time << EOL;
1041 Log.Level(LOGD) << "Zone : " <<
1042 (int)(NList + i)->StartZone << EOL;
1043 Log.Level(LOGD) << "--Information in index file:" << EOL;
1044 Log.Level(LOGD) << "NName : " << Elem.Name << EOL;
1045 Log.Level(LOGD) << "NTime : " << Elem.Time << EOL;
1046 Log.Level(LOGD) << "Zone : " << Elem.StartZone << EOL;
1047 IndexChanged;
1048 fclose(fh);
1049 return TRUE;
1050 }
1051 }
1052 fclose(fh);
1053 return FALSE;
1054 } // CompileNeed
1055
Names(char * Buff)1056 char * NodeLists::Names(char * Buff)
1057 {
1058 NodeListElem * Elem;
1059 int i;
1060 char * tmt;
1061
1062 Buff[0] = '\0';
1063
1064 for(Elem = NList, i = 0; i < Lists; Elem++, i++)
1065 {
1066 tmt = strrchr(Elem->Name, PATHDELIMC);
1067
1068 if(tmt != NULL)
1069 {
1070 tmt++;
1071 }
1072 else
1073 {
1074 tmt = Elem->Name;
1075 }
1076
1077 strcat(Buff, tmt);
1078
1079 if(i + 1 < Lists)
1080 {
1081 strcat(Buff, ", ");
1082 }
1083 }
1084 return Buff;
1085 } // Names
1086
Compile(void)1087 bool NodeLists::Compile(void)
1088 {
1089 FILE * fh;
1090 char * tmt;
1091 int tmp;
1092 int i;
1093
1094 // 1 - Write new _empty_ header.
1095 fh = fopen(IndexName, "wb");
1096
1097 if(fh == NULL)
1098 {
1099 Log.Level(LOGE) << "Unable to create index file." << EOL;
1100 return FALSE;
1101 }
1102
1103 tmt = (char *)malloc(
1104 sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem) * Lists);
1105 CheckMem(tmt);
1106 memset(tmt, '\0',
1107 sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem) * Lists);
1108
1109 if(fwrite(tmt, sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem) *
1110 Lists, 1, fh) != 1)
1111 {
1112 Log.Level(LOGE) <<
1113 "Unable to write temporary header to index file." << EOL;
1114 fclose(fh);
1115 free(tmt);
1116 return FALSE;
1117 }
1118
1119 free(tmt);
1120
1121 // 2 - Write compiled nodelists.
1122
1123 for(i = 0; i < Lists; i++)
1124 {
1125 if(!ParseOneNodelist(NList + i))
1126 {
1127 fclose(fh);
1128 return FALSE;
1129 }
1130 }
1131
1132 if(!FlushNodelist(fh))
1133 {
1134 fclose(fh);
1135 return FALSE;
1136 }
1137
1138 // 3 - Write new, true header.
1139
1140 if(fseek(fh, 0, SEEK_SET) != 0)
1141 {
1142 Log.Level(LOGE) <<
1143 "Unable to set pointer to the beginning of the index file." << EOL;
1144 fclose(fh);
1145 return FALSE;
1146 }
1147
1148 tmp = NdlSign;
1149
1150 if(fwrite(&tmp, sizeof(tmp), 1, fh) != 1)
1151 {
1152 Log.Level(LOGE) << "Unable to write a signature to the index file." <<
1153 EOL;
1154 fclose(fh);
1155 return FALSE;
1156 }
1157
1158 if(fwrite(&Lists, sizeof(Lists), 1, fh) != 1)
1159 {
1160 Log.Level(LOGE) <<
1161 "Unable to write a Nodelist counter to the index file." << EOL;
1162 fclose(fh);
1163 return FALSE;
1164 }
1165
1166 for(i = 0; i < Lists; i++)
1167 {
1168 if((MaxNodelistAge != (time_t)-1) && (NodelistTurnOff != TRUE))
1169 {
1170 if((time(NULL) - (NList + i)->Time) > MaxNodelistAge)
1171 {
1172 Log.Level(LOGW) << "Nodelist '" <<
1173 (NList + i)->Name << "' too old." << EOL;
1174 Log.Level(LOGI) <<
1175 "Checking of existence in nodelists is turned off." << EOL;
1176 NodelistTurnOff = TRUE;
1177 Log.Level(LOGD) << "Age : " <<
1178 (int)(time(NULL) - (NList + i)->Time) << EOL;
1179 Log.Level(LOGD) << "MaxAge: " << (int)MaxNodelistAge << EOL;
1180 }
1181 }
1182
1183 if(fwrite(NList + i, sizeof(NodeListElem), 1, fh) != 1)
1184 {
1185 Log.Level(LOGE) <<
1186 "Unable to write a Nodelist information to the index file." << EOL;
1187 fclose(fh);
1188 return FALSE;
1189 }
1190 }
1191 fclose(fh);
1192 return TRUE;
1193 } // Compile
1194
1195 #ifdef _DEBUG
1196
IPrint(Nch * Ind)1197 void IPrint(Nch * Ind)
1198 {
1199 if(Ind != NULL)
1200 {
1201 fprintf(stderr, "---%p>", Ind);
1202
1203 while(Ind->Number != -1)
1204 {
1205 fprintf(stderr, " %u", Ind->Number);
1206 Ind++;
1207 }
1208 fprintf(stderr, "\n");
1209 }
1210 }
1211
1212 #endif
LoadOneIndex(FILE * fh,Nch * & Ind)1213 bool NodeLists::LoadOneIndex(FILE * fh, Nch * & Ind)
1214 {
1215 unsigned int tmp;
1216 Nch * tmt;
1217 unsigned int i;
1218
1219 if(fread(&tmp, sizeof(tmp), 1, fh) != 1)
1220 {
1221 return FALSE;
1222 }
1223
1224 tmt = (Nch *)malloc((tmp + 1) * sizeof(Nch));
1225 CheckMem((char *)tmt);
1226 memset(tmt, 0, (tmp + 1) * sizeof(Nch));
1227 tmt[tmp].Number = (unsigned int)-1;
1228
1229 for(i = 0; i < tmp; i++)
1230 {
1231 if(fread(&tmt[i].Number, sizeof(int), 1, fh) != 1)
1232 {
1233 return FALSE;
1234 }
1235 }
1236 Ind = tmt;
1237
1238 for(i = 0; i < tmp; i++)
1239 {
1240 if(LoadOneIndex(fh, Ind[i].Sub) == FALSE)
1241 {
1242 return FALSE;
1243 }
1244 }
1245 return TRUE;
1246 } // LoadOneIndex
1247
Load(void)1248 bool NodeLists::Load(void)
1249 {
1250 FILE * fh;
1251
1252 if(!Enabled() || NodelistTurnOff)
1253 {
1254 return TRUE;
1255 }
1256
1257 if(CompileNeed())
1258 {
1259 if(!Compile())
1260 {
1261 if(NodelistTurnOff)
1262 {
1263 Log.Level(LOGN) << "Nodelists are not used." << EOL;
1264 return TRUE;
1265 }
1266
1267 return FALSE;
1268 }
1269 }
1270 else
1271 {
1272 if(NodelistTurnOff)
1273 {
1274 Log.Level(LOGN) << "Nodelists are not used." << EOL;
1275 return TRUE;
1276 }
1277 }
1278
1279 fh = fopen(IndexName, "rb");
1280
1281 if(fh == NULL)
1282 {
1283 Log.Level(LOGE) << "Unable to open index file." << EOL;
1284 return FALSE;
1285 }
1286
1287 if(fseek(fh, sizeof(NdlSign) + sizeof(int) + sizeof(NodeListElem) * Lists,
1288 SEEK_SET) != 0)
1289 {
1290 Log.Level(LOGE) << "Unable to seek in index file." << EOL;
1291 return FALSE;
1292 }
1293
1294 if(!LoadOneIndex(fh, Index))
1295 {
1296 Log.Level(LOGE) << "Unable to load index file." << EOL;
1297 return FALSE;
1298 }
1299
1300 fclose(fh);
1301 return TRUE;
1302 } // Load
1303
Srch(Nch * Addr,unsigned int Number)1304 Nch * NodeLists::Srch(Nch * Addr, unsigned int Number)
1305 {
1306 if(Addr == NULL)
1307 {
1308 return NULL;
1309 }
1310
1311 while(Addr->Number != (unsigned int)-1)
1312 {
1313 if((Addr->Number & 0xffff) == (Number & 0xffff))
1314 {
1315 return Addr;
1316 }
1317
1318 Addr++;
1319 }
1320 return NULL;
1321 }
1322
FindHub(FA const & f)1323 unsigned int NodeLists::FindHub(FA const & f)
1324 {
1325 Nch * tmt;
1326 unsigned int currHub = 0;
1327
1328 // If nodelists are switched off or it is necesary to say that a node exists
1329 // then return A_HOST for any address.
1330 // In other parts of this program compare return value of this
1331 // function only with (-1).
1332
1333 if(NodelistTurnOff)
1334 {
1335 return 0;
1336 }
1337
1338 if(ExistInNodelist(f) != (unsigned int)-1)
1339 {
1340 tmt = Srch(Index, f.Zone() & 0xffff);
1341
1342 if(tmt == NULL)
1343 {
1344 return 0;
1345 }
1346
1347 tmt = tmt->Sub;
1348 tmt = Srch(tmt, f.Net() & 0xffff);
1349
1350 if(tmt == NULL)
1351 {
1352 return 0;
1353 }
1354
1355 if(f.Node() == 0)
1356 {
1357 return 0;
1358 }
1359
1360 tmt = tmt->Sub;
1361
1362 while((tmt->Number & 0xffff) != (f.Node() & 0xffff))
1363 {
1364 if((tmt->Number & A_MASK) == A_HUB)
1365 {
1366 currHub = tmt->Number;
1367 }
1368
1369 if((((tmt + 1)->Number & 0xffff) == (f.Node() & 0xffff)) &&
1370 (((tmt + 1)->Number & A_MASK) == A_HUB))
1371 {
1372 currHub = (tmt + 1)->Number;
1373 }
1374
1375 tmt++;
1376 }
1377 }
1378
1379 if(currHub != 0)
1380 {
1381 currHub = currHub & 0xffff;
1382 }
1383
1384 return currHub;
1385 } // FindHub
1386
ExistInNodelist(FA const & f)1387 unsigned int NodeLists::ExistInNodelist(FA const & f)
1388 {
1389 Nch * tmt;
1390
1391 // If nodelists are switched off or it is necesary to say that a node exists
1392 // then return A_HOST for any address.
1393 // In other parts of this program compare return value of this
1394 // function only with (-1).
1395
1396 if(NodelistTurnOff)
1397 {
1398 return A_HOST;
1399 }
1400
1401 tmt = Srch(Index, f.Zone() & 0xffff);
1402
1403 if(tmt == NULL)
1404 {
1405 return (SoftCheckInNodelists == FALSE) ? (unsigned int)-1 : A_HOST;
1406 }
1407
1408 tmt = tmt->Sub;
1409 tmt = Srch(tmt, f.Net() & 0xffff);
1410
1411 if(tmt == NULL)
1412 {
1413 return (SoftCheckInNodelists == FALSE) ? (unsigned int)-1 : A_HOST;
1414 }
1415
1416 if(f.Node() == 0)
1417 {
1418 return A_HOST;
1419 }
1420
1421 tmt = tmt->Sub;
1422 tmt = Srch(tmt, f.Node() & 0xffff);
1423
1424 if(tmt == NULL)
1425 {
1426 return (unsigned int)-1;
1427 }
1428
1429 if((f.Point() & 0xffff) != 0)
1430 {
1431 switch(CheckPoints)
1432 {
1433 case CHECKPNT_HARD:
1434
1435 if(Srch(tmt->Sub, f.Point() & 0xffff) == NULL)
1436 {
1437 return (unsigned int)-1;
1438 }
1439
1440 break;
1441
1442 case CHECKPNT_SOFT:
1443
1444 if(tmt->Sub == NULL)
1445 {
1446 break;
1447 }
1448
1449 if(tmt->Sub->Number == (unsigned int)-1)
1450 {
1451 break;
1452 }
1453
1454 if(Srch(tmt->Sub, f.Point() & 0xffff) == NULL)
1455 {
1456 return (unsigned int)-1;
1457 }
1458
1459 break;
1460
1461 case CHECKPNT_NEVER:
1462 break;
1463 } // switch
1464 }
1465
1466 return tmt->Number;
1467 } // ExistInNodelist
1468
GetFlags(FA const & f)1469 unsigned int NodeLists::GetFlags(FA const & f)
1470 {
1471 Nch * tmt;
1472
1473 if(NodelistTurnOff)
1474 {
1475 return A_NONE;
1476 }
1477
1478 tmt = Srch(Index, f.Zone() & 0xffff);
1479
1480 if(tmt == NULL)
1481 {
1482 return A_NONODE;
1483 }
1484
1485 tmt = tmt->Sub;
1486 tmt = Srch(tmt, f.Net() & 0xffff);
1487
1488 if(tmt == NULL)
1489 {
1490 return A_NONODE;
1491 }
1492
1493 if(f.Node() == 0)
1494 {
1495 return A_HOST;
1496 }
1497
1498 tmt = tmt->Sub;
1499 tmt = Srch(tmt, f.Node() & 0xffff);
1500
1501 if(tmt == NULL)
1502 {
1503 return A_NONODE;
1504 }
1505
1506 return tmt->Number;
1507 } // GetFlags
1508
SayNodelistFlags(FA const & f)1509 void SayNodelistFlags(FA const & f)
1510 {
1511 unsigned int i;
1512
1513 i = Ndl.GetFlags(f);
1514
1515 if(i == A_NONODE)
1516 {
1517 Log.Level(LOGD) << "NONODE";
1518 return;
1519 }
1520
1521 i &= A_MASK;
1522
1523 if(i == A_DOWN)
1524 {
1525 Log.Level(LOGD) << "DOWN";
1526 }
1527
1528 if(i == A_HOLD)
1529 {
1530 Log.Level(LOGD) << "HOLD";
1531 }
1532
1533 if(i == A_HUB)
1534 {
1535 Log.Level(LOGD) << "HUB";
1536 }
1537
1538 if(i == A_HOST)
1539 {
1540 Log.Level(LOGD) << "HOST";
1541 }
1542
1543 if(i == A_PVT)
1544 {
1545 Log.Level(LOGD) << "PVT";
1546 }
1547
1548 if(i == A_REGION)
1549 {
1550 Log.Level(LOGD) << "REGION";
1551 }
1552 } // SayNodelistFlags
1553
InSubHubs(FA const & Addr,FA const & Mask)1554 bool NodeLists::InSubHubs(FA const & Addr, FA const & Mask)
1555 {
1556 Nch * tmt;
1557 bool Existing;
1558
1559 // If the node used as a mask is in the nodelist and it is a hub or host
1560 // then check if the Addr belongs to the "subhub" list of nodes linked in
1561 // the nodelist to the hub or to the host. Otherwise check a point.
1562 // And there is a mess with regions. Blast them thrice together with
1563 // those who developed Fidonet standards.
1564
1565 Existing = (ExistInNodelist(Addr) == (unsigned int)-1 ? FALSE : TRUE);
1566
1567 // Node is equal Mask?
1568 if((Addr.Zone() & 0xffff) == (Mask.Zone() & 0xffff) &&
1569 (Addr.Net() & 0xffff) == (Mask.Net() & 0xffff) &&
1570 (Addr.Node() & 0xffff) == (Mask.Node() & 0xffff))
1571 {
1572 if((Addr.Point() & 0xffff) != 0)
1573 {
1574 return Existing;
1575 }
1576
1577 return TRUE;
1578 }
1579
1580 if(Existing == FALSE)
1581 {
1582 return FALSE;
1583 }
1584
1585 // Is the node in the same zone?
1586 if((Addr.Zone() & 0xffff) != (Mask.Zone() & 0xffff))
1587 {
1588 return FALSE;
1589 }
1590
1591 // Search the zone of mask.
1592 tmt = Srch(Index, Mask.Zone() & 0xffff);
1593
1594 if(tmt == NULL)
1595 {
1596 // Log.Level(LOGD) << "Zone " << (Mask.Zone() & 0xffff) << " missing" <<
1597 // EOL;
1598 return FALSE;
1599 }
1600
1601 tmt = tmt->Sub;
1602
1603 if(tmt == NULL)
1604 {
1605 // Log.Level(LOGD) << "Zone " << (Mask.Zone() & 0xffff) << " Empty" << EOL;
1606 return FALSE;
1607 }
1608
1609 // Search the Net of mask.
1610 tmt = Srch(tmt, Mask.Net() & 0xffff);
1611
1612 if(tmt == NULL)
1613 {
1614 // Log.Level(LOGD) << "Net " << (Mask.Zone() & 0xffff) << ":" <<
1615 // (Mask.Net() & 0xffff) << " missing" << EOL;
1616 return FALSE;
1617 }
1618
1619 if((tmt->Number & A_MASK) == A_REGION)
1620 {
1621 if((Mask.Node() & 0xffff) == 0)
1622 {
1623 // Mask to region. Search net.
1624 while(tmt->Number != (unsigned int)-1)
1625 {
1626 if((tmt->Number & 0xffff) == (Addr.Net() & 0xffff))
1627 {
1628 // The net is found. Search node.
1629 tmt = tmt->Sub;
1630
1631 if(tmt == NULL)
1632 {
1633 return FALSE;
1634 }
1635
1636 if((Addr.Node() & 0xffff) == 0)
1637 {
1638 return TRUE;
1639 }
1640
1641 if(Srch(tmt, Addr.Node() & 0xffff) == NULL)
1642 {
1643 return FALSE;
1644 }
1645 else
1646 {
1647 return TRUE;
1648 }
1649 }
1650
1651 tmt++;
1652
1653 if((tmt->Number & A_MASK) == A_REGION)
1654 {
1655 // Next region began but net was not found...
1656 return FALSE;
1657 }
1658 }
1659 return FALSE;
1660 }
1661 }
1662
1663 if((Addr.Net() & 0xffff) != (Mask.Net() & 0xffff))
1664 {
1665 // Log.Level(LOGD) << "Node " << Addr << " not equal to mask " << Mask << "
1666 // and mask is not a regional mask." << EOL;
1667 return FALSE;
1668 }
1669
1670 tmt = tmt->Sub;
1671
1672 if(tmt == NULL)
1673 {
1674 // Log.Level(LOGD) << "Node " << Addr << " not equal to mask " << Mask << "
1675 // and no nodes in mask net." << EOL;
1676 return FALSE;
1677 }
1678
1679 // If the mask is a HOST then the beginning has already been found.
1680 // Otherwise continue the search.
1681 if((Mask.Node() & 0xffff) != 0)
1682 {
1683 tmt = Srch(tmt, Mask.Node() & 0xffff);
1684
1685 if(tmt == NULL)
1686 {
1687 return FALSE;
1688 }
1689
1690 // if Mask is not a hub - leave.
1691 if((tmt->Number & A_MASK) != A_HUB)
1692 {
1693 return FALSE;
1694 }
1695
1696 tmt++;
1697 }
1698
1699 // Ok. We have the beginning of the list.
1700 // Search the node down to a hub or to the end of the list.
1701 while(tmt->Number != (unsigned int)-1 && (tmt->Number & A_MASK) != A_HUB)
1702 {
1703 if((tmt->Number & 0xffff) == (Addr.Node() & 0xffff))
1704 {
1705 return Existing;
1706 }
1707
1708 tmt++;
1709 }
1710 return FALSE;
1711 } // InSubHubs
1712
AddNodelist(char * tmt,int TempZone)1713 int NodeLists::AddNodelist(char * tmt, int TempZone)
1714 {
1715 char Buff[512];
1716 struct stat NdlStat;
1717 NodeListElem * Elem;
1718
1719 memset(Buff, 0, 512);
1720
1721 if(strlen(tmt) == 0)
1722 {
1723 yyerror("Missing parameter: nodelist name or mask.");
1724 return -1;
1725 }
1726
1727 if((TempZone <= 0) && (TempZone != -3))
1728 {
1729 yyerror("Zone number must be between 1 and 65535");
1730 return -1;
1731 }
1732
1733 if(TempZone != -3)
1734 {
1735 StartZone = TempZone;
1736 }
1737 else
1738 {
1739 StartZone = 0;
1740 }
1741
1742 if(FindNodelist(tmt, Buff) != 0)
1743 {
1744 return -1;
1745 }
1746
1747 if(stat(Buff, &NdlStat) != 0)
1748 {
1749 Log.Level(LOGE) << "Unable to get information about nodelist '" <<
1750 Buff << "'." << EOL;
1751 return -1;
1752 }
1753
1754 Elem = (NodeListElem *)malloc(sizeof(NodeListElem) + 100);
1755 CheckMem((char *)Elem);
1756 memcpy(Elem->Name, Buff, 512);
1757 Elem->Time = NdlStat.st_mtime;
1758 Elem->StartZone = StartZone;
1759 Log.Level(LOGD) << "NTime : " << (int)Elem->Time << EOL;
1760
1761 if(NList == NULL)
1762 {
1763 NList = Elem;
1764 Lists = 1;
1765 }
1766 else
1767 {
1768 tmt = (char *)realloc(NList, (Lists + 1) * sizeof(NodeListElem));
1769 CheckMem((char *)tmt);
1770 NList = (NodeListElem *)tmt;
1771 memcpy(NList + Lists, Elem, sizeof(NodeListElem));
1772 Lists++;
1773 free(Elem);
1774 }
1775
1776 return 0;
1777 } // AddNodelist
1778
1779 // --------------------------------------------------------------------
1780
SetMaxNodelistAge(int tmt)1781 int SetMaxNodelistAge(int tmt)
1782 {
1783 if(MaxNodelistAge != (time_t)-1)
1784 {
1785 yyerror("Max nodelists age already set.");
1786 return -1;
1787 }
1788
1789 if(tmt < 1)
1790 {
1791 yyerror("Parameter must be a number greater than 0.");
1792 return -1;
1793 }
1794
1795 MaxNodelistAge = tmt * 24 * 60 * 60;
1796 return 0;
1797 }
1798
1799 // --------------------------------------------------------------------
1800
SetNodelist(char * tmt,int TempZone)1801 int SetNodelist(char * tmt, int TempZone)
1802 {
1803 return Ndl.AddNodelist(tmt, TempZone);
1804 }
1805