1 Program PMkNL;
2 {$Define MKNL}
3 {$I-}
4 {$IfDef SPEED}
5 {$Else}
6  {$IfDef VIRTUALPASCAL}
7   {$Define VP}
8   {$M 65520}
9  {$Else}
10   {$M 65520, 0, 655360}
11  {$EndIf}
12 {$EndIf}
13 
14 Uses
15 {$IfDef VP}
16  OS2Base, OS2Def,
17 {$EndIf}
18 {$IfDef UNIX}
19  Linux,
20 {$EndIf}
21  dos,
22  mkglobt, mkmisc, mkmsgabs, mkmsgfid, mkmsgezy, mkmsgjam, mkmsghud, mkmsgsqu,
23  crc, types, generalp, log, inifile2;
24 {$IfDef VP}
25  {$IfDef VPDEMO}
26   {$Dynamic VP11DEMO.LIB}
27  {$EndIf}
28 {$EndIf}
29 
30 Const
31  Name : String[7]              = 'ProMkNL';
32  ShortName : String[5]         = 'PMKNL';
33 {Current Version}
34  Version : String[20]           =
35 {$IfDef OS2}
36  '/2 '
37 {$Else}
38  {$IfDef UNIX}
39  '/Lx '
40  {$Else}
41   {$IfDef DPMI}
42   '/16 '
43   {$Else}
44   '/8 '
45   {$EndIf}
46  {$EndIf}
47 {$EndIf}
48  +'0.9beta9';
49 
50  cSunday = 0;
51  cMonday = 1;
52  cTuesday = 2;
53  cWednesday = 3;
54  cThursday = 4;
55  cFriday = 5;
56  cSaturday = 6;
57  cAll = 7;
58 
59  cYes = 1;
60 
61  cNIntl = 2; {Notify: force INTL}
62  cNCrash = 4; {Notify: set crash flag}
63  cNKillSent = 8; {Notify: set kill/sent flag}
64  cSIntl = 2; {Submit: force INTL}
65  cSCrash = 4; {Submit: set crash flag}
66  cSKillSent = 8; {Submit: set kill/sent flag}
67 
68  cTComposite = 0;
69  cTZone = 1;
70  cTRegion = 2;
71  cTNet = 3;
72  cTHub = 4;
73  cTNode = 5;
74 
75  cEInvalidType = 1;
76  cEEmptyAddr = 2;
77  cEInvalidAddr = 3;
78  cEEmptyPhone = 4;
79  cEInvalidPhone = 5;
80  cEEmptyBaud = 6;
81  cEInvalidBaud = 7;
82  cEEmptyFlag = 8;
83  cEInvalidFlag = 9;
84  cWEmptySysName = 128+1;
85  cWEmptyLocation = 128+2;
86  cWEmptySysOpName = 128+3;
87  cWInvalidComment = 128+4;
88  cWEmptyLine = 128+5;
89  cWEOF = 128+6;
90 
91  cPhoneChars : Set of Char = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', '*', '#'];
92 
93  cNumFlags = 49;
94  cFlags : Array[1..cNumFlags] of String[10] = (
95   'CM', 'MO', 'LO',
96   'MN', 'XA', 'XB', 'XC', 'XP', 'XR', 'XW', 'XX',
97   'V21', 'V22', 'V29', 'V32', 'V32B', 'V33', 'V34', 'V34+', 'H96', 'HST',
98   'H14', 'H16', 'MAX', 'PEP', 'CSP', 'ZYX', 'Z19', 'VFC', 'V32T',
99   'V90C', 'V90S', 'X2C', 'X2S',
100   'X75', 'V110H', 'V110L', 'ISDN', 'V120H', 'V120L',
101   'MNP', 'V42', 'V42B',
102   '#01', '#02', '#03', '#09', '#18', '#20');
103 
104 
105 Type
106  PCfg = ^TCfg;
107  PData = ^TData;
108  PFiles = ^TFiles;
109  PBaud = ^TBaud;
110 
111  TNetAddr = {21 Byte}
112   Record
113   Zone, Net, Node, Point: Word;
114   Domain: String[12];
115   end;
116 
117  TFile =
118   Record
119   Typ: Byte;
120   PartAddr: Word;
121   UPD: String[100];
122   NotifyAddr: TNetAddr;
123   SkipComments: Boolean;
124   End;
125 
126  TData = Array[1..100] of String[255]; {25kb}
127  TFiles = Array[1..100] of TFile;
128  TBaud = Array[1..30] of Word;
129 
130  TCfg =
131   Record
132   {Section GENERAL}
133   LogFile: String128;
134   LogLevel: Byte;
135   MasterDir: String128;
136   UpdateDir: String128;
137   BadDir: String128;
138   OutPath: String128;
139   MailDir: String128;
140   NetMail: String128;
141 
142   {Section <SectionName>}
143   OutFile: String40;
144   OutDiff: String40;
145   Prolog: String128;
146   Epilog: String128;
147   CopyRight: String128;
148   ProcessDay: Byte;
149   NotifyErr: Byte;
150   NotifyOK: Byte;
151   OwnAddr: TNetAddr;
152   SubmitAddr: TNetAddr;
153   Submit: Byte;
154   NetName: String40;
155   NLType: Byte;
156   CreateBatch: String;
157   CallBatch: String;
158   Data: PData;
159   NumData: Byte;
160   Files: PFiles;
161   NumFiles: Byte;
162   Baud: PBaud;
163   NumBaud: Byte;
164   End;
165 
166 Var
167  Ini: IniObj;
168  lh: Word; {LogHandle}
169  Cfg: PCfg;
170  SecName: String40;
171  NM: AbsMsgPtr;
172  NMOpen: Boolean;
173  DoProcess: Boolean;
174 
Addr2Strnull175 Function Addr2Str(Addr: TNetAddr): String;
176 Var
177   s: String;
178 
179   Begin
180   With Addr do
181     Begin
182     s := IntToStr(Zone) + ':' + IntToStr(Net) + '/' + IntToStr(Node);
183     If (Point <> 0) then s := s + '.' + IntToStr(Point);
184     If (Domain <> '') then s := s + '@' + Domain;
185     Addr2Str := s;
186     End;
187   End;
188 
Addr2StrNDnull189 Function Addr2StrND(Addr: TNetAddr): String; {no domain}
190 Var
191   s: String;
192 
193   Begin
194   With Addr do
195     Begin
196     s := IntToStr(Zone) + ':' + IntToStr(Net) + '/' + IntToStr(Node);
197     If (Point <> 0) then s := s + '.' + IntToStr(Point);
198     Addr2StrND := s;
199     End;
200   End;
201 
202 Procedure PartStr2Addr(s: String; var Addr: TNetAddr);
203 Var
204 {$IfDef VIRTUALPASCAL}
205   i: LongInt;
206 {$Else}
207   i: Integer;
208 {$EndIf}
209 
210   Begin
211   If (Pos(':', s) = 0) then Val(s, Addr.Zone, i)
212   Else
213    Begin
214    Val(Copy(s, 1, Pos(':', s)-1), Addr.Zone, i);
215    Delete(s, 1, Pos(':', s));
216    If (Pos('/', s) = 0) then Val(s, Addr.Net, i)
217    Else
218     Begin
219     Val(Copy(s, 1, Pos('/', s)-1), Addr.Net, i);
220     Delete(s, 1, Pos('/', s));
221     If (Pos('.', s) = 0) then Val(s, Addr.Node, i)
222     Else
223      Begin
224      Val(Copy(s, 1, Pos('.', s)-1), Addr.Node, i);
225      Delete(s, 1, Pos('.', s));
226      Val(s, Addr.Point, i);
227      End;
228     End;
229    End;
230   End;
231 
232 Procedure Str2Addr(s: String; var Addr: TNetAddr);
233 Var
234 {$IfDef VIRTUALPASCAL}
235   i: LongInt;
236 {$Else}
237   i: Integer;
238 {$EndIf}
239 
240   Begin
241   If (Pos(':', s) = 0) then Addr.Zone := 0
242   Else
243     Begin
244     Val(Copy(s, 1, Pos(':', s) - 1), Addr.Zone, i);
245     Delete(s, 1, Pos(':', s));
246     End;
247   If (Pos('/', s) = 0) then
248     Begin
249     End
250   Else
251     Begin
252     Val(Copy(s, 1, Pos('/', s) - 1), Addr.Net, i);
253     Delete(s, 1, Pos('/', s));
254     End;
255   If (Pos('.', s) = 0) or
256    ((Pos('.', s) > Pos('@', s)) and (Pos('@', s) > 0)) then
257     Begin
258     Addr.Point := 0;
259     If (Pos('@', s) = 0) then
260       Begin
261       Val(s, Addr.Node, i);
262       Addr.Domain := '';
263       End
264     Else
265       Begin
266       Val(Copy(s, 1, Pos('@', s) - 1), Addr.Node, i);
267       Delete(s, 1, Pos('@', s));
268       Addr.Domain := UpStr(s);
269       End;
270     End
271   Else
272     Begin
273     Val(Copy(s, 1, Pos('.', s) - 1), Addr.Node, i);
274     Delete(s, 1, Pos('.', s));
275     If (Pos('@', s) = 0) then
276       Begin
277       Val(s, Addr.Point, i);
278       Addr.Domain := '';
279       End
280     Else
281       Begin
282       Val(Copy(s, 1, Pos('@', s) - 1), Addr.Point, i);
283       Delete(s, 1, Pos('@', s));
284       Addr.Domain := UpStr(s);
285       End;
286     End;
287   End;
288 
CompAddrnull289 Function CompAddr(A1, A2: TNetAddr): Boolean;
290 Var
291   C: Boolean;
292 
293   Begin
294   c := ((A1.Zone = 0) or (A2.Zone = 0) or (A1.Zone = A2.Zone));
295   c := c and ((A1.Net = 0) or (A2.Net = 0) or (A1.Net = A2.Net));
296   c := c and (A1.Node = A2.Node);
297   c := c and (A1.Point = A2.Point);
298   c := c and ((A1.Domain = '') or (A2.Domain = '') or (UpStr(A1.Domain) = UpStr(A2.Domain)));
299   CompAddr := c;
300   End;
301 
302 Procedure TNetAddr2MKAddr(A1: TNetAddr; Var MKAddr: AddrType);
303   Begin
304   MKAddr.Zone := A1.Zone;
305   MKAddr.Net := A1.Net;
306   MKAddr.Node := A1.Node;
307   MKAddr.Point := A1.Point;
308   MKAddr.Domain := A1.Domain;
309   End;
310 
311 
312 Procedure Syntax;
313  Begin
314  WriteLn('Syntax: '+ShortName+' <SectionName> [/P|/T]');
315  End;
316 
ParseCfgnull317 Function ParseCfg: Boolean;
318 Var
319 {$IfDef VP}
320  Error: LongInt;
321 {$Else}
322  Error: Integer;
323 {$EndIf}
324  s, s1: String;
325  i: Byte;
326  p: Byte;
327  b: Boolean;
328 
329  Begin
330  ParseCfg := True;
331  Cfg^.LogFile := Ini.ReadEntry('GENERAL', 'LOG');
332  If (Cfg^.LogFile = '') then
333   Begin
334   WriteLn('no Logfile defined!');
335   ParseCfg := False;
336   Exit;
337   End;
338  Val(Ini.ReadEntry('GENERAL', 'LOGLEVEL'), Cfg^.LogLevel, Error);
339  Cfg^.MasterDir := AddDirSep(Ini.ReadEntry('GENERAL', 'MASTER'));
340  Cfg^.UpdateDir := AddDirSep(Ini.ReadEntry('GENERAL', 'UPDATE'));
341  Cfg^.BadDir := AddDirSep(Ini.ReadEntry('GENERAL', 'BAD'));
342  Cfg^.OutPath := AddDirSep(Ini.ReadEntry('GENERAL', 'OUTPATH'));
343  Cfg^.MailDir := AddDirSep(Ini.ReadEntry('GENERAL', 'MAILDIR'));
344  Cfg^.Netmail := Ini.ReadEntry('GENERAL', 'NETMAIL');
345 
346  If Ini.GetSecNum(SecName) = 0 then
347   Begin
348   WriteLn('Section "'+SecName+'" not found or empty!');
349   ParseCfg := False;
350   Exit;
351   End;
352  Cfg^.OutFile := Ini.ReadEntry(SecName, 'OUTFILE');
353  Cfg^.OutDiff := Ini.ReadEntry(SecName, 'OUTDIFF');
354  Cfg^.Prolog := Ini.ReadEntry(SecName, 'PROLOG');
355  Cfg^.Epilog := Ini.ReadEntry(SecName, 'EPILOG');
356  Cfg^.Copyright := Ini.ReadEntry(SecName, 'COPYRIGHT');
357  Cfg^.CreateBatch := Ini.ReadEntry(SecName, 'CREATEBATCH');
358  Cfg^.CallBatch := Ini.ReadEntry(SecName, 'CALLBATCH');
359  s := UpStr(Ini.ReadEntry(SecName, 'PROCESS'));
360  If (s = 'MONDAY') then Cfg^.ProcessDay := cMonday
361  Else If (s = 'TUESDAY') then Cfg^.ProcessDay := cTuesday
362  Else If (s = 'WEDNESDAY') then Cfg^.ProcessDay := cWednesday
363  Else If (s = 'THURSDAY') then Cfg^.ProcessDay := cThursday
364  Else If (s = 'FRIDAY') then Cfg^.ProcessDay := cFriday
365  Else If (s = 'SATURDAY') then Cfg^.ProcessDay := cSaturday
366  Else If (s = 'SUNDAY') then Cfg^.ProcessDay := cSunday
367  Else If (s = 'ALL') then Cfg^.ProcessDay := cAll
368  Else
369   Begin
370   WriteLn('Invalid ProcessDay: '+s);
371   Cfg^.ProcessDay := 0;
372   End;
373  s := UpStr(Ini.ReadEntry(SecName, 'NOTIFYERR'));
374  Cfg^.NotifyErr := 0;
375  If (Pos('YES', s) > 0) then Inc(Cfg^.NotifyErr, cYes);
376  If (Pos('CRASH', s) > 0) then Inc(Cfg^.NotifyErr, cNCrash);
377  If (Pos('INTL', s) > 0) then Inc(Cfg^.NotifyErr, cNIntl);
378  If (Pos('KILLSENT', s) > 0) then Inc(Cfg^.NotifyErr, cNKillSent);
379  s := UpStr(Ini.ReadEntry(SecName, 'NOTIFYOK'));
380  Cfg^.NotifyOK := 0;
381  If (Pos('YES', s) > 0) then Inc(Cfg^.NotifyOK, cYes);
382  If (Pos('CRASH', s) > 0) then Inc(Cfg^.NotifyOK, cNCrash);
383  If (Pos('INTL', s) > 0) then Inc(Cfg^.NotifyOK, cNIntl);
384  If (Pos('KILLSENT', s) > 0) then Inc(Cfg^.NotifyOK, cNKillSent);
385  Str2Addr(Ini.ReadEntry(SecName, 'OWNADDR'), Cfg^.OwnAddr);
386  s := UpStr(Ini.ReadEntry(SecName, 'SUBMIT'));
387  If (s <> '') then
388   Begin
389   Cfg^.Submit := cYes;
390   If (Pos(',', s) > 0) then
391    Begin
392    Str2Addr(Copy(s, 1, Pos(',', s)-1), Cfg^.SubmitAddr);
393    If (Pos('CRASH', s) > 0) then Inc(Cfg^.Submit, cSCrash);
394    If (Pos('INTL', s) > 0) then Inc(Cfg^.Submit, cSIntl);
395    If (Pos('KILLSENT', s) > 0) then Inc(Cfg^.Submit, cSKillSent);
396    End
397   Else Str2Addr(s, Cfg^.SubmitAddr);
398   End
399  Else Cfg^.Submit := 0;
400  Cfg^.NetName := Ini.ReadEntry(SecName, 'NETNAME');
401  s1 := UpStr(Ini.ReadEntry(SecName, 'TYPE'));
402  If (s1 = 'COMPOSITE') then Cfg^.NLType := cTComposite
403  Else If (s1 = 'ZONE') then Cfg^.NLType := cTZone
404  Else If (s1 = 'REGION') then Cfg^.NLType := cTRegion
405  Else If (s1 = 'NETWORK') then Cfg^.NLType := cTNet
406  Else If (s1 = 'NET') then Cfg^.NLType := cTNet
407  Else If (s1 = 'HUB') then Cfg^.NLType := cTHub
408  Else If (s1 = 'NODE') then Cfg^.NLType := cTNode
409  Else Cfg^.NLType := cTNode;
410  s := Ini.ReadEntry(SecName, 'ALLOWBAUD');
411  If (s = '') then Cfg^.NumBaud := 0
412  Else
413   Begin
414   i := 0;
415   p := Pos(',', s);
416   While (p > 0) do
417    Begin
418    Inc(i);
419    Val(Copy(s, 1, p-1), Cfg^.Baud^[i], Error);
420    Delete(s, 1, p);
421    p := Pos(',', s);
422    End;
423   Inc(i);
424   Val(Copy(s, 1, p-1), Cfg^.Baud^[i], Error);
425   Cfg^.NumBaud := i;
426   End;
427  i := 0;
428  Ini.SetSection(SecName);
429  With Ini do b := (UpStr(ReSecEnName) <> 'DATA') and (UpStr(ReSecEnName) <> 'FILE');
430  While b do
431   Begin
432   If not Ini.SetNextOpt then
433    Begin
434    Cfg^.NumData := 0;
435    Cfg^.NumFiles := 0;
436    Exit;
437    End;
438   b := (UpStr(Ini.ReSecEnName) <> 'DATA') ;
439   b := b and (UpStr(Ini.ReSecEnName) <> 'FILE')
440   End;
441  While UpStr(Ini.ReSecEnName) = 'DATA' do
442   Begin
443   Inc(i);
444   Cfg^.Data^[i] := Ini.ReSecEnValue;
445   s := Cfg^.Data^[i];
446   If not Ini.SetNextOpt then Break;
447   End;
448  Cfg^.NumData:= i;
449  While (UpStr(Ini.ReSecEnName) <> 'FILE') do
450   Begin
451   If not Ini.SetNextOpt then
452    Begin
453    Cfg^.NumFiles := 0;
454    Exit;
455    End;
456   End;
457  i := 0;
458  While UpStr(Ini.ReSecEnName) = 'FILE' do
459   Begin
460   Inc(i);
461   s := KillLeadingSpcs(Ini.ReSecEnValue);
462   With Cfg^.Files^[i] do
463    Begin
464    s1 := UpStr(Copy(s, 1, Pos(',', s)-1));
465    Delete(s, 1, Pos(',', s));
466    s := KillLeadingSpcs(s);
467    If (s1 = 'ZONE') then Typ := cTZone
468    Else If (s1 = 'REGION') then Typ := cTRegion
469    Else If (s1 = 'NETWORK') then Typ := cTNet
470    Else If (s1 = 'NET') then Typ := cTNet
471    Else If (s1 = 'HUB') then Typ := cTHub
472    Else If (s1 = 'NODE') then Typ := cTNode
473    Else Typ := cTNode;
474    Val(Copy(s, 1, Pos(',', s)-1), PartAddr, Error);
475    Delete(s, 1, Pos(',', s));
476    UPD := KillLeadingSpcs(Copy(s, 1, Pos(',', s)-1));
477    Delete(s, 1, Pos(',', s));
478    If (Pos(',', s) > 0) then
479     Begin
480     Str2Addr(Copy(s, 1, Pos(',', s)-1), NotifyAddr);
481     Delete(s, 1, Pos(',', s));
482     If (UpStr(s) = 'SKIPCOMMENTS') then SkipComments := True;
483     End
484    Else
485     Begin
486     Str2Addr(s, NotifyAddr);
487     SkipComments := False;
488     End;
489    End;
490   If not Ini.SetNextOpt then Break;
491   End;
492  Cfg^.NumFiles:= i;
493  End;
494 
495 Procedure Init;
496 Var
497  DT: TimeTyp;
498  CfgName: String;
499  s: String;
500 
501  Begin
502  WriteLn(Name+Version);
503  WriteLn;
504  If (ParamCount < 1) then
505   Begin
506   Syntax;
507   Halt(1);
508   End;
509  SecName := ParamStr(1);
510  If FileExist(GetEnv(ShortName)+DirSep+LowStr(ShortName)+'.cfg') then
511   CfgName := GetEnv(ShortName)+DirSep+LowStr(ShortName)+'.cfg'
512  Else CfgName := LowStr(ShortName)+'.cfg';
513  Ini.Init(CfgName);
514  New(Cfg);
515  New(Cfg^.Data);
516  New(Cfg^.Files);
517  New(Cfg^.Baud);
518  If not ParseCfg then
519   Begin
520   Dispose(Cfg^.Baud);
521   Dispose(Cfg^.Files);
522   Dispose(Cfg^.Data);
523   Dispose(Cfg);
524   Ini.Done;
525   Halt(2);
526   End;
527  Today(DT);
528  s := ParamStr(2);
529  DoProcess := (Upcase(s[2]) = 'P') or ((DT.DayOfWeek = Cfg^.Processday) or
530    (Cfg^.ProcessDay = cAll));
531  DoProcess := DoProcess and not (Upcase(s[2]) = 'T');
532  lh := OpenLog(Binkley, Cfg^.LogFile, ShortName, Name+Version);
533  LogSetScrLevel(lh, 5);
534  LogSetLogLevel(lh, Cfg^.LogLevel);
535  LogSetCurLevel(lh, 4);
536  LogWriteLn(lh, 'Section '+SecName);
537  LogSetCurLevel(lh, 1);
538  End;
539 
540 Procedure Done;
541  Begin
542  CloseLog(lh);
543  Ini.Done;
544  Dispose(Cfg^.Files);
545  Dispose(Cfg^.Data);
546  Dispose(Cfg);
547  End;
548 
OpenNMnull549 Function OpenNM: Boolean;
550  Begin
551  OpenNM := True;
552  Case UpCase(Cfg^.NetMail[1]) of
553   'H': NM := New(HudsonMsgPtr, Init);
554   'S': NM := New(SqMsgPtr, Init);
555   'F': NM := New(FidoMsgPtr, Init);
556   'E': NM := New(EzyMsgPtr, Init);
557  Else
558   Begin
559   LogSetCurLevel(LH, 1);
560   LogWriteLn(LH, 'Invalid type for netmail area!');
561   OpenNM := False;
562   Exit;
563   End;
564  End;
565  NM^.SetMsgPath(Copy(Cfg^.NetMail, 2, Length(Cfg^.NetMail) - 1));
566  If (NM^.OpenMsgBase <> 0) then
567   Begin
568   LogSetCurLevel(LH, 1);
569   LogWriteLn(LH, 'Couldn''t open netmail area!');
570   Dispose(NM, Done);
571   OpenNM := False;
572   Exit;
573   End;
574  If (UpCase(Cfg^.NetMail[1]) = 'F') then FidoMsgPtr(NM)^.SetDefaultZone(0);
575  NM^.SetMailType(mmtNetMail);
576  End;
577 
578 Procedure CloseNM;
579  Begin
580  If (NM^.CloseMsgBase <> 0) then
581   Begin
582   LogSetCurLevel(LH, 1);
583   LogWriteLn(LH, 'Couldn''t close netmail area!');
584   End;
585  Dispose(NM, Done);
586  End;
587 
GetMsgIDnull588 Function GetMsgID : String;
589 Var
590  MsgIDFile: Text;
591  CurMsgID: ULong;
592  Dir: String;
593  s: String;
594  Error: Integer;
595 
596  begin
597  Dir := GetEnv('MSGID');
598  If (Dir = '') then Dir := AddDirSep(Cfg^.MasterDir)
599  Else Dir := AddDirSep(Dir);
600  Assign(MsgIDFile, Dir + 'msgid.dat');
601  {$I-} ReSet(MsgIDFile); {$I+}
602  If (IOResult = 0) then
603   begin
604   ReadLn(MsgIDFile, s);
605   While (s[Byte(s[0])] = #10) or (s[Byte(s[0])] = #13) do Dec(s[0]);
606   Val(s, CurMsgID, Error);
607   If (Error <> 0) or (CurMsgID = 0) then CurMsgID := 1;
608   Close(MsgIDFile);
609   end
610  Else CurMsgID := 1; {Reset MsgID if no MSGID.DAT is found}
611  GetMsgID := WordToHex(word(CurMsgID SHR 16)) + WordToHex(word(CurMsgID));
612  Inc(CurMsgID);
613  {$I-} ReWrite(MsgIDFile); {$I+}
614  If (IOResult = 0) then
615   Begin
616   Write(MsgIDFile, CurMsgID, #13#10);
617   Close(MsgIDFile);
618   End;
619  end;
620 
621 Procedure SetDT(MB: AbsMsgPtr);
622 Var
623  DT: TimeTyp;
624  s: String;
625 
626  Begin
627  With MB^ do
628   Begin
629   Today(DT);
630   If (DT.Year > 100) then DT.Year := DT.Year mod 100;
631   Now(DT);
632   If (DT.Month > 9) then s := IntToStr(DT.Month) + '-'
633   Else s := '0' + IntToStr(DT.Month) + '-';
634   If (DT.Day > 9) then s := s + IntToStr(DT.Day) + '-'
635   Else s := s + '0' + IntToStr(DT.Day) + '-';
636   If (DT.Year > 9) then s := s + IntToStr(DT.Year)
637   Else s := s + '0' + IntToStr(DT.Year);
638   SetDate(s);
639   If (DT.Hour > 9) then s := IntToStr(DT.Hour) + ':'
640   Else s := '0' + IntToStr(DT.Hour) + ':';
641   If (DT.Min > 9) then s := s + IntToStr(DT.Min)
642   Else s := s + '0' + IntToStr(DT.Min);
643   SetTime(s);
644   End;
645  End;
646 
647 Procedure SendOK(e: TFile);
648 Var
649  MKAddr: AddrType;
650  s: String;
651 
652  Begin
653  LogSetCurLevel(lh, 3);
654  LogWriteLn(lh, 'sending OK-receipt to '+Addr2Str(e.NotifyAddr));
655  If not OpenNM then Exit;
656  With NM^ do
657   Begin
658   StartNewMsg;
659   s := Name + Version;
660   SetFrom(s);
661   TNetAddr2MKAddr(Cfg^.OwnAddr, MKAddr);
662   SetOrig(MKAddr);
663   SetTo('Coordinator');
664   TNetAddr2MKAddr(e.NotifyAddr, MKAddr);
665   SetDest(MKAddr);
666   SetSubj(e.UPD+' received: No errors');
667   SetLocal(True);
668   SetCrash((Cfg^.NotifyOK and cNCrash) > 0);
669   SetKillSent((Cfg^.NotifyOK and cNKillSent) > 0);
670   SetDT(NM);
671   If (Cfg^.OwnAddr.Point <> 0) then DoStringLn(#1'FMPT '+IntToStr(Cfg^.OwnAddr.Point));
672   If (e.NotifyAddr.Point <> 0) then DoStringLn(#1'TOPT '+IntToStr(e.NotifyAddr.Point));
673   If (e.NotifyAddr.Zone <> Cfg^.OwnAddr.Zone) or ((Cfg^.NotifyOK and cNIntl) > 0) then
674    DoStringLn(#1'INTL '+Addr2StrND(e.NotifyAddr)+' '+Addr2StrND(Cfg^.OwnAddr));
675   DoStringLn(#1'MSGID: '+Addr2Str(Cfg^.OwnAddr)+' '+GetMsgID);
676   DoStringLn(#1'PID: '+Name+Version);
677   DoStringLn('Your nodelist update, '+e.UPD+', has been received and processsed without errors.');
678   DoStringLn('--- '+Name+Version);
679   If (WriteMsg <> 0) then
680    Begin
681    LogSetCurLevel(LH, 1);
682    LogWriteLn(LH, 'Couldn''t write to netmail area!');
683    End;
684   End;
685  CloseNM;
686  End;
687 
688 Procedure Mark(e: TFile; lNum: Word; Line: String; Error: Byte; ErrStr: String);
689 Var
690  MKAddr: AddrType;
691  s: String;
692 
693  Begin
694  If (Cfg^.NotifyErr and cYes) = 0 then Exit;
695  If not NMOpen then
696   Begin
697   LogSetCurLevel(lh, 3);
698   LogWriteLn(lh, 'sending ERROR-receipt to '+Addr2Str(e.NotifyAddr));
699   If not OpenNM then Exit;
700   With NM^ do
701    Begin
702    StartNewMsg;
703    s := Name + Version;
704    SetFrom(s);
705    TNetAddr2MKAddr(Cfg^.OwnAddr, MKAddr);
706    SetOrig(MKAddr);
707    SetTo('Coordinator');
708    TNetAddr2MKAddr(e.NotifyAddr, MKAddr);
709    SetDest(MKAddr);
710    SetSubj(e.UPD+' received: contained ERRORS!');
711    SetLocal(True);
712    SetCrash((Cfg^.NotifyErr and cNCrash) > 0);
713    SetKillSent((Cfg^.NotifyErr and cNKillSent) > 0);
714    SetDT(NM);
715    If (Cfg^.OwnAddr.Point <> 0) then DoStringLn(#1'FMPT '+IntToStr(Cfg^.OwnAddr.Point));
716    If (e.NotifyAddr.Point <> 0) then DoStringLn(#1'TOPT '+IntToStr(e.NotifyAddr.Point));
717    If (e.NotifyAddr.Zone <> Cfg^.OwnAddr.Zone) or ((Cfg^.NotifyOK and cNIntl) > 0) then
718     DoStringLn(#1'INTL '+Addr2StrND(e.NotifyAddr)+' '+Addr2StrND(Cfg^.OwnAddr));
719    DoStringLn(#1'MSGID: '+Addr2Str(Cfg^.OwnAddr)+' '+GetMsgID);
720    DoStringLn(#1'PID: '+Name+Version);
721    DoStringLn('Your nodelist update, '+e.UPD+', contained the following mistakes: ');
722    DoStringLn('');
723    End;
724   NMOpen := True;
725   End;
726  With NM^ do
727   Begin
728   DoStringLn('Line '+IntToStr(lNum)+' > '+Line);
729   Case Error of
730    cEInvalidType : DoStringLn('Error: Invalid type: "'+ErrStr+'"');
731    cEEmptyAddr : DoStringLn('Error: Empty address');
732    cEInvalidAddr : DoStringLn('Error: Invalid address: "'+ErrStr+'"');
733    cEEmptyPhone : DoStringLn('Error: Empty phonenumber');
734    cEInvalidPhone : DoStringLn('Error: Invalid phonenumber: "'+ErrStr+'"');
735    cEEmptyBaud : DoStringLn('Error: Empty baudrate');
736    cEInvalidBaud : DoStringLn('Error: Invalid baudrate: "'+ErrStr+'"');
737    cEEmptyFlag : DoStringLn('Error: Empty flag');
738    cEInvalidFlag : DoStringLn('Error: Invalid flag: "'+ErrStr+'"');
739    cWEmptySysName : DoStringLn('Warning: Empty systemname');
740    cWEmptyLocation : DoStringLn('Warning: Empty location');
741    cWEmptySysOpName : DoStringLn('Warning: Empty SysOp-name');
742    cWInvalidComment : DoStringLn('Warning: Invalid comment-type: "'+ErrStr+'"');
743    cWEmptyLine: DoStringLn('Warning: empty line');
744    cWEOF: DoStringLn('Warning: EOF-character (#26/#$19) found');
745    Else
746     Begin
747     DoStringLn('Error: unknown error #'+IntToStr(Error)+': "'+ErrStr+'". Please contact author!');
748     End;
749    End;
750   DoStringLn('');
751   End;
752  End;
753 
CheckUPDnull754 Function CheckUPD(fn: FileStr; e: TFile): Boolean;
755 Var
756  line: String;
757  f: Text;
758  lNum: Word;
759  Bad: Boolean;
760  Warnings: Boolean;
761 
762  Procedure ParseLine(Line: String);
763  Var
764   p: Byte;
765   s, l: String;
766   IsPvt: Boolean;
767   x, y: Word;
768 {$IfDef VP}
769   Error: LongInt;
770 {$Else}
771   Error: Integer;
772 {$EndIf}
773   UFlags: Boolean;
774 
775   Begin
776   If (Line = '') then
777    Begin
778    Mark(e, lNum, Line, cWEmptyLine, '');
779    Warnings := True;
780    Exit;
781    End;
782   If (Line[1] = #26) then
783    Begin
784    Mark(e, lNum, Line, cWEOF, '');
785    Warnings := True;
786    Exit;
787    End;
788   If (Line[1] = ';') then
789    Begin
790    If (Length(Line) = 1) then Exit;
791    If not (UpCase(Line[2]) in ['S', 'U', 'F', 'A', 'E', ' ', #26]) then
792     Begin
793     Mark(e, lNum, Line, cWInvalidComment, Line[2]);
794     Warnings := True;
795     End;
796    Exit;
797    End;
798   IsPvt := False;
799   UFlags := False;
800   l := Line;
801   {type}
802   p := Pos(',', l);
803   If (p > 1) then
804    Begin
805    s := UpStr(Copy(l, 1, p-1));
806    If not ((s = 'ZONE') or (s = 'REGION') or (s = 'HOST') or (s = 'HUB') or
807     (s = 'PVT') or (s = 'HOLD') or (s = 'DOWN')) then
808      Begin
809      Bad := True;
810      Mark(e, lNum, Line, cEInvalidType, s);
811      End
812    Else if (s = 'PVT') then IsPvt := True;
813    End;
814   Delete(l, 1, p);
815   {address}
816   p := Pos(',', l);
817   s := Copy(l, 1, p-1);
818   If (s = '') then
819    Begin
820    Bad := True;
821    Mark(e, lNum, Line, cEEmptyAddr, '');
822    End
823   Else
824    Begin
825    Val(s, x, Error);
826    If (Error <> 0) then
827     Begin
828     Bad := True;
829     Mark(e, lNum, Line, cEInvalidAddr, s);
830     End;
831    End;
832   Delete(l, 1, p);
833   {SystemName}
834   p := Pos(',', l);
835   s := Copy(l, 1, p-1);
836   If (s = '') then
837    Begin
838    Mark(e, lNum, Line, cWEmptySysName, '');
839    Warnings := True;
840    End;
841   Delete(l, 1, p);
842   {Location}
843   p := Pos(',', l);
844   s := Copy(l, 1, p-1);
845   If (s = '') then
846    Begin
847    Mark(e, lNum, Line, cWEmptyLocation, '');
848    Warnings := True;
849    End;
850   Delete(l, 1, p);
851   {SysOpName}
852   p := Pos(',', l);
853   s := Copy(l, 1, p-1);
854   If (s = '') then
855    Begin
856    Mark(e, lNum, Line, cWEmptySysOpName, '');
857    Warnings := True;
858    End;
859   Delete(l, 1, p);
860   {Phone}
861   p := Pos(',', l);
862   s := UpStr(Copy(l, 1, p-1));
863   If not IsPvt then
864    Begin
865    If (s = '') then
866     Begin
867     Bad := True;
868     Mark(e, lNum, Line, cEEmptyPhone, '');
869     End
870    Else
871     Begin
872     Error := 0;
873     For x := 1 to Length(s) do If not (s[x] in cPhoneChars) then Error := 1;
874     If (Error > 0) then
875      Begin
876      Bad := True;
877      Mark(e, lNum, Line, cEInvalidPhone, s);
878      End;
879     Delete(l, 1, p);
880     End;
881    End;
882   {baud}
883   p := Pos(',', l);
884   s := Copy(l, 1, p-1);
885   If (s = '') then
886    Begin
887    Bad := True;
888    Mark(e, lNum, Line, cEEmptyBaud, '');
889    End
890   Else
891    Begin
892    Val(s, x, Error);
893    If (Error <> 0) then
894     Begin
895     Bad := True;
896     Mark(e, lNum, Line, cEInvalidBaud, s);
897     End;
898    If (Cfg^.NumBaud = 0) then
899     Begin
900     If not ((x = 300) or (x = 1200) or (x = 2400) or (x = 9600)) then
901      Begin
902      Bad := True;
903      Mark(e, lNum, Line, cEInvalidBaud, s);
904      End;
905     End
906    Else
907     Begin
908     Error := 1;
909     For y := 1 to Cfg^.NumBaud do If (x = Cfg^.Baud^[y]) then Error := 0;
910     If (Error = 1) then
911      Begin
912      Bad := True;
913      Mark(e, lNum, Line, cEInvalidBaud, s);
914      End;
915     End;
916    End;
917   Delete(l, 1, p);
918   {flags}
919   p := Pos(',', l);
920   While (p > 0) and not UFlags do
921    Begin
922    s := UpStr(Copy(l, 1, p-1));
923    Error := 1;
924    If (s = '') then
925     Begin
926     Bad := True;
927     Mark(e, lNum, Line, cEEmptyFlag, '');
928     Error := 0;
929     End
930    Else If (s = 'U') then
931     Begin
932     Error := 0;
933     UFlags := True
934     End
935    Else If (s[1] = 'G') then Error := 0
936    Else If (s[1] = 'U') then Error := 0
937    Else
938     Begin
939     For x := 1 to cNumFlags do If (s = cFlags[x]) then Error := 0;
940     End;
941    If (Error = 1) then
942     Begin
943     Bad := True;
944     Mark(e, lNum, Line, cEInvalidFlag, s);
945     End;
946    Delete(l, 1, p);
947    p := Pos(',', l);
948    End;
949   End;
950 
951  Begin
952  CheckUPD := True;
953  Bad := False;
954  Warnings := False;
955  lNum := 0;
956  NMOpen := False;
957  Assign(f, fn);
958  {$I-} ReSet(f); {$I+}
959  If (IOResult <> 0) then
960   Begin
961   LogSetCurLevel(lh, 1);
962   LogWriteLn(lh, 'Could not open "'+fn+'"!');
963   CheckUPD := False;
964   Exit;
965   End;
966 
967  While not EOF(f) do
968   Begin
969   Inc(lNum);
970   ReadLn(f, Line);
971   While ((Line[Byte(Line[0])] = #13) or (Line[Byte(Line[0])] = #10)) do
972    Line[0] := Char(Byte(Line[0])-1);
973   ParseLine(Line);
974   End;
975 
976  {$I-} Close(f); {$I+}
977  If (IOResult <> 0) then
978   Begin
979   LogSetCurLevel(lh, 1);
980   LogWriteLn(lh, 'Could not close "'+fn+'"!');
981   End;
982 
983  CheckUPD := not Bad;
984  If NMOpen then
985   Begin
986   NM^.DoStringLn('');
987   NM^.DoStringLn('--- '+Name+Version);
988   If (NM^.WriteMsg <> 0) then
989    Begin
990    LogSetCurLevel(LH, 1);
991    LogWriteLn(LH, 'Couldn''t write to netmail area!');
992    End;
993   CloseNM;
994   End;
995  NMOpen := False;
996  lNum := IOResult;
997  If (((not Warnings) and (not Bad)) and ((Cfg^.NotifyOK and cYes) > 0)) then SendOK(e);
998  lNum := IOResult;
999  End;
1000 
1001 Procedure SubmitUPD(fn: FileStr);
1002 Var
1003  MKAddr: AddrType;
1004  s: String;
1005 
1006  Begin
1007  LogSetCurLevel(lh, 3);
1008  LogWriteLn(lh, 'submitting '+fn+' to '+Addr2Str(Cfg^.SubmitAddr));
1009  If not OpenNM then Exit;
1010  With NM^ do
1011   Begin
1012   StartNewMsg;
1013   s := Name + Version;
1014   SetFrom(s);
1015   TNetAddr2MKAddr(Cfg^.OwnAddr, MKAddr);
1016   SetOrig(MKAddr);
1017   SetTo('Coordinator');
1018   TNetAddr2MKAddr(Cfg^.SubmitAddr, MKAddr);
1019   SetDest(MKAddr);
1020   SetSubj(fn);
1021   SetLocal(True);
1022   SetFAttach(True);
1023   SetCrash((Cfg^.Submit and cSCrash) > 0);
1024   SetKillSent((Cfg^.Submit and cSKillSent) > 0);
1025   SetDT(NM);
1026   If (Cfg^.OwnAddr.Point <> 0) then DoStringLn(#1'FMPT '+IntToStr(Cfg^.OwnAddr.Point));
1027   If (Cfg^.SubmitAddr.Point <> 0) then DoStringLn(#1'TOPT '+IntToStr(Cfg^.SubmitAddr.Point));
1028   If (Cfg^.SubmitAddr.Zone <> Cfg^.OwnAddr.Zone) or ((Cfg^.Submit and cSIntl) > 0) then
1029    DoStringLn(#1'INTL '+Addr2StrND(Cfg^.SubmitAddr)+' '+Addr2StrND(Cfg^.OwnAddr));
1030   DoStringLn(#1'MSGID: '+Addr2Str(Cfg^.OwnAddr)+' '+GetMsgID);
1031   DoStringLn(#1'PID: '+Name+Version);
1032   If (WriteMsg <> 0) then
1033    Begin
1034    LogSetCurLevel(LH, 1);
1035    LogWriteLn(LH, 'Couldn''t write to netmail area!');
1036    End;
1037   End;
1038  CloseNM;
1039  End;
1040 
1041 Procedure LookForUPDs;
1042 Var
1043  i: LongInt;
1044 
1045  Begin
1046  If (Cfg^.NumFiles = 0) then Exit;
1047  For i := 1 to Cfg^.NumFiles do
1048   Begin
1049   If FileExist(Cfg^.MailDir + Cfg^.Files^[i].UPD) then
1050    Begin
1051    LogSetCurLevel(lh, 3);
1052    LogWriteLn(lh, 'Found "'+Cfg^.MailDir + Cfg^.Files^[i].UPD+'" ('+Addr2Str(Cfg^.Files^[i].NotifyAddr)+')');
1053    If CheckUPD(Cfg^.MailDir + Cfg^.Files^[i].UPD, Cfg^.Files^[i]) then
1054     Begin
1055     If FileExist(Cfg^.UpdateDir + Cfg^.Files^[i].UPD) then DelFile(Cfg^.UpdateDir + Cfg^.Files^[i].UPD);
1056     If not MoveFile(Cfg^.MailDir + Cfg^.Files^[i].UPD, Cfg^.UpdateDir + Cfg^.Files^[i].UPD) then
1057      Begin
1058      LogSetCurLevel(lh, 1);
1059      LogWriteLn(lh, 'Could not move "'+Cfg^.MailDir + Cfg^.Files^[i].UPD+'"');
1060      LogWriteLn(lh, 'to "'+Cfg^.UpdateDir + Cfg^.Files^[i].UPD+'"!');
1061      End;
1062     End
1063    Else
1064     Begin
1065     If FileExist(Cfg^.BadDir + Cfg^.Files^[i].UPD) then DelFile(Cfg^.BadDir + Cfg^.Files^[i].UPD);
1066     If not MoveFile(Cfg^.MailDir + Cfg^.Files^[i].UPD, Cfg^.BadDir + Cfg^.Files^[i].UPD) then
1067      Begin
1068      LogSetCurLevel(lh, 1);
1069      LogWriteLn(lh, 'Could not move "'+Cfg^.MailDir + Cfg^.Files^[i].UPD+'"');
1070      LogWriteLn(lh, 'to "'+Cfg^.BadDir + Cfg^.Files^[i].UPD+'"!');
1071      End
1072     End;
1073    End;
1074   End;
1075  End;
1076 
1077 Procedure DoBatch(UPDName: String; DayNum: Word);
1078 Var
1079  f: Text;
1080  d: DirStr;
1081  n: NameStr;
1082  s, s1: String;
1083  PrevDayNum: Word;
1084  DT: TimeTyp;
1085  DTUnx: ULong;
1086 
1087  Begin
1088  Assign(f, Cfg^.CreateBatch);
1089  {$I-} ReWrite(f); {$I+}
1090  If (IOResult <> 0) then
1091   Begin
1092   LogSetCurLevel(lh, 1);
1093   LogWriteLn(lh, 'Could not open "'+Cfg^.CreateBatch+'"!');
1094   Exit;
1095   End;
1096 
1097  FSplit(UPDName, d, n, s);
1098  Today(DT); Now(DT);
1099  DTUnx := DTToUnixDate(DT);
1100  DTUnx := DTUnx - 7*24*60*60;
1101  UnixToDT(DTUnx, DT);
1102  PrevDayNum := DayOfYear(DT);
1103  s1 := d+n + ' no-diff ' + IntToStr(DayNum div 100) + ' ' + IntToStr((DayNum div 10) mod 10) +
1104   ' ' + IntToStr(DayNum mod 10) + ' ' + IntToStr(PrevDayNum div 100) + ' ' +
1105   IntToStr((PrevDayNum div 10) mod 10) + ' ' + IntToStr(PrevDayNum mod 10);
1106  WriteLn(f, Cfg^.CallBatch + ' ' + s1);
1107 
1108  {$I-} Close(f); {$I+}
1109  If (IOResult <> 0) then
1110   Begin
1111   LogSetCurLevel(lh, 1);
1112   LogWriteLn(lh, 'Could not close "'+Cfg^.CreateBatch+'"!');
1113   End;
1114 {$IfDef UNIX}
1115  ChMod(Cfg^.CreateBatch, 493); {Octal 755}
1116 {$EndIf}
1117  LogSetCurLevel(lh, 3);
1118  LogWriteLn(lh, 'created batch '+Cfg^.CreateBatch);
1119  End;
1120 
1121 Procedure CreateUPD;
1122 Var
1123  f, f1: Text;
1124  i: Byte;
1125  DT: TimeTyp;
1126  s: String;
1127  fn: FileStr;
1128  fn2: PathStr;
1129  CRC: Word;
1130 
1131  Procedure CopyFile(fname: String; IsCpy: Boolean; SkipComments: Boolean);
1132  Var
1133   ftc: Text;
1134   p: Byte;
1135 
1136   Begin
1137   Assign(ftc, fname);
1138   {$I-} ReSet(ftc); {$I+}
1139   If (IOResult <> 0) then
1140    Begin
1141    LogSetCurLevel(lh, 1);
1142    LogWriteLn(lh, 'Could not open "'+fname+'"!');
1143    End
1144   Else
1145    Begin
1146    While not EOF(ftc) do
1147     Begin
1148     ReadLn(ftc, s);
1149     While ((s[Byte(s[0])] = #13) or (s[Byte(s[0])] = #10)) do
1150      s[0] := Char(Byte(s[0])-1);
1151     If (s = '') then Continue;
1152     If (s[1] = #26) then Continue; {skip EOF}
1153     If ((s[1] = ';') and SkipComments) then Continue;
1154     If IsCpy then If (Pos('####', s) > 0) then
1155      Begin
1156      p := Pos('####', s);
1157      Delete(s, p, 4);
1158      Insert(IntToStr(DT.Year), s, p);
1159      End;
1160     Write(f, s, #13#10);
1161     End;
1162    {$I-} Close(ftc); {$I+}
1163    If (IOResult <> 0) then
1164     Begin
1165     LogSetCurLevel(lh, 1);
1166     LogWriteLn(lh, 'Could not close "'+fname+'"!');
1167     End
1168    End;
1169   End;
1170 
1171  Begin
1172  Today(DT); Now(DT);
1173  If (DT.Year < 38) then DT.Year := DT.Year + 2000
1174  Else If (DT.Year < 1900) then DT.Year := DT.Year + 1900;
1175  Write('Day #'+IntToStr(DayOfYear(DT)), #13#10);
1176  FSplit(Cfg^.OutFile, s, fn, s);
1177  Assign(f, Cfg^.OutPath+fn+'.tmp');
1178  {$I-} ReWrite(f); {$I+}
1179  If (IOResult <> 0) then
1180   Begin
1181   LogSetCurLevel(lh, 1);
1182   LogWriteLn(lh, 'Could not open "'+Cfg^.OutPath+fn+'.tmp"!');
1183   Exit;
1184   End;
1185 
1186  If (Cfg^.NLType = cTcomposite) then
1187   Begin
1188   {copy copyright}
1189   If (Cfg^.CopyRight <> '') then CopyFile(Cfg^.CopyRight, True, False);
1190   {copy prolog}
1191   If (Cfg^.ProLog <> '') then CopyFile(Cfg^.ProLog, False, False);
1192   End;
1193 
1194  {copy DATA-lines from config}
1195  If (Cfg^.NumData > 0) then For i := 1 to Cfg^.NumData do
1196   Begin
1197   Write(f, Cfg^.Data^[i], #13#10);
1198   End;
1199  Write(f, ';A', #13#10);
1200 
1201  {copy FILES}
1202  If (Cfg^.NumFiles > 0) then For i := 1 to Cfg^.NumFiles do
1203   Begin
1204   if (pos(dirSep, cfg^.files^[i].upd) > 0) then
1205    Begin
1206    If FileExist(Cfg^.Files^[i].UPD) then
1207     Begin
1208     CopyFile(Cfg^.Files^[i].UPD, False, Cfg^.Files^[i].SkipComments);
1209     Write(f, ';A', #13#10);
1210     End;
1211    End
1212   Else
1213    Begin
1214    If FileExist(Cfg^.UpdateDir+Cfg^.Files^[i].UPD) then
1215     Begin
1216     CopyFile(Cfg^.UpdateDir+Cfg^.Files^[i].UPD, False, Cfg^.Files^[i].SkipComments);
1217     Write(f, ';A', #13#10);
1218     End;
1219    End;
1220   End;
1221 
1222  If (Cfg^.NLType = cTcomposite) then
1223   Begin
1224   {copy epilog}
1225   If (Cfg^.EpiLog <> '') then CopyFile(Cfg^.Epilog, False, False);
1226   End;
1227 
1228  {$I-} Close(f); {$I+}
1229  If (IOResult <> 0) then
1230   Begin
1231   LogSetCurLevel(lh, 1);
1232   LogWriteLn(lh, 'Could not close "'+Cfg^.OutPath+fn+'.tmp"!');
1233   End;
1234 
1235  {calc CRC}
1236  CRC := GetCRC16(Cfg^.OutPath+fn+'.tmp');
1237 
1238  {copy temp file to final file}
1239  If (Pos('.', Cfg^.OutFile) > 0) then fn2 := Cfg^.OutPath + Cfg^.OutFile
1240  Else fn2 := Cfg^.OutPath + Cfg^.OutFile + '.' + IntToStr03(DayOfYear(DT));
1241  Assign(f1, fn2);
1242  {$I-} ReWrite(f1); {$I+}
1243  If (IOResult <> 0) then
1244   Begin
1245   LogSetCurLevel(lh, 1);
1246   LogWriteLn(lh, 'Could not open "'+fn2+'"!');
1247   Exit;
1248   End;
1249  {$I-} ReSet(f); {$I+}
1250  If (IOResult <> 0) then
1251   Begin
1252   LogSetCurLevel(lh, 1);
1253   LogWriteLn(lh, 'Could not open "'+Cfg^.OutPath+fn+'.tmp"!');
1254   Exit;
1255   End;
1256 
1257  {add copyright}
1258  If (Cfg^.NLType = cTcomposite) then
1259   Begin
1260   Write(f1, ';A '+Cfg^.NetName + ' Nodelist for '+WkDaysEng[DT.DayOfWeek]+', '+
1261              MonthsEng[DT.Month]+' '+IntToStr(DT.Day)+', '+IntToStr(DT.Year)+
1262              ' -- Day number '+IntToStr(DayOfYear(DT))+' : ' + IntToStr(CRC),
1263              #13#10);
1264   End;
1265 
1266   While not EOF(f) do
1267    Begin
1268    ReadLn(f, s);
1269    While ((s[Byte(s[0])] = #13) or (s[Byte(s[0])] = #10)) do
1270     s[0] := Char(Byte(s[0])-1);
1271    If (s = '') then Continue;
1272    If (s[1] = #26) then Continue;
1273    Write(f1, s, #13#10);
1274    End;
1275 
1276  {$I-} Close(f1); {$I+}
1277  If (IOResult <> 0) then
1278   Begin
1279   LogSetCurLevel(lh, 1);
1280   LogWriteLn(lh, 'Could not close "'+fn2+'"!');
1281   End;
1282  {$I-} Close(f); {$I+}
1283  If (IOResult <> 0) then
1284   Begin
1285   LogSetCurLevel(lh, 1);
1286   LogWriteLn(lh, 'Could not close "'+Cfg^.OutPath+fn+'.tmp"!');
1287   End;
1288  {$I-} Erase(f); {$I+}
1289  If (IOResult <> 0) then
1290   Begin
1291   LogSetCurLevel(lh, 1);
1292   LogWriteLn(lh, 'Could not delete "'+Cfg^.OutPath+fn+'.tmp"!');
1293   End;
1294  LogSetCurLevel(lh, 4);
1295  LogWriteLn(lh, 'created UPD '+fn2);
1296  If (Cfg^.Submit and cYes) > 0 then SubmitUPD(fn2);
1297  If (Cfg^.CreateBatch <> '') and (Cfg^.CallBatch <> '') then DoBatch(fn2, DayOfYear(DT));
1298  End;
1299 
1300 Procedure Run;
1301  Begin
1302  LookForUPDs;
1303  If DoProcess then CreateUPD;
1304  End;
1305 
1306 
1307 Begin
1308 Init;
1309 Run;
1310 Done;
1311 End.
1312 
1313