	SUBROUTINE GET_COMMAND
C
C	Get a VAXNET Command.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*128 COMMAND
	LOGICAL CHECK_CMD, CHECK_MODEM, REPROMPT
	INTEGER SIZE
	LOGICAL STATUS
C
C	Request VAXNET command.
C
	CALL CANCEL_IO			! Cancel all I/O.
	CALL WRITE_USER (SS)		! Single space first.
50	CALL PROMPT_USER (CMD_PROMPT(1:PROMPT_SIZE),
	1			%REF(COMMAND), LEN(COMMAND))
	SIZE = LBYTE_COUNT		! Copy the byte count.
	IF (WANTS_HELP) THEN
		COMMAND = 'HELP'	! Set dummy HELP command
		SIZE = 4		!	and the command size.
	ENDIF
	IF (SIZE .EQ. 0) GO TO 200
	STATUS = CHECK_CMD (COMMAND, REPROMPT, SIZE)
	IF ( (.NOT. STATUS) .OR. REPROMPT) THEN
		GO TO 50		! Get the next command.
	ENDIF
200	IF ( BATCH_MODE .OR. (.NOT. REMOTE_ASSIGNED) ) THEN
		GO TO 50		! Stay in the command loop.
	ENDIF
	IF (MODEM_CHECK .AND. AUTODIAL) THEN	! Ensure modem is ready.
	    IF ( .NOT. CHECK_MODEM() ) GO TO 50	! Stay in command code.
	ENDIF
	CALL REENABLE()			! Reenable everything.
	RETURN
	END

	LOGICAL FUNCTION CHECK_CMD (CMD, REPROMPT, CSIZE)
C
C	This routine check for a valid VAXNET command.
C
C	Inputs:
C		CMD	= Character string descriptor with the command.
C		REPROMPT= Logical to determine if VAXNET should reprompt
C			  on a successful command.
C		CSIZE	= The command line size.
C
C	Outputs:
C		Returns success if command was valid.
C		REPROMPT is set .TRUE. to prompt for another command.
C
	INCLUDE 'COM.INC/NOLIST'
	INCLUDE 'KEYWORDS.INC/NOLIST'

	CHARACTER*(*) CMD

	INTEGER*4 B, S, E, EC, CSIZE
	LOGICAL REPROMPT, CVT_DTB, PARSE_CMD, CHECK_REMOTE
	LOGICAL OPEN_VCMDFILE, NEXT_VCMDFILE
C
C	Dispatch to command processing routines.  All commands which
C	should prevent VAXNET from re-prompting upon completion should
C	exit via the code at label 9900.
C
	CHECK_CMD = .TRUE.			! Presume success.
	REPROMPT = .FALSE.			! Prevent re-prompting.
