(*----------------------------------------------------------------------*)
(*   Process_Script --- Convert PibTerm script file to in-core code.    *)
(*----------------------------------------------------------------------*)

OVERLAY PROCEDURE Process_Script;

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Process_Script                                       *)
(*                                                                      *)
(*     Purpose:    Convert PibTerm script file to in-core instructions. *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*         Process_Script;                                              *)
(*                                                                      *)
(*      Remarks:                                                        *)
(*                                                                      *)
(*         The entire script file is read and converted to an in-core   *)
(*         representation which can be executed.                        *)
(*                                                                      *)
(*         At this time, user-defined labels are not allowed.  There    *)
(*         are some variable related to them here, however.  The next   *)
(*         time around (PibTerm v4.0) they will be used to allow for    *)
(*         case statements and procedures in scripts.                   *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

CONST                              (* Maximum # of labels allowed *)
   Max_Script_Labels = 20;
                                   (* Maximum stack depth         *)
   Max_Script_Stack  = 10;

TYPE                               
                                   (* Points to a label reference *)

   Script_Label_Ptr = ^Script_Label_Reference;

                                   (* Records one label reference *)
   Script_Label_Reference = RECORD
                                               (* Offset in script buffer *)
                               Buffer_Pos : INTEGER;
                                               (* Next reference *)
                               Next_Ref   : Script_Label_Ptr;

                            END;

   Script_Label_Type = RECORD
                                          (* Label name *)
                          Name       : STRING[12];
                                          (* Label definition position *)
                          Buffer_Pos : INTEGER;
                                          (* Pointer to first reference *)
                          First_Ref  : Script_Label_Ptr;

                       END;

VAR
                                   (* Number of labels currently defined *)

   Script_Label_Count     : INTEGER;

                                   (* Script label definition vector *)

   Script_Labels          : ARRAY[1..Max_Script_Labels] OF Script_Label_Type;

                                   (* Current stack levels, conditional     *)
                                   (* script commands.                      *)

   Script_Repeat_Level    : INTEGER;
   Script_If_Level        : INTEGER;
   Script_While_Level     : INTEGER;

                                   (* Stacks for conditional commands       *)

   Script_Repeat_Stack    : ARRAY[1..Max_Script_Stack] OF INTEGER;
   Script_If_Stack        : ARRAY[1..Max_Script_Stack] OF INTEGER;
   Script_While_Stack     : ARRAY[1..Max_Script_Stack] OF INTEGER;

   L                      : INTEGER;
   I                      : INTEGER;
   K                      : INTEGER;
   IS                     : INTEGER;
   Local_Save             : Saved_Screen_Ptr;
   Ch                     : CHAR;
   Text_Line              : AnyStr;
   Byte_File              : FILE OF BYTE;
   OK_Script_Command      : BOOLEAN;
   Script_Command_Token   : AnyStr;
   Script_Line            : AnyStr;
   Saved_Script_Line      : AnyStr;
   Current_Script_Command : PibTerm_Command_Type;

   Script_Debug_File      : TEXT;
   Script_Debug_Mode      : BOOLEAN;

(*----------------------------------------------------------------------*)
(*             Get_Quoted_String --- pick up string in quotes           *)
(*----------------------------------------------------------------------*)

PROCEDURE Get_Quoted_String(     S    : AnyStr;
                             VAR IS   : INTEGER;
                             VAR QS   : AnyStr;
                             VAR Quote: CHAR );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Get_Quoted_String                                    *)
(*                                                                      *)
(*     Purpose:    Extracts quoted string from a string.                *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Get_Quoted_String(      S    : AnyStr;                        *)
(*                           VAR IS    : INTEGER;                       *)
(*                           VAR QS    : AnyStr;                        *)
(*                           VAR Quote : CHAR );                        *)
(*                                                                      *)
(*            S     --- string containing quoted string                 *)
(*            IS    --- current position in S                           *)
(*            QS    --- resultant extracted string (no quotes)          *)
(*            Quote --- quote character (blank if quotes not found)     *)
(*                                                                      *)
(*      Remarks:                                                        *)
(*                                                                      *)
(*         A quote within a string can be entered by putting two quotes *)
(*         together, e.g., 'ab''c' -->  ab'c.                           *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   LS         : INTEGER;
   End_String : BOOLEAN;

BEGIN (* Get_Quoted_String *)
                                   (* Null string is default *)
   QS    := '';
   Quote := ' ';
                                   (* Skip leading blanks *)
   LS    := LENGTH( S );

   WHILE ( IS <= LS ) AND ( S[IS] = ' ' ) DO
      IS := IS + 1;
                                   (* See if we have a quote *)
   IF ( IS <= LS ) THEN
      BEGIN

         IF S[IS] IN ['''','"'] THEN
            BEGIN
                                   (* Pickup quoted string is so *)
               Quote      := S[IS];
               End_String := FALSE;

               REPEAT

                  IS := IS + 1;
                                   (* Note:  two quotes in a row used   *)
                                   (*        to indicate single quote   *)
                                   (*        to be inserted into string *)

                  IF IS <= LS THEN
                     IF S[IS] <> Quote THEN
                        QS := QS + S[IS]
                     ELSE
                        BEGIN
                           IF ( IS + 1 ) <= LS THEN
                              IF S[IS+1] = Quote THEN
                                 BEGIN
                                    QS := QS + Quote;
                                    IS := IS + 1;
                                 END
                              ELSE
                                 End_String := TRUE
                           ELSE
                              End_String := TRUE;
                        END
                  ELSE
                     End_String := TRUE;

               UNTIL End_String;

            END;

      END;

END   (* Get_Quoted_String *);

(*----------------------------------------------------------------------*)
(*                     Get_String --- Pick up string                    *)
(*----------------------------------------------------------------------*)

PROCEDURE Get_String(     S    : AnyStr;
                      VAR IS   : INTEGER;
                      VAR QS   : AnyStr;
                      VAR Delim: CHAR );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Get_String                                           *)
(*                                                                      *)
(*     Purpose:    Extracts string up to a delimeter.                   *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Get_String(      S     : AnyStr;                              *)
(*                     VAR IS    : INTEGER;                             *)
(*                     VAR QS    : AnyStr;                              *)
(*                     VAR Delim : CHAR );                              *)
(*                                                                      *)
(*            S     --- string containing string to extract             *)
(*            IS    --- current position in S                           *)
(*            QS    --- resultant extracted string                      *)
(*            Delim --- delimeter character                             *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   LS         : INTEGER;
   End_String : BOOLEAN;
   Ch         : CHAR;

BEGIN (* Get_String *)
                                   (* Null string is default *)
   QS    := '';
   Delim := ' ';
                                   (* Skip leading blanks *)
   LS    := LENGTH( S );

   WHILE ( IS <= LS ) AND ( S[IS] = ' ' ) DO
      IS := IS + 1;
                                   (* Copy up to non-letter, non-digit *)
   End_String := FALSE;

   IF ( IS <= LS ) THEN
      REPEAT

         Ch := S[IS];

         IF ( Ch IN ['A'..'Z','a'..'z','0'..'9'] ) THEN
            BEGIN
               QS := QS + Ch;
               IS := IS + 1;
            END
         ELSE
            BEGIN
               End_String := TRUE;
               Delim      := Ch;
            END;

      UNTIL End_String;

END   (* Get_String *);

(*----------------------------------------------------------------------*)
(*                  Get_Integer --- pick up integer                     *)
(*----------------------------------------------------------------------*)

PROCEDURE Get_Integer(     S     : AnyStr;
                       VAR IS    : INTEGER;
                       VAR Qnum  : BOOLEAN;
                       VAR IntVal: INTEGER );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Get_Integer                                          *)
(*                                                                      *)
(*     Purpose:    Extracts integer from a string.                      *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Get_Integer(      S      : AnyStr;                            *)
(*                      VAR IS     : INTEGER;                           *)
(*                      VAR Qnum   : BOOLEAN;                           *)
(*                      VAR IntVal : INTEGER   );                       *)
(*                                                                      *)
(*            S      --- string containing quoted string                *)
(*            IS     --- current position in S                          *)
(*            Qnum   --- TRUE if a number extracted                     *)
(*            IntVal --- integer extracted or 0 if none                 *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   LS         : INTEGER;
   End_Of_Num : BOOLEAN;
   Int_Sign   : INTEGER;

BEGIN (* Get_Integer *)
                                   (* Skip leading blanks *)
   LS     := LENGTH( S );

   WHILE ( IS <= LS ) AND ( S[IS] = ' ' ) DO
      IS := IS + 1;
                                   (* Default value is zero *)
   IntVal     := 0;
   Qnum       := FALSE;
   End_Of_Num := FALSE;
   Int_Sign   := 1;
                                   (* Pick up minus sign    *)
   IF ( IS <= LS ) THEN
      IF ( S[IS] = '-' ) THEN
         BEGIN
            Int_Sign := -1;
            IS       := IS + 1;
         END;
                                   (* Pick up digits if any *)
   REPEAT

      IF ( IS <= LS ) THEN
         IF S[IS] IN ['0'..'9'] THEN
            BEGIN
               IntVal := IntVal * 10 + ORD( S[IS] ) - ORD('0');
               Qnum   := TRUE;
            END
         ELSE
            End_Of_Num := TRUE
      ELSE
         End_Of_Num := TRUE;

      IF ( NOT End_Of_Num ) THEN
         IS := IS + 1;

   UNTIL ( End_Of_Num );

   IntVal := IntVal * Int_Sign;

END   (* Get_Integer *);

(*----------------------------------------------------------------------*)
(*    Copy_String_To_Buffer --- Copy string from script line to buffer  *)
(*----------------------------------------------------------------------*)

PROCEDURE Copy_String_To_Buffer;

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Copy_String_To_Buffer                                *)
(*                                                                      *)
(*     Purpose:    Copies quoted string from script line to buffer      *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Copy_String_To_Buffer;                                        *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   L     : INTEGER;
   Quote : CHAR;
   I     : INTEGER;

BEGIN (* Copy_String_To_Buffer *)

   Get_Quoted_String( Script_Line, IS, Text_Line, Quote );

   L := LENGTH( Text_Line );

   IF ( NOT ( Quote IN ['''','"'] ) ) THEN
      L := 0;

   Script_Buffer_Pos                 := Script_Buffer_Pos + 1;
   Script_Buffer^[Script_Buffer_Pos] := L;

   IF Script_Debug_Mode THEN
       WRITE( Script_Debug_File , Script_Buffer_Pos:4 , ' ', L:4 );

   FOR I := 1 TO L DO
      BEGIN
         Script_Buffer_Pos                 := Script_Buffer_Pos + 1;
         Script_Buffer^[Script_Buffer_Pos] := ORD( Text_Line[I] );
      END;

   IF Script_Debug_Mode THEN
       BEGIN
          WRITE  ( Script_Debug_File , ' ', Text_Line );
          WRITELN( Script_Debug_File );
       END;

END   (* Copy_String_To_Buffer *);

(*----------------------------------------------------------------------*)
(*     Copy_Integer_To_Buffer --- Copy integer to script line buffer    *)
(*----------------------------------------------------------------------*)

PROCEDURE Copy_Integer_To_Buffer( IntVal : INTEGER );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Copy_Integer_To_Buffer                               *)
(*                                                                      *)
(*     Purpose:    Copies integer to script line buffer                 *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Copy_Integer_To_Buffer( IntVal : INTEGER );                   *)
(*                                                                      *)
(*           IntVal --- Value to place in script buffer                 *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   Int_Bytes : ARRAY[1..2] OF BYTE ABSOLUTE IntVal;

BEGIN (* Copy_Integer_To_Buffer *)

   Script_Buffer_Pos                 := Script_Buffer_Pos + 1;
   Script_Buffer^[Script_Buffer_Pos] := Int_Bytes[1];

   IF Script_Debug_Mode THEN
       WRITELN( Script_Debug_File , Script_Buffer_Pos:4 , ' ',
                Int_Bytes[1]:4, Int_Bytes[2]:4, ' ', IntVal:8,
                ' (Integer)');

   Script_Buffer_Pos                 := Script_Buffer_Pos + 1;
   Script_Buffer^[Script_Buffer_Pos] := Int_Bytes[2];

END   (* Copy_Integer_To_Buffer *);

(*----------------------------------------------------------------------*)
(*        Copy_Byte_To_Buffer --- Copy byte to script line buffer       *)
(*----------------------------------------------------------------------*)

PROCEDURE Copy_Byte_To_Buffer( ByteVal : INTEGER );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Copy_Byte_To_Buffer                                  *)
(*                                                                      *)
(*     Purpose:    Copies byte to script line buffer                    *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Copy_Byte_To_Buffer( IntVal : INTEGER );                      *)
(*                                                                      *)
(*           ByteVal --- Value to place in script buffer                *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

BEGIN (* Copy_Byte_To_Buffer *)

   Script_Buffer_Pos                 := Script_Buffer_Pos + 1;
   Script_Buffer^[Script_Buffer_Pos] := ByteVal;

   IF Script_Debug_Mode THEN
       BEGIN
          WRITE( Script_Debug_File , Script_Buffer_Pos:4 , ' ', ByteVal,
                 ' (Byte)' );
          IF ( ByteVal > 32 ) AND ( ByteVal < 127 ) THEN
             WRITE( Script_Debug_File , ' (',CHR( ByteVal ),')' );
          WRITELN( Script_Debug_File );
       END;

END   (* Copy_Integer_To_Buffer *);

(*----------------------------------------------------------------------*)
(*    Copy_Protocol_To_Buffer --- Copy transfer protocol to buffer      *)
(*----------------------------------------------------------------------*)

PROCEDURE Copy_Protocol_To_Buffer;

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Copy_Protocol_To_Buffer                              *)
(*                                                                      *)
(*     Purpose:    Copies file transfer protocol to buffer              *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Copy_Protocol_To_Buffer;                                      *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   LS                : INTEGER;
   Transfer_Protocol : Transfer_Type;
   Trans_Mode        : STRING[10];
   End_Of_Protocol   : BOOLEAN;
   Delim             : CHAR;

BEGIN (* Copy_Protocol_To_Buffer *)

                                   (* Get transfer mode *)

   Get_String( Script_Line, IS, Trans_Mode, Delim );

   IF LENGTH( Trans_Mode ) > 0 THEN
      Trans_Mode := UpperCase( Trans_Mode )
   ELSE
      Trans_Mode := 'Z';

   Transfer_Protocol := Default_Transfer_Type;

   IF Trans_Mode = 'A'  THEN
      Transfer_Protocol := Ascii
   ELSE IF Trans_Mode = 'X'  THEN
      Transfer_Protocol := Xmodem_Chk
   ELSE IF Trans_Mode = 'XC' THEN
      Transfer_Protocol := Xmodem_CRC
   ELSE IF Trans_Mode = 'Y'  THEN
      Transfer_Protocol := Ymodem
   ELSE IF Trans_Mode = 'YB' THEN
      Transfer_Protocol := Ymodem_Batch
   ELSE IF Trans_Mode = 'T'  THEN
      Transfer_Protocol := Telink
   ELSE IF Trans_Mode = 'TC' THEN
      Transfer_Protocol := Telink
   ELSE IF Trans_Mode = 'M'  THEN
       Transfer_Protocol := Modem7_Chk
   ELSE IF Trans_Mode = 'MC'  THEN
       Transfer_Protocol := Modem7_CRC
   ELSE IF Trans_Mode = 'M7' THEN
      Transfer_Protocol := Modem7_CRC
   ELSE IF Trans_Mode = 'K' THEN
         BEGIN
            Transfer_Protocol    := Kermit;
            Kermit_File_Type_Var := Kermit_Ascii;
         END
      ELSE IF Trans_Mode = 'KB' THEN
         BEGIN
            Transfer_Protocol    := Kermit;
            Kermit_File_Type_Var := Kermit_Binary;
         END;

   Copy_Integer_To_Buffer( ORD( Transfer_Protocol ) + 1 );

END   (* Copy_Protocol_To_Buffer *);

(*----------------------------------------------------------------------*)
(*    Extract_Script_Command --- Extract command type from script line  *)
(*----------------------------------------------------------------------*)

PROCEDURE Extract_Script_Command( VAR OK_Script_Command : BOOLEAN );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Extract_Script_Command                               *)
(*                                                                      *)
(*     Purpose:    Extracts command name from script line               *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Extract_Script_Command( VAR OK_Script_Command : BOOLEAN );    *)
(*                                                                      *)
(*           OK_Script_Command --- set TRUE if legitimate command       *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   Found : BOOLEAN;
   L     : INTEGER;

BEGIN (* Extract_Script_Command *)

                                   (* Remove initial, trailing blanks *)

   Script_Line := LTRIM( TRIM( Script_Line ) );
   L           := LENGTH( Script_Line );

                                   (* If nothing left, ignore this line *)

   IF ( L < 1 ) THEN
      Current_Script_Command := Null_Command
   ELSE
      BEGIN
                                   (* Append blank to script line *)

         Script_Line := Script_Line + ' ';

                                   (* Pick up command name        *)

         Script_Command_Token := '';
         I                    := 1;

         WHILE( Script_Line[I] <> ' ' ) DO
            BEGIN
               Script_Command_Token := Script_Command_Token +
                                       UpCase( Script_Line[I] );
               I                    := I + 1;
            END;
                                    (* Abbreviate command to 8 chars *)

         IF ( LENGTH( Script_Command_Token ) > 8 ) THEN
            Script_Command_Token := COPY( Script_Command_Token, 1, 8 );

                                    (* Strip command text from front *)
                                    (* of script text line           *)
         I := I + 1;

         IF ( L - I + 1 ) > 0 THEN
            Script_Line := COPY( Script_Line, I, L - I + 1 )
         ELSE
            Script_Line := '';

                                   (* Look up command in valid command list *)
         I     := 0;
         Found := FALSE;

         REPEAT
            I     := I + 1;
            Found := ( Script_Command_Token = Script_File_Command_Names[I] );
         UNTIL  ( Found OR ( I >= Max_Script_File_Commands ) );

         IF ( NOT Found ) THEN
            Current_Script_Command := Bad_Command
         ELSE
            Current_Script_Command := Script_File_Commands[I];

      END;

   OK_Script_Command := Current_Script_Command <> Bad_Command;

END   (* Extract_Script_Command *);

(*----------------------------------------------------------------------*)
(*      Emit_Wait_String_Command --- Emit wait for string command       *)
(*----------------------------------------------------------------------*)

PROCEDURE Emit_Wait_String_Command( VAR OK_Script_Command: BOOLEAN );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Emit_Wait_String_Command                             *)
(*                                                                      *)
(*     Purpose:    Emit command to wait for specified string            *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Emit_Wait_String_Command( VAR OK_Script_Command : BOOLEAN );  *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   Qnum   : BOOLEAN;
   IntVal : INTEGER;

BEGIN (* Emit_Wait_String_Command *)

                                   (* String to wait for *)
   Copy_String_To_Buffer;
                                   (* Null reply string  *)

   Copy_Byte_To_Buffer( 0 );
                                   (* Number of seconds to wait *)
   IS := IS + 1;

   Get_Integer( Script_Line, IS, Qnum, IntVal );

   IF ( NOT Qnum ) THEN
      IntVal := 30;

   Copy_Integer_To_Buffer( IntVal );

                                   (* Failure label *)

   Copy_Integer_To_Buffer( Script_Buffer_Pos + 3 );

   OK_Script_Command := TRUE;

END   (* Emit_Wait_String_Command *);

(*----------------------------------------------------------------------*)
(*           Emit_If_Command --- Emit IF conditional command            *)
(*----------------------------------------------------------------------*)

PROCEDURE Emit_If_Command(     False_Label       : INTEGER;
                           VAR OK_Script_Command : BOOLEAN );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Emit_If_Command                                      *)
(*                                                                      *)
(*     Purpose:    Emit IF conditional command                          *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Emit_If_Command(     False_Label       : INTEGER;             *)
(*                         VAR OK_Script_Command : BOOLEAN );           *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   Qnum   : BOOLEAN;
   IntVal : INTEGER;
   PStr   : AnyStr;
   I      : INTEGER;
   L      : INTEGER;
   Delim  : CHAR;
   Save_IS: INTEGER;

   NextP      : INTEGER;
   NextP_Bytes: ARRAY[1..2] OF BYTE ABSOLUTE NextP;

BEGIN (* Emit_If_Command *)
                                   (* Back up 1 byte in script buffer   *)
                                   (* We overwrite existing instruction *)
                                   (* with the proper IF guy here.      *)

   Script_Buffer_Pos := Script_Buffer_Pos - 1;

                                   (* Pick up type of condition *)

   Get_String( Script_Line, IS, PStr, Delim );

   L    := LENGTH( PStr );
   PStr := UpperCase( PStr );
                                   (* No condition -- bad *)
   IF ( L = 0 ) THEN
      BEGIN
         PStr := 'BAD';
         L    := 3;
      END;
                                   (* Look for NOT *)

   IF ( PStr = 'NOT' ) THEN
      BEGIN

         I := 0;

         Get_String( Script_Line, IS, PStr, Delim );

         IS   := IS + 1;

         L    := LENGTH( PStr );
         PStr := UpperCase( PStr );

      END
   ELSE
      I := 1;
                                   (* True branch -- next statement *)

   NextP := Script_Buffer_Pos + 8;

                                   (* Analyze condition type *)
   IF ( L >= 3 ) THEN
      IF COPY( PStr, 1, 3 ) = 'CON' THEN
         BEGIN
            Copy_Byte_To_Buffer( ORD( IfConSy ) );
            Copy_Integer_To_Buffer( I );
            Copy_Integer_To_Buffer( NextP );
            Copy_Integer_To_Buffer( False_Label );
         END
      ELSE IF COPY( PStr, 1, 3 ) = 'WAI' THEN
         BEGIN
            Copy_Byte_To_Buffer( ORD( IfFoundSy ) );
            Copy_Integer_To_Buffer( I );
            Copy_Integer_To_Buffer( NextP );
            Copy_Integer_To_Buffer( False_Label );
         END
      ELSE IF COPY( PStr, 1, 3 ) = 'LOC' THEN
         BEGIN
            Save_IS := IS;
            Get_Quoted_String( Script_Line, IS, PStr, Delim );
            L := LENGTH( PStr );
            IF ( NOT ( Delim IN ['''','"'] ) ) THEN
               L := 0;
            Copy_Byte_To_Buffer( ORD( IfLocStrSy ) );
            Copy_Integer_To_Buffer( I );
            Copy_Integer_To_Buffer( NextP + L + 1 );
            Copy_Integer_To_Buffer( False_Label );
            IS := Save_IS;
            Copy_String_To_Buffer;
         END
      ELSE IF COPY( PStr, 1, 3 ) = 'REM' THEN
         BEGIN
            Save_IS := IS;
            Get_Quoted_String( Script_Line, IS, PStr, Delim );
            L := LENGTH( PStr );
            IF ( NOT ( Delim IN ['''','"'] ) ) THEN
               L := 0;
            Copy_Byte_To_Buffer( ORD( IfRemStrSy ) );
            Copy_Integer_To_Buffer( I );
            Copy_Integer_To_Buffer( NextP + L + 1 );
            Copy_Integer_To_Buffer( False_Label );
            IS := Save_IS;
            Copy_String_To_Buffer;
         END
      ELSE
         OK_Script_Command := FALSE
   ELSE
      OK_Script_Command := FALSE;

END   (* Emit_If_Command *);

(*----------------------------------------------------------------------*)
(*   Parse_Script_Command --- Parse and convert script to internal code *)
(*----------------------------------------------------------------------*)

PROCEDURE Parse_Script_Command( VAR OK_Script_Command : BOOLEAN );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Parse_Script_Command                                 *)
(*                                                                      *)
(*     Purpose:    Parse and convert script line to internal code.      *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Parse_Script_Command( VAR OK_Script_Command : BOOLEAN );      *)
(*                                                                      *)
(*           OK_Script_Command --- set TRUE if legitimate command       *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

VAR
   Qnum   : BOOLEAN;
   IntVal : INTEGER;
   ByteVal: BYTE;
   Quote  : CHAR;
   Delim  : CHAR;
   L      : INTEGER;
   I      : INTEGER;
   J      : INTEGER;
   SvPos  : INTEGER;
   PStr   : AnyStr;

   NextP      : INTEGER;
   NextP_Bytes: ARRAY[1..2] OF BYTE ABSOLUTE NextP;

BEGIN (* Parse_Script_Command *)
                                   (* Assume command is OK to start   *)
   OK_Script_Command := TRUE;
                                   (* Insert command type into buffer *)

   Copy_Byte_To_Buffer( ORD( Current_Script_Command ) );

                                   (* Pick up and insert command-dependent *)
                                   (* information into script buffer.      *)
   IS := 1;

   CASE Current_Script_Command OF

      SuspendSy,
      DelaySy    : BEGIN
                      Get_Integer( Script_Line, IS, Qnum, IntVal );
                      IF ( NOT Qnum ) THEN
                         IntVal := 1;
                      Copy_Integer_To_Buffer( IntVal );
                   END;

      CaptureSy,
      DialSy,
      DosSy,
      InputSy,
      MessageSy,
      RedialSy,
      STextSy,
      TextSy,
      WaitSy     : Copy_String_To_Buffer;

      RInputSy   : BEGIN
                                   (* Copy prompt string to script buffer *)

                      Copy_String_To_Buffer;

                                   (* Assume echo mode *)
                      I := 1;
                                   (* See if NOECHO appears *)

                      Get_String( Script_Line, IS, PStr, Delim );

                      PStr := UpperCase( PStr );

                      IF ( Pstr = 'NOECHO' ) THEN
                         I := 0;

                                   (* Insert echo/noecho flag in buffer *)

                      Copy_Integer_To_Buffer( I );

                   END;

      IfLocStrSy : BEGIN
                                   (* Increment IF level *)

                      Script_If_Level := Script_If_Level + 1;
                      Script_If_Stack[Script_If_Level] :=
                         -Script_Buffer_Pos;

                                   (* Emit a conditional *)

                      Emit_If_Command( 0 , OK_Script_Command );

                   END;

      ElseSy     : BEGIN
                      IF ( Script_If_Level > 0 ) THEN
                         BEGIN

                                   (* Get address of IF statement *)
                                   (* Remember offset is negative *)

                            J := -Script_If_Stack[ Script_If_Level ];

                                   (* Back up over Else *)

                            Script_Buffer_Pos := Script_Buffer_Pos - 1;

                                   (* Insert GOTO here to branch  *)
                                   (* around FALSE code.          *)

                            Copy_Byte_To_Buffer( ORD( GoToSy ) );

                                   (* Address of GoTo not defined   *)
                                   (* since we don't know it yet -- *)
                                   (* leave it zero, and stuff the  *)
                                   (* address of cell to receive    *)
                                   (* fixup address later on IF     *)
                                   (* stack.                        *)

                            Script_If_Stack[ Script_If_Level ] :=
                               Script_Buffer_Pos + 1;

                            Copy_Integer_To_Buffer( 0 );

                                   (* Fixup FALSE branch address in IF *)

                            NextP := Script_Buffer_Pos + 1;

                            Script_Buffer^[ J + 5 ] := NextP_Bytes[1];
                            Script_Buffer^[ J + 6 ] := NextP_Bytes[2];

                            IF Script_Debug_Mode THEN
                               BEGIN
                                  WRITELN( Script_Debug_File ,
                                           '      Fixup at ', ( J + 5 ):4,
                                           ' to be ',NextP_Bytes[1]:4,
                                          NextP_Bytes[2]:4, ' = ',NextP:8 );
                               END;

                         END
                      ELSE
                         OK_Script_Command := FALSE;

                   END;

      EndIfSy    : BEGIN

                      IF ( Script_If_Level > 0 ) THEN
                         BEGIN

                            J := Script_If_Stack[ Script_If_Level ];
                            Script_If_Level := Script_If_Level - 1;

                                   (* Fixup GoTo before ELSE or   *)
                                   (* FALSE branch in original IF *)
                                   (* if no else.                 *)

                            NextP := Script_Buffer_Pos;

                            IF ( J > 0 ) THEN
                               BEGIN
                                  Script_Buffer^[ J     ] := NextP_Bytes[1];
                                  Script_Buffer^[ J + 1 ] := NextP_Bytes[2];
                                  IF Script_Debug_Mode THEN
                                     BEGIN
                                        WRITELN( Script_Debug_File ,
                                                 '      Fixup at ', ( J ):4,
                                                 ' to be ',NextP_Bytes[1]:4,
                                                 NextP_Bytes[2]:4, ' = ',NextP:8 );
                                     END;

                               END
                            ELSE
                               BEGIN
                                  J := -J;
                                  Script_Buffer^[ J + 5 ] := NextP_Bytes[1];
                                  Script_Buffer^[ J + 6 ] := NextP_Bytes[2];
                                  IF Script_Debug_Mode THEN
                                     BEGIN
                                        WRITELN( Script_Debug_File ,
                                                 '      Fixup at ', ( J + 5 ):4,
                                                 ' to be ',NextP_Bytes[1]:4,
                                                 NextP_Bytes[2]:4, ' = ',NextP:8 );
                                     END;


                               END;

                                   (* Erase EndIf from buffer *)

                            Script_Buffer_Pos := Script_Buffer_Pos - 1;

                         END
                      ELSE
                         OK_Script_Command := FALSE;

                   END;

      KeySendSy  : BEGIN
                      Get_String( Script_Line, IS, PStr, Delim );
                      L := LENGTH( PStr );
                      PStr := UpperCase( PStr );
                      IF ( L > 0 ) THEN
                         BEGIN
                            I := POS( PStr[1] , 'FACS' );
                            IF ( I > 0 ) THEN
                               BEGIN
                                  J := 2;
                                  Get_Integer( PStr, J, Qnum, IntVal );
                                  IF ( Qnum AND ( IntVal >= 0 ) AND
                                     ( IntVal <= 10 ) ) THEN
                                     BEGIN
                                        CASE I OF
                                           1: I := 58;
                                           2: I := 103;
                                           3: I := 93;
                                           4: I := 83;
                                        END (* Case *);
                                        ByteVal := I + IntVal;
                                        Copy_Byte_To_Buffer( ByteVal );
                                     END (* Qnum *);
                               END (* I > 0 *);
                         END (* L > 0 *);
                   END;

      WaitStrSy  : Emit_Wait_String_Command( OK_Script_Command );

      WhenSy     : BEGIN
                      Copy_String_To_Buffer;
                      IS := IS + 1;
                      Copy_String_To_Buffer;
                   END;

      ReceiveSy  : BEGIN
                      Copy_String_To_Buffer;
                      IS := IS + 1;
                      Copy_Protocol_To_Buffer;
                   END;

      SendSy     : BEGIN
                      Copy_String_To_Buffer;
                      IS := IS + 1;
                      Copy_Protocol_To_Buffer;
                   END;

      RepeatSy   : BEGIN
                                   (* Increment repeat level *)

                      Script_Repeat_Level := Script_Repeat_Level + 1;

                                   (* Remember where repeat starts. *)

                      Script_Repeat_Stack[Script_Repeat_Level] :=
                         Script_Buffer_Pos;

                                   (* Erase repeat command *)

                      Script_Buffer_Pos   := Script_Buffer_Pos   - 1;

                   END;

      UntilSy    : BEGIN
                      IF ( Script_Repeat_Level > 0 ) THEN
                         BEGIN

                                   (* Pop REPEAT address off stack *)

                            J := Script_Repeat_Stack[ Script_Repeat_Level ];
                            Script_Repeat_Level := Script_Repeat_Level - 1;

                                   (* Emit end of loop test *)

                            Emit_If_Command( J , OK_Script_Command );

                         END
                      ELSE
                         OK_Script_Command := FALSE;
                   END;

      WhileSy    : BEGIN
                                   (* Increment While level *)

                      Script_While_Level := Script_While_Level + 1;
                      Script_While_Stack[Script_While_Level] :=
                         Script_Buffer_Pos;

                                   (* Emit conditional command *)

                      Emit_If_Command( 0 , OK_Script_Command );

                   END;

      EndWhileSy : BEGIN

                      IF ( Script_While_Level > 0 ) THEN
                         BEGIN

                            J := Script_While_Stack[ Script_While_Level ];
                            Script_While_Level := Script_While_Level - 1;

                            Script_Buffer^[Script_Buffer_Pos] := ORD( GoToSy );
                            Copy_Integer_To_Buffer( J );

                            NextP := Script_Buffer_Pos + 1;

                            Script_Buffer^[ J + 5 ] := NextP_Bytes[1];
                            Script_Buffer^[ J + 6 ] := NextP_Bytes[2];

                            IF Script_Debug_Mode THEN
                               BEGIN
                                  WRITELN( Script_Debug_File ,
                                           '      Fixup at ', ( J + 5 ):4,
                                           ' to be ',NextP_Bytes[1]:4,
                                           NextP_Bytes[2]:4, ' = ',NextP:8 );
                               END;

                         END
                      ELSE
                         OK_Script_Command := FALSE;

                   END;

      ParamSy    : BEGIN

                      Get_String( Script_Line, IS, PStr, Delim );

                      Copy_Byte_To_Buffer( ORD( PStr[1] ) );
                      Copy_Byte_To_Buffer( ORD( PStr[2] ) );

                      IF Delim = '=' THEN
                         IS := IS + 1;

                      L                 := 0;
                      Script_Buffer_Pos := Script_Buffer_Pos + 1;
                      SvPos             := Script_Buffer_Pos;

                      FOR I := IS TO LENGTH( Script_Line ) DO
                         BEGIN
                            L                 := L + 1;
                            Copy_Byte_To_Buffer( ORD( Script_Line[I] ) );
                         END;

                      Script_Buffer^[SvPos] := L;

                   END;

      ELSE;

   END (* CASE *);

END   (* Parse_Script_Command *);

(*----------------------------------------------------------------------*)
(*   Fix_Label_References --- Fix up label references in script buffer  *)
(*----------------------------------------------------------------------*)

PROCEDURE Fix_Label_References( VAR OK_Script_Command : BOOLEAN );

(*----------------------------------------------------------------------*)
(*                                                                      *)
(*     Procedure:  Fix_Label_References                                 *)
(*                                                                      *)
(*     Purpose:    Fix up label references in script buffer             *)
(*                                                                      *)
(*     Calling Sequence:                                                *)
(*                                                                      *)
(*        Fix_Label_References( VAR OK_Script_Command : BOOLEAN );      *)
(*                                                                      *)
(*           OK_Script_Command --- set TRUE if fixups went OK           *)
(*                                                                      *)
(*----------------------------------------------------------------------*)

BEGIN (* Fix_Label_References *)

   OK_Script_Command := TRUE;

END   (* Fix_Label_References *);

(*----------------------------------------------------------------------*)

BEGIN (* Process_Script *)

                                   (* Save current screen *)
   Save_Screen( Local_Save );
   Draw_Menu_Frame( 10, 10, 78, 20, Menu_Frame_Color,
                    Menu_Text_Color, 'Scan script file' );

                                   (* Pick up script file name *)
                                   (* if not already supplied  *)

   IF ( LENGTH( Script_File_Name ) = 0 ) THEN
      BEGIN
         WRITE('Script file name ? ');
         READLN( Script_File_Name );
      END;
                                   (* Fix up script file name *)

   Script_File_Name := UpperCase( Script_File_Name );

   IF ( POS( '.', Script_File_Name ) = 0 ) THEN
      Script_File_Name := Script_File_Name + '.SCR';

                                   (* See if script file exists *)

   ASSIGN( Byte_File , Script_File_Name );
      (*$I-*)
   RESET ( Byte_File );
      (*$I+*)

   IF ( Int24Result <> 0 ) THEN
      BEGIN

         WRITELN(' ');
         WRITELN('Script file ',Script_File_Name,' not found.');
         WRITELN(' ');

         Really_Wait_String  := FALSE;
         Script_Suspend_Time := 0.0;
         Script_File_Mode    := FALSE;

                                   (* Restore previous screen *)
         DELAY( Two_Second_Delay );

         Restore_Screen( Local_Save );
         Reset_Global_Colors;
                                   (* Quit now *)
         EXIT;

      END
   ELSE
      BEGIN
         WRITELN(' ');
         WRITELN('Beginning scan of script file ',Script_File_Name);
         WRITELN(' ');
      END;
                                   (* Get size of script file.     *)
                                   (* Allocate command buffer of   *)
                                   (* same length to hold compiled *)
                                   (* script commands.             *)

   Script_Buffer_Size := FileSize( Byte_File );

   CLOSE( Byte_File );

   IF ( Script_File_Name = 'ZZBOGUS.SCR' ) THEN
      BEGIN
         ASSIGN ( Script_Debug_File , 'ZZBOGUS.DBG' );
         REWRITE( Script_Debug_File );
         Script_Debug_Mode := TRUE;
      END
   ELSE
      Script_Debug_Mode := FALSE;

   GetMem( Script_Buffer , Script_Buffer_Size );

                                   (* Current offset in script buffer *)
   Script_Buffer_Pos  := 0;
                                   (* No labels yet defined         *)
   Script_Label_Count := 0;
                                   (* All stacks empty              *)
   Script_Repeat_Level := 0;
   Script_If_Level     := 0;
   Script_While_Level  := 0;
                                   (* Open script file as text file *)

   ASSIGN( Script_File , Script_File_Name );
      (*$I-*)
   RESET ( Script_File );
      (*$I+*)
                                   (* Read and compile lines from  *)
                                   (* script file                  *)
   REPEAT
                                   (* Read script line             *)

      READLN( Script_File , Script_Line );

      Saved_Script_Line := Script_Line;
      OK_Script_Command := TRUE;

                                   (* Check for serious read error *)
      IF Int24Result <> 0 THEN
         OK_Script_Command := FALSE

                                   (* Skip comment lines           *)

      ELSE IF ( LENGTH( Script_Line ) > 0 ) THEN
         IF ( Script_Line[1] <> '*' ) THEN

                                   (* Parse and store compiled command *)
            BEGIN

               IF Script_Debug_Mode THEN
                  BEGIN
                     WRITELN( Script_Debug_File , '--- next statement --- ' );
                     WRITELN( Script_Debug_File , '<', Script_Line, '>' );
                     WRITELN( Script_Debug_File , '--- ');
                  END;

               Extract_Script_Command( OK_Script_Command );

               IF OK_Script_Command THEN
                  Parse_Script_Command  ( OK_Script_Command );

               IF ( NOT Ok_Script_Command ) THEN
                  BEGIN

                     WRITELN('>>> Error in the following script line: ');
                     WRITELN( Saved_Script_Line );

                     WRITE('Hit any key to continue ... ');

                     READ( Kbd, Ch );

                     IF ( ORD( Ch ) = ESC ) AND KeyPressed THEN
                        READ( Kbd, Ch );

                  END;

         END;

   UNTIL ( EOF( Script_File ) OR ( NOT OK_Script_Command ) );

                                   (* Close script file.             *)
      (*$I-*)
   CLOSE( Script_File );
      (*$I+*)

   I := Int24Result;
                                   (* Drop "finish script" command   *)
                                   (* into script buffer.            *)

   IF Script_Debug_Mode THEN
      WRITELN( Script_Debug_File , '--- Exit statement follows ... ');

   Copy_Byte_To_Buffer( ORD( ExitSy ) );

                                   (* Check if stacks empty.  If not,  *)
                                   (* error from unclosed loop.        *)

   OK_Script_Command := OK_Script_Command           AND
                        ( Script_Repeat_Level = 0 ) AND
                        ( Script_If_Level     = 0 ) AND
                        ( Script_While_Level  = 0 );

                                   (* Fix up label references          *)
   IF OK_Script_Command THEN
      Fix_Label_References( OK_Script_Command );

                                   (* Now point to start of buffer     *)
   Script_Buffer_Pos := 0;
                                   (* If everything OK, allow script   *)
                                   (* to execute, else release buffer. *)
   Really_Wait_String  := FALSE;
   Script_Suspend_Time := 0.0;

   IF OK_Script_Command THEN
      BEGIN
         Script_File_Mode   := TRUE;
         WRITELN('Script file OK.');
      END
   ELSE
      BEGIN
         WRITELN('Script file will not be executed.');
         Script_File_Mode   := FALSE;
         FREEMEM( Script_Buffer , Script_Buffer_Size );
      END;
                                   (* Restore previous screen *)
   DELAY( Two_Second_Delay );

   Restore_Screen( Local_Save );
   Reset_Global_Colors;

   IF Script_Debug_Mode THEN
      CLOSE( Script_Debug_File );

END   (* Process_Script *);
