Unit BBSDEF;

(*

  This unit is (C) 1992, 1996, Gallagher & Robertson, A/S, and may not be
  distributed in any modified form whatsoever without our permission.
  Any program making use of these definitions may be distributed freely
  but support for BBS systems making use of such programs will be given
  *ONLY* if the complete source of such programs has been checked
  by Gallagher & Robertson for correctness. In addition, no charge may
  be made for distribution of such programs without our express
  permission.
  
  These rules are for the protection of our users, in order that we may
  ensure that programs which access BBS files do so in an orderly
  fashion. Infringement of these rules will be treated as an illegal
  action and will be prosecuted by all means at our disposal.

*)


{ BBSDEF.PAS -------------------------------------------------------------------
    This module contains all 'global' constant and type definitions for the
    BBS system.
------------------------------------------------------------------------------ }

interface

const
  MBBSIsBetaVersion = false;
  MBBSMajorVersion = 10;
  MBBSMinorVersion = 4;

{ general usage TYPE definitions --------------------------------------------- }

type
  NameT      = String[30];                           {      - " -              }
  PassT      = String[8];                            { for PASSWORD            }
               { note: password of one character with value 0xFF means
                       that user must enter new password at next login;
                       password length byte of NINE signifies enmcrypted
                       password.                                               }
  MenuNameT  = String[12];                           { name of a menu          }
  TelNoT     = String[16];                           { for Telephone numbers   }
  ModemStrT  = String[60];                           { for modem strings       }
  PathNameT  = String[60];                           { path names              }

{ Compile-time constants ----------------------------------------------------- }

const
  MaxConferences1 = 75;         { maximum number of conferences (part 1)       }
  MaxConferences2 = 225;        { maximum number of conferences (part 2)       }
  MaxMessageLines = 499;        { absolute maximum, must recompile to get more }
  MaxFileDirs1    = 99;         { maximum number of file directories (part 1)  }
  MaxFileDirs2    = 255;        { maximum number of file directories (part 2)  }

{ general usage record TYPE definitions -------------------------------------- }

type
  DayOfYearT = record                                { a DATE                  }
    year,
    month,
    day     : byte;
  end {DayOfYearT};

  TimeOfDayT = record                                { a TIME                  }
    hour,
    minute,
    second  : byte;
  end {TimeOfDayT};

  DateTimeT = record                                 { DATE and TIME           }
    date    : DayOfYearT;
    time    : TimeOfDayT;
  end {DateTimeT};

{ miscellaneous definitions ---------------------------------------------------}

type
  AccessTypes  = (ReadMessage, WriteMessage, SendFiles, ReceiveFiles,
                  SigopFunctions, SysopFunctions, OpenDoors, RFU);
  AccessType   = set of AccessTypes;
  ProtocolT    = (NoProtocol,                        { add more later }
                  AsciiP, XModemP, XModemCRCP, YModemP, OldZ1, OldZ2,
                  OldZ3, OldK, YmodemBP, YmodemGP, Modem7P, TelinkP,
                  ExtProt1, ExtProt2, ExtProt3, ExtProt4);
const  
  LastProtocol = ExtProt4;
type
  MenuT        = (NoviceM, ExpertM, JuniorM, SuperM);
  TerminalT    = (TTY, ANS, VIP);
  MsgReadT     = (marked, forwards, backwards, reference);
  ConfSwitchT  = (ImmRead,ImmWrite,Mail,Pmess,UseAlias,ImmJoin,Obljoin,Resume);
  ConfSwitches = set of ConfSwitchT;
  MenuStatus   = (MenuRead, Menumain, MenuUtil, MenuFils, MenuSysp,
                  MenuEnter);
  CharSets     = (IBNchars, US7chars, ISOchars, IBMchars, 
                  UK7chars, GE7chars, SF7chars, NO7chars,
                  DE7chars, IT7chars, FR7chars, SP7chars,
                  MACchars);
  MsgNumT      = word;

{ definitions for the USERS file --------------------------------------------- }

type
  ConfT        = record                              { for user record         }
    LastRead   : MsgNumT;                            { value $FFFE = invited   }
                                                     { value $FFFF = nonmember }
    Access     : AccessType;                         { security this conference}
  end {ConfType};

  PrefT   = (SendARC, SendTXT, SendZIP, SendLHARC, SendARJ);

  UserRecord   = record
    Name       : NameT;                              { First AND Last name     }
    Address    : NameT;                              { Street address          }
    CityState  : NameT;                              { City/State              }
    HomeTelno  : TelnoT;                             { home telephone          }
    WorkTelno  : TelnoT;                             { work telephone          }
    Password   : PassT;                              { users password          }
    Alias      : NameT;
    Registered : DayOfYearT;                         { registered on system    }
    LastAccess : DateTimeT;                          { last time on system     }
    FilesDate  : DateTimeT;                          { last date files checked }
    LastLogOff : DateTimeT;                          { last logged out         }
    Resume     : MsgNumT;                            { resume number           }
    ResumeAdr  : boolean;                            { add address to resume   }
    TimeLimit  : integer;                            { daily time limit        }
    TimeUsed   : integer;                            { minutes on system today }
    FileUsed   : integer;                            { minutes in file menu    }
    FileLimit  : integer;                            { daily file limit        }

    TimesOn    : longint;                            { number of times on      }
    ATimesOn   : longint;                            { times on last reset     }
    Uploaded   : longint;                            { files uploaded          }
    AUploaded  : longint;                            { uploaded last reset     }
    Downloaded : longint;                            { files downloaded        }
    ADownloaded: longint;                            { downloaded last reset   }
    MsgsLeft   : longint;                            { messages entered        }
    AMsgsLeft  : longint;                            { messages entered lst res}
    MsgsRead   : longint;                            { messages read           }
    MsgsDumped : longint;                            { messages dumped         }
    Totaltime  : longint;                            { total time on system    }
    Atotaltime : longint;                            { at last reset           }
    BytesDown  : longint;                            { downloaded bytes        }
    AbytesDown : longint;                            { at last reset           }
    BytesUp    : longint;                            { bytes uploaded          }
    AbytesUp   : longint;                            { at last reset           }
    PageLength : integer;                            { page length             }
    Protocol   : ProtocolT;                          { download protocol       }
    ReadType   : MsgReadT;                           { dflt read type          }
    Menutype   : MenuT;                              { menu display type       }
    ANSI       : boolean;                            { wants ANSI graphics     }
    FullScreen : boolean;                            { wants full screen edit  }
    TermType   : TerminalT;                          { terminal type           }
    Charset    : Charsets;                           { character set           }
    OvrMode    : boolean;                            { overlay in editor       }
    NoRowCol   : boolean;                            { turned off rowcol       }
    NoShow     : boolean;                            { no conference show      }
    Killed     : boolean;                            { user is dead            }
    NoMsgCol   : boolean;                            { no colours in msgs      }
    AutoXFR    : byte;                               { more coming?            }
    Polls      : integer;                            { one bit per poll        }
    LangKey    : char;                               { language key if any     }
    XFiltering : boolean;                            { filtering noise         }
                                                     {  -- no longer supported }
    ShortForm  : boolean;                            { short message format    }
    MainScript : String[12];                         { script to use if any    }
    MsgFilter  : byte;                               { system msg filter       }
    SendPref   : PrefT;                              { preference for SEND     }
    IncludeOwn : boolean;                            { wants own messages back }
    WarnMsg    : MsgNumT;                            { number of warning msg   }
    RightMargin: byte;                               { message margin (0=>72)  }
    ExternName : String[12];                         { internet name           }
    RsvQWK     : word;                               { reserved for QWK door   }
    ChatDisable: boolean;                            { CHAT and NODE disable   }
    FileBrowse : boolean;                            { browse in file lists    }
    ScriptWork : array [1..20] of char;              { !UREAD/!UWRITE          }
    ExternRef  : longint;                            { recnum for external ind }
                     { if this is zero, then assume record number in user file }
    Spare      : array [1..1] of char;               { RFU                     }
    LastOnNode : word;                               { Last on node number     }
    Conf       : array [0..MaxConferences2] of ConfT;{ zero is for MAIN        }
  end {UserRecord};

  (* Note, this was changed in version 9.9c and OldUserRecord should
     be used for systems earlier than this                          *)

  OldUserRecord   = record
    Name       : NameT;                              { First AND Last name     }
    Address    : NameT;                              { Street address          }
    CityState  : NameT;                              { City/State              }
    Password   : PassT;                              { users password          }
    LastAccess : DateTimeT;                          { last time on system     }
    FilesDate  : DateTimeT;                          { last date files checked }
    TimesOn    : integer;                            { number of times on      }
    Uploaded   : integer;                            { files uploaded          }
    Downloaded : integer;                            { files downloaded        }
    PageLength : integer;                            { page length             }
    TimeUsed   : integer;                            { minutes on system today }
    Protocol   : ProtocolT;                          { download protocol       }
    HomeTelno  : TelnoT;                             { home telephone          }
    WorkTelno  : TelnoT;                             { work telephone          }
    Menutype   : MenuT;                              { menu display type       }
    ANSI       : boolean;                            { wants ANSI graphics     }
    FullScreen : boolean;                            { wants full screen edit  }
    TermType   : TerminalT;                          { terminal type           }
    Charset    : Charsets;                           { character set           }
    OvrMode    : boolean;                            { overlay in editor       }
    NoRowCol   : boolean;                            { turned off rowcol       }
    TimeLimit  : integer;                            { daily time limit        }
    NoShow     : boolean;                            { no conference show      }
    Killed     : boolean;                            { user is dead            }
    NoMsgCol   : boolean;                            { no colours in msgs      }
    ReadType   : MsgReadT;                           { dflt read type          }
    AutoXFR    : byte;                               { more coming?            }
    Polls      : integer;                            { one bit per poll        }
    LangKey    : char;                               { language key if any     }
    Filtering  : boolean;                            { filtering noise         }
    Resume     : integer;                            { resume number           }
    ResumeAdr  : boolean;                            { add address to resume   }
    ATimesOn   : integer;                            { times on last reset     }
    AUploaded  : integer;                            { uploaded last reset     }
    ADownloaded: integer;                            { downloaded last reset   }
    AMsgsLeft  : integer;                            { messages entered lst res}
    MsgsLeft   : integer;                            { messages entered        }
    MsgsRead   : integer;                            { messages read           }
    MsgsDumped : integer;                            { messages dumped         }
    LastLogOff : DateTimeT;                          { last logged out         }
    Alias      : NameT;
    ShortForm  : boolean;                            { short message format    }
    FileUsed   : integer;                            { minutes in file menu    }
    FileLimit  : integer;                            { daily file limit        }
    OldIndent  : boolean;                            { autoindent NOT USED     }
    MainScript : String[12];                         { script to use if any    }
    NotUsed    : boolean;
    MsgFilter  : byte;                               { system msg filter       }
    Totaltime  : longint;                            { total time on system    }
    Atotaltime : longint;                            { at last reset           }
    BytesDown  : longint;                            { downloaded bytes        }
    AbytesDown : longint;                            { at last reset           }
    SendPref   : PrefT;                              { preference for SEND     }
    IncludeOwn : boolean;                            { wants own messages back }
    BytesUp    : longint;                            { bytes uploaded          }
    AbytesUp   : longint;                            { at last reset           }
    WarnMsg    : integer;                            { not actually there 9.9b }
    Spare      : array [1..4] of char;               { RFU                     }
    Conf       : array [0..MaxConferences1] of ConfT;{ zero is for MAIN        }
  end {OldUserRecord};

const
  MsgNumInvited     = $FFFE;
  MsgNumNoReference = $FFFF;
  MsgNumNotPresent  = $0000;
  MsgNumMinimum     = $0001;
  MsgNumMaximum     = $FFFD;

{ definitions for the FILES file --------------------------------------------- }

  (* Note, this was changed in version 9.1 and OldFileRecord should
     be used for systems earlier than this                          *)

type
  FileRecord   = record
    Name       : MenuNameT;                          { Short name of file      }
    PathName   : PathNameT;                          { Where to find file      }
    Description: String[40];                         { File description        }
    DateStamp  : DateTimeT;                          { date/time of file       }
    FileSiz    : longint;                            { size of file            }
    FileFrom   : integer;                            { submitter               }
    FileTo     : integer;                            { receiver, if DirNum <0  }
    Downloads  : longint;                            { times downloaded        }
    ASCII      : boolean;                            { may be ASCII d'ld       }
    DirNum     : integer;                            { directory number        }
                                                     { -1 for privates         }
    ConfNo     : integer;                            { private to conference   }
    Available  : boolean;                            { private altogether      }
    FileDesc   : MsgNumT;                            { ptr to message w/desc   }
    Notify     : boolean;                            { notify uploader         }
    Reserve1   : byte;                               { reserved                }
    Reserve    : array [1..113] of byte;             { future use              }
  end {FileRecord};

  (* definition of file record for pre-9.1 systems *)

  OldFileRecord   = record
    Name       : MenuNameT;                          { Short name of file      }
    PathName   : PathNameT;                          { Where to find file      }
    Description: String[40];                         { File description        }
    DateStamp  : DateTimeT;                          { date/time of file       }
    FileSize   : real;                               { size of file            }
    FileFrom   : integer;                            { submitter               }
    Downloads  : integer;                            { times downloaded        }
    ASCII      : boolean;                            { may be ASCII d'ld       }
    DirNumber  : integer;                            { directory number        }
                                                     { 9999 is PI/ALL          }
                                                     { neg. numbers are users  }
    ConfNo     : integer;                            { private to conference   }
    Available  : boolean;                            { private altogether      }
  end {OldFileRecord};

{ definitions for the MESSAGES file ------------------------------------------ }

{ Notes: TextOffs is REAL for historical reasons (TP3 didn't have a longint
         data type). One day...
 
         To find the text in MTEXT.nn, do a Seek(mtext, trunc(msg.TextOffs)
         and then read NrBytes bytes from the file.

         The format in the bytes you just read in is a single byte
         containing the length of the first line, followed by the
         data for that line, followed by a single byte for the length
         of line two, followed by the data for line two, and so on.

         Support for external user names:

            Add one or two lines at the start of the data in MTEXT.
            An alternative FROM address should be stored with a prefix
                of 0x1E in the first byte
            An alternative TO address should be stored with a prefix
                of 0x1F in the first byte

            For example, a message from 'mike@gar.no' saying "Hi!"
            would be stored in MTEXT as

              #$0C#$1E'mike@gar.no'#$03'Hi!'

            No null bytes or anything like that, this is not C. This
            format is signalled by a NEGATIVE number of lines in the
            NrLines field. The negative number there should NOT include
            the header lines (in the example above it would contain -1)

            When using external names the values stored in MsgFrom and/or
            MsgTo should be the user number for the external mail server.

         Additionally, *all* lines in a message that begin with 0x18
         will be stripped (but not otherwise processed) before a message
         is presented to the user. This can be used by external programs
         for storage of private information. Such lines should *not*
         be included in the NrLines line count either.
                  
}   

type
  MsgStatusT   = (NormalMsg, KilledByAuthor, KilledBySysop, ArchivedMsg,
                  MovedBySysop);
  SecureT      = (SecNone, SecPassword, SecReceiver);
  MessageLine  = String[80];

  MessageRecord   = record
    Number     : MsgNumT;                            { Message Number          }
    MsgStatus  : MsgStatusT;                         { Message Status          }
    Security   : SecureT;                            { Message security        }
    MsgFrom    : integer;                            { user number             }
    MsgTo      : integer;                            { Ditto (-1=ALL)          }
    Subject    : NameT;                              { subject of message      }
    TimeStamp  : DateTimeT;                          { time entered            }
    MsgRead    : boolean;                            { read by receiver        }
    RefTo      : MsgNumT;                            { refers to               }
    RefBy      : MsgNumT;                            { first answer            }
    RefNxt     : MsgNumT;                            { next in this thread     }
    NrLines    : integer;                            { number of lines         }
    NrBytes    : word;                               { number of bytes         }
    TextOffs   : real;                               { offset in text file     }
  end {MessageRecord};

{ For reset of statistics ---------------------------------------------------- }

  StatisticsRecord = record
    ResetDate  : DateTimeT;
  end {StatisticsRecord};

{ The configuration record definitions --------------------------------------- }

{ System colour definitions }

const
  SC_Default          = 1;
  SC_MainCmd          = 2;
  SC_FileCmd          = 3;
  SC_UtilCmd          = 4;
  SC_SysopCmd         = 5;
  SC_ReadCmd          = 6;
  SC_ReadRefCmd       = 7;
  SC_MarkCmd          = 8;
  SC_SearchCmd        = 9;
  SC_ReadModeCmd      = 10;
  SC_SysMaintCmd      = 11;
  SC_ChatCmd          = 12;
  SC_ProtoCmd         = 13;
  SC_ArcFormCmd       = 14;
  SC_MsgHead          = 15;
  SC_MsgAddress       = 16;
  SC_MsgSubject       = 17;
  SC_MsgText          = 18;
  SC_MsgQuote         = 19;
  SC_ListHead         = 20;
  SC_ListLeft         = 21;
  SC_ListRight        = 22;
  SC_BrowseTitle      = 23;
  SC_BrowseHead       = 24;
  SC_BrowseName       = 25;
  SC_BrowseMarked     = 26;
  SC_BrowseDescr      = 27;
  SC_BrowseEnd        = 28;
  SC_EditHead         = 29;
  SC_EditText         = 30;
  SC_EditWarn         = 31;
  SC_EditQuote        = 32;
  SC_ChatSysop        = 33;
  SC_ChatUser         = 34;


type
  EventT = record
    EventName          : NameT;
    WeekDay            : word;
    Earliest           : TimeOfDayT;
    Latest             : TimeofDayT;
    LastRun            : DateTimeT;
    BusyPhone          : boolean;
  end {EventT};

type
  ConfigRecord = record
    CFGID              : array [1..4] of char;                        { 'MBBS' }
    CFGlevel           : word;
    BaseName           : NameT;                             { name of this BBS }
    SYSOPname          : NameT;
    SYSOPpassword      : PassT;                          { LOCAL password only }
    CommsPort          : word;
    OpenModemAt        : word;                           { NB: $FFF0 = 115Kb   }
    ModemInitString    : ModemStrT;
    ModemAnswerString  : ModemStrT;
    ModemOffHookString : ModemStrT;
    ModemOnHookString  : ModemStrT;
    MaxLinesMessage    : word;
    MaxActiveConf      : word;                     { limited to MaxConferences2}
    MaxActiveDirs      : word;                     { limited to MaxFileDirs2   }
    NewUserTimeLimit   : word;
    UploadsPrivate     : boolean;
    SleepTime          : word;
    UnusedSW           : boolean;
    NumberOfEvents     : word;
    MainDIR            : PathNameT;
    MenuDIR1           : PathNameT;
    FileDIR            : PathNameT;
    AllowARC           : boolean;
    XAllowZmodem       : boolean;     (* no longer used *)
    MenuDIR2           : PathNameT;
    ScratchPath        : PathNameT;
    CallersPath        : PathNameT;
    SysopsPath         : PathNameT;
    NodesPath          : PathNameT;
    RemotePath         : PathNameT;
    NodeNumber         : word;
    MaxNodes           : word;
    ImmUpload          : boolean;
    ImmDownload        : boolean;
    ClosedSystem       : boolean;
    ClosedPassword     : PassT;
    DirectSystem       : boolean;
    SpaceRequired      : word;        (* in 100Kb units *)
    ArcCommand         : PathNameT;
    XDszCommand        : PathNameT;   (* no longer used *)
    XAllowKermit       : boolean;     (* no longer used *)
    XKermitCommand     : PathNameT;   (* no longer used *)
    UseAlias           : boolean;
    HoldDIR            : PathNameT;
    ArxCommand         : PathNameT;
    NorwegianChars     : boolean;
    DefaultCharSet     : CharSets;
    NoOutputInt        : boolean;
    DefaultByRef       : boolean;
    ConstantSpeed      : boolean;
    CommsPortBase      : word;
    CommsPortIRQ       : word;
    ConfNames1         : array [0..MaxConferences1] of NameT;
    ConfSW1            : array [0..MaxConferences1] of ConfSwitches;
    ConfOrder1         : array [0..MaxConferences1] of byte;
    ConfDefaultMsg1    : array [0..MaxConferences1] of word;
    DirNames1          : array [0..MaxFileDirs1] of NameT;
    SysopMacros        : array [1..10] of NameT;
    Events             : array [1..20] of EventT;
    TimePeriods        : array [0..23] of word;
    Quarantines        : array [0..23] of word;
    FileOrder1         : array [0..MaxFileDirs1] of byte;
    DirectConnect      : boolean;
    NoCarrierPause     : word;
    NumberDoors        : word;
    ChatDIR            : PathNameT;
    PollTime           : word;
    NewUserFileLimit   : word;
    ImmDownConf        : word;
    DefaultScript      : String[12];
    ModemCallString    : ModemStrT;
    NoFkeys            : boolean;
    LocalSysop         : boolean;
    SwapDIR            : PathNameT;
    Dummy              : boolean;
    NewUserOpen        : boolean;
    AllowZIP           : boolean;
    ZIPcommand         : PathNameT;
    UNZIPcommand       : PathNameT; 
    SuppressLocal      : boolean;
    InitiallyAvailable : boolean;
    DontEcho           : boolean;
    CarrierDetect      : boolean;
    RestrictByConf     : word;
    ModemNoDTR         : boolean;
    ModemDropString    : ModemStrT;
    UseFlowControl     : boolean;
    Watchdogsupport    : boolean;
    FOSSIL             : boolean;
    DateProtect        : boolean;
    LocalDownload      : PathNameT;
    ScriptDIR          : PathNameT;
    FastStart          : boolean;
    AllowLHARC         : boolean;
    LHARCcommand       : PathNameT;
    AllowARJ           : boolean;
    ARJcommand         : PathNameT;
    BreakWithBreak     : boolean;
    BackupDirectory    : PathNameT;
    DisableTempSysop   : boolean;
    DisableTempSysopScr: boolean;
    MinimumSpeed       : word;
    DisableSwapping    : boolean;
    ChatLimit          : word; (* minutes *)
    DirNames2          : array [MaxFileDirs1+1..MaxFileDirs2] of NameT;
    FileOrder2         : array [MaxFileDirs1+1..MaxFileDirs2] of byte;
    ConfNames2         : array [MaxConferences1+1..MaxConferences2] of NameT;
    ConfSW2            : array [MaxConferences1+1..MaxConferences2] of ConfSwitches;
    ConfOrder2         : array [MaxConferences1+1..MaxConferences2] of byte;
    ConfDefaultMsg2    : array [MaxConferences1+1..MaxConferences2] of word;
    CarrierWaitTime    : word;
    EatLineFeeds       : boolean;
    NoColourInput      : boolean;
    ExternalProtocols  : word;
    ExternalProtoNam   : array [1..4] of string[8];
    ExternalProtoCmd   : array [1..4] of string[128];
    NewUserChatDisable : boolean;
    TempSysopRemote    : boolean;
    HaveCallerID       : boolean;
    DataCallIsOnline   : boolean;
    ConfMailMsg        : integer;
    ConfFileMsg        : integer;
    ConfScriptMsg      : integer;
    ConfHelloMsg       : integer;
    ConfCommentMsg     : integer;
    GrabLimit          : word;       (* in Kb *)
    SysColours         : array [1..34] of word; (* see SC_xxxx defs *)
  end {ConfigRecord};

{ Definitions for nodes file ------------------------------------------------- }

type NodeStateT        = (NodeInactive,    NodeActive,
                          NodeUploading,   NodeDownloading,
                          NodeEntering,    NodeUnAvailable,
                          NodeChattingN,   NodeChattingG, 
                          NodeChattingS,   NodeInDOS,
                          NodeWaiting,     NodeInScript,
                          NodeDown,        NodeGRABbing,
                          NodePacking,     NodeUnpacking,
                          NodeSending,     NodeInDoor,
                          NodeInEvent,     NodeActiveNC,
                          NodeScript1,     NodeScript2,
                          NodeScript3,     NodeScript4,
                          NodeScript5,     NodeScript6,
                          NodeScript7,     NodeScript8,
                          NodeScript9,     NodeScript10,
                          NodeUsingQWK);

type
  NodeRecord = record
    NodeStatus         : NodeStateT;
    LastUser           : NameT;
    LastUserFrom       : NameT;
    UserSpeed          : word;    { 0 = local, else speed divided by 10! }
    ChattingWith       : integer;
    UsingANSI          : boolean;
    UsingMNP           : boolean;
    CallersFile        : PathNameT;
  end {NodeRecord};


{ Definitions for MSGQ files ------------------------------------------------- }

  (* This record is used for internode communication. Usually it contains
     the sending node number, the 'message priority' (corresponds to the
     user message filter/MF value), the message length, and the message
     text.

     If the text of the message contains one or more numbers enclosed
     between the ASCII STX (#2) and ETX (#3) characters then the entire
     sequence will be replaced with the contents of the equivalent text
     from the BBSTXT file.

     Special cases of node texts are:

     Length = 1, contents of the single character:

        #0 -> log out current user
        #1 -> re-read access and time limits from user record
        #6 -> reboot this node

     Length = 4, first character = #0 (NUL):

        Last two characters are integer, least significant byte first,
        which will be used for the operations below:

        Character two =

        #0 -> decrease time allowed
        #1 -> set time allowed
        #2 -> increase time allowed
        #3 -> decrease extra filetime quota
        #4 -> set extra filetime quota
        #5 -> increase extra filetime quota

        (for example, a record with length 4 and hex content

          00 02 0A 00

         would increase the time allowed online by 10 minutes)

      If the first byte of the text is an ASCII DEL (0x7F) then
      this character will be removed before display and a beep
      will *not* be sent when the message is presented.

     *)
       

  NodeTextT    = record
    FromNode   : integer;
    MsgPrio    : byte;
    MsgLen     : integer;
    MsgText    : array [1..72] of char;
  end {NodeTextT};



(* The FilCalCrc routine is the routine that generates the key value that
   will used in the FILIND file to help fast lookup of filenames in that file *)

function  FilCalcCRC (N : MenuNameT) : integer;

(* The UserKeyGen routine is the routine that generates the key value that will
   be used in the MINDEX file to help fast lookup of user names in that file  *)

(* The MsgExists function should be used to test whether a message number
   in any context represents a real message or not.                           *)

function MsgExists (N : MsgNumT) : boolean;

function UserKeyGen (N : NameT) : integer;

(* The UpperCaseS routine is used to convert strings to upper case            *)

procedure UpperCaseS (var S);

(* The UpperCaseOf routine is used to return uppercase strings                *)

function UpperCaseOf (S : string) : string;

(* The UpperCaseEqual routine is used to compare case-insensitively           *)

function UpperCaseEqual (S1, S2 : string) : boolean;

(* The UpperCase array is used to convert characters to upper case            *)

type
  TUpperCase = array [#$00..#$FF] of char;
const
  UpperCase : TUpperCase = (
   #00,#01,#02,#03,#04,#05,#06,#07,#08,#09,#10,#11,#12,#13,#14,#15,
   #16,#17,#18,#19,#20,#21,#22,#23,#24,#25,#26,#27,#28,#29,#30,#31,
   ' ','!','"','#','$','%','&','''','(',')','*','+',',','-','.','/',
   '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
   '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
   'P','Q','R','S','T','U','V','W','X','Y','Z','[','\',']','^','_',
   '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
   'P','Q','R','S','T','U','V','W','X','Y','Z','[','\',']','^','_',
   '€','š','�','A','Ž','A','�','€','E','E','E','I','I','I','Ž','�',
   '�','’','’','O','™','O','U','U','Y','™','š','�','œ','�','ž','Ÿ',
   'A','I','O','U','¥','¥','¦','§','¨','©','ª','«','¬','­','®','¯',
   '°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿',
   'À','Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï',
   'Ð','Ñ','Ò','Ó','Ô','Õ','Ö','×','Ø','Ù','Ú','Û','Ü','Ý','Þ','ß',
   'à','á','â','ã','ä','å','æ','ç','è','é','ê','ë','ì','í','î','ï',
   'ð','ñ','ò','ó','ô','õ','ö','÷','ø','ù','ú','û','ü','ý','þ',#$FF);

implementation

{ UserKeyGen -------------------------------------------------------------------
    Generate the key for a given name.
  ---------------------------------------------------------------------------- }
function UserKeyGen (N : NameT) : integer;

  var
    Offset : integer;

  function MappedLetter : integer;
    var
      C : char;
    begin
      if Offset > length(N) then
        MappedLetter := 0
      else
        begin
          C := N[Offset];
          case C of
            ' '      : MappedLetter := 0;
            'A'..'Z' : MappedLetter := ord(C)-$40;
            'Ž',
            '�',
            '’'      : MappedLetter := 1;
            '€'      : MappedLetter := 3;
            '�'      : MappedLetter := 5;
            '™',
            '�'      : MappedLetter := 15;
            'š'      : MappedLetter := 21;
            else       MappedLetter := 31;
          end;
        end;
    end {MappedLetter};

  var
    UserKey : integer;

  begin
    UpperCaseS (N);
    Offset := 1;
    UserKey := MappedLetter shl 10;
    repeat
      inc(Offset);
    until (Offset>length(N)) or (N[Offset]=' ');
    repeat
      inc(Offset);
    until (Offset>length(N)) or (N[Offset]<>' ');
    UserKey := UserKey or (MappedLetter shl 5);
    inc(Offset);
    UserKeyGen := UserKey or MappedLetter;
  end {UserKeyGen};

{ FilCalcCRC -------------------------------------------------------------------
    CRC calculate for the given name.

    The file FILIND contains the CRC of each file name in FILDIR,
    in the same order.

    The FILFAT file contains three arrays [-1..MaxFileDirs2] of
    longint. The packing process sorts all files in the same
    directory into the same area of the FILDIR file (for speed
    in searching), while new files are placed at the end of
    FILDIR. FILFAT is used to record the first record number
    in the sorted portion of FILDIR (in the first array), the
    last record number (in the second array) and the first
    occurrence in the unsorted portion at the end of the file
    (in the third array). Searching in a single directory can
    thus be confined to the area between the corresponding
    entries from the first two arrays, and from the entry in
    the third array to the end of the file.

  ---------------------------------------------------------------------------- }
function FilCalcCRC (N : MenuNameT) : integer;
  var
    CS : integer;
    C : char;
    I : integer;
  begin
    CS := 0;
    for I := 1 to length(N) do
      begin
        C := N[I];
        Inline (
        $B9/$08/$00/         {       MOV     CX,8             }
        $8A/$46/<C/          {       MOV     AL,[BP+C]        }
        $8B/$5E/<CS/         {       MOV     BX,[BP+CS]       }
        $D1/$E3/             { L0    SHL     BX,1             }
        $9C/                 {       PUSHF                    }
        $D0/$E0/             {       SHL     AL,1             }
        $73/$04/             {       JNB     L1               }
        $81/$CB/$01/$00/     {       OR      BX,1             }
        $9D/                 { L1    POPF                     }
        $73/$04/             {       JNB                      }
        $81/$F3/$21/$10/     {       XOR     BX,1021          }
        $E2/$EC/             { L2    LOOP    L0               }
        $89/$5E/<CS          {       MOV     [BP+CS],BX       }
        );
      end;
    FilCalcCRC := CS;
  end {FilCalcCRC};

procedure UpperCaseS (var S);
  var
    I  : integer;
    St : string absolute S;
  begin
    for I := 1 to length(St) do St[I] := UpperCase[St[I]];
  end {UpperCaseS};

function UpperCaseOf (S : string) : string;
  begin
    UpperCaseS (S);
    UpperCaseOf := S;
  end {UpperCaseOf};

function UpperCaseEqual (S1, S2 : string) : boolean;
  begin
    UpperCaseS (S1);
    UpperCaseS (S2);
    UpperCaseEqual := (S1=S2);
  end;

function MsgExists (N : MsgNumT) : boolean;
  begin
    MsgExists := (N >= MsgNumMinimum) and (N <= MsgNumMaximum);
  end;

end {BBSDEF}.

