{ rcli - Robot Command Language Interpreter. }
   PROGRAM rcli ( INPUT, OUTPUT, commands, log ) ;

{ External References : }
   %INCLUDE 'inc:util.pas'       { Imports Routines :-
                                      setterminalmode
                                      testsymbol
                                 }
   %INCLUDE 'dwb:general.inc'    { Imports Routines :-
                                      upper_case_of
                                      format_integer
                                 }
   %INCLUDE 'dwb:strings.def'    { Imports Types :-
                                      string255
                                      string80
                                 }
   %INCLUDE 'dwb:strings.inc'    { Imports Routines :-
                                      yes_to
                                      raw_string
                                      effective_length
                                      skip_over_blanks
                                      integer_number
                                      positive_integer
                                      strings_match
                                 }
   %INCLUDE 'dwb:v200ccp.inc'    { Imports Routines :-
                                      enable_keypad
                                      disable_keypad
                                 }
   %INCLUDE 'dwb_2:interp.inc'  { Imports Types :-
                                      coords
                                      coord_mode
                                      long_str
                                      posn_ptr
                                   Imports Constant :-
                                      end_of_com_line
                                   Imports Routines :-
                                      init_interp
                                      interpreter
                                      get_home
                                      log_home
                                      log_homes
                                      log_macros
                                      log_position
                                 }
   %INCLUDE 'dwb_2:robtop.inc'   { Imports Routines :-
                                      activate_robot
                                      deactivate_robot
                                      move_arc
                                      base_swivel
                                      shoulder_tilt
                                      elbow_tilt
                                      wrist_tilt
                                      wrist_swivel
                                      open_gripper
                                      move_x
                                      move_y
                                      move_z
                                 }

   %INCLUDE 'dwb_2:roblow.inc'  { Imports Routines :-
                                     get_position
                                     report_failure
                                 }

{ Global Constants : }
   CONST

      cont_char       = '_' ; { flags continuation line following }
      blank           = ' ' ; { blank character }
      esc_ascii       = 27  ; { ascii value of the escape character }
      beep            =  7  ; { ascii value of the BEL character }
      filename_len    = 20  ; { maximum length of filename strings }
      quit_char       = 'Q' ; { terminate session command }

{    input/output commands }
      io_coms = [ 'I', 'O' ] ;

{ Global Types : }
   TYPE

      line_pntr  = ^input_line ;

      input_line = RECORD
                      previous : line_pntr ;
                      contents : string255 ;
                      no_chars : 0..255 ;
                      next     : line_pntr
                   END ;

      filename = VARYING [ filename_len ] OF CHAR ;

{ Global Variables : }
   VAR

      commands      : TEXT ;       { file containing Robot commands }
      log           : TEXT ;       { file to contain log of Robot commands }
      first_line    : line_pntr ;  { points to first line of user input }
      last_line     : line_pntr ;  { points to last line of user input }
      com_line      : long_str ;   { command line obtained from user }
      err_mess      : long_str ;   { error message returned by interpreter }
      com_line_len  : INTEGER ;    { length of command line }
      comm_list     : posn_ptr ;   { command list returned by interpreter }
      user_quits    : BOOLEAN ;    { user wants to finish ? }
      keypad        : BOOLEAN ;    { user requested keyapd mode ? }
      obeying       : BOOLEAN ;    { command file to be executed ? }
      logging       : BOOLEAN ;    { commands etc. to be logged ? }
      commented     : BOOLEAN ;    { command line contains comment ? }
      external_comm : BOOLEAN ;    { command handled by external interpreter ? }
      posn_ok       : BOOLEAN ;    { position obtained from robot o.k. }
      curr_posn     : coords ;     { to hold current robot position }
      com_file      : filename ;   { name of file containing commands }
      log_file      : filename ;   { name of file to contain log }

{ exists - Does the specified file exist ? ( From SSI library ) }
   FUNCTION exists ( filename : string255 ) : BOOLEAN ; EXTERNAL ;

{------------------------------- initialised_ok -------------------------------}

{ initialised_ok => Initialise global variables, robot & interpreter. }
   FUNCTION initialised_ok : BOOLEAN ;

{ Local Variables : }
   VAR

      ok          : BOOLEAN ; { locally used function result }
      curr_coords : coords ;  { current robot coordinates }
      home0_loc   : coords ;  { robot home zero position }
      ndofs       : INTEGER ; { number of degrees of freedom for the robot }

   BEGIN { initialised_ok }