C
C	The parse command line routine is called twice.  The first is used
C	to get the command ending position.  The second is used to get the
C	first keyword starting and ending positions (if any).
C
100	B = 0					! Start at first character.
	CALL PARSE_CMD (CMD(1:CSIZE),B,S,E)	! Parse the command.
	EC = E					! Ending command position.
	CALL PARSE_CMD (CMD(1:CSIZE),B,S,E)	! Find start of first keyword.
	IF     ( CMD(1:1) .EQ. '@' ) THEN	! Command file input.
		IF (S .EQ. 0) S = 2		! Start at second character.
		IF (OPEN_VCMDFILE (CMD, CSIZE, S)) THEN
		    GO TO 100			! Parse the command line.
		ENDIF
	ELSEIF ( CMD(1:1) .EQ. '$' ) THEN	! Spawn a DCL command.
		CALL SPAWN_DCL (CMD(1:CSIZE), (NOCLISYM .OR. NOLOGNAM) )
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(auto_cmd)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    CALL REENABLE()		! Enable CTRL/C AST's.
		    CALL AUTO_LOGIN()		! Attempt auto login.
		    IF (.NOT. CONTROLC_TYPED) GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(clear_cmd)(1:EC) ) THEN
		LMAX_TYPEAHEAD = 0		! Maximum local typeahead.
		LOCAL_MAXIMUM = 0		! Maximum local bytes read.
		MAX_TYPEAHEAD = 0		! Maximum remote typeahead.
		REMOTE_MAXIMUM = 0		! Maximum remote bytes read.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(cross_cmd)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    CROSS_FILE = .TRUE.		! Set crossfile flag.
		    CALL REENABLE()		! Enable CTRL/C AST's.
		    CALL WAIT_CROSSFILE()	! Wait for Crossfile.
		    IF (.NOT. CONTROLC_TYPED) GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(dump_cmd)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    DUMP_MODE = .TRUE.		! Show we're in dump mode.
		    FLOW = TO_REMOTE		! Show we're going to remote.
		    CALL DUMP_FILE(CMD(1:CSIZE))! Dump file(s) to the remote.
		    DUMP_MODE = .FALSE.		! No longer in dump mode.
		    GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(debug_key)(1:EC) ) THEN
		DEBUG_MODE = .TRUE.		! Enable debug mode.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(dialup_cmd)(1:EC) ) THEN
		CALL GET_PHONE_NUMBER(CMD(S:E)) ! Get/set the phone number.
		GO TO 200			! Use common REDIAL code.
	ELSEIF ( (CMD(1:EC) .EQ. CMD_TBL(exit_cmd)(1:EC)) .OR.
	1	 (CMD(1:EC) .EQ. CMD_TBL(quit_cmd)(1:EC)) ) THEN
		CALL FINISH()			! Exit from Vaxnet.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(get_cmd)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    FLOW = TO_VAX		! Show we're going to the VAX.
		    CALL GETSEND(CMD(1:CSIZE))	! Get a file from the remote.
		    GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(help_cmd)(1:EC) ) THEN
		IF (S .EQ. 0) THEN
		    CALL GET_HELP(' ')		! Get help on everything.
		ELSE
		    CALL GET_HELP(CMD(S:CSIZE))	! Get help on a command.
		ENDIF
	ELSEIF ( (CMD(1:EC) .EQ. CMD_TBL(hangup_cmd)(1:EC)) .OR.
	1	  (CMD(1:EC) .EQ. CMD_TBL(discon_cmd)(1:EC)) ) THEN
		IF (CHECK_REMOTE()) THEN
		    CALL HANGUP_MODEM()		! Go hangup the modem.
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(interrupt_key)(1:EC) ) THEN
		CALL SETUP_INTERRUPT (CMD(S:E))	! Change interrupt character.
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(logfile_key)(1:EC) ) THEN
		CALL ENABLE_LOGFILE (CMD(S:E))	! Open a log file.
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(modem_key)(1:EC) ) THEN
		CALL GET_MODEM_TYPE (CMD(S:E))	! Request the modem type.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(pause_cmd)(1:EC) ) THEN
		CALL PAUSE_VAXNET()		! Pause Vaxnet.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(redial_cmd)(1:EC) ) THEN
