Metropoli BBS
VIEWER: kfrxhost.rx MODE: TEXT (CP437)
/*═════════════════════════════════════════════════════════════════════════╗
║  INTENTION:                                                              ║
║  ----------                                                              ║
║  This Script is intended to be used as a small ZOC-HOST for remote       ║
║  access. The intention is 99.9999% availability, whatever happens on     ║
║  the remote side or on the line.                                         ║
║                                                                          ║
║  Warranty:                                                               ║
║  ---------                                                               ║
║  There no warranty whatsoever. Take it, use it, modify it or leave it.   ║
║                                                                          ║
║  Directory Scheme                                                        ║
║  ----------------                                                        ║
║  This Script is written for following directory scheme:                  ║
║  (Files marked with ** belong to this package.)                          ║
║  Files KfRxHost.Std and KfRxHost.Con must be adapted to your equipment.  ║
║  The files delivered are just samples.                                   ║
║                                                                          ║
║  ZOC-DIRECTORY─┐                                                         ║
║                ├──KFRXHOST─────────┬───────HOST                          ║
║                │   │               │        │                            ║
║                │ **KfRxHost.Pw     │        Standard Host Directory      ║
║                │   (Passwords)     │        Contains files to be send to ║
║                │                   │        Remote                       ║
║                │                   │        (Directory can be changed by ║
║                │                   │         SuperAuthority)             ║
║                │                   │                                     ║
║                │                   └───────REMOTE                        ║
║                │                            │                            ║
║                │                            Files received from Remote   ║
║                │                                                         ║
║                ├──LOG                                                    ║
║                │   │                                                     ║
║                │   KfRxHost.Log                                          ║
║                │                                                         ║
║                ├──SCRIPT                                                 ║
║                │   │                                                     ║
║                │ **KfRxHost                                              ║
║                │   (This Script)                                         ║
║                │                                                         ║
║                └──OPTIONS                                                ║
║                    │                                                     ║
║                  **KfRxHost.Con   The host option file for connection    ║
║                  **KfRxHost.Std   The standard option file, likely       ║
║                                   to be a copy of option file STANDARD   ║
║                                                                          ║
║  PassWords                                                               ║
║  ---------                                                               ║
║  Passwords are kept in file KfRxHost.Pw. This file may contain any       ║
║  number of pass words.                                                   ║
║                                                                          ║
║  Authorities:                                                            ║
║    Two Levels of Authority are implemented:                              ║
║      Guest Authority. Allows Host->Remote transfer from one              ║
║                       Directory (HOST\HOST).                             ║
║      Super Authority. Allows Change of Host->Remote Directory            ║
║                                                                          ║
║  Format:                                                                 ║
║    Column 1:                                                             ║
║      G The following Word is a Password for Guest Authority              ║
║      S The following Word is a Password for Super Authority              ║
║      x Anything else than G or S is treated as comment                   ║
║                                                                          ║
║  Calling KfRxHost from PhoneBook:                                        ║
║  --------------------------------                                        ║
║  May give problems by needing manual interventions on (probably          ║
║  unattended) host when disconnecting.                                    ║
║                                                                          ║
║  IMPORTANT!!! Notes:                                                     ║
║  -------------------                                                     ║
║  - Set ASC DELAY in HostOpts options transfer to 0.                      ║
║  - Set CD Valid in HostOpts options transfer ON.                         ║
║  - EMULATION is supposed to be set to ANSI.                              ║
║  - It is strongly recommended to select OVERRIDE in HostOpts Transfer    ║
║    to avoid question on the eventually unattended Host.                  ║
║                                                                          ║
║  Conventions writing this Procedure:                                     ║
║  -----------------------------------                                     ║
║  - All Global Variables start with "G._". EG: G._Filename is a           ║
║    global variable. This makes it easier to make this variables known    ║
║    to the Expose Statement.                                              ║
║    Local variables must not begin with "_", under certain circumstances, ║
║    this would lead to confusion with global variables.                   ║
║    EG: G._Anton could probably be confused with _Anton                   ║
║                                                                          ║
╚═════════════════════════════════════════════════════════════════════════*/
/*═════════════════════════════════════════════════════════════════════════╗
║          Constants and Variables to be adapted for your needs:           ║
║        (Look for line ******* START of SETTING DEFAULTS *******)         ║
║ NAME            Type   Comment                     Original Defaults     ║
║ G._DirHost      String Directory for Host->Remote  "KFRXHOST\HOST\"      ║
║ G._DirRemote    String Directory for Remote->Host  "KFRXHOST\REMOTE\"    ║
║ G._DriveHost    String Drive for Host->Remote      Path of ZOC           ║
║ G._HostOpts     String Option File for connection  "KfRxHost.Con"        ║
║ G._LogInWait    Digit  Maximum Wait during LogIn   20                    ║
║ G._MaxWait      Digit  Max. Wait for remote input  1000                  ║
║ G._WakeUpTime   Digit  Ring remote if no input     300                   ║
║ G._NumOfRings   Digit  Number of Rings             2                     ║
║ G._PassWordFile String Name of PassWordFile        "KFRXHOST\KfRxHost.Pw"║
║ G._ProcLog      String LogFile                     "Log\KfRxHost.Log"    ║
║ G._ProcLogOn    Digit  0 no logging, 1 logging.    1                     ║
║ G._ProcName     String Name of this REXX-Procedure "KfRxHost"            ║
║ G._StdOpts      String Standard Option File        "KfRxHost.Std"        ║
║                                                                          ║
╚═════════════════════════════════════════════════════════════════════════*/