{ setup terminal for free scrolling and enable keypad keys }
   setterminalmode ( 8 ) ;
   enable_keypad ;

{ initialise "quit program" & "keypad mode" flags }
   user_quits := FALSE ;
   keypad := FALSE ;

{ setup command and log file filenames and flags }
   obeying := FALSE ;
   com_file := '' ;
   logging := FALSE ;
   log_file := '' ;

{ initialise input line pointers }
   first_line  := NIL ;
   last_line   := NIL ;
   comm_list   := NIL ;

{ write out startup banner }
   WRITELN ;
   WRITELN ( 'Robot Command Language Interpreter Version 2.0 25/06/86' ) ;
   WRITELN ;
   WRITELN ( 'Type ? for help information.' ) ;
   WRITELN ;

{ setup robot }
   activate_robot ( curr_coords, ok ) ;

{ if robot initialised o.k. then initialise interpreter }
   IF ( ok ) THEN BEGIN
      get_position ( arcs, curr_coords, ok ) ;
      ndofs := 6 ;
      init_interp ( ndofs, curr_coords )
   END ;

   IF ( ok ) THEN BEGIN

{    does the user want to position the robot in its "home 0" position ? }
      IF ( yes_to ( 'Position Robot at "Home 0" (Y/N) ? ' ) ) THEN BEGIN
         get_home ( 0, home0_loc, ok ) ;
         IF ( ok ) THEN BEGIN
            move_arc ( TRUE, home0_loc, ok ) ;
            IF ( NOT ok ) THEN BEGIN
               report_failure ( '! Warning : Unable to home robot - ' ) ;
               ok := yes_to ( 'Carry on anyway (Y/N) ? ' ) ;
               IF ( ok ) THEN BEGIN
                  get_position ( arcs, curr_coords, ok ) ;
                  give_posn ( curr_coords ) ;
               END
            END
         END
      END
   END ;

{ something has gone wrong, say so }
   IF ( NOT ok ) THEN WRITELN ( '! Error : Unable to initialise robot.' ) ;

{ return function result }
   initialised_ok := ok

   END ; { of function initialised_ok -----------------------------------------}

{----------------------------------- shut_down --------------------------------}

{ shut_down => Tidy eveything up when user quits. }
   PROCEDURE shut_down ;

   BEGIN { shut_down }

{ disable keypad keys, de-activate Robot & remind user to switch off arm }
   disable_keypad ;
   deactivate_robot ;
   WRITELN ( 'Do not forget to switch the Robot arm off.' )

   END ; { of procedure shut_down ---------------------------------------------}

{-------------------------------- remove_input --------------------------------}

{ remove_input => Clean up the internal input line buffer. }
   PROCEDURE remove_input ;

{ Local Variables : }
   VAR

      saved : line_pntr ; { used to index through line list }

   BEGIN { remove_input }

{ keep looping until all lines are removed }
   WHILE ( first_line <> NIL ) DO BEGIN

{    save current first line pointer then move it down list }
      saved := first_line ; first_line := first_line^.next ;

{    get rid of record pointed at by saved pointer }
      DISPOSE ( saved )
   END ;

{ reset pointer to last line of input }
   last_line := NIL

   END ; { of procedure remove_input ------------------------------------------}

{------------------------------ get_command_line ------------------------------}

{ get_command_line => Get a command line from the user. }
   PROCEDURE get_command_line ;

{ Local Variables : }
   VAR

      a_line    : string255 ; { line read from user }
      line_len  : 0..255 ;    { effective length of line read from user }
      finished  : BOOLEAN ;   { has user finished input ? }
      commented : BOOLEAN ;   { comment encountered on command line ? }

{ Local Procedures : }

{--------------------------------- insert_line --------------------------------}

{ insert_line => Insert a new input line into the internal store. }
   PROCEDURE insert_line ;

{ Local Variables : }
   VAR

      local_line : line_pntr ; { pointer to new line record }

   BEGIN { insert_line }

{ create a new line record and load it with data }
   NEW ( local_line ) ;
   WITH local_line^ DO BEGIN
      previous := NIL ;
      contents := a_line ;
      no_chars := line_len ;
      next := NIL
   END;

{ check if it is the first line on the list }
   IF ( first_line = NIL ) THEN BEGIN
      first_line := local_line ;
      last_line := local_line
   END