200		IF (CHECK_REMOTE()) THEN
		    CALL REENABLE()		! Enable CTRL/C AST's.
		    CALL DIAL_MODEM()		! Redial the autodial modem.
		    IF (.NOT. CONTROLC_TYPED .AND. MODEM_ONLINE) THEN
			IF (CROSS_FILE) CALL WAIT_CROSSFILE()
			IF (.NOT. CONTROLC_TYPED) THEN
			    CALL AUTO_LOGIN()	! Attempt auto-login.
		        ENDIF
		    ENDIF
		    IF ( (.NOT. CONTROLC_TYPED) .AND.
	1			(MODEM_ONLINE) ) GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(remote_key)(1:EC) ) THEN
		CALL GET_PORT (CMD(S:E))	! Change the remote port.
		CALL SETUP_REMOTE(.TRUE.)	! Setup remote characteristics.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(set_cmd)(1:EC) ) THEN
		IF (S .EQ. 0) S = CSIZE + 1	! No parameters to pass.
		CALL SET_PARAMETER (CMD(S:CSIZE))	! Set parameter.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(script_cmd)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    DUMP_MODE = .TRUE.		! Show we're in dump mode.
		    FLOW = TO_REMOTE		! Show we're going to remote.
		    CALL SCRIPT_FILE(CMD(1:CSIZE)) ! Script file(s) to remote.
		    DUMP_MODE = .FALSE.		! No longer in dump mode.
		    GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(send_cmd)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    FLOW = TO_REMOTE		! Show we're going to remote.
		    CALL GETSEND(CMD(1:CSIZE))	! Send a file to the remote.
		    GO TO 9900
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(show_cmd)(1:EC) ) THEN
		IF (S .EQ. 0) S = CSIZE + 1	! No parameters to pass.
		CALL SHOW_PARAMETER (CMD(S:CSIZE))	! Show parameters.
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(slave_cmd)(1:EC) ) THEN
		SLAVE_MODE = .TRUE.		! Enable slave mode.
		CALL FLUSH_LOGFILE()		! Flush the log file.
	ELSEIF ( (CMD(1:EC) .EQ. CMD_TBL(spawn_cmd)(1:EC)) .OR.
	1	  (CMD(1:EC) .EQ. CMD_TBL(push_cmd)(1:EC)) ) THEN
		IF (S .GT. 0) THEN
		    CALL SPAWN_DCL ('$ '//CMD(S:CSIZE), 0)
		ELSE
		    CALL SPAWN_DCL ('$', 0)	! Spawn a new process.
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(speed_key)(1:EC) ) THEN
		IF (CHECK_REMOTE()) THEN
		    CALL SETUP_BAUDRATE (CMD(S:E)) ! Change the remote speed.
		ENDIF
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(status_cmd)(1:EC) ) THEN
		CALL SHOW_STATUS(.TRUE.)	! Show the status report.
	ELSEIF ( CMD(1:EC) .EQ. KEY_TBL(system_key)(1:EC) ) THEN
		CALL SETUP_SYSTEM (CMD(S:E))	! Change the system type.
		CALL SETUP_REMOTE(.TRUE.)
	ELSEIF ( CMD(1:EC) .EQ. CMD_TBL(take_cmd)(1:EC) ) THEN
		IF (S .EQ. 0) S = CSIZE + 1	! No file name specified.
		IF (OPEN_VCMDFILE (CMD, CSIZE, S)) THEN
		    GO TO 100			! Parse the command line.
		ENDIF
	ELSE					! Something else (ERROR).
		CALL WRITE_USER ('*** Invalid command "'//CMD(1:EC)//
	1			   '" type "?" for help. ***'//SS//BELL)
		IF (VCMD_MODE) THEN
		    CLOSE (UNIT=VCMD_UNIT)	! Close the command file.
		    VCMD_MODE = .FALSE.		! Show not in command mode.
		ENDIF
		CHECK_CMD = .FALSE.		! Show invalid command.
	ENDIF
	REPROMPT = .TRUE.			! Re-prompt for another cmd.
C
C	If a command file is open, read the next command line.
C
9900	IF (VCMD_MODE) THEN
	    IF (NEXT_VCMDFILE (CMD, CSIZE)) THEN
		GO TO 100			! Process the next command.
	    ENDIF
	ENDIF
	RETURN
	END

	LOGICAL FUNCTION CHECK_REMOTE
C
C	This routine ensures a remote port is assigned and if not,
C	displays a message and returns failure.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*(*) NO_PORT

	PARAMETER (NO_PORT =
	1 '*** The remote port is not assigned. ***'//BELL//SS)

	IF (REMOTE_ASSIGNED) THEN
	    CHECK_REMOTE = .TRUE.		! Show port is assigned.
	ELSE
	    CHECK_REMOTE = .FALSE.		! Show port not assigned.
	    CALL WRITE_USER (NO_PORT)		! Tell the user no go.
	ENDIF
	RETURN
	END

	LOGICAL FUNCTION PARSE_CMD (CMD,BEGIN,START,END)
C
C	This routine parses command line of the form:
C
C		keyword1 keyword2 ... keywordn
C		keyword1,keyword2,... keywordn
C
C	Multiple spaces between keywords are skipped.
C
C	Inputs:
C		CMD   = String descriptor with the command line.
C		BEGIN = Beginning position within the command line.
C			If zero, don't skip to first space/comma.
C
C	Outputs:
C		BEGIN = Starting position for the next parse.
C		START = Starting position of the keyword (0 if none).
C		END   = Ending position of the keyword (-1 if none).
C
C	Routine value:
C		Returns .TRUE./.FALSE. = keyword found/not found.
C
	CHARACTER*(*) CMD
	INTEGER*4 BEGIN, START, END, PTR, SIZE

	PARSE_CMD = .FALSE.			! Initialize return value.
	START = 0				! Presume no keyword found.
	END = -1				! No ending position found.
	SIZE = LEN(CMD)				! Get length of the command.
	IF (BEGIN .EQ. 0) THEN			! Don't skip to first space.
	    PTR = 1				! Starting position of keyword.
	    GO TO 400				! Go find end of the keyword.
	ENDIF
C
C	Find a delimiter (either a space or a comma).
C
	DO 100 I = BEGIN,SIZE
	    PTR = I				! Copy starting position.
	    IF (CMD(I:I) .EQ. ' ') GO TO 200	! We found a delimiter.
	    IF (CMD(I:I) .EQ. ',') GO TO 200	! Comma is valid delimeter.
100	CONTINUE
	RETURN
C
C	Find start of the keyword.
C
200	DO 300 I = PTR+1,SIZE
	    PTR = I				! Save the current index.
	    IF (CMD(I:I) .NE. ' ') GO TO 400	! Skip over all spaces.
300	CONTINUE
	RETURN
C
C	Check for a double quote.
C
400	IF (CMD(PTR:PTR) .NE. '"') GO TO 500	! Not a double quote.
	PTR = PTR + 1				! Point past the quote.
	START = PTR				! Save start of keyword.
	DO 450 I = PTR,SIZE
	    PTR = I				! Save the current index.
	    IF (CMD(I:I) .EQ. '"') THEN		! Found end of quoted string.
		BEGIN = PTR + 1			! Start here for next parse.
		END = PTR - 1			! Point to end of the keyword.
		GO TO 1100			! Use common return ...
	    ENDIF
450	CONTINUE
	GO TO 600				! Point to end of the string.
C
C	Find the end of the keyword (either a space or comma).
C
500	START = PTR				! Copy the starting position.
	DO 550 I = PTR+1,SIZE
	    PTR = I				! Save the current index.
	    IF (CMD(I:I) .EQ. ' ') GO TO 1000	! Found end of keyword.
	    IF (CMD(I:I) .EQ. ',') GO TO 1000	! Comma is valid delimeter.
550	CONTINUE
600	PTR = SIZE + 1				! Point past end of the string.
C
C	Update the beginning position for the next parse.
C
1000	BEGIN = PTR				! Start here for next parse.
	END = PTR - 1				! Point to end of the keyword.
1100	PARSE_CMD = .TRUE.			! Show keyword was found.
	RETURN
	END

	SUBROUTINE REENABLE
C
C	This routine is called to reenable everything.  It enables
C	CTRL/C AST's, enables the receiver and transmitter for the
C	mainline code for interactive mode, and disables the CTRL/C
C	typed flag.
C
	INCLUDE 'COM.INC/NOLIST'

	CALL SET_CONTROLC_AST()		! Enable CTRL/C AST's.
	RECEIVER_BUSY = .FALSE.		! Enable the receiver (remote).
	XMITTER_BUSY = .FALSE.		! Enable the transmitter (local).
	CONTROLC_TYPED = .FALSE.	! Reset the CTRL/C typed flag.
	RETURN
	END

	LOGICAL FUNCTION OPEN_VCMDFILE (CMDL, CSIZE, FIRST)
C
C	This routine is used to open a command file which contains
C	VAXNET commands.  This allows us to change many parameters.
C
C	Inputs:
C		CMDL  = The command file name (plus '@' or 'TAKE').
C		CSIZE = Size of the command file name.
C		FIRST = Starting file name position.
C
C	Outputs:
C		CMDL = The command line read.
C		CSIZE = The command line size.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*(*) MODULE_NAME, CMDL
	PARAMETER (MODULE_NAME = 'OPEN_VCMDFILE')
	INTEGER CSIZE, FIRST, E

	OPEN_VCMDFILE = .FALSE.			! Presume no command file.
C
C	If the command file is open, close it first.
C
	IF (VCMD_MODE) THEN
	    CLOSE (UNIT=VCMD_UNIT)		! Close the command file.
	ENDIF
	VCMD_FILE = CMDL(FIRST:CSIZE)		! Copy the command file name.
	VCMD_SIZE = (CSIZE - FIRST) + 1		! Setup the comand file size.
	VCMD_MODE = .FALSE.			! Show not in a command file.
	IF (VCMD_SIZE .LE. 0) RETURN		! No file name was specified.
C
C	Append a .COM extension if none was specified.
C
	SON = INDEX (VCMD_FILE, ']') + 1	! Point to start of file name.
	IF (INDEX (VCMD_FILE(SON:VCMD_SIZE), '.') .EQ. 0) THEN
	    VCMD_FILE = VCMD_FILE(1:VCMD_SIZE)//'.COM' ! Append the extension.
	    VCMD_SIZE = VCMD_SIZE + 4		! Adjust the file name size.
	ENDIF
C
C	Open the command file for input.
C
	OPEN (UNIT=VCMD_UNIT, TYPE='OLD', READONLY, SHARED,
	1		FILE=VCMD_FILE(1:VCMD_SIZE), ERR=9900)

	VCMD_MODE = .TRUE.			! Show a command file is open.

	ENTRY NEXT_VCMDFILE (CMDL, CSIZE)
C
C	Read the a record from the command file.
C
	NEXT_VCMDFILE = .FALSE.			! Presume no more files.
	IF (.NOT. VCMD_MODE) RETURN		! We're not in command mode.
100	READ (VCMD_UNIT, 200, END=500, ERR=9900) CSIZE, CMDL
200	FORMAT (Q,A)
	IF (CSIZE .EQ. 0) GO TO 100		! Read another record.
	IF (VERIFY_MODE) THEN			! Verify the command line.
	    CALL WRITE_USER (CMD_PROMPT(1:PROMPT_SIZE)//CMDL(1:CSIZE)//SS)
	ENDIF
	IF ( (CMDL(1:1) .EQ. '!') .OR. (CMDL(1:1) .EQ. ';') ) THEN
	    GO TO 100				! This is a comment line.
	ENDIF
	E = INDEX (CMDL, '"')			! Search for double quote.
	IF (E .EQ. 0) E = CSIZE			! None found, convert all.
	CALL CVT_UPPER (CMDL(1:E))		! Convert it to uppercase.
	NEXT_VCMDFILE = .TRUE.			! Return success.
	RETURN
C
C	Here for end of file.
C
500	CLOSE (UNIT=VCMD_UNIT)			! Close the command file.
	VCMD_MODE = .FALSE.			! Show not in command mode.
	RETURN

9900	CALL RMS_ERROR (MODULE_NAME)		! Report the RMS error.
	RETURN
	END