/*═════════════════════════════════════════════════════════════════════════╗
║  Main                                                                    ║
╚═════════════════════════════════════════════════════════════════════════*/
Main:
   /*──────────────────────────────────────────────────────────────────────┐
   │  Set Error Exits and End                                              │
   └──────────────────────────────────────────────────────────────────────*/
   Signal on Error    Name ProgTerm
   Signal on Failure  Name ProgTerm
   Signal on Halt     Name ProgTerm
   Signal on Syntax   Name ProgTerm

   /*──────────────────────────────────────────────────────────────────────┐
   │  Set Version                                                          │
   └──────────────────────────────────────────────────────────────────────*/
   G._ProcVers     = "2.0"           /* Version */

   /*──────────────────────────────────────────────────────────────────────┐
   │  Set drive and directory where program was started from               │
   └──────────────────────────────────────────────────────────────────────*/
   G._PrgDir   = Directory()
   G._PrgDrive = Left(G._PrgDir, 2)
   G._PrgDir   = SubStr(G._PrgDir,3)

   /*══════════════════════════════════════════════════════════════════════╗
   ║               ******* START of SETTING DEFAULTS *******               ║
   ╚══════════════════════════════════════════════════════════════════════*/
   G._DriveHost    = G._PrgDrive             /* Cur. Drive for Host->Remote   */
   G._DirHost      = G._PrgDir"\KFRXHOST\Host\"   /* Dir for Host->Remote     */
   G._DirRemote    = G._PrgDir"\KFRXHOST\Remote\" /* Dir for Remote->Host     */
   G._ProcName     = "KfRxHost"              /* Name of this REXX-Prozedure   */
   G._NumOfRings   = 2                       /* Number of Rings               */
   G._MaxWait      = 1000                    /* Max Wait for Inp. from Remote */
   G._WakeUpTime   = 300                     /* Ring remote if no input       */
   G._LogInWait    = 20                      /* Maximum Wait during LogIn     */
   G._PassWordFile = "KFRXHOST\KfRxHost.Pw"  /* Name of Pass Word File */
   G._ProcLog      = "LOG\"G._ProcName".Log" /* LogFile                 */
   G._ProcLogOn    = 1                       /* Set Logging on          */
   G._StdOpts      = "KfRxHost.Std"          /* Standard Option File    */
   G._HostOpts     = "KfRxHost.Con"          /* Option File for connect */

   /*══════════════════════════════════════════════════════════════════════╗
   ║                ******* END of SETTING DEFAULTS *******                ║
   ╚══════════════════════════════════════════════════════════════════════*/

   /*──────────────────────────────────────────────────────────────────────┐
   │  Set G._Test to 1 for easier handling of password etc.                │
   │  PassWord is then "S" for Super, "G" for Guest                        │
   └──────────────────────────────────────────────────────────────────────*/
   G._Test = 0

   /*──────────────────────────────────────────────────────────────────────┐
   │  Address ZOC environment and Turnoff Host Echo                        │
   └──────────────────────────────────────────────────────────────────────*/
   ADDRESS ZOC
   SETHOST 0

   /*──────────────────────────────────────────────────────────────────────┐
   │  Set Special Characters and Sequences                                 │
   └──────────────────────────────────────────────────────────────────────*/
   G._ClearScreen = "^[[2J^[[1H"
   G._CrLf        = "^M^J"
   G._Cr          = "^M"
   G._Bs          = "^H"
   G._Bell        = "^G"

   /*──────────────────────────────────────────────────────────────────────┐
   │  Set Constants and Variables                                          │
   └──────────────────────────────────────────────────────────────────────*/
   G._LeftBlanks   = "         "
   G._ListSize     = 15
   G._TempFile1    = "HostTmp1.tmp"
   G._TempFile2    = "HostTmp2.tmp"

   /*──────────────────────────────────────────────────────────────────────┐
   │  contains list of known drives x:s,x:s, etc                           │
   │  x: = drive, s = FileSystem (H or F)                                  │
   └──────────────────────────────────────────────────────────────────────*/
   G._FileSysList  = ""

   /*──────────────────────────────────────────────────────────────────────┐
   │  Set DownLoad Path                                                    │
   └──────────────────────────────────────────────────────────────────────*/
   SETDLPATH G._DirRemote

   /*──────────────────────────────────────────────────────────────────────┐
   │  Setup for Logging                                                    │
   └──────────────────────────────────────────────────────────────────────*/
   LOGGING 0
   IF G._ProcLog <> "" then LOGNAME G._ProcLog

   /*──────────────────────────────────────────────────────────────────────┐
   │  MainLoop                                                             │
   └──────────────────────────────────────────────────────────────────────*/
   MainLoop:
   DO Forever
      /*───────────────────────────────────────────────────────────────────┐
      │  Address ZOC environment and Turnoff Host Echo                     │
      └───────────────────────────────────────────────────────────────────*/
      ADDRESS ZOC
      SETHOST 0
      WRITELN "Setting up for Connection"
      SEND  "ATS0=0"G._CrLf
      /*───────────────────────────────────────────────────────────────────┐
      │  Hangup and load HOSTOPTS, if available                            │
      └───────────────────────────────────────────────────────────────────*/
      HANGUP
      DELAY 2
      LOADOPTS G._HostOpts
      /*───────────────────────────────────────────────────────────────────┐
      │  Disable dial in                                                   │
      └───────────────────────────────────────────────────────────────────*/
      SEND  "ATS0=0"G._CrLf
      DELAY 1
      /*───────────────────────────────────────────────────────────────────┐
      │  Set/Stop Logging                                                  │
      └───────────────────────────────────────────────────────────────────*/
      IF G._ProcLogOn <> 0 then LOGGING 1
      ELSE                     LOGGING 0
      /*───────────────────────────────────────────────────────────────────┐
      │  check, whether valid cd option is set. exit if not                │
      └───────────────────────────────────────────────────────────────────*/
      IF ZocCarrier() = "N/A" then DO
         WRITELN "ERROR: CD Valid in OPTIONS SERIAL not Set."G._CrLf
         SIGNAL Finito
      END
      /*───────────────────────────────────────────────────────────────────┐
      │  Set Num of Rings                                                  │
      └───────────────────────────────────────────────────────────────────*/
      WRITE G._ClearScreen
      SEND  "ATS0="G._NumOfRings""G._CrLf
      DELAY 1
      /*───────────────────────────────────────────────────────────────────┐
      │  Wait for Connection and Backspace from Remote                     │
      └───────────────────────────────────────────────────────────────────*/
      CALL WaitForConnect
      CALL WaitForBS
      /*───────────────────────────────────────────────────────────────────┐
      │  Backspace received                                                │
      └───────────────────────────────────────────────────────────────────*/
      CALL Welcome
      CALL GetPassWord /* Set G._Authority: SUPER="S", GUEST="G", FAILURE="F" */
      G._FirstMenu = 2
      IF G._Test <> 0 then G._FirstMenu = 1    /* avoid time consuming menu */
      DO WHILE G._Authority <> "F"
         G._CurDrive   = G._DriveHost            /* Current drive      */
         G._CurDir     = G._DirHost              /* Current directory  */
         G._CurPath    = G._CurDrive||G._CurDir  /* Current Full Path  */
         G._CurFileSys = "U" /* Current file syst.: U=Unknown, H=HPFS, F=FAT */
         CALL Menu
         CALL Wsend '"'G._LeftBlanks'CHOICE: "'
         SELECT
            WHEN G._Choice = "F" then DO
               CALL Wsend "Show dir (F)iles"G._CrLf
               CALL ShowDir G._CurPath||"*",0
               CALL CloseDelFile G._TempFile1
            END
            WHEN G._Choice = "?" then DO
               G._FirstMenu = 2
               ITERATE
            END
            WHEN G._Choice = "D" then DO
               CALL Wsend "(D)ownload file(s) "G._CrLf
               CALL DownL
               CALL CloseDelFile G._TempFile1
            END
            WHEN G._Choice = "U" then DO
               CALL Wsend "(U)pload file(s) "G._CrLf
               CALL UpL
            END
            WHEN G._Choice = "G" then DO
               CALL Wsend "(G)oodbye "
               G._Authority = "F"                      /* HANGUP */
            END
            WHEN G._Authority = "S" & G._Choice = "C" then DO
               CALL Wsend "(C)hange directory "
               CALL ChangeDir
            END
            WHEN G._Authority = "S" & G._Choice = "S" then DO
               CALL Wsend "(S)earch file(s) "G._CrLf
               CALL SearchFile
            END
            WHEN G._Authority = "S" & G._Choice = "H" then DO
               CALL Wsend "Shutdown (H)ost "G._CrLf
               IF Ask_YN("You really want to shut down the host","N") <> 0 then DO
                  G._Authority = "H" /* Shut Host */
               END
            END
            OTHERWISE DO
               CALL Wsend ">"G._Choice"< is Invalid "
            END
         END
         CALL Wsend G._CrLf
         If G._Authority = "F" | G._Authority = "H" then DO
            SETHOST 0
            G._FirstMenu = 1
            CALL Wsend "Goodbye. Thanks for Calling. "
            If G._Authority = "H" then CALL Wsend "Host Closing."
            CALL Wsend G._CrLF
            DELAY 1
            If G._Authority = "F" then DO
               HANGUP
               DELAY 1
            END
            If G._Authority = "H" then SIGNAL Finito
         END
      END
   END