{ otherwise put it on the end }
   ELSE BEGIN
      last_line^.next := local_line ;
      local_line^.previous := last_line ;
      last_line := local_line
   END ;

{ check for continuation character at the end of the line }
   finished := ( a_line [ line_len ] <> cont_char )

   END ; { of procedure insert_line -------------------------------------------}

{--------------------------------- clean_input --------------------------------}

{ clean_input => Get rid of spaces and continuation characters etc. }
   PROCEDURE clean_input ;

{ Local Variables : }
   VAR

      curr_line : line_pntr ; { used to scan through input line buffer }
      i         : 0..255 ; { index to individual characters }
      too_long  : BOOLEAN ; { command line too long ? }

   BEGIN { clean_input }

{ initialise the command line, its length and the "no errors" flag }
   com_line := '' ;
   com_line_len := 0 ;
   too_long := FALSE ;

{ loop through input line buffer combining all input lines }
   curr_line := first_line ;
   WHILE ( ( curr_line <> NIL ) AND ( NOT too_long ) ) DO BEGIN

{    loop through all characters of the given line }
      WITH curr_line^ DO FOR i := 1 TO no_chars DO BEGIN
         too_long := ( com_line_len > 253 ) ;
         IF ( NOT too_long ) THEN BEGIN
            IF ( commented ) THEN BEGIN
               com_line := com_line + contents [ i ] ;
               com_line_len := com_line_len + 1
            END
            ELSE IF ( NOT ( contents [ i ] IN [ blank, cont_char ] ) ) THEN
              BEGIN
               com_line := com_line + upper_case_of ( contents [ i ] ) ;
               com_line_len := com_line_len + 1 ;
               IF ( contents [ i ] = end_of_com_line ) THEN commented := TRUE ;
            END
         END
      END ;
      curr_line := curr_line^.next
   END ;

{ if com line too long report error, discard input & get new command line
  it is O.K. if have only lost a comment, just gets truncated }
   IF ( NOT commented ) THEN BEGIN
      IF ( too_long ) THEN BEGIN
         WRITELN ( '! Error : Command line too long.' ) ;
         remove_input ;
         get_command_line
      END

{    otherwise mark end of command line }
      ELSE BEGIN
         com_line := com_line + end_of_com_line ;
         com_line_len := com_line_len + 1 ;
      END
   END

   END ; { of procedure clean_input --------------------------------------}

   BEGIN { get_command_line }

{ initialise "command line commented" flag }
   commented := FALSE ;

{ loop until some command input is obtained }
   WHILE ( first_line = NIL ) DO BEGIN

