{$IFNDEF OS2}
{$F+,O+}
{$ENDIF}
{$I-}

Unit Parse;

{*********************************************************}
{*                      PARSE.PAS                        *}
{*                                                       *}
{*  Copyright (c) Konstantin Klyagin, 1996-98,           *}
{*                exspecially for Tornado BBS System     *}
{*                                                       *}
{*********************************************************}

Interface

Uses
  OpCrt,
  Resource,
{$IFNDEF WIN32}
  DOS,
{$ELSE}
  Windows,
  SysUtils,
{$ENDIF}
  TGlob,
  Users,
  Parser,
  tMisc;

Function ReadLanguage (Var L: PBigCollection; FileName: PathStr): Boolean;
Function ReadDoorWay (FileName: PathStr; Var R: DoorWayControlRec): Boolean;
Function ReadLimit (Var Limit: LimitRec; L: System. Word): Boolean;

Function OpenFileAreas: Boolean;
Function ReadFileArea (Var FileArea: tFileArea; Num: System. Word): Boolean;
Procedure CloseFileAreas;

Function OpenMsgAreas: Boolean;
Function ReadMsgArea (Var MsgArea: tMsgArea; Num: System. Word): Boolean;
Procedure CloseMsgAreas;

Function OpenFileGroups: Boolean;
Function ReadFileGroup (Var FileGroup: tFileGroup; Num: System. Word): Boolean;
Procedure CloseFileGroups;

Function OpenMsgGroups: Boolean;
Function ReadMsgGroup (Var MsgGroup: tMsgGroup; Num: System. Word): Boolean;
Procedure CloseMsgGroups;

Function opUpgrades (FileName: PathStr): Boolean;
Function rUpgrade (CallNum: LongInt; Var UR: UpgradeRec): Boolean;
Procedure pUpgradeDone;

Procedure SetMsgArea (Num: LongInt);
Procedure SetFileArea (Num: LongInt);

Procedure SetMsgGroup (Num: LongInt);
Procedure SetFileGroup (Num: LongInt);

Function lang (LangItem: LongInt): String;
Function MtoAbs (Group, Area: Word): Word;

Var
  msa, fla, fgr, mgr, upg : tConfigParser;
  faFile, maFile, fgFile, mgFile : File;
  rIO : Integer;
  S1, S2 : String;

Const
  faOpen    : Boolean = False;
  maOpen    : Boolean = False;
  fgOpen    : Boolean = False;
  mgOpen    : Boolean = False;
  limOpen   : Boolean = False;
  protOpen  : Boolean = False;
  ReCompile : Boolean = False;

Implementation

Uses
  Log,
{$IFDEF WIN32}
  Classes,
{$ENDIF}
  Objects,
  TimeTask;

Type
  PLimitRec = ^LimitRec;
  PLimitCollection = ^TLimitCollection;
  TLimitCollection = Object (TSortedCollection)
    Function Compare (Key1, Key2: Pointer): {$IFDEF OS2} LongInt {$ELSE} Integer {$ENDIF}; Virtual;
  End;

Const
  Hdr  : tBinaryHdr = (Version: ''; ExeName: '');

Var
  FPoz : LongInt;

Function LimCompare (Key1, Key2: Pointer): Integer;
Begin
  LimCompare := 0;
  If PLimitRec (Key1)^. Level < PLimitRec (Key2)^. Level Then LimCompare := -1;
  If PLimitRec (Key1)^. Level > PLimitRec (Key2)^. Level Then LimCompare :=  1;
End;

Function TLimitCollection. Compare (Key1, Key2: Pointer): {$IFDEF OS2} LongInt {$ELSE} Integer {$ENDIF};
Begin
  Compare := LimCompare (Key1, Key2);
End;

Function NewPLimitRec (Const S: LimitRec): PLimitRec;
Var
  P     : PLimitRec;

Begin
  GetMem (P, SizeOf (LimitRec));
  P^ := S; NewPLimitRec := P;
End;

Procedure DisposePLimitRec (P: PLimitRec);
Begin
  If P <> nil Then FreeMem (P, SizeOf (LimitRec))
End;

Function ReadLanguage (Var L: PBigCollection; FileName: PathStr): Boolean;
Type
  tLangArray = Array [0..laLastLanguageNum] Of String [220];

Var
  S  : String;
  P  : tConfigParser;
  L1 : ^tLangArray;
  i  : LongInt;
  X  : PXLATrec;
  F1 : File;