/*MAIN END*/

/*========================================================================*/

/*═════════════════════════════════════════════════════════════════════════╗
║                         Procedures and Functions                         ║
╚═════════════════════════════════════════════════════════════════════════*/

/*═════════════════════════════════════════════════════════════════════════╗
║  ChangeDir     Set G._DriveHost and G._DirHost                           ║
╚═════════════════════════════════════════════════════════════════════════*/
ChangeDir: Procedure Expose G.
   CdValid = 1
   WaitText = G._CrLf||G._LeftBlanks"Enter Drive and/or Directory to change to: "
   CALL WaitForLine WaitText,1,0,0,1        /* UpCase Empty WaitTime Echo */
   /*──────────────────────────────────────────────────────────────────────┐
   │  Get new Drive and/or Dir                                             │
   └──────────────────────────────────────────────────────────────────────*/
   IF SubStr(G._RxLastLine,2,1) = ":" then DO
      G._DriveHost = Left(G._RxLastLine,2)
      G._DirHost   = SubStr(G._RxLastLine,3)
   END
   ELSE DO
      G._DriveHost = G._CurDrive
      G._DirHost   = G._RxLastLine
   END
   IF Left(G._DirHost,1) == "." then DO
      CdValid = 0
      CALL Wsend "Going Back not allowed"G._CrLf
   END
   IF CdValid <> 0 then DO
      IF  Left(G._DirHost,1) <> "\" then G._DirHost = G._CurDir||G._DirHost
      IF Right(G._DirHost,1) <> "\" then G._DirHost = G._DirHost"\"
      /*───────────────────────────────────────────────────────────────────┐
      │  Determine HPFS/FAT. Because fomat of dierectory listing differs   │
      └───────────────────────────────────────────────────────────────────*/
      CALL SetFileSystem G._DriveHost
      /*───────────────────────────────────────────────────────────────────┐
      │  Check wether drive exists                                         │
      └───────────────────────────────────────────────────────────────────*/
      IF G._CurFileSys = "U" then CdValid = 0
   END
   IF CdValid <> 0 then DO
      /*───────────────────────────────────────────────────────────────────┐
      │  Check wether directory exists                                     │
      └───────────────────────────────────────────────────────────────────*/
      CD_Dir = G._DriveHost||G._DirHost
      CD_Str = "dir "CD_Dir"abcdefgh.ijk>NUL"
      Signal OFF Error
      ADDRESS CMD CD_Str
      RetCode = rc
      Signal on Error    Name ProgTerm
      IF RetCode <> 18 & RetCode <> 0 then DO
         CdValid = 0
         CALL Wsend '"Path >'CD_Dir'< not valid'G._CrLf'"'
      END
   END
   IF CdValid = 0 then DO
      /*───────────────────────────────────────────────────────────────────┐
      │  Not a valid path, restore original                                │
      └───────────────────────────────────────────────────────────────────*/
      G._DriveHost = G._CurDrive
      G._DirHost   = G._CurDir
   END
   G._FirstMenu = 1
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  SetFileSystem    Sets variable G._CurFileSys according to Arg(1), Drive.║
╚═════════════════════════════════════════════════════════════════════════*/
SetFileSystem: Procedure Expose G.
   FsDrive = Arg(1)
   /*──────────────────────────────────────────────────────────────────────┐
   │  Get Drive                                                            │
   └──────────────────────────────────────────────────────────────────────*/
   IF SubStr(FsDrive,2,1) = ":" then FsDrive = Left(FsDrive,2)
   ELSE FsDrive = G._PrgDrive
   Parse UPPER VAR FsDrive FsDrive
   /*──────────────────────────────────────────────────────────────────────┐
   │  Check wether already known                                           │
   └──────────────────────────────────────────────────────────────────────*/
   FsPos = Pos(FsDrive,G._FileSysList)
   IF FsPos <> 0 then DO
      G._CurFileSys = SubStr(G._FileSysList,FsPos+2,1)
      RETURN
   END
   /*──────────────────────────────────────────────────────────────────────┐
   │  Try wether able to handle file names longer than 8 (HPFS)            │
   └──────────────────────────────────────────────────────────────────────*/
   Signal OFF Error
   Address CMD '"dir 'FsDrive'\123456789>NUL"'
   RetCode = rc
   Signal on Error Name ProgTerm
   SELECT
      WHEN RetCode = 206 then G._CurFileSys = "F"   /* Name too long */
      WHEN RetCode = 18  then G._CurFileSys = "H"   /* File not found */
      WHEN RetCode = 0   then G._CurFileSys = "H"   /* File found */
      OTHERWISE G._CurFileSys = "U"
   END
   /*──────────────────────────────────────────────────────────────────────┐
   │  If file system determined, add it to the list                        │
   └──────────────────────────────────────────────────────────────────────*/
   IF G._CurFileSys = "U" then CALL Wsend '"Drive >'FsDrive'< unknown'G._CrLf'"'
   ELSE G._FileSysList = G._FileSysList||FsDrive||G._CurFileSys","
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  ReadFn  ARG(1) if non 0, return each line                               ║
║          Read a line with file name from G._TempFile1 to G._FileNameLine ║
║          and return filename                                             ║
║          If EOF: Set G._EndOfFile1 to non 0                              ║
╚═════════════════════════════════════════════════════════════════════════*/
ReadFn: Procedure Expose G.
   G._EndOfFile1 = 0
   DO Forever
      /*───────────────────────────────────────────────────────────────────┐
      │  Test for EndOfFile                                                │
      └───────────────────────────────────────────────────────────────────*/
      TempFile1State = Stream(G._TempFile1,'S')
      IF Left(TempFile1State,5) <> "READY" then DO
         G._EndOfFile1 = 1
         G._FileNameLine = ""
         RETURN
      END
      G._FileNameLine = Linein(G._TempFile1)
      /*───────────────────────────────────────────────────────────────────┐
      │  Check wether it is a file name, if so LEAVE                       │
      └───────────────────────────────────────────────────────────────────*/
      IF Arg(1) = 0 then CALL GetFn
      ELSE G._FileName = G._FileNameLine
      IF G._FileName <> "" then LEAVE
   END
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  GetFn  Check wether G._FileNameLine contains a G._FileName.             ║
║         If so set it to G._FileName. Else set G._FileName to ""          ║
║                                                                          ║
║       - Example of Lines in HPFS:                                        ║
║         0000000001111111111222222222233333333334444444444                ║
║         1234567891234567890123456789012345678901234567890                ║
║          3.05.94  19.11     105827           0  DOKUSP.TXT               ║
║         25.03.94  20.29      <DIR>        1236  Cmd                      ║
║       - Criterias for Filename in HPFS:                                  ║
║         Column 3 must be a "."                                           ║
║         G._FileNameLine 3rd word must not by <DIR>                       ║
║                                                                          ║
║       - Example of Lines in FAT:                                         ║
║         0000000001111111111222222222233333333334444444444                ║
║         1234567890123456789012345678901234567890123456789                ║
║         BAT          <DIR>     29.01.94  15.23                           ║
║         BAT      1   <DIR>     29.01.94  15.23                           ║
║         ABC               243  27.08.94  12.52                           ║
║         BCD      1        243  27.08.94  13.52                           ║
║         CDE      TXT      243  27.08.94  14.52                           ║
║         12345678 TXT      243  27.08.94  15.52                           ║
║       - Criterias for Filename in FAT:                                   ║
║         Column 26 must be a '.' (period                                  ║
║         Line must not contain "<DIR>"                                    ║
║         Filename = 1st Word, FileExt is 3 letters starting in Column 10  ║
║                                                                          ║
╚═════════════════════════════════════════════════════════════════════════*/
GetFn: Procedure Expose G.
   G._FileName = ""
   IF G._CurFileSys = "H" then DO
      /*───────────────────────────────────────────────────────────────────┐
      │  This is HPFS                                                      │
      └───────────────────────────────────────────────────────────────────*/
      IF SubStr(G._FileNameLine,3,1) <> "."     then RETURN
      IF SubWord(G._FileNameLine,3,1) = "<DIR>" then RETURN
      G._FileName = SubWord(G._FileNameLine,5,1)
   END
   ELSE IF G._CurFileSys = "F" then DO
      /*───────────────────────────────────────────────────────────────────┐
      │  This is FAT                                                       │
      └───────────────────────────────────────────────────────────────────*/
      IF SubStr(G._FileNameLine,26,1) <> "." then RETURN
      IF Pos("<DIR>",G._FileNameLine) <>  0  then RETURN
      G._FileName = Left(G._FileNameLine,8)
      G._FileName = Strip(G._FileName)
      FileExt = SubStr(G._FileNameLine,10,3)
      IF FileExt <> "   " then DO
         FileExt = Strip(FileExt)
         G._FileName = G._FileName"."FileExt
      END
   END
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  Menu. Print menu and set G._Choice. G._Choice = "ERROR" if failure.     ║
╚═════════════════════════════════════════════════════════════════════════*/
Menu: Procedure Expose G.
   G._Choice = "ERROR"
   CALL ShowMenu
   WaitText = G._CrLf||G._LeftBlanks"Your Choice "
   IF G._Authority = "G" then WaitText = WaitText"(F/D/U/G/?): "
   ELSE                       WaitText = WaitText"(F/D/U/G/C/S/H/?): "
   CALL WaitForLine WaitText,1,0,0,1           /* UpCase Empty WaitTime Echo */
   G._Choice = G._RxLastLine
Return

/*═════════════════════════════════════════════════════════════════════════╗
║  ShowMenu, Draw the whole menu with alle menu- and info-points           ║
║    G._FirstMenu > 1: Draw the whole menu with alle menu- and info-point  ║
║    G._FirstMenu > 0: Draw info-point                                     ║
╚═════════════════════════════════════════════════════════════════════════*/
ShowMenu: Procedure Expose G.
   IF G._FirstMenu > 1 then DO
      CALL Wsend G._ClearScreen
         /*             y   x   Text                                       */
         CALL MenuPoint 2,  10, 'F', ' Show dir (F)iles   ' /* Row 1 Left  */
         CALL MenuPoint 5,  10, 'D', ' (D)ownload file(s) ' /* Row 2 Left  */
         CALL MenuPoint 8,  10, 'U', ' (U)pload file(s)   ' /* Row 3 Left  */
         CALL MenuPoint 11, 10, 'G', ' (G)oodbye          ' /* Row 4 Left  */
      IF G._Authority = "S" THEN DO
         CALL MenuPoint 5,  45, 'C', ' (C)hange directory ' /* Row 2 Right */
         CALL MenuPoint 8,  45, 'S', ' (S)earch file(s)   ' /* Row 3 Right */
         CALL MenuPoint 11, 45, 'H', ' Shutdown (H)ost    ' /* Row 4 Right */
      END
      G._FirstMenu = 1
   END
   IF G._FirstMenu > 0 then DO
      CALL Wsend G._CrLf
      IF G._Authority = "S" then PathInfo = G._CurPath
      ELSE                    PathInfo = "Standard Host Directory"
      CALL InfoLine '^[[0;34;47mCurrent HostDir:^[[30m  'overlay(PathInfo,'                                        ')
   END
   G._FirstMenu = 0
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  MenuPoint. Draw one menu-point with ARG(line, col, shortcut, name)      ║
╚═════════════════════════════════════════════════════════════════════════*/
MenuPoint: Procedure Expose G.
   CALL Wsend '"^[['ARG(1)';'ARG(2)'H"'
   CALL Wsend '"^[[1;37;47m┌───^[[0;30;47m┐"'
   CALL Wsend '"^[[1;37m┌────────────────────^[[0;30;47m┐^[[40m"'

   CALL Wsend '"^[['ARG(1)+1';'ARG(2)'H"'
   CALL Wsend '"^[[1;37;47m│ ^[[0;34;47m'ARG(3)' ^[[30m│"'
   CALL Wsend '"^[[1;37m│^[[0;30;47m'ARG(4)'│^[[40m"'

   CALL Wsend '"^[['ARG(1)+2';'ARG(2)'H"'
   CALL Wsend '"^[[1;37;47m└^[[0;30;47m───┘"'
   CALL Wsend '"^[[1;37m└^[[0;30;47m────────────────────┘^[[40m"'
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  InfoLine    draw one info-point with ARG(col, text)                     ║
╚═════════════════════════════════════════════════════════════════════════*/
InfoLine: Procedure Expose G.
   CALL Wsend G._CrLf
   CALL Wsend '"'G._LeftBlanks'^[[1;37;47m┌──────────────────────────────────"'
   CALL Wsend '"──────────────────────────^[[0;30;47m┐"'

   CALL Wsend "^[[0m"G._CrLf
   CALL Wsend '"'G._LeftBlanks'^[[1;37;47m│ ^[[0;30;47m'ARG(1)' ^[[30m│"'

   CALL Wsend "^[[0m"G._CrLf
   CALL Wsend '"'G._LeftBlanks'^[[1;37;47m└^[[0;30;47m────────────────────────"'
   CALL Wsend '"────────────────────────────────────┘"'
   CALL Wsend "^[[0m"G._CrLf
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  GetPassWord  Set G._Authority: "G" (Guest), "S" (Super) or "F" (Failed) ║
╚═════════════════════════════════════════════════════════════════════════*/
GetPassWord: Procedure Expose G.
   G._Authority = "F"
   WaitText = "Enter Password within the next "G._LogInWait" seconds: "
   IF G._Test <> 0 then WaitText = WaitText" (Test) "
   CALL WaitForLine WaitText,1,0,G._LogInWait,0 /* UpCase Empty WaitTime Echo */
   IF G._Test <> 0 then DO
      /*───────────────────────────────────────────────────────────────────┐
      │  Simplified Pass word checking                                     │
      └───────────────────────────────────────────────────────────────────*/
      IF       G._RxLastLine = "S" then G._Authority = "S"
      ELSE IF  G._RxLastLine = "G" then G._Authority = "G"
   END
   ELSE DO
      /*───────────────────────────────────────────────────────────────────┐
      │  Check against Password file                                       │
      └───────────────────────────────────────────────────────────────────*/
      PassWordState = Stream(G._PassWordFile,'C',"OPEN Read")
      IF Left(PassWordState,5) = "READY" then DO
         DO Forever
            /*─────────────────────────────────────────────────────────────┐
            │  Test for EndOfFile                                          │
            └─────────────────────────────────────────────────────────────*/
            PassWordState = Stream(G._PassWordFile,'S')
            IF Left(PassWordState,5) <> "READY" then LEAVE
            /*─────────────────────────────────────────────────────────────┐
            │  Read and compare line                                       │
            └─────────────────────────────────────────────────────────────*/
            PassWord = Linein(G._PassWordFile)
            Parse UPPER VAR PassWord PassWord
            Authority = Left(PassWord,1)
            IF Authority <> "S" & Authority <> "G" then ITERATE  /* comment */
            IF SubWord(PassWord,2) = G._RxLastLine then DO
               G._Authority = Authority
               LEAVE
            END
         END
         PassWordState = Stream(G._PassWordFile,'C',"CLOSE")
      END
      ELSE WRITELN '"Error opening 'G._PassWordFile' for read.'G._CrLf'"'
   END
   WRITELN ""
   IF      G._Authority = "S" then CALL Wsend G._CrLf"Authority level: SUPER"G._CrLf
   ELSE IF G._Authority = "G" then CALL Wsend G._CrLf"Authority level: GUEST"G._CrLf
   ELSE CALL Wsend G._CrLf"Authorization failed"G._CrLf
   DELAY 1
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  Welcome. Draw welcome Text                                              ║
╚═════════════════════════════════════════════════════════════════════════*/
Welcome: Procedure Expose G.
   WelCome1 = '"     ___  ___ ___ 'G._CrLf'"'
   WelCome2 = '"    |__ || _ | __|'G._CrLf'"'
   WelCome3 = '"     / /_||_|| |_      ZOC 'G._ProcName'-Script Version 'G._ProcVers''G._CrLf'"'
   WelCome4 = '"    |____|___|___|'G._CrLf'"'

   CALL Wsend G._ClearScreen""G._CrLf""G._CrLf
   CALL Wsend Welcome1
   CALL Wsend Welcome2
   CALL Wsend Welcome3
   CALL Wsend Welcome4
   CALL Wsend G._CrLf
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  WaitForLine WaitText UpCase Empty WaitTime Echo                         ║
║              1        2     3        4      5                            ║
║              UpCase   <> 0 converts data to upper case                   ║
║              Empty    <> 0 waits also for empty lines                    ║
║              WaitTime <> 0 overrides G._MaxWait                          ║
║              Echo     <> 0 Set Host Echo                                 ║
║  Waits for a line of data                                                ║
║  If Line is NoCarrier then Signal LostCarrier                            ║
║  If G._MaxWait is exhausted then Signal Restart                          ║
╚═════════════════════════════════════════════════════════════════════════*/
WaitForLine: Procedure Expose G.
   WaitTime = G._MaxWait
   WakeUpCounter = 0
   IF Arg(4) <> 0 then WaitTime = Arg(4) /* Overrides G._MaxWait ? */
   DO Forever
      CurWait = WaitTime
      If WaitTime > G._WakeUpTime then CurWait = G._WakeUpTime
      WaitTime = WaitTime - CurWait
      TIMEOUT CurWait
      IF Arg(1) <> "" then CALL Wsend '"'Arg(1)'"'
      CALL TestNoCarrier
      IF Arg(5) <> 0 then SETHOST 1         /* Set Host Echo ? */
      ELSE SETHOST 0
      IF Arg(3) = 0 then GETLINE            /* Wait also for empty lines ? */
      ELSE WAIT G._Cr
      RetCode = rc
      SETHOST 0
      IF RetCode = 0 then LEAVE
      ELSE DO
         CALL TestNoCarrier                 /* Restart if lost carrier */
         WakeUpCounter = WakeUpCounter + 1
         If WaitTime > 0 then DO
            SELECT
               WHEN WakeUpCounter = 3 then Nbr = "rd"
               WHEN WakeUpCounter = 2 then Nbr = "nd"
               WHEN WakeUpCounter = 1 then Nbr = "st"
               OTHERWISE                   Nbr = "th"
            END
            CALL Wsend '"'G._Bell''G._CrLf'Please wake up! This is your 'WakeUpCounter''Nbr'"'
            IF WaitTime <= (G._WakeUpTime) then DO
               CALL Wsend '" and final"'
            END
            CALL Wsend '" Call. 'WaitTime' seconds before LOGOFF. 'G._CrLf'"'
            ITERATE
         END
         CALL Wsend '"'G._CrLf'You are logged off because of no activity.'G._CrLf'"'
         Delay 1
         Signal Restart
      END
   END
   G._RxLastLine = ZocLastLine()
   IF Left(G._RxLastLine,10) = "NO CARRIER" then Signal LostCarrier
   G._RxLastLine = Strip(G._RxLastLine)
   IF Arg(2) <> 0 then Parse UPPER VAR G._RxLastLine G._RxLastLine /* UpCase ? */
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  WaitForBS                                                               ║
║  Waits G._LogInWait for BackSpace from remote.                           ║
║  If G._LogInWait is exhausted then go to Restart                         ║
║  If Carrier is lost Signal LostCarrier                                   ║
╚═════════════════════════════════════════════════════════════════════════*/
WaitForBS: Procedure Expose G.
   CALL TestNoCarrier
   CALL Wsend '"Hit BACKSPACE within the next 'G._LogInWait' seconds to start host ... 'G._CrLf'"'
   TIMEOUT G._LogInWait
   WAIT G._Bs
   IF rc <> 0 then DO
      CALL TestNoCarrier
      Signal Restart
   END
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  WaitForConnect                                                          ║
║  Waits for Connect and subsequent CR                                     ║
╚═════════════════════════════════════════════════════════════════════════*/
WaitForConnect: Procedure Expose G.
   DO Forever
      WRITELN  "Waiting for call ..."
      TIMEOUT 32000
      /*───────────────────────────────────────────────────────────────────┐
      │  Wait for Connect.                                                 │
      └───────────────────────────────────────────────────────────────────*/
      WAIT "CONNECT"
      IF rc <> 0 then ITERATE                     /* keep waiting */
      /*───────────────────────────────────────────────────────────────────┐
      │  Wait for Connect.                                                 │
      └───────────────────────────────────────────────────────────────────*/
      TIMEOUT 10
      WAIT G._Cr
      G._RxLastLine = ZocCarrier()
      IF Left(G._RxLastLine,7) = "CARRIER" then LEAVE
   END
   WRITELN  "Remote Call received"
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  Wsend. Write and Send. Important: Does SETHOST 0 when exiting           ║
╚═════════════════════════════════════════════════════════════════════════*/
Wsend: Procedure Expose G.
   IF ZOCCARRIER() = "CARRIER" then DO
      SETHOST 1
      SEND  ARG(1)
   END
   SETHOST 0
   WRITE ARG(1)
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  DownL                                                                   ║
╚═════════════════════════════════════════════════════════════════════════*/
DownL: Procedure Expose G.
   DownLCount = 0
   WaitText = "Enter Filename to download. Wildcards welcome: "
   CALL WaitForLine WaitText,0,0,0,1                 /* UpCase Empty WaitTime Echo */
   IF G._RxLastLine = "" then DO
      CALL Wsend "DownLoad cancelled"G._CrLF
      RETURN
   END
   LoadFile = G._RxLastLine
   Fn = G._CurPath||LoadFile
   CALL ShowDir G._CurPath||LoadFile,0
   IF FilesFound = 0 then RETURN
   IF Ask_YN("You want to download this file(s)","Y") = 0 then RETURN
   TempFile1State = Stream(G._TempFile1,"C","seek = 1")
   OkCount  = 0
   ErrCount = 0
   Error = 0
   DO Forever
      CALL ReadFn 0                     /* sets G._EndOfFile1 */
      IF G._EndOfFile1 <> 0 then LEAVE
      /*───────────────────────────────────────────────────────────────────┐
      │  If Error occured on transmission of previous File, ask wether to  │
      │  continue.                                                         │
      └───────────────────────────────────────────────────────────────────*/
      IF Error <> 0 then DO
         Delay 3
         IF Ask_YN("Error occured on transmission. Continue", "Y") = 0 then LEAVE
      END
      Fn = G._CurPath||G._FileName
      /*───────────────────────────────────────────────────────────────────┐
      │  Upload to remote. Turn Host Echo off immediately after. This must │
      │  be done to avoid sending Garbage across the line when transfer    │
      │  is cancelled.                                                     │
      └───────────────────────────────────────────────────────────────────*/
      UPLOAD z Fn
      G._RxLastLine = ZocResult()
      SETHOST 0
      /*───────────────────────────────────────────────────────────────────┐
      │  Analyze Status of transmission                                    │
      └───────────────────────────────────────────────────────────────────*/
      WRITELN "ZocResult=>"G._RxLastLine"<"
      CALL Wsend '"'G._CrLf'Transmission Status for File 'Fn': "'
      IF G._RxLastLine = "##OK##" then do
         OkCount = OkCount + 1
         CALL Wsend "Ok."
         Error = 0
      END
      ELSE DO
         Error = 1
         ErrCount = ErrCount + 1
         Delay 3
         CALL Wsend "NOT Ok."
      END
      CALL Wsend G._CrLf
      DownLCount = DownLCount + 1
   END
   CALL Wsend '"Transmission Report: ***** 'OkCount' File(s) Ok. "'
   CALL Wsend '"***** 'ErrCount' File(s) NOT Ok. ***** 'G._CrLf'"'
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  UpL                                                                     ║
╚═════════════════════════════════════════════════════════════════════════*/
UpL: Procedure Expose G.
   /*──────────────────────────────────────────────────────────────────────┐
   │  Download from remote. Turn Host Echo off immediately after. This is  │
   │  to avoid sending Garbage across  the line when cancel is hit.        │
   └──────────────────────────────────────────────────────────────────────*/
   Download z G._DirRemote
   G._RxLastLine = ZocResult()
   SETHOST 0
   /*──────────────────────────────────────────────────────────────────────┐
   │  Analyze result of transmission                                       │
   └──────────────────────────────────────────────────────────────────────*/
   CALL Wsend "Transmission Report: Host detected "
   If G._RxLastLine = "##OK##" then DO
      CALL Wsend "no Error."G._CrLf
   END
   ELSE DO
      CALL Wsend "Error."G._CrLf
   END
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  SearchFile   /AD=Directories only, /S=also subdirs                      ║
╚═════════════════════════════════════════════════════════════════════════*/
SearchFile: Procedure Expose G.
   WaitText = G._LeftBlanks"Enter [/AD ][/S ][drive:][\dir\]filename to search for: "
   CALL WaitForLine WaitText,0,1,0,1           /* UpCase Empty WaitTime Echo */
   IF G._RxLastLine = "" then RETURN
   Filename = G._RxLastLine
   /*──────────────────────────────────────────────────────────────────────┐
   │  Preceede with drive and/or parent dir if omitted                     │
   └──────────────────────────────────────────────────────────────────────*/
   IF Substr(Filename,2,1) <> ":" then DO
      IF Left(Filename,1) <> "\" then DO
         Filename = G._Curdrive||G._CurDir||Filename
      END
      ELSE Filename = G._Curdrive||Filename
   END

   CALL Wsend '"'G._LeftBlanks'Searching for >'Filename'<, please wait ...'G._CrLf''G._CrLf'"'
   CALL ShowDir Filename,1
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  ShowDir  ARG(1)=filespec.  ARG(2)=show complete list                    ║
║           On Exit:                                                       ║
║              If Files found: FilesFound = 1, G._TempFile1 is open.       ║
║              Else            FilesFound = 0.                             ║
╚═════════════════════════════════════════════════════════════════════════*/
ShowDir: Procedure Expose G.
   SD_String = Arg(1)
   CALL closedelfile G._TempFile1
   CALL SetFileSystem SD_String               /* Set Variable G._CurFileSys */
   IF G._CurFileSys = "U" then RETURN
   SD_String = "dir "SD_String">"G._TempFile1
   Signal OFF Error
   ADDRESS CMD SD_String
   RetCode = rc
   Signal on Error Name ProgTerm
   IF RetCode <> 0 then DO
      CALL Wsend "No Files found"G._CrLf
      FilesFound = 0
      RETURN
   END
   FilesFound = 1
   /*──────────────────────────────────────────────────────────────────────┐
   │  Open G._TempFile1                                                    │
   └──────────────────────────────────────────────────────────────────────*/
   TempFile1State = Stream(G._TempFile1,'C',"OPEN Read")
   IF Left(TempFile1State,5) <> "READY" then DO
      ErrMess = "Error opening "G._TempFile1" for read"
      CALL Wsend ErrMess
      RETURN
   END
   CALL ShowList Arg(2)
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  ShowList ARG(1) = 0: show only files, else show whole list.             ║
║               Splits G._TempFile1 in groups of G._ListSize and displays. ║
║               G._TempFile1 is open when coming here                      ║
╚═════════════════════════════════════════════════════════════════════════*/
ShowList: Procedure Expose G.
   G._EndOfFile1        = 0
   TempFile2OutState = 0
   TempFile2State    = ""
   DO Until G._EndOfFile1 <> 0
      /*───────────────────────────────────────────────────────────────────┐
      │  Clear and Open TempFile2 for Write                                │
      └───────────────────────────────────────────────────────────────────*/
      CALL CloseDelFile G._TempFile2
      TempFile2State = Stream(G._TempFile2,'C',"OPEN Write")
      IF Left(TempFile2State,5) <> "READY" then DO
         ErrMess = "Error opening "G._TempFile2" for write"
         CALL Wsend ErrMess
         LEAVE
      END
      /*───────────────────────────────────────────────────────────────────┐
      │  Copy a number of Lines to file TempFile2                          │
      └───────────────────────────────────────────────────────────────────*/
      DO LineCount = 0 to G._ListSize
         /*────────────────────────────────────────────────────────────────┐
         │ Get a line containing a file name from G._TempFile1             │
         └────────────────────────────────────────────────────────────────*/
         CALL ReadFn ARG(1)
         IF G._EndOfFile1 then LEAVE
         /*────────────────────────────────────────────────────────────────┐
         │  Skip if line in G._TempFile1 does not contain a file name      │
         └────────────────────────────────────────────────────────────────*/
         IF G._FileNameLine = "" then DO
            LineCount = LineCount - 1   /* Adjust count back if not a file */
            ITERATE
         END
         /*────────────────────────────────────────────────────────────────┐
         │  Write Line with file name to TempFile2                         │
         └────────────────────────────────────────────────────────────────*/
         TempFile2OutState = Lineout(G._TempFile2,G._FileNameLine)
         IF TempFile2OutState <> 0 then DO
            ErrMess = "Error writing to "G._TempFile2
            CALL Wsend ErrMess
            LEAVE
         END
      END
      IF TempFile2OutState <> 0 then LEAVE        /* trouble with list file */
      TempFile2State = Stream(G._TempFile2,'C',"CLOSE")
      /*───────────────────────────────────────────────────────────────────┐
      │  Upload TempFile2, unless it is empty                              │
      └───────────────────────────────────────────────────────────────────*/
      IF LineCount <> 0 then UPLOAD a1 G._TempFile2
      IF LineCount >= G._ListSize then DO
         IF Ask_YN("More","Y") = 0 then LEAVE
      END
   END
   CALL CloseDelFile G._TempFile2
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  Ask_YN     Ask Yes/No Question. Return 1 Yes, 0 if No.                  ║
║     Ask_YN txt Y or N                                                    ║
║            txt: Text to Ask                                              ║
║            Y:   Default is Yes, other: Default is No                     ║
║     Question must be answered with Y, N, or Enter only.                  ║
║     Enter only returns default.                                          ║
╚═════════════════════════════════════════════════════════════════════════*/
Ask_YN: Procedure Expose G.
   AskText = G._LeftBlanks||Arg(1)
   IF Arg(2) = "Y" | Arg(2) = "y"  then Default = 1
   ELSE                                 Default = 0
   IF Default = 1 then AskText = AskText" (Y,n) ? "
   ELSE                AskText = AskText" (y,N) ? "
   DO Forever
      CALL WaitForLine AskText,1,1,0,1         /* UpCase Empty WaitTime Echo */
      IF G._RxLastLine = "" then RETURN Default
      Answer = Left(G._RxLastLine,1)
      IF Answer = "Y" then RETURN 1
      IF Answer = "N" then RETURN 0
   END
/* End Ask_YN */

/*═════════════════════════════════════════════════════════════════════════╗
║  CloseDelFile  Close and delete file                                     ║
╚═════════════════════════════════════════════════════════════════════════*/
CloseDelFile: Procedure Expose G.
   FileName = Arg(1)
   FState = Stream(FileName,'C',Query exists)
   IF FState <> "" then DO
      FState = Stream(FileName,'C',"CLOSE")
      ADDRESS CMD '"del 'FileName'"'
   END
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  TestNoCarrier    Restart if  carrier lost                               ║
║                                                                          ║
╚═════════════════════════════════════════════════════════════════════════*/
TestNoCarrier: Procedure Expose G.
   IF ZocCarrier() = "NO CARRIER" then DO
      SETHOST 0                 /* turnoff evtl. host echo */
      Signal LostCarrier
   END
RETURN

/*═════════════════════════════════════════════════════════════════════════╗
║  LostCarrier                                                             ║
╚═════════════════════════════════════════════════════════════════════════*/
LostCarrier:
   WRITELN G._CrLf"**********Carrier lost. Restarting.**********"G._CrLf
   Signal Restart
/* LostCarrier End*/

/*═════════════════════════════════════════════════════════════════════════╗
║  Restart                                                                 ║
╚═════════════════════════════════════════════════════════════════════════*/
Restart:
   SETHOST 0
   Signal Main
/*Restart End*/

/*========================================================================*/

/*═════════════════════════════════════════════════════════════════════════╗
║                            Ending Procedures                             ║
╚═════════════════════════════════════════════════════════════════════════*/

/*═════════════════════════════════════════════════════════════════════════╗
║  ProgTerm                                                                ║
╚═════════════════════════════════════════════════════════════════════════*/
ProgTerm:
   ADDRESS ZOC
   SETHOST 0
   WRITELN "*** Program terminated at Line "SIGL". ***"
SIGNAL Finito

/*═════════════════════════════════════════════════════════════════════════╗
║  Common Exit                                                             ║
╚═════════════════════════════════════════════════════════════════════════*/
Finito:
   ADDRESS ZOC
   SETHOST 0
   HANGUP
   WRITELN "Trying to re-load Options "G._StdOpts
   LOADOPTS G._StdOpts
   IF G._ProcLogOn <> 0 then LOGGING 1
   ELSE                     LOGGING 0
   WRITELN G._ProcName"-Script Version "G._ProcVers" ended"
Exit
[ RETURN TO DIRECTORY ]