{    loop collecting lines ( line plus continuations }
      REPEAT

{       reading commands from a file ? }
         IF ( obeying ) THEN BEGIN

{          user interupt ? }
            IF ( testsymbol = esc_ascii ) THEN BEGIN
               obeying := FALSE ;
               com_file := '' ;
               remove_input ;
               a_line := '' ;
               WRITELN ( CHR ( beep ), '! User interupt.' )
            END

{          otherwise get line from com file, revert to user input on EOF }
            ELSE BEGIN
               READLN ( commands, a_line ) ;
               IF ( EOF ( commands ) ) THEN  BEGIN
                  obeying := FALSE ;
                  com_file := ''
               END
            END
         END

{       no, so get commands from user }
         ELSE BEGIN

{          output the prompt and get the users reply }
            WRITE ( 'RCL> ' ) ;
            a_line := raw_string ( TRUE ) ;
            WRITELN
         END ;

{       get length of command line ignoring trailing blanks }
         line_len := effective_length ( a_line ) ;

{       if it is totally blank input has finished }
         finished := ( line_len = 0 ) ;
         IF ( NOT finished ) THEN BEGIN

{          have to check for lone continuation characters }
            IF ( ( skip_over_blanks ( a_line ) = line_len ) AND
              ( a_line [ line_len ] = cont_char ) ) THEN BEGIN
               IF ( first_line = NIL ) THEN BEGIN
                  WRITELN ( '! Unexpected continuation character.' ) ;
                  finished := TRUE
               END
            END
            ELSE insert_line
         END ;
      UNTIL ( finished )
   END ;

{ clean up the user input and transfer it into the command line buffer }
   clean_input

   END ; { of procedure get_command_line --------------------------------------}

{------------------------------ keypad_entered_ok -----------------------------}

{ keypad_entered_ok => Command line contains valid enter keypad command ? }
   FUNCTION keypad_entered_ok : BOOLEAN ;

{ Local Variables : }
   VAR

     ok     : BOOLEAN ; { locally used function result }

   BEGIN { keypad_entered_ok }

{ only an <ESC>?M is considered valid }
   ok := ( ( com_line [ 2 ] = '?' ) AND ( com_line [ 3 ] = 'M' ) ) ;
   IF ( ok ) THEN keypad := TRUE

{ report error }
   ELSE WRITELN ( '! Error : Invalid escape sequence.' ) ;

   keypad_entered_ok := ok { return result }

   END ; { of procedure keypad_entered_ok -------------------------------------}

{------------------------------- exec_key_coms --------------------------------}

{ exec_key_coms => Execute keypad commands. }
   PROCEDURE exec_key_coms ;

{ Local Variables : }
   VAR

      key_string : string255 ; { holds keypad key escape sequences }
      string_len : INTEGER ; { length of string obtained }
      ok         : BOOLEAN ; { was keypad command executed ok ? }
      hloc       : coords ;  { used for "Home" and "Locate home" key commands }
      hexists    : BOOLEAN ; { specified home exists ? }
      id         : INTEGER ; { home id. number used for "Home" key command }

   BEGIN { exec_key_coms }

{ tell user how to get back to "normal" mode }
   WRITELN ( 'Press the keypad minus key to return to normal mode.' ) ;

{ loop getting key commands and executing them }
   REPEAT

{ debug -- stops dummy IMP routines writing to the log file ! }
   WRITE ( CHR ( 0 ) ) ;

{    ignore any empty strings }
      REPEAT
         key_string := raw_string ( FALSE ) ;
         string_len := LENGTH ( key_string ) ;
         IF ( string_len = 0 ) THEN WRITE ( CHR ( beep ) ) ;
      UNTIL ( string_len > 0 ) ;

{    now sort out and execute the valid key commands }
      IF ( ORD ( key_string [ 1 ] ) = esc_ascii ) THEN BEGIN
         IF ( string_len > 1 ) THEN CASE key_string [ 2 ] OF
            '?' : IF ( string_len > 2 ) THEN CASE key_string [ 3 ] OF
                     'l' : BEGIN
                              wrist_tilt ( FALSE, -1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'W[-1]' )
                           END ;
                     'm' : keypad := FALSE ;
                     'n' : BEGIN
                              wrist_tilt ( FALSE, +1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'W[1]' )
                           END ;
                     'p' : BEGIN
                              move_y ( FALSE, -5, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'Y[-5}' )
                           END ;
                     'q' : BEGIN
                              move_y ( FALSE, +5, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'Y[5]' )
                           END ;
                     'u' : BEGIN
                              shoulder_tilt ( FALSE, -1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'S[-1]' )
                           END ;
                     'v' : BEGIN
                              shoulder_tilt ( FALSE, +1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'S[1]' )
                           END ;
                     't' : BEGIN
                              move_x ( FALSE, -15, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'X[-15]' )
                           END ;
                     'r' : BEGIN
                              elbow_tilt ( FALSE, -1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'E[-1]' )
                           END ;
                     's' : BEGIN
                              elbow_tilt ( FALSE, +1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'E[1]' )
                           END ;
                     'w' : BEGIN
                              move_x ( FALSE, +15, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'X[15]' )
                           END ;
                     'x' : BEGIN
                              open_gripper ( FALSE, -1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'G[-1]' )
                           END ;
                     'y' : BEGIN
                              open_gripper ( FALSE, +1, ok ) ;
                              IF ( logging ) THEN WRITELN ( log, 'G[1]' )
                           END ;
                     OTHERWISE WRITE ( CHR ( beep ) )
                  END { of case }
                  ELSE WRITE ( CHR ( beep ) ) ;
            'A' : BEGIN
                     move_z ( FALSE, +5, ok ) ;
                     IF ( logging ) THEN WRITELN ( log, 'Z[5]' )
                  END ;
            'B' : BEGIN
                     move_z ( FALSE, -5, ok ) ;
                     IF ( logging ) THEN WRITELN ( log, 'Z[-5]' )
                  END ;
            'C' : BEGIN
                     base_swivel ( FALSE, +2, ok ) ;
                     IF ( logging ) THEN WRITELN ( log, 'B[1]' )
                  END ;
            'D' : BEGIN
                     base_swivel ( FALSE, -2, ok ) ;
                     IF ( logging ) THEN WRITELN ( log, 'B[-1]' )
                  END ;
            'H' : BEGIN
                     id := integer_number ( 'Home identification number ? ' ) ;
                     get_home ( id, hloc, hexists ) ;
                     IF ( hexists ) THEN BEGIN
                        move_arc ( TRUE, hloc, ok ) ;
                        IF ( logging ) THEN BEGIN
                           WRITE ( log, 'H[' ) ;
                           format_integer ( log, id ) ;
                           WRITELN ( log, ']' )
                        END
                     END ELSE
                        WRITELN ( '! Error : Invalid home id number.' )
                  END ;
            't' : BEGIN
                     get_position ( arcs, hloc, ok ) ;
                     IF ( ok ) THEN BEGIN
                        id := positive_integer ( 'Home id number ? ' ) ;
                        create_home ( id, hloc ) ;
                        IF ( logging ) THEN log_home ( log, id, hloc )
                     END ELSE
                        WRITELN ( '! Error : Failed to obtain robot position.' )
                  END ;
            '^' : BEGIN
                     wrist_swivel ( FALSE, -10, ok ) ;
                     IF ( logging ) THEN WRITELN ( log, 'T[-1]' )
                  END ;
            ']' : BEGIN
                     wrist_swivel ( FALSE, +10, ok ) ;
                     IF ( logging ) THEN WRITELN ( log, 'T[1]' )
                  END ;
            OTHERWISE WRITE ( CHR ( beep ) )
         END { of case }
         ELSE WRITE ( CHR ( beep ) )
      END ELSE WRITE ( CHR ( beep ) ) ;

{    if execution error has occurred report it and reset the error flag }
      IF ( NOT ok ) THEN BEGIN
         WRITE ( CHR ( beep ) ) ;
         report_failure ( '! Error during command execution - ' ) ;
         ok := TRUE
      END ;
   UNTIL ( NOT keypad )

   END ; { of procedure exec_key_coms -----------------------------------------}

{------------------------------ proc_local_coms -------------------------------}

{ proc_local_coms => Handle any commands not appropriate to the interpreter. }
   PROCEDURE proc_local_coms ;

{ Local Variables : }
   VAR

      name     : filename ; { filename, if any, read from command line }
      com_char : CHAR ;     { saves command character }
      marker   : INTEGER ;  { remembers character position }
      finished : BOOLEAN ;  { filename accumulated ? }
      c        : INTEGER ;  { pointer to current character }
      ok       : BOOLEAN ;  { no errors ? }

   BEGIN { proc_local_coms }

{ initialise "command not handlede locally" flag }
   external_comm := FALSE ;

{ user wants to stop ? }
   user_quits := ( com_line [ 1 ] = quit_char ) ;
   IF ( NOT user_quits ) THEN BEGIN

{    check for input/output command }
      IF ( com_line [ 1 ] IN io_coms ) THEN BEGIN

{       initialise local filename }
         name := '' ;
         com_char := com_line [ 1 ] ;
         IF ( com_line [ 2 ] = '[' ) THEN BEGIN
            c := 3 ;
            REPEAT
               finished := ( com_line [ c ] = ']' ) ;
               IF ( NOT finished ) THEN BEGIN
                  ok := ( com_line [ c ] IN [ 'A'..'Z', '0'..'9', ':', '.', '_' ] ) ;
                  IF ( ok ) THEN BEGIN
                     name := name + com_line [ c ] ;
                     c := c + 1 ;
                     IF ( LENGTH ( name ) = filename_len ) THEN
                        ok := ( com_line [ c ]  = ']' )
                  END
               END
            UNTIL ( finished OR ( NOT ok ) ) ;

{          output an error message if necessary }
            IF ( NOT ok ) THEN
               WRITELN ( '! Error : Input/Output filename not valid.' )

            ELSE BEGIN

               ok := ( com_line [ c ] = ']' ) ;

{             if no errors then sort out which I/O command to process }
               IF ( ok ) THEN BEGIN

{                redirect input command ? }
                  IF ( com_char = 'I' ) THEN BEGIN

{                   reverting to terminal input ? }
                     IF ( name = '' ) THEN obeying := FALSE

{                   no, command file already open ? }
                     ELSE IF ( strings_match ( com_file, name ) ) THEN
                        obeying := TRUE

{                   no so reset command file if it exists }
                     ELSE BEGIN

{                      does specified file exist ? }
                        IF ( exists ( name ) ) THEN BEGIN

{                         set "obeying com file" flag & ready command file }
                           obeying := TRUE ;
                           com_file := name ;
                           RESET ( commands, com_file )
                        END
                        ELSE WRITELN ( '! Error : '+name+' not found.' )
                     END
                  END

{                redirect output ( logging ) command ? }
                  ELSE IF ( com_char = 'O' ) THEN BEGIN

{                   reverting to no logging ? }
                     IF ( name = '' ) THEN logging := FALSE

{                   no, log file already open ? }
                     ELSE IF ( strings_match ( log_file, name ) ) THEN
                        logging := TRUE

{                   no, so open new log file for writing }
                     ELSE BEGIN

{                      if a file of this name already exists check for re-write }
                        IF ( exists ( name ) ) THEN
                           logging := yes_to ( name+' already exists. Overwrite ? (Y/N)' )
                        ELSE logging := TRUE ;
                        IF ( logging ) THEN BEGIN
                           log_file := name ;
                           REWRITE ( log, log_file )
                        END
                     END ;

{                   log current homes, macro definitions & current position }
                     IF ( logging ) THEN BEGIN
                        log_homes ( log ) ;
                        log_macros ( log ) ;
                        log_position ( log )
                     END
                  END
               END

{             report error }
               ELSE WRITELN ( '! Error : "]" expected.' )
            END
         END

{       report error }
         ELSE WRITELN ( '! Error : "[" expected' )
      END

{    check for "enter keypad mode" command }
      ELSE IF ( ORD ( com_line [ 1 ] ) = esc_ascii ) THEN

         external_comm := ( NOT keypad_entered_ok )

{    must be a command to be handled by the interpreter }
      ELSE external_comm := TRUE
   END

   END ; { of procedure proc_local_coms ---------------------------------------}

{------------------------------- exec_comm_list -------------------------------}

{ exec_comm_list => Move to all positions on the given command list. }
   PROCEDURE exec_comm_list ( VAR comm_list : posn_ptr ; err_mess : long_str ) ;

{ Local Variables : }
   VAR

      last_comm : posn_ptr ; { points to last record used ( for disposal ) }
      moved_ok  : BOOLEAN ;  { movement completed ok ? }
      i : INTEGER ; { debug }

   BEGIN { exec_comm_list }

{ initialise "move completed ok" flag }
   moved_ok := TRUE ;

{ move to all positions on command list unless an error occurs }
   WHILE ( ( comm_list <> NIL ) AND moved_ok ) DO BEGIN

{debug }
{  FOR i := 0 TO 5 DO WRITE ( comm_list^.posn [ i ] : 8 : 1 ) ;
  WRITELN ; }

      move_arc ( TRUE, comm_list^.posn, moved_ok ) ;
      comm_list := comm_list^.next ;
   END ;

{ check for error performing move }
   IF ( NOT moved_ok ) THEN BEGIN
      report_failure ( '! Error performing move - ' ) ;
      get_position ( arcs, curr_posn, posn_ok ) ;
      give_posn ( curr_posn )
   END

{ if no error occured during movement phase output any interpreter errors }
   ELSE IF ( LENGTH ( err_mess ) > 0 ) THEN WRITELN ( err_mess ) ;

{ dispose of command list }
   WHILE ( comm_list <> NIL ) DO BEGIN
      last_comm := comm_list ;
      comm_list := last_comm^.next ;
      DISPOSE ( last_comm )
   END

   END ; { of procedure exec_comm_list ----------------------------------------}

   BEGIN { main program }

{ if initialisation is successful loop until user decides to quit }
   IF ( initialised_ok ) THEN REPEAT
      get_command_line ;
      proc_local_coms ;
      IF ( keypad ) THEN BEGIN
         exec_key_coms ;
         get_position ( arcs, curr_posn, posn_ok ) ;
         give_posn ( curr_posn )
      END ELSE IF ( external_comm ) THEN BEGIN
         interpreter ( com_line, logging, log, err_mess, comm_list ) ;
         exec_comm_list ( comm_list, err_mess ) ;
      END ;
      remove_input ;
   UNTIL ( user_quits ) ;

{ tidy up }
   shut_down

   END. { of program rcli -----------------------------------------------------}