Begin
  ReadLanguage := False;
  If Not ParserOpen (P, FileName, tpoWriteLog) Then
  Begin
    LogWrite ('!', sm (smFile) + Trim (NiceFileName (FileName, 30)) + sm (smNotFound));
    Exit;
  End;

  L^. FreeAll;
  L^. DeleteAll;

  While XLATs^. Count > 0 Do
  Begin
    FreeMem (XLATs^. At (0), SizeOf (tXLATrec));
    XLATs^. AtDelete (0);
  End;

  New (L1);
  FillChar (L1^, SizeOf (L1^), 0);

  While Not ParserEnd (P) Do
  Begin
    S := '';
    S1 := ParserRead (P, S2);

    If S2 = 'MAIN' Then
    Begin
      ParserGetParam (P, tptString, '', S);
      If S1 = 'LANGUAGE' Then L1^ [0] := S Else
      If S1 = 'RAW_XLAT_NAME' Then L1^ [4] := S Else
      If S1 = 'ASK_XLAT' Then L1^ [5] := UpString (S);
    End Else
    If S2 = 'FILES' Then
    Begin
      ParserGetParam (P, tptFilePath, '', S);
      If S1 = 'TXTFILES' Then L1^ [1] := S Else
      If S1 = 'MENUS' Then L1^ [2] := S Else
      If S1 = 'NEWS' Then L1^ [3] := S;
    End Else
    If (S2 = 'XLAT') And (S1 <> '') Then
    Begin
      Assign (F1, S1);
      ParserGetParam (P, tptString, '', S);
      Reset (F1, 1);

      If IOResult = 0 Then
      Begin
        GetMem (X, SizeOf (tXLATrec));
        BlockRead (F1, X^. Table, SizeOf (X^. Table));
        Close (F1);
        If IOResult <> 0 Then;
        For i := 1 To Length (S) Do S [i] := X^. Table [Ord (S [i])];
        X^. Name := S;
        X^. FileName := S1;
        XLATs^. Insert (X);
      End;
    End Else
    If S2 = 'LANGUAGE_SECTION' Then
    Begin
      If S1 = 'QWK_CONTROLS' Then
      Write;
      ParserGetParam (P, tptQuote, '', S);
      If S1 = 'ENTER_FOR_CONTINUE' Then L1^ [6] := S Else
      If S1 = 'INACTIVE' Then L1^ [7] := S Else
      If S1 = 'MORE' Then L1^ [8] := S Else
      If S1 = 'SELECTFILE' Then L1^ [9] := S Else
      If S1 = 'SECURITYLOW' Then L1^ [10] := S Else
      If S1 = 'TOPIC_FOR_CHAT' Then L1^ [11] := S Else
      If S1 = 'PAGING_FOR_CHAT' Then L1^ [12] := S Else
      If S1 = 'SYSOP_ABSENT' Then L1^ [13] := S Else
      If S1 = 'MSGNUM' Then L1^ [14] := S Else
      If S1 = 'SELECTPROTOCOL' Then L1^ [15] := S Else
      If S1 = 'SYSOP_BUSY' Then L1^ [16] := S Else
      If S1 = 'SYSOP_ANSWERED' Then L1^ [17] := S Else
      If S1 = 'ACCESS_DENIED' Then L1^ [18] := S Else
      If S1 = 'ORGANIZATION' Then L1^ [19] := S Else
      If S1 = 'CHOOSE_PASSWORD' Then L1^ [20] := S Else
      If S1 = 'EMPTY_FAREA' Then L1^ [21] := S Else
      If S1 = 'REPEAT_PASSWORD' Then L1^ [22] := S Else
      If S1 = 'PASSWORDS_NOT_SAME' Then L1^ [23] := S Else
      If S1 = 'BIRTHDATE' Then L1^ [24] := S Else
      If S1 = 'FAREALINE' Then L1^ [25] := S Else
      If S1 = 'ALIAS_EXIST' Then L1^ [26] := S Else
      If S1 = 'TIME_EXEEDED' Then L1^ [27] := S Else
      If S1 = 'MSGHEAD' Then L1^ [28] := S Else
      If S1 = 'FIDO_ADDRESS' Then L1^ [29] := S Else
      If S1 = 'PAUSING' Then L1^ [30] := S Else
      If S1 = 'HOME_PHONE' Then L1^ [31] := S Else
      If S1 = 'DATA_PHONE' Then L1^ [32] := S Else
      If S1 = 'DISPLAY_LINES' Then L1^ [33] := S Else
      If S1 = 'MSGFOOTER' Then L1^ [34] := S Else
      If S1 = 'LOCATION' Then L1^ [35] := S Else
      If S1 = 'PASSWORD_INCORRECT' Then L1^ [36] := S Else
      If S1 = 'YOUR_CHOICE' Then L1^ [37] := S Else
      If S1 = 'PASSWORD' Then L1^ [38] := S Else
      If S1 = 'LOGOFF' Then L1^ [39] := S Else
      If S1 = 'QUIT' Then L1^ [40] := S Else
      If S1 = 'HANGUPINACTIVE' Then L1^ [41] := S Else
      If S1 = 'PAGE_LIMIT' Then L1^ [42] := S Else
      If S1 = 'LIST_HEADER' Then L1^ [43] := S Else
      If S1 = 'ENTER_MSG_LINE' Then L1^ [44] := S Else
      If S1 = 'POST_MENU' Then L1^ [45] := S Else
      If S1 = 'NOMOREMESSAGES' Then L1^ [46] := S Else
      If S1 = 'ERRORMBCLOSE' Then L1^ [47] := S Else
      If S1 = 'MSGFROM' Then L1^ [48] := S Else
      If S1 = 'MSGTO' Then L1^ [49] := S Else
      If S1 = 'MSGSUBJ' Then L1^ [50] := S Else
      If S1 = 'NEXT_MSG' Then L1^ [51] := S Else
      If S1 = 'MSGDATE' Then L1^ [52] := S Else
      If S1 = 'CHANGE' Then L1^ [53] := S Else
      If S1 = 'ERRORSAVEMSG' Then L1^ [54] := S Else
      If S1 = 'MSGSAVED' Then L1^ [55] := S Else
      If S1 = 'SELECTMAREA' Then L1^ [56] := S Else
      If S1 = 'MSGSTRING' Then L1^ [57] := S Else
      If S1 = 'MSGOPENERROR' Then L1^ [58] := S Else
      If S1 = 'NOMSGSINAREA' Then L1^ [59] := S Else
      If S1 = 'YES' Then L1^ [60] := S Else
      If S1 = 'NO' Then L1^ [61] := S Else
      If S1 = 'INFOLOC' Then L1^ [62] := S Else
      If S1 = 'INFONAME' Then L1^ [63] := S Else
      If S1 = 'INFOORG' Then L1^ [64] := S Else
      If S1 = 'INFOADDRESS1' Then L1^ [65] := S Else
      If S1 = 'INFOADDRESS2' Then L1^ [66] := S Else
      If S1 = 'INFOADDRESS3' Then L1^ [67] := S Else
      If S1 = 'INFOHPHONE' Then L1^ [68] := S Else
      If S1 = 'INFOBPHONE' Then L1^ [69] := S Else
      If S1 = 'INFOFIRSTCALL' Then L1^ [70] := S Else
      If S1 = 'INFOLASTCALL' Then L1^ [71] := S Else
      If S1 = 'INFOBIRTHDATE' Then L1^ [72] := S Else
      If S1 = 'INFOSECURITY' Then L1^ [73] := S Else
      If S1 = 'INFOCALLNUM' Then L1^ [74] := S Else
      If S1 = 'WANTTOREG' Then L1^ [75] := S Else
      If S1 = 'YOURNAMENOTFOUND' Then L1^ [76] := S Else
      If S1 = 'SCANMSGAREAS' Then L1^ [77] := S Else
      If S1 = 'SCANFILEAREAS' Then L1^ [78] := S Else
      If S1 = 'SEND_FILE' Then L1^ [79] := S Else
      If S1 = 'RECV_FILE' Then L1^ [80] := S Else
      If S1 = 'LINE_STATUS' Then L1^ [81] := S Else
      If S1 = 'FILE_NAME' Then L1^ [82] := S Else
      If S1 = 'NOT_FOUND' Then L1^ [83] := S Else
      If S1 = 'MSG_WRITE_TEXT1' Then L1^ [84] := S Else
      If S1 = 'TOO_SLOW' Then L1^ [85] := S Else
      If S1 = 'MSG_WRITE_TEXT2' Then L1^ [86] := S Else
      If S1 = 'MSG_WRITE_TEXT3' Then L1^ [87] := S Else
      If S1 = 'MSG_WRITE_FOOTER' Then L1^ [88] := S Else
      If S1 = 'MSG_NO_PREVIOUS' Then L1^ [89] := S Else
      If S1 = 'ALIAS' Then L1^ [90] := S Else
      If S1 = 'YES_NO_QUIT' Then L1^ [91] := S Else
      If S1 = 'PRIVATE_MSG_FROM' Then L1^ [92] := S Else
      If S1 = 'PRIVATE_MSG_AREA' Then L1^ [93] := S Else
      If S1 = 'PRIVATE_WANT_READ' Then L1^ [94] := S Else
      If S1 = 'MSG_UNKNOWN_FORMAT' Then L1^ [95] := S Else
      If S1 = 'PRIVATE_WANT_ANSWER' Then L1^ [96] := S Else
      If S1 = 'MSG_DELETE' Then L1^ [97] := S Else
      If S1 = 'USER_NAME' Then L1^ [98] := S Else
      If S1 = 'MULTI_MSGS_ENABLE' Then L1^ [99] := S Else
      If S1 = 'MULTI_MSGS_DISABLE' Then L1^ [100] := S Else
      If S1 = 'YES_NO' Then L1^ [101] := S Else
      If S1 = 'NO_YES' Then L1^ [102] := S Else
      If S1 = 'MULTI_LINE_DISABLE' Then L1^ [103] := S Else
      If S1 = 'MULTI_SEND_MSG' Then L1^ [104] := S Else
      If S1 = 'NAME_ENTERED' Then L1^ [105] := S Else
      If S1 = 'BEGIN_CHAT' Then L1^ [106] := S Else
      If S1 = 'END_CHAT' Then L1^ [107] := S Else
      If S1 = 'ADDRESS1' Then L1^ [108] := S Else
      If S1 = 'ADDRESS2' Then L1^ [109] := S Else
      If S1 = 'ADDRESS3' Then L1^ [110] := S Else
      If S1 = 'USER' Then L1^ [111] := S Else
      If S1 = 'USER_NOT_FOUND' Then L1^ [112] := S Else
      If S1 = 'MORE_NUMS' Then L1^ [113] := S Else
      If S1 = 'UL_DESCRIPTION' Then L1^ [114] := S Else
      If S1 = 'NO_FILES_SELECTED' Then L1^ [115] := S Else
      If S1 = 'DEL_FILE_NO' Then L1^ [116] := S Else
      If S1 = 'FORBIDDEN_PASSWORD' Then L1^ [117] := S Else
      If S1 = 'ADD_FILE' Then L1^ [118] := S Else
      If S1 = 'ADD_FILE_NOT_FOUND' Then L1^ [119] := S Else
      If S1 = 'DL_LIMIT_EXCEED' Then L1^ [120] := S Else
      If S1 = 'CLEAR_LIST' Then L1^ [121] := S Else
      If S1 = 'CHOOSE_FILEAREA' Then L1^ [122] := S Else
      If S1 = 'USE_FRAMES' Then L1^ [123] := S Else
      If S1 = 'SEARCH_PRIV_MAIL' Then L1^ [124] := S Else
      If S1 = 'FIND_NEW_FILES' Then L1^ [125] := S Else
      If S1 = 'IS_REG_CORRECT' Then L1^ [126] := S Else
      If S1 = 'FILES_MORE' Then L1^ [127] := S Else
      If S1 = 'TAG_FILES_NUM' Then L1^ [128] := S Else
      If S1 = 'ARCNAME' Then L1^ [129] := S Else
      If S1 = 'ARC_HEAD' Then L1^ [130] := S Else
      If S1 = 'QUERY_ARCNAME' Then L1^ [131] := S Else
      If S1 = 'NEW_SINCE_DATE' Then L1^ [132] := S Else
      If S1 = 'PASSWORD_TOO_SHORT1' Then L1^ [133] := S Else
      If S1 = 'PASSWORD_TOO_SHORT2' Then L1^ [134] := S Else
      If S1 = 'MISSING' Then L1^ [135] := S Else
      If S1 = 'USERS_HEAD' Then L1^ [136] := S Else
      If S1 = 'NO_FILES_FOUND' Then L1^ [137] := S Else
      If S1 = 'DL_TIME_LIMIT' Then L1^ [138] := S Else
      If S1 = 'TODAYS_HEAD' Then L1^ [139] := S Else
      If S1 = 'PRIVATE' Then L1^ [140] := S Else
      If S1 = 'OUT_PAGE_TIME' Then L1^ [141] := S Else
      If S1 = 'QUOTE_MSG' Then L1^ [142] := S Else
      If S1 = 'OUT_OF_TRYES' Then L1^ [143] := S Else
      If S1 = 'EVENT_TIME_LEFT' Then L1^ [144] := S Else
      If S1 = 'EVENT_EXIT' Then L1^ [145] := S Else
      If S1 = 'MINUTES' Then L1^ [146] := S Else
      If S1 = 'USE_HOT_KEYS' Then L1^ [147] := S Else
      If S1 = 'PAUSE_AFTER_EACH' Then L1^ [148] := S Else
      If S1 = 'CHOOSE_EMULATION' Then L1^ [149] := S Else
      If S1 = 'UPLOAD_PLUS' Then L1^ [150] := S Else
      If S1 = 'LOCAL_DOWNLOAD' Then L1^ [151] := S Else
      If S1 = 'LOCAL_UPLOAD' Then L1^ [152] := S Else
      If S1 = 'LOCAL_UL_FILE_NAME' Then L1^ [153] := S Else
      If S1 = 'LOCAL_DL_DIR' Then L1^ [154] := S Else
      If S1 = 'LD_DIR_NOT_FOUND' Then L1^ [155] := S Else
      If S1 = 'LU_FILE_NOT_FOUND' Then L1^ [156] := S Else
      If S1 = 'COPY_FILE' Then L1^ [157] := S Else
      If S1 = 'COPY_TO' Then L1^ [158] := S Else
      If S1 = 'COPY_OK' Then L1^ [159] := S Else
      If S1 = 'COPY_ERROR' Then L1^ [160] := S;

      If S1 = 'COPY_DONE' Then L1^ [161] := S Else
      If S1 = 'UPLOAD_SPACE' Then L1^ [162] := S Else
      If S1 = 'NO_NEW_FOUND' Then L1^ [163] := S Else
      If S1 = 'ABORTED' Then L1^ [164] := S Else
      If S1 = 'FREE_DL' Then L1^ [165] := S Else
      If S1 = 'ASK_FILE_NAME' Then L1^ [166] := S Else
      If S1 = 'DOWNLOAD_DESCS' Then L1^ [167] := S Else
      If S1 = 'NO_PRIV_FOUND' Then L1^ [168] := S Else
      If S1 = 'PREV_TAG_AREA' Then L1^ [169] := S;

      If S1 = 'NEXT_BULLETIN' Then L1^ [170] := S Else
      If S1 = 'SEARCH_MASK' Then L1^ [171] := S Else
      If S1 = 'ML_MESSAGE_FROM' Then L1^ [172] := S Else
      If S1 = 'ML_LINE' Then L1^ [173] := S Else
      If S1 = 'LINE_NUMBER' Then L1^ [174] := S Else
      If S1 = 'UPLOAD_MSG' Then L1^ [175] := S Else
      If S1 = 'LOGIN_NALLOW' Then L1^ [176] := S Else
      If S1 = 'DOWNLOAD_HEAD1' Then L1^ [177] := S Else
      If S1 = 'DOWNLOAD_HEAD2' Then L1^ [178] := S Else
      If S1 = 'DOWNLOAD_HEAD3' Then L1^ [179] := S Else
      If S1 = 'DOWNLOAD_HEAD4' Then L1^ [180] := S Else
      If S1 = 'PRIV_MSG' Then L1^ [181] := S Else
      If S1 = 'QUERY_PRIVATE' Then L1^ [182] := S Else
      If S1 = 'ENTER_MSG_NUMS' Then L1^ [183] := S Else
      If S1 = 'REG_RESUME' Then L1^ [184] := S Else
      If S1 = 'ON_ANOTHER_LINE' Then L1^ [185] := S Else
      If S1 = 'CHOOSE_FILEGROUP' Then L1^ [186] := S Else
      If S1 = 'NOT_ARCHIVE' Then L1^ [187] := S Else
      If S1 = 'CHOOSE_MSGGROUP' Then L1^ [188] := S Else
      If S1 = 'FROMUSERS_AREA' Then L1^ [189] := S Else
      If S1 = 'FILE_FROM_USER' Then L1^ [190] := S Else
      If S1 = 'UL_FOR_USER' Then L1^ [191] := S Else
      If S1 = 'MSG_KEYS' Then L1^ [192] := S Else
      If S1 = 'FAREA_KEYS' Then L1^ [193] := S Else
      If S1 = 'POST_KEYS' Then L1^ [194] := S Else
      If S1 = 'YESNO_KEYS' Then L1^ [195] := S Else
      If S1 = 'YESNOQUIT_KEYS' Then L1^ [196] := S Else
      If S1 = 'LOGOFF_MESSAGE' Then L1^ [197] := S Else
      If S1 = 'USE_FS_EDITOR' Then L1^ [198] := S Else
      If S1 = 'FS_ANSI_REQUIRED' Then L1^ [199] := S Else
      If S1 = 'UL_ALREADY_EXISTS' Then L1^ [200] := S Else
      If S1 = 'ARE_YOU_SURE' Then L1^ [201] := S Else
      If S1 = 'MSG_FROM_SYSOP' Then L1^ [202] := S Else
      If S1 = 'CANNOT_CONNECT' Then L1^ [203] := S Else
      If S1 = 'TELNET_TRYING' Then
        L1^ [204] := S
      Else
      If S1 = 'ENTER_USER_NUMS' Then L1^ [205] := S Else
      If S1 = 'QWK_SCAN' Then L1^ [206] := S Else
      If S1 = 'QWK_AJUST' Then L1^ [207] := S Else
      If S1 = 'QWK_NOT_FOUND' Then L1^ [208] := S Else
      If S1 = 'REP_OK' Then L1^ [209] := S Else
      If S1 = 'XLATS' Then L1^ [210] := S Else
      If S1 = 'CHOOSE_XLAT' Then L1^ [211] := S Else
      If S1 = 'QWK_LIST' Then L1^ [212] := S Else
      If S1 = 'QWK_ENTERNUM' Then L1^ [213] := S Else
      If S1 = 'QWK_CONTROLS' Then L1^ [214] := S Else
      If S1 = 'NO_QWK_AREAS' Then L1^ [215] := S Else
      If S1 = 'QWK_ADDED' Then L1^ [216] := S Else
      If S1 = 'QWK_DELETED' Then L1^ [217] := S Else
      If S1 = 'TELNET_CONNECTED' Then L1^ [218] := S Else
      If S1 = 'TELNET_CONNECTION' Then L1^ [219] := S Else
      If S1 = 'TELNET_CLOSED' Then L1^ [220] := S Else
      If S1 = 'UNABLE_TO_RESOLVE' Then L1^ [221] := S Else
      If S1 = 'DEL_LINES' Then L1^ [222] := S Else
      If S1 = 'FSEDIT_STATUSLINE' Then L1^ [223] := S Else
      If S1 = 'TRC_LINE' Then L1^ [224] := S Else
      If S1 = 'TRC_JOINED' Then L1^ [225] := S Else
      If S1 = 'TRC_LEFT' Then L1^ [226] := S Else
      If S1 = 'TRC_ENTERED' Then L1^ [227] := S Else
      If S1 = 'TRC_LEFT_BBS' Then L1^ [228] := S;
    End;
  End;

  For i := 0 To laLastLanguageNum Do L^. InsLine (L1^ [i]);
  Dispose (L1);
  ParserClose (P);
  ReadLanguage := True;
End;

Function opMsgAreas (FileName: PathStr): Boolean;
Begin
  opMsgAreas := ParserOpen (msa, FileName, tpoWriteLog);
End;

Function rMsgArea (Var MsgArea: tMsgArea): Boolean;
Var
  Bt : Byte;

Procedure ExecParams;
Begin
  With MsgArea Do
  Begin
    If S1 = 'NAME' Then ParserGetParam (msa, tptQuote, '', Name) Else
    If S1 = 'GATEWAY' Then ParserGetParam (msa, tptString, '', GateWay) Else
    If S1 = 'ORIGIN' Then ParserGetParam (msa, tptQuote, '', Origin) Else
    If S1 = 'BASETYPE' Then ParserGetParam (msa, tptFixedList, 'JAM SQUISH HUDSON FIDO', BaseType) Else
    If S1 = 'PRIVATE' Then ParserGetParam (msa, tptExtBoolean, '', Private) Else
    If S1 = 'BOARDNUM' Then ParserGetParam (msa, tptWord, '', BoardNum) Else
    If S1 = 'SCAN_PRIVMAIL' Then ParserGetParam (msa, tptBoolean, '', ScanPrivMail) Else
    If S1 = 'BASEPATH' Then
    Begin
      ParserGetParam (msa, tptFilePath, '', S1);
      BasePath := AddBackSlash (S1);
    End Else
    If S1 = 'GROUP' Then
    Begin
      ParserGetParam (msa, tptString, '', S1);
      Group := UpString (S1);
    End Else
    If S1 = 'TYPE' Then
    Begin
      ParserGetParam (msa, tptFixedList, 'ECHOMAIL NETMAIL', Bt);
      Byte (AreaType) := Bt+1;
    End Else
    If S1 = 'USEADDRESS' Then
    Begin
      ParserGetParam (msa, tptString, '', S1);
      ParseStrAddr (S1, Address);
    End Else
    If S1 = 'READ_SECURITY' Then
    Begin
      ParserGetParam (msa, tptString, '', S1);
      Str2Security (S1, ReadSec, S1);
      ReadFlags := S1;
    End Else
    If S1 = 'WRITE_SECURITY' Then
    Begin
      ParserGetParam (msa, tptString, '', S1);
      Str2Security (S1, WriteSec, S1);
      WriteFlags := S1;
    End Else
    If S1 = 'SHOW_SECURITY' Then
    Begin
      ParserGetParam (msa, tptString, '', S1);
      Str2Security (S1, ShowSec, S1);
      ShowFlags := S1;
    End Else
    If S1 = 'SYSOP_SECURITY' Then
    Begin
      ParserGetParam (msa, tptString, '', S1);
      Str2Security (S1, SysOpSec, S1);
      SysOpFlags := S1;
    End;
  End;
End;

Begin
  FillChar (MsgArea, SizeOf (MsgArea), 0);

  If ParserEnd (msa) Then rMsgArea := False Else
  Begin
    rMsgArea := True;
    S1 := ParserRead (msa, S2); S2 := '';

    Repeat
      If S2 = '' Then ExecParams Else
      Begin
        ParserGetBack (msa);
        Exit;
      End;

      S1 := ParserRead (msa, S2);
    Until ParserEnd (msa);

  End;
End;

Procedure pMsgDone;
Begin
  ParserClose (msa);
End;

Function opFileAreas (FileName: PathStr): Boolean;
Begin
  opFileAreas := True;
  If fla. Handle <> Nil Then Exit;
  opFileAreas := ParserOpen (fla, FileName, tpoWriteLog);
End;

Function rFileArea (Var FileArea: tFileArea): Boolean;
Begin
  FillChar (FileArea, SizeOf (FileArea), 0);

  With FileArea Do
  Begin
    Name := '';
    DLPath := '';
    FileList := FileArea. DLPath + 'files.bbs';
  End;

  rFileArea := Not ParserEnd (fla);
  S1 := ParserRead (fla, S2); S2 := '';

  While (S2 = '') And Not ParserEnd (fla) Do
  With FileArea Do
  Begin
    If S1 = 'NAME' Then ParserGetParam (fla, tptQuote, '', Name) Else
    If S1 = 'DLPATH' Then
    Begin
      ParserGetParam (fla, tptFilePath, '', S1);
      If DLPath <> '' Then DLPath := DLPath + #255;
      DLPath := DLPath + S1;
    End Else
    If S1 = 'ULPATH' Then
    Begin
      ParserGetParam (fla, tptFilePath, '', ULPath);
      ULPath := AddBackSlash (ULPath);
    End Else
    If S1 = 'FILELIST' Then ParserGetParam (fla, tptFilePath, '', FileList) Else
    If S1 = 'SCAN_NEWFILES' Then ParserGetParam (fla, tptBoolean, '', ScanNewFiles) Else
    If S1 = 'COPY_LOCAL' Then ParserGetParam (fla, tptBoolean, '', CopyLocal) Else
    If S1 = 'DL_SECURITY' Then
    Begin
      ParserGetParam (fla, tptString, '', S1);
      Str2Security (S1, DL_Security, S1);
      DL_Flags := S1;
    End Else
    If S1 = 'UL_SECURITY' Then
    Begin
      ParserGetParam (fla, tptString, '', S1);
      Str2Security (S1, UL_Security, S1);
      UL_Flags := S1;
    End Else
    If S1 = 'LIST_SECURITY' Then
    Begin
      ParserGetParam (fla, tptString, '', S1);
      Str2Security (S1, List_Security, S1);
      List_Flags := S1;
    End Else
    If S1 = 'SHOW_SECURITY' Then
    Begin
      ParserGetParam (fla, tptString, '', S1);
      Str2Security (S1, ShowSec, S1);
      ShowFlags := S1;
    End Else
    If S1 = 'MIN_SPEED' Then ParserGetParam (fla, tptWord, '', MinSpeed) Else
    If S1 = 'GROUP' Then
    Begin
      ParserGetParam (fla, tptString, '', S1);
      Group := UpString (S1);
    End Else
    If S1 = 'FLIST_FORMAT' Then
    Begin
      ParserGetParam (fla, tptFixedList, 'FILESBBS CD-LIST', FListFormat);
      If FListFormat = fCDList Then DLPath := ExtractWord (1, DLPath, [#255]);
    End;

    S1 := ParserRead (fla, S2);
  End;

  If S2 <> '' Then ParserGetBack (fla);
End;

Procedure pFileDone;
Begin
  ParserClose (fla);
End;

Function opFileGroups (FileName: PathStr): Boolean;
Begin
  opFileGroups := True;
  If fgr. Handle <> Nil Then Exit;
  opFileGroups := ParserOpen (fgr, FileName, tpoWriteLog);
End;

Function rFileGroup (Var FileGroup: tFileGroup): Boolean;
Begin
  If ParserEnd (fgr) Then rFileGroup := False Else
  With FileGroup Do
  Begin
    rFileGroup := True;
    S1 := ParserRead (fgr, S2); S2 := '';

    While Not ParserEnd (fgr) And (S2 = '') Do
    Begin
      If S1 = 'NAME' Then ParserGetParam (fgr, tptQuote, '', Name) Else
      If S1 = 'SHOW_SECURITY' Then
      Begin
        ParserGetParam (fgr, tptString, '', S1);
        Str2Security (S1, ShowSec, S1);
        ShowFlags := S1;
      End Else
      If S1 = 'GROUP_TAG' Then
      Begin
        ParserGetParam (fgr, tptString, '', S1);
        Tag := DelSpaces (UpString (S1));
      End;

      S1 := ParserRead (fgr, S2);
    End;

    If S2 <> '' Then ParserGetBack (fgr);
  End;
End;

Procedure pFileGroupDone;
Begin
  ParserClose (fgr);
End;

Function opMsgGroups (FileName: PathStr): Boolean;
Begin
  opMsgGroups := True;
  If mgr. Handle <> Nil Then Exit;
  opMsgGroups := ParserOpen (mgr, FileName, tpoWriteLog);
End;

Function rMsgGroup (Var MsgGroup: tMsgGroup): Boolean;
Begin
  FillChar (MsgGroup, SizeOf (MsgGroup), 0);

  If ParserEnd (mgr) Then rMsgGroup := False Else
  With MsgGroup Do
  Begin
    rMsgGroup := True;
    S1 := ParserRead (mgr, S2); S2 := '';

    While Not ParserEnd (mgr) And (S2 = '') Do
    Begin
      If S1 = 'NAME' Then ParserGetParam (mgr, tptQuote, '', Name) Else
      If S1 = 'SHOW_SECURITY' Then
      Begin
        ParserGetParam (mgr, tptString, '', S1);
        Str2Security (S1, ShowSec, S1);
        ShowFlags := S1;
      End Else
      If S1 = 'GROUP_TAG' Then
      Begin
        ParserGetParam (mgr, tptString, '', S1);
        Tag := DelSpaces (UpString (S1));
      End;

      S1 := ParserRead (mgr, S2);
    End;

    If S2 <> '' Then ParserGetBack (mgr);
  End;
End;

Procedure pMsgGroupDone;
Begin
  ParserClose (mgr);
End;

Function opUpgrades (FileName: PathStr): Boolean;
Begin
  opUpgrades := True;
  If upg. Handle <> Nil Then Exit;
  opUpgrades := ParserOpen (upg, FileName, tpoWriteLog);
End;

Function rUpgrade (CallNum: LongInt; Var UR: UpgradeRec): Boolean;
Var
  Date, Mask : String [12];
  UR1 : UpgradeRec;
  Reason : String;

Function CheckBirthDay: Boolean;
Begin
  CheckBirthDay :=
    ((Reason = 'BIRTHDAY') and (CallNum = -1)) And
    ((R. LastDate <> DateL) or (R. NoCalls = 1));
End;

Function CheckDate: Boolean;
Begin
  CheckDate := False;
  If Trim (ExtractWord (1, Reason, [' ', '('])) = 'DATE' Then
  Begin
    Date := ReFormatDate (StrDate, 'DD-MM-YYYY', Cnf. DateMask);
    Mask := Trim (ExtractWord (2, Reason, ['(', ')']));

    If DateMatch (Date, Mask) Then
    If (R. LastDate <> DateL) or (R. NoCalls = 1) Then CheckDate := True;
  End;
End;

Function CheckCall: Boolean;
Begin
  CheckCall := False;

  If Trim (ExtractWord (1, Reason, [' ', '('])) = 'CALL' Then
  If Str2Long (Trim (ExtractWord (2, Reason, ['(', ')']))) = CallNum Then CheckCall := True;
End;

Begin
  FillChar (UR1, SizeOf (UR1), 0);
  rUpgrade := False;
  S1 := ParserRead (upg, S2); S2 := '';

  If ParserEnd (upg) Then Exit Else
  With UR1 Do
  Repeat
    If S2 = '' Then
    Begin
      If S1 = 'REASON' Then
      Begin
        ParserGetParam (upg, tptString, '', S1);
        Reason := UpString (S1);
      End Else
      If S1 = 'FILENAME' Then ParserGetParam (upg, tptFilePath, '', FileName) Else
      If S1 = 'CHECK' Then ParserGetParam (upg, tptString, '', Check) Else
      If S1 = 'SECURITY' Then ParserGetParam (upg, tptString, '', Security) Else
      If S1 = 'MODE' Then ParserGetParam (upg, tptFixedList, 'FOREVER TODAY', Mode) Else
      If S1 = 'INFORM_VIA' Then
      Begin
        ParserGetParam (upg, tptString, '', S1);
        InformVia := UpString (S1);
      End;

      S1 := ParserRead (upg, S2);
    End Else
    Begin
      If CheckBirthDay Or CheckDate Or CheckCall Then
      Begin
        UR := UR1;
        rUpgrade := True;
        Exit;
      End Else
        S2 := '';
    End;
  Until ParserEnd (upg);
End;

Procedure pUpgradeDone;
Begin
  ParserClose (upg);
End;

Function ReadDoorWay (FileName: PathStr; Var R: DoorWayControlRec): Boolean;
Var
  C : Char;
  M : tDoorWayCommands;
  dwc : tConfigParser;

Begin
  If Not ParserOpen (dwc, FileName, tpoWriteLog) Then ReadDoorWay := False Else
  With R Do
  Begin
    S1 := ''; S2 := '';
    FillChar (R, SizeOf (R), 0);
    For C := 'A' To 'Z' Do Drives [C] := 65535;
    For M := hcCls To hcKill Do SecLevel [M] := 65535;

    Repeat

      If S2 = 'DRIVES' Then
      Begin
        If Copy (S1, 1, 6) = 'DRIVE_'
        Then
          ParserGetParam (dwc, tptWord, '', Drives [UpCase (S1 [7])])
        Else
        If S1 = 'START_DIR' Then
        Begin
          ParserGetParam (dwc, tptFilePath, '', S1);
          StartDir := AddBackSlash (S1);

          If (StartDir [Length (StartDir)] = '\') and
             (StartDir [Length (StartDir)-1] <> ':')
          Then StartDir := Copy (StartDir, 1, Length (StartDir)-1);
        End;
      End Else
      If S2 = 'RESTRICTIONS' Then
      Begin
        If S1 = 'ENTER_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, Enter, S1);
          flgEnter := S1;
        End Else
        If S1 = 'CHDIR_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcCD], S1);
          SecFlags [hcCD] := S1;
        End Else
        If S1 = 'TREE_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcTree], S1);
          SecFlags [hcTree] := S1;
        End Else
        If S1 = 'MKDIR_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcMkDir], S1);
          SecFlags [hcMkDir] := S1;
        End Else
        If S1 = 'RMDIR_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcRmDir], S1);
          SecFlags [hcRmDir] := S1;
        End Else
        If S1 = 'DIRSIZE_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcDirSize], S1);
          SecFlags [hcDirSize] := S1;
        End Else
        If S1 = 'DIR_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcDir], S1);
          SecFlags [hcDir] := S1;
        End Else
        If S1 = 'DEL_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcDel], S1);
          SecFlags [hcDel] := S1;
        End Else
        If S1 = 'REN_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcRename], S1);
          SecFlags [hcRename] := S1;
        End Else
        If S1 = 'COPY_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcCopy], S1);
          SecFlags [hcCopy] := S1;
        End Else
        If S1 = 'BOOT_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcBoot], S1);
          SecFlags [hcBoot] := S1;
        End Else
        If S1 = 'SHELL_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcShell], S1);
          SecFlags [hcShell] := S1;
        End Else
        If S1 = 'XDEL_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcxDel], S1);
          SecFlags [hcxDel] := S1;
        End Else
        If S1 = 'UL_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcUL], S1);
          SecFlags [hcUL] := S1;
        End Else
        If S1 = 'DL_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcDL], S1);
          SecFlags [hcDL] := S1;
        End Else
        If S1 = 'DATE_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcDate], S1);
          SecFlags [hcDate] := S1;
        End Else
        If S1 = 'TIME_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcTime], S1);
          SecFlags [hcTime] := S1;
        End Else
        If S1 = 'ARCVIEW_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcArcView], S1);
          SecFlags [hcArcView] := S1;
        End Else
        If S1 = 'TYPE_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcType], S1);
          SecFlags [hcType] := S1;
        End Else
        If S1 = 'LOCATE_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcLocate], S1);
          SecFlags [hcLocate] := S1;
        End Else
        If S1 = 'TASKLIST_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcTaskList], S1);
          SecFlags [hcTaskList] := S1;
        End Else
        If S1 = 'KILL_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcKill], S1);
          SecFlags [hcKill] := S1;
        End Else
        If S1 = 'EDIT_SECURITY' Then
        Begin
          ParserGetParam (dwc, tptString, '', S1);
          Str2Security (S1, SecLevel [hcEdit], S1);
          SecFlags [hcEdit] := S1;
        End;
      End;

      S1 := ParserRead (dwc, S2);

    Until ParserEnd (dwc);

    ReadDoorWay := True;
    ParserClose (dwc);
  End;
End;

Var
  fTime1, fTime2 : LongInt;
  BinName        : PathStr;
  F1             : {$IFNDEF WIN32} File {$ELSE} THandle {$ENDIF};

Function OpenFileAreas: Boolean;
Var
  R     : tFileArea;
  i     : LongInt;

Label
  ReRead;

Begin
  BinName := AddBackSlash (JustPathName (Cnf. FileAreasFile)) + 'filearea' + LineExt;
  OpenFileAreas := True;

  If faOpen Then Exit;
  fTime1 := -1; fTime2 := -1;

  If Not FileExists (Cnf. FileAreasFile) Then
  Begin
    OpenFileAreas := False;
    Exit;
  End Else
  Begin
  {$IFNDEF WIN32}
    Assign (F1, Cnf. FileAreasFile);
    ReSet (F1);
    GetFTime (F1, fTime1); Close (F1);
  {$ELSE}
    F1 := FileOpen (Cnf. FileAreasFile, fmOpenRead or fmShareDenyNone);
    fTime1 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End;

  If FileExists (BinName) Then
  Begin
  {$IFNDEF WIN32}
    Assign (F1, BinName); ReSet (F1);
    GetFTime (F1, fTime2); Close (F1);
  {$ELSE}
    F1 := FileOpen (BinName, fmOpenRead or fmShareDenyNone);
    fTime2 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End;

  FillChar (Hdr, SizeOf (Hdr), #0);
  Assign (faFile, BinName);
  ReSet (faFile, 1);

  If IOResult = 0 Then
  Begin
    BlockRead (faFile, Hdr, SizeOf (Hdr));
    fAreasAmount := Trunc ((FileSize (faFile)-SizeOf (Hdr))/SizeOf (tFileArea));
  End Else
    ReWrite (faFile, 1);

  If (fTime1 <> fTime2) Or ReCompile Or Not BinaryHdrValid (Hdr) Then
  Begin
    ReRead:
    ReWrite (faFile, 1);
    fAreasAmount := 0;
    BlockWrite (faFile, OrigHdr, SizeOf (tBinaryHdr));

    If opFileAreas (Cnf. FileAreasFile) Then
    While rFileArea (R) Do
    Begin
      BlockWrite (faFile, R, SizeOf (R));
      Inc (fAreasAmount);
    End;

    pFileDone;

  {$IFNDEF WIN32}
    SetFTime (faFile, fTime1);
  {$ENDIF}
    Close (faFile);
    ReSet (faFile, 1);

  {$IFDEF WIN32}
    F1 := FileOpen (BinName, fmOpenWrite or fmShareDenyNone);
    FileSetDate (F1, fTime1);
    FileClose (F1);
  {$ENDIF}

  End;
  faOpen := True;
End;

Function ReadFileArea (Var FileArea: tFileArea; Num: System. Word): Boolean;
Label
  Loop;

Begin
  If IOResult <> 0 Then;
  ReadFileArea := False; FillChar (FileArea, SizeOf (FileArea), #0);
  If Trunc ((FileSize (faFile)-SizeOf (Hdr))/SizeOf (FileArea)) < Num Then Exit;

  Loop:
  FPoz := SizeOf (FileArea);
  FPoz := SizeOf (Hdr) + FPoz*(Num-1);
  Seek (faFile, FPoz);
  BlockRead (faFile, FileArea, SizeOf (FileArea));

  rIO := IOResult;
  ReadFileArea := (rIO = 0);

  If (rIO = 100) And (Not EoF (faFile)) Then
  Begin
    ReCompile := True;
    CloseFileAreas;
    OpenFileAreas;
    ReCompile := False;
    GoTo Loop;
  End;
End;

Procedure CloseFileAreas;
Begin
  If faOpen Then Close (faFile);
  faOpen := False;
End;

Function OpenMsgAreas: Boolean;
Var
  R : tMsgArea;

Label
  ReRead;

Begin
  BinName := AddBackSlash (JustPathName (Cnf. MsgAreasFile)) + 'msgarea' + LineExt;
  OpenMsgAreas := True;

  If maOpen Then Exit;
  fTime1 := -1; fTime2 := -1;

  If Not FileExists (Cnf. MsgAreasFile) Then
  Begin
    OpenMsgAreas := False;
    Exit;
  End Else
  Begin
  {$IFNDEF WIN32}
    Assign (F1, Cnf. MsgAreasFile); ReSet (F1);
    GetFTime (F1, fTime1); Close (F1);
  {$ELSE}
    F1 := FileOpen (Cnf. MsgAreasFile, fmOpenRead or fmShareDenyNone);
    fTime1 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End;

  If FileExists (BinName) Then
  Begin
  {$IFNDEF WIN32}
    Assign (F1, BinName); ReSet (F1);
    GetFTime (F1, fTime2); Close (F1);
  {$ELSE}
    F1 := FileOpen (BinName, fmOpenRead or fmShareDenyNone);
    fTime2 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End;

  FillChar (Hdr, SizeOf (Hdr), #0);
  Assign (maFile, BinName);
  ReSet (maFile, 1);

  If IOResult = 0 Then
  Begin
    BlockRead (maFile, Hdr, SizeOf (Hdr));
    mAreasAmount := Trunc ((FileSize (maFile)-SizeOf (Hdr))/SizeOf (tMsgArea));
  End Else
    ReWrite (maFile, 1);

  If (fTime1 <> fTime2) Or ReCompile Or Not BinaryHdrValid (Hdr) Then
  Begin
    ReRead:
    ReWrite (maFile, 1);
    mAreasAmount := 0;
    BlockWrite (maFile, OrigHdr, SizeOf (tBinaryHdr));

    If opMsgAreas (Cnf. MsgAreasFile) Then
    While rMsgArea (R) Do
    Begin
      BlockWrite (maFile, R, SizeOf (R));
      Inc (mAreasAmount);
    End;

    pMsgDone;

  {$IFNDEF WIN32}
    SetFTime (maFile, fTime1);
  {$ENDIF}
    Close (maFile);
    ReSet (maFile, 1);
  {$IFDEF WIN32}
    F1 := FileOpen (BinName, fmOpenWrite or fmShareDenyNone);
    FileSetDate (F1, fTime1);
    FileClose (F1);
  {$ENDIF}
  End;

  maOpen := True;
End;

Function ReadMsgArea (Var MsgArea: tMsgArea; Num: System. Word): Boolean;

Label
  Loop;

Begin
  ReadMsgArea := False; FillChar (MsgArea, SizeOf (MsgArea), #0);
  If Trunc ((FileSize (maFile)-SizeOf (Hdr))/SizeOf (MsgArea)) < Num Then Exit;

  Loop:
  FPoz := SizeOf (MsgArea);
  FPoz := SizeOf (Hdr) + FPoz*(Num-1);
  Seek (maFile, FPoz);
  BlockRead (maFile, MsgArea, SizeOf (MsgArea));

  rIO := IOResult;
  ReadMsgArea := (rIO = 0);

  If (rIO = 100) And (Not EoF (maFile)) Then
  Begin
    ReCompile := True;
    CloseMsgAreas; OpenMsgAreas;
    ReCompile := False;
    GoTo Loop;
  End;
End;

Procedure CloseMsgAreas;
Begin
  If maOpen Then Close (maFile);
  maOpen := False;
End;

Function ReadLimit (Var Limit: LimitRec; L: System. Word): Boolean;
Var
  LimColl  : PLimitCollection;
  tmpLim   : LimitRec;
  i        : System. Word;
  P        : tConfigParser;
  j        : Integer;

Label
  EoP;

Begin
  ReadLimit := False;

  If ParserOpen (P, Cnf. LimitsFile, tpoWriteLog) Then
  Begin
    FillChar (tmpLim, SizeOf (tmpLim), #0);
    LimColl := New (PLimitCollection, Init (5, 5));

    With tmpLim Do
    While Not ParserEnd (P) Do
    Begin
      S1 := ParserRead (P, S2);

      If (S2 <> '') Or (ParserEnd (P) And (S2 = '')) Then
      Begin
        If Level > 0 Then LimColl^. Insert (NewPLimitRec (tmpLim));
        FillChar (tmpLim, SizeOf (tmpLim), #0);
        S2 := '';
      End;

      If S1 = 'LEVEL' Then ParserGetParam (P, tptWord, '', Level) Else
      If S1 = 'TIMELIMIT' Then ParserGetParam (P, tptLongInt, '', Time) Else
      If S1 = 'KBLIMIT' Then ParserGetParam (P, tptLongInt, '', KbLimit) Else
      If S1 = 'MIN_SPEED' Then ParserGetParam (P, tptLongInt, '', MinSpeed) Else
      If S1 = 'MAX_SESSION' Then ParserGetParam (P, tptWord, '', SessionTime) Else
      If S1 = 'LOGIN_TIME' Then
      Begin
        ParserGetParam (P, tptTime, '', S1);
        Str2TimeArray (S1, LoginTime);
      End;
    End;

    ParserClose (P);
    j := LimColl^. Count;

    Repeat
      Dec (j);
      If j >= 0 Then tmpLim := PLimitRec (LimColl^. At (j))^;
    Until (tmpLim. Level <= L) Or (j < 0);

    Limit := tmpLim;
    Limit. Level := L;
    If j >= 0 Then ReadLimit := True;

    While LimColl^. Count > 0 Do
    Begin
      DisposePLimitRec (LimColl^. At (0));
      LimColl^. AtDelete (0);
    End;

    Dispose (LimColl, Done);
  End;
End;

Function OpenFileGroups: Boolean;
Var
  R                     : tFileGroup;
  i                     : LongInt;

Label
  ReRead;

Begin
  BinName := AddBackSlash (JustPathName (Cnf. FileGroupsFile)) + 'fgroups' + LineExt;
  OpenFileGroups := True;

  If fgOpen Then Exit;
  fTime1 := -1; fTime2 := -1;

  If FileExists (Cnf. FileGroupsFile) Then
  Begin
  {$IFNDEF WIN32}
    Assign (F1, Cnf. FileGroupsFile); ReSet (F1);
    GetFTime (F1, fTime1); Close (F1);
  {$ELSE}
    F1 := FileOpen (Cnf. FileGroupsFile, fmOpenRead or fmShareDenyNone);
    fTime1 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End Else
  Begin
    OpenFileGroups := False;
    Exit;
  End;

  If FileExists (BinName) Then
  Begin
  {$IFNDEF WIN32}
    Assign (F1, BinName); ReSet (F1);
    GetFTime (F1, fTime2); Close (F1);
  {$ELSE}
    F1 := FileOpen (BinName, fmOpenRead or fmShareDenyNone);
    fTime2 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End;

  FillChar (Hdr, SizeOf (Hdr), #0);
  Assign (fgFile, BinName);
  ReSet (fgFile, 1);

  If IOResult = 0 Then
  Begin
    BlockRead (fgFile, Hdr, SizeOf (Hdr));
    fGroupsAmount := Trunc ((FileSize (fgFile)-SizeOf (Hdr))/SizeOf (tFileGroup));
  End Else
    ReWrite (fgFile, 1);

  If (fTime1 <> fTime2) Or ReCompile Or Not BinaryHdrValid (Hdr) Then
  Begin
    ReRead:
    ReWrite (fgFile, 1); i := 1; fGroupsAmount := 0;
    BlockWrite (fgFile, OrigHdr, SizeOf (tBinaryHdr));

    If opFileGroups (Cnf. FileGroupsFile) Then
    While rFileGroup (R) Do
    Begin
      BlockWrite (fgFile, R, SizeOf (R));
      Inc (i); Inc (fGroupsAmount);
    End;

    pFileGroupDone;

  {$IFNDEF WIN32}
    SetFTime (fgFile, fTime1);
  {$ENDIF}
    Close (fgFile);
    ReSet (fgFile, 1);
  {$IFDEF WIN32}
    F1 := FileOpen (BinName, fmOpenWrite or fmShareDenyNone);
    FileSetDate (F1, fTime1);
    FileClose (F1);
  {$ENDIF}
  End;
  fgOpen := True;
End;

Function ReadFileGroup (Var FileGroup: tFileGroup; Num: System. Word): Boolean;
Label
  Loop;

Begin
  ReadFileGroup := False;
  If Trunc ((FileSize (fgFile)-SizeOf (Hdr))/SizeOf (FileGroup)) < Num Then Exit;

  Loop:
  FPoz := SizeOf (FileGroup);
  FPoz := SizeOf (Hdr) + FPoz*(Num-1);
  Seek (fgFile, FPoz);
  BlockRead (fgFile, FileGroup, SizeOf (FileGroup));

  rIO := IOResult;
  ReadFileGroup := (rIO = 0);

  If (rIO = 100) And (Not EoF (fgFile)) Then
  Begin
    ReCompile := True;
    CloseFileGroups;
    OpenFileGroups;
    ReCompile := False;
    GoTo Loop;
  End;
End;

Procedure CloseFileGroups;
Begin
  If fgOpen Then Close (fgFile);
  fgOpen := False;
End;

Function OpenMsgGroups: Boolean;
Var
  R                     : tMsgGroup;
  i                     : LongInt;

Label
  ReRead;

Begin
  BinName := AddBackSlash (JustPathName (Cnf. MsgGroupsFile)) + 'mgroups' + LineExt;
  OpenMsgGroups := True;

  If mgOpen Then Exit;
  fTime1 := -1; fTime2 := -1;

  If FileExists (Cnf. MsgGroupsFile) Then
  Begin
  {$IFNDEF WIN32}
    Assign (F1, Cnf. MsgGroupsFile); ReSet (F1);
    GetFTime (F1, fTime1); Close (F1);
  {$ELSE}
    F1 := FileOpen (Cnf. MsgGroupsFile, fmOpenRead or fmShareDenyNone);
    fTime1 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End Else
  Begin
    OpenMsgGroups := False;
    Exit;
  End;

  If FileExists (BinName) Then
  Begin
  {$IFNDEF WIN32}
    Assign (F1, BinName); ReSet (F1);
    GetFTime (F1, fTime2); Close (F1);
  {$ELSE}
    F1 := FileOpen (BinName, fmOpenRead or fmShareDenyNone);
    fTime2 := FileGetDate (F1);
    FileClose (F1);
  {$ENDIF}
  End;

  FillChar (Hdr, SizeOf (Hdr), #0);
  Assign (mgFile, BinName);
  ReSet (mgFile, 1);

  If IOResult = 0 Then
  Begin
    BlockRead (mgFile, Hdr, SizeOf (Hdr));
    mGroupsAmount := Trunc ((FileSize (mgFile)-SizeOf (Hdr))/SizeOf (tMsgGroup));
  End Else
    ReWrite (mgFile, 1);

  If (fTime1 <> fTime2) Or ReCompile Or Not BinaryHdrValid (Hdr) Then
  Begin
    ReRead:
    ReWrite (mgFile, 1); i := 1; mGroupsAmount := 0;
    BlockWrite (mgFile, OrigHdr, SizeOf (tBinaryHdr));

    If opMsgGroups (Cnf. MsgGroupsFile) Then
    While rMsgGroup (R) Do
    Begin
      BlockWrite (mgFile, R, SizeOf (R));
      Inc (i); Inc (mGroupsAmount);
    End;

    pMsgGroupDone;

  {$IFNDEF WIN32}
    SetFTime (mgFile, fTime1);
  {$ENDIF}
    Close (mgFile);
    ReSet (mgFile, 1);
  {$IFDEF WIN32}
    F1 := FileOpen (BinName, fmOpenWrite or fmShareDenyNone);
    FileSetDate (F1, fTime1);
    FileClose (F1);
  {$ENDIF}
  End;

  mgOpen := True;
End;

Function ReadMsgGroup (Var MsgGroup: tMsgGroup; Num: System. Word): Boolean;
Label
  Loop;

Begin
  ReadMsgGroup := False;
  If Trunc ((FileSize (mgFile)-SizeOf (Hdr))/SizeOf (MsgGroup)) < Num Then Exit;

  Loop:
  FPoz := SizeOf (MsgGroup);
  FPoz := SizeOf (Hdr) + FPoz*(Num-1);
  Seek (mgFile, FPoz);
  BlockRead (mgFile, MsgGroup, SizeOf (MsgGroup));

  rIO := IOResult;
  ReadMsgGroup := (rIO = 0);

  If (rIO = 100) And (Not EoF (mgFile)) Then
  Begin
    ReCompile := True;
    CloseMsgGroups;
    OpenMsgGroups;
    ReCompile := False;
    GoTo Loop;
  End;
End;

Procedure CloseMsgGroups;
Begin
  If mgOpen Then Close (mgFile);
  mgOpen := False;
End;

Procedure SetMsgArea (Num: LongInt);
Var
  i, C : Word;
  ta   : tMsgArea;

Function SearchFirst: Boolean;
Var
  i : Word;

Begin
  R. MsgArea := 0;
  C := 0;

  For i := 1 To mAreasAmount Do
  Begin
    If Not ReadMsgArea (ta, i) Then Break;

    If (ta. Name <> '') And (WordInString (MsgGroup. Tag, ta. Group) Or
       (ta. Group = '') Or (mGroupsAmount = 0)) Then
    Begin
      Inc (C);

      If (ta. ShowSec <= R. Security) And FlagsValid
         (R. Flags, ta. ShowFlags) Then
      Begin
        R. MsgArea := C;
        PhysMsgArea := i;
        MsgArea := ta;
        Break;
      End;
    End;

  End;

End;

Begin
  If Num <= 0 Then
  If R. MsgArea <> 0 Then Num := R. MsgArea Else Num := 1;

  If Num > mAreasGroup Then
  Begin
    If MsgArea. Name = '' Then
    Begin
      OpenMsgAreas;
      SearchFirst;
      CloseMsgAreas;
    End;

    If R. MsgArea = 0 Then FillChar (MsgArea, SizeOf (MsgArea), 0);
    Exit;
  End;

  R. MsgArea := 0;

  If OpenMsgAreas Then
  Begin
    C := 0;

    For i := 1 To mAreasAmount Do
    Begin
      If Not ReadMsgArea (ta, i) Then
      Begin
        If i = 1 Then
        Begin
          R. MsgArea := 0;
          FillChar (ta, SizeOf (ta), 0);
        End Else
          SearchFirst;

        Break;
      End;

      If WordInString (MsgGroup. Tag, ta. Group) Or (ta. Group = '') Or
         (mGroupsAmount = 0) And (ta. ShowSec <= R. Security) And
         FlagsValid (R. Flags, ta. ShowFlags) Then
      Begin
        Inc (C);
        If C = Num Then
        Begin
          R. MsgArea := C;
          PhysMsgArea := i;
          MsgArea := ta;
        End;
      End;

    End;

    mmAreasAmount := C;
    CloseMsgAreas;
  End;

  If R. MsgArea = 0 Then FillChar (MsgArea, SizeOf (MsgArea), 0);
End;

Procedure SetFileArea (Num: LongInt);
Var
  i, C : Word;
  ta   : tFileArea;

Function SearchFirst: Boolean;
Var
  i : Word;

Begin
  R. FileArea := 0;
  C := 0;

  For i := 1 To fAreasAmount Do
  Begin
    If Not ReadFileArea (ta, i) Then Break;

    If (ta. Name <> '') And (WordInString (FileGroup. Tag, ta. Group) Or
       (ta. Group = '') Or (fGroupsAmount = 0)) Then
    Begin
      Inc (C);

      If (ta. ShowSec <= R. Security) And FlagsValid
         (R. Flags, ta. ShowFlags) Then
      Begin
        R. FileArea := C;
        PhysFileArea := i;
        FileArea := ta;
        Break;
      End;
    End;
  End;
End;

Begin
  If Num <= 0 Then
  If R. FileArea <> 0 Then Num := R. FileArea Else Num := 1;

  If Num > fAreasGroup Then
  Begin
    If FileArea. Name = '' Then
    Begin
      OpenFileAreas;
      SearchFirst;
      CloseFileAreas;
    End;
    If R. FileArea = 0 Then FillChar (FileArea, SizeOf (FileArea), 0);
    Exit;
  End;

  R. FileArea := 0;

  If OpenFileAreas Then
  Begin
    C := 0;

    For i := 1 To fAreasAmount Do
    Begin
      If Not ReadFileArea (ta, i) Then
      Begin
        If i = 1 Then
        Begin
          R. FileArea := 0;
          FillChar (ta, SizeOf (ta), 0);
        End Else
          SearchFirst;

        Break;
      End;

      If WordInString (FileGroup. Tag, ta. Group) Or (ta. Group = '') Or
         (fGroupsAmount = 0) And (ta. ShowSec <= R. Security) And
         FlagsValid (R. Flags, ta. ShowFlags) Then
      Begin
        Inc (C);
        If C = Num Then
        Begin
          PhysFileArea := i;
          R. FileArea := C;
          FileArea := ta;
        End;
      End;
    End;

    ffAreasAmount := C;
    CloseFileAreas;
  End;

  If R. FileArea = 0 Then FillChar (FileArea, SizeOf (FileArea), 0);
End;

Procedure SetMsgGroup (Num: LongInt);

Procedure SearchFirst;
Begin
  R. MsgGroup := 1;
  ReadMsgGroup (MsgGroup, 1);

  While Not ((MsgGroup. Name <> '') And
        FlagsValid (R. Flags, MsgGroup. ShowFlags) And
        (MsgGroup. ShowSec <= R. Security)) Do
  Begin
    Inc (R. MsgGroup);
    If Not ReadMsgGroup (MsgGroup, R. MsgGroup) Then Break;
  End;

  If Not ((MsgGroup. Name <> '') And
     FlagsValid (R. Flags, MsgGroup. ShowFlags) And
     (MsgGroup. ShowSec <= R. Security)) Then
  Begin
    FillChar (MsgGroup, SizeOf (MsgGroup), #0);
    R. MsgGroup := 0;
  End;
End;

Procedure CountAreas;
Var
  i : Word;
  tMA : tMsgArea;

Begin
  i := 1;
  mAreasGroup := 0;

  If OpenMsgAreas Then
  While ReadMsgArea (tMA, i) Do
  Begin
    Inc (i);
    If (tMA. Name <> '') And FlagsValid (R. Flags, tMA. ShowFlags) And
       (tMA. ShowSec <= R. Security) And
       (WordInString (MsgGroup. Tag, tMA. Group) Or
       (tMA. Group = '') Or (MsgGroup. Tag = ''))
    Then Inc (mAreasGroup);
  End;
End;

Var
  ta    : tMsgGroup;
  C, i  : Word;

Begin
  If Num <= 0 Then Num := R. MsgGroup;
  If Num = 0 Then Num := 1;

  If Num > mGroupsAmount Then
  Begin
    If MsgGroup. Name = '' Then
    If OpenMsgGroups Then
    Begin
      SearchFirst;
      CloseMsgGroups;
    End;
  End Else
  Begin
    R. MsgGroup := 0;

    If OpenMsgGroups Then
    Begin
      C := 0;

      For i := 1 To mGroupsAmount Do
      Begin
        If Not ReadMsgGroup (ta, i) Then
        Begin
          If i = 1 Then R. MsgGroup := 0 Else SearchFirst;
          MsgGroup := ta;
          Break;
        End;

        If (ta. ShowSec <= R. Security) And FlagsValid (R. Flags, ta. ShowFlags) Then
        Begin
          Inc (C);
          If C = Num Then
          Begin
            R. MsgGroup := C;
            MsgGroup := ta;
          End;
        End;
      End;

      mmGroupsAmount := C;
      CloseMsgGroups;
    End;
  End;

  If R. MsgGroup = 0 Then FillChar (MsgGroup, SizeOf (MsgGroup), 0);
  CountAreas;
End;

Procedure SetFileGroup (Num: LongInt);

Procedure SearchFirst;
Begin
  R. FileGroup := 1;
  ReadFileGroup (FileGroup, 1);

  While Not ((FileGroup. Name <> '') And
        FlagsValid (R. Flags, FileGroup. ShowFlags) And
        (FileGroup. ShowSec <= R. Security))
  Do Begin
    Inc (R. FileGroup);
    If Not ReadFileGroup (FileGroup, R. FileGroup) Then Break;
  End;

  If Not ((FileGroup. Name <> '') And
     FlagsValid (R. Flags, FileGroup. ShowFlags) And
     (FileGroup. ShowSec <= R. Security)) Then
  Begin
    FillChar (FileGroup, SizeOf (FileGroup), #0);
    R. FileGroup := 0;
  End;
End;

Procedure CountAreas;
Var
  i : Word;
  tFA : tFileArea;

Begin
  i := 1;
  fAreasGroup := 0;

  If OpenFileAreas Then
  While ReadFileArea (tFA, i) Do
  Begin
    Inc (i);
    If (tFA. Name <> '') And FlagsValid (R. Flags, tFA. ShowFlags) And
       (tFA. ShowSec <= R. Security) And
       (WordInString (FileGroup. Tag, tFA. Group) Or
       (tFA. Group = '') Or (FileGroup. Tag = ''))
    Then Inc (fAreasGroup);
  End;
End;

Var
  ta    : tFileGroup;
  C, i  : Word;

Begin
  If Num <= 0 Then Num := R. FileGroup;
  If Num = 0 Then Num := 1;

  If Num > fGroupsAmount Then
  Begin
    If FileGroup. Name = '' Then
    If OpenFileGroups Then
    Begin
      SearchFirst;
      CloseFileGroups;
    End;
  End Else
  Begin
    R. FileGroup := 0;

    If OpenFileGroups Then
    Begin
      C := 0;

      For i := 1 To fGroupsAmount Do
      Begin
        If Not ReadFileGroup (ta, i) Then
        Begin
          If i = 1 Then R. FileGroup := 0 Else SearchFirst;
          FileGroup := ta;
          Break;
        End;

        If (ta. ShowSec <= R. Security) And FlagsValid (R. Flags, ta. ShowFlags) Then
        Begin
          Inc (C);
          If C = Num Then
          Begin
            R. FileGroup := C;
            FileGroup := ta;
          End;
        End;
      End;

      ffGroupsAmount := C;
      CloseFileGroups;
    End;
  End;

  If R. FileGroup = 0 Then FillChar (FileGroup, SizeOf (FileGroup), 0);
  CountAreas;
End;

Function MtoAbs (Group, Area: Word): Word;
Var
  Aprior : Boolean;
  G      : tMsgGroup;
  A      : tMsgArea;
  C, i   : Word;

Begin
  C := 0;
  Aprior := mgOpen;
  If Group > mGroupsAmount Then Group := 1;

  If Not Aprior Then OpenMsgGroups;
  ReadMsgGroup (G, Group);
  If Not Aprior Then CloseMsgGroups;

  Aprior := maOpen;
  If Area > mAreasAmount Then Area := 1;
  If Not Aprior Then OpenMsgAreas;

  For i := 1 To mAreasAmount Do
  Begin
    ReadMsgArea (A, i);
    If WordInString (G. Tag, A. Group) Or (A. Group = '') Or
       (fGroupsAmount = 0) Then
    Begin
      Inc (C);
      If C = Area Then
      Begin
        MtoAbs := i;
        Break;
      End;
    End;
  End;

  If Not Aprior Then CloseMsgAreas;
End;

Function lang (LangItem: LongInt): String;
Begin
  If LangItem <= Language^. Count-1 Then lang := GetStr (Language^. At (LangItem)) Else lang := '';
End;

End.