;PFUCMD.MAC;3     2-SEP-79 15:57:17    EDIT BY TAFT
;PFUCMD.MAC;2     2-JUN-77 21:34:33    EDIT BY TAFT
; Make control-F not echo
;PFUCMD.MAC;1    10-MAR-77 14:04:33    EDIT BY TAFT
; Split out from PUPFTP.MAC

; Copyright 1979 by Xerox Corporation

	TITLE PFUCMD -- PUP FTP USER COMMAND INTERPRETER ROUTINES
	SUBTTL E. A. Taft / March 1977

	SEARCH PUPDEF,PFUDEF,STENEX
	USEVAR FTPVAR,FTPPVR


; Input keyword from terminal
;	A/ -length ,, address of keyword table
; Returns +1:  Not found, no word input, or line delete typed
;	+2:  Found, A/ address of matching keyword table entry
; In either case, the word that was input is described by
; WRDLEN and WRDBYT.  Recognition is performed if the keyword
; is terminated by Escape, and the tail is appended to CMDBUF.
; On either return, D contains the attributes of the terminating
; character.
; Clobbers A-D

INKEY::	PUSHJ P,SAVE1##		; Get another ac
	MOVE P1,A		; Use it to hold cmd tbl ptr
INKEY1:	MOVEI A,KEYHLP		; Routine to call on "?"
	MOVSI B,(C.ALPH+C.NUM)	; Only alphanumerics allowed
	TRO F,RAISEF		; Raise lower case letters
	PUSHJ P,INWORD		; Input a word
	 JRST INKEY9		; Line delete typed, return +1
	JUMPE C,INKEY9		; Give error return on empty word
	HRROI A,TEMP##		; Copy the word here
	PUSHJ P,CPYWRD
	MOVE A,P1		; Set command table ptr
	HRROI B,TEMP##		; Get byte ptr to start of word
	PUSHJ P,NAMSRC##	; Search for name in table
	 JRST INKEY2		; Not found
	JRST INKEY6		; Found, give success return

; If exact match not found, check for unique initial substring.
;	A/ AOBJN ptr to smallest entry > key
INKEY2:	JUMPGE A,INKEY8		; Fail if off end of table
	AOBJP A,INKEY3		; Advance to next, jump if none
	PUSHJ P,NAMCMP##	; Compare strings
	 JUMPN D,[TRNN F,ESCAPF	; Ambiguous, terminated by Escape?
		 JRST INKEY8	; No, fail as if not found
		ERROR <>	; Yes, ring bell to say ambiguous
		JRST APWORD]	; Resume INWORD for more input
	 JRST INKEY3		; Did not match, continue
	PUSHJ P,SCREWUP##	; Wasn't supposed to match!
INKEY3:	SUB A,[1,,1]		; Now back to current entry
	PUSHJ P,NAMCMP##	; Compare strings
	 JUMPN D,INKEY4		; Jump if initial substring
	 JRST INKEY8		; No match, fail
	PUSHJ P,SCREWUP##

; Got unique initial substring
; Print and append tail if input terminated by Escape
;	D/ String pointer to tail
INKEY4:	TRNN F,ESCAPF		; Input terminated by Escape?
	 JRST INKEY6		; No, just give success return
	PUSH P,A		; Yes, save ptr to matching entry
	TYPE <%4S>		; Print tail
	PUSHJ P,BAKBYT		; Back up to overwrite terminator
INKEY5:	ILDB A,D		; Get char of tail
	JUMPN A,[IDPB A,CMDBYT	; If non-null, append to command
		AOS WRDLEN	; Account in word length
		JRST INKEY5]	; Repeat
	MOVEI A,33		; Put Escape on end
	IDPB A,CMDBYT
	POP P,A			; Restore ptr to matching entry

; Here to take success return
INKEY6:	PUSHJ P,ESPACE		; Print space if Escape typed
	AOS 0(P)		; Preset +2 return

; Here to take fail return
INKEY8:	LDB B,CMDBYT		; Recover terminator
	MOVE D,CHRTAB(B)	; Return attributes for terminator
INKEY9:	TRZ F,RAISEF		; Stop raising lower-case letters
	POPJ P,

; Help routine called from within INWORD if "?" typed
;	P1/ Keyword table pointer (as passed to INKEY)
; Returns +1
; Clobbers A, B

KEYHLP:	TYPE < One of the following:%/>
	SKIPA D,P1		; Copy keyword table pointer
KEYHL1:	PRINT ","		; Comma
	MOVEI A,101		; Get width of page
	RFMOD
	LDB C,[POINT 7,B,17]
	RFPOS			; Get current position on page
	CAIGE C,↑D15(B)		; Room for another keyword?
	 PRINT EOL		; No, go to new line
	HLRO B,0(D)		; Get key name string ptr
	TYPE < %2S>		; Print it
	AOBJN D,KEYHL1		; Repeat for all
	POPJ P,


; Copy last word input (not including terminator) to another place.
; Characters are quoted as appropriate for inclusion in property
; lists.
;	A/ Destination string ptr
; Returns +1
; Updates A, clobbers B, C, appends null to destination string

CPYWRD::TLC A,-1		; Turn -1 lh into byte ptr
	TLCN A,-1
	 HRLI A,(POINT 7)
	MOVE B,WRDBYT		; Get ptr to start of last word
CPYWR1:	ILDB C,B		; Get a byte
	CAMN B,CMDBYT		; Was that the terminator?
	 JRST [	MOVE B,A	; Yes, append null
		SETZ C,
		IDPB C,B
		POPJ P,]	; Done
	CAIE C,"("		; Character need quoting?
	CAIN C,")"
	 JRST .+3		; Yes
	CAIE C,PQUOTE
	 JRST CPYWR2		; No
	MOVEI C,PQUOTE		; Yes, insert quote
	IDPB C,A
	LDB C,B			; Recover the character
CPYWR2:	IDPB C,A		; Store it
	JRST CPYWR1		; Repeat


; Confirm typed-in command by awaiting carriage return
; Returns +1:  Aborted
;	+2:  Confirmed
; Clobbers A-D

CONFRM::LDB A,CMDBYT		; Get existing terminator
	MOVE D,CHRTAB(A)	; Get attributes for character
	MOVEI A,100		; Set to wakeup on all characters
	MOVEI B,77B23+2B25+1B29
	SFMOD
CONFR1:	TLNE D,(C.CEOL)		; End-of-line character?
	 JRST SKPRET##		; Yes, return +2
	TLNE D,(C.SPAC)		; Space?
	 JRST .+3		; Yes, just ignore
	ERROR < ? >		; No, complain
	PUSHJ P,BAKBYT		; Remove byte from buffer
	HRROI A,[ASCIZ /Confirm with carriage return/]
	SETZ B,			; No legal word constituents
	PUSHJ P,INWORD		; Input a character
	 POPJ P,		; Line delete, abort command
	JRST CONFR1		; Got char, check it

; Input and edit word up to a terminator
;	A/ String pointer to text to print if "?" input
;	   or 0,,address of routine to call
;	B/ Attribute bits denoting legal constituent characters
; Returns +1:  Line delete encountered
;	+2:  Normal, A/ Terminating character
;		B/ Byte pointer to start of word (also in WRDBYT)
;		C/ Character count (also in WRDLEN)
;		D/ Attribute bits for terminating character
; Leading spaces and tabs are ignored if space is not a legal
; constituent character (but are still stored in the buffer).
; Terminating character stored in buffer but not included in count.
; If the terminator is Escape, the ESCAPF flag is set.

INWORD::MOVEM A,HLPDAT		; Store help data
	MOVEM B,WRDATR		; Store character attributes
	SETZM WRDLEN		; Init # characters in current word
	MOVE A,CMDBYT		; Preserve pointer to start
	MOVEM A,WRDBYT
	POP P,WRDXIT		; Save return in case APWORD called
	MOVEM P,WRDPDP		; Save stack pointer too
	JRST INWOR0		; Enter edit loop

; Enter here to append to an existing word
; Overwrites existing terminator (assumed to be Escape)
; Returns to where INWORD was last called from!

APWORD::MOVE P,WRDPDP		; Get stack ptr from INWORD call
	PUSHJ P,BAKBYT		; Back up byte pointer
INWOR0:	PUSH P,WRDXIT		; Put exit point back on stack

; Loop here for each character input
INWOR1:	TRNE F,NEOLEF		; Want EOL echoing off?
	 JRST [	MOVEI A,100	; Yes, get current control bits
		RFCOC
		PUSH P,B	; Save current settings
		PUSH P,C
		TRZ B,3B21+3B27	; Turn off cr, lf, eol
		TRZ C,3B27
		SFCOC		; Issue revised settings
		BIN		; Input the char
		EXCH B,-1(P)	; Save char, restore old settings
		POP P,C
		SFCOC
		POP P,A		; Restore char
		JRST .+2]
	PBIN			; Input character from terminal
	TRZ F,ESCAPF		; Assume it is not Escape
INWOR2:	MOVE D,CHRTAB(A)	; Get attributes and dispatch adr
	TRNE D,-1		; Is there a special dispatch?
	 JRST 0(D)		; Yes, take it
INWOR3:	IDPB A,CMDBYT		; No, append char to string
	HRRZ B,CMDBYT		; Make sure not overflowing buffer
	CAIL B,CMDBUF+CMDLEN-1
	 JRST [	MOVSI D,(C.CDEL)  ; Treat as command delete
		ERROR(<%/Command too long>,1)]
	TDNN D,WRDATR		; Is this a terminating character?
	 JRST INWOR4		; Yes
	AOS WRDLEN		; No, count characters in word
	JRST INWOR1		; Back for more

INWOR4:	PUSHJ P,APPNUL		; Append null to command
	LDB A,CMDBYT		; Recover terminating char
	MOVE B,WRDBYT		; Get starting byte pointer
	MOVE C,WRDLEN		; Get character count
	JRST SKPRET##		; Return +2

; Special characters handled by INWORD

; Lower case letters
LOWERC:	TRNE F,RAISEF		; Want to raise lower case letters?
	 SUBI A,40		; Yes, do so
	JRST INWOR3

; Control-V
CTRLV:	PBIN			; Input next character literally
	MOVSI D,(C.ALPH)	; Treat as alphabetic
	JRST INWOR3

; Control-A
CTRLA:	SKIPG WRDLEN		; Any characters to delete?
	 JRST [	ERROR <>	; No, ring bell
		JRST INWOR1]	; Ignore
	PRINT "\"		; Yes, print backslash
	LDB A,CMDBYT		; Get last character typed in
	TRNN F,NECHOF		; Unless echoing off
	 PRINT (A)		; Print it
	PUSHJ P,BAKBYT		; Back up byte pointer
	SOS WRDLEN		; Decrement count
	JRST INWOR1		; On to next character

; Control-W
CTRLW:	SKIPG WRDLEN		; Any characters to delete?
	 JRST [	ERROR <>	; No, ring bell
		JRST INWOR1]	; Ignore
	PRINT "←"		; Yes, note word delete
	SETZM WRDLEN		; Zero character count
	MOVE A,WRDBYT		; Reset byte pointer
	MOVEM A,CMDBYT
	JRST INWOR1		; On to next character

; "?"
INQUES:	SKIPE WRDLEN		; First character of word?
	 JRST INWOR3		; No, treat normally
	MOVE A,HLPDAT		; Yes, get help data word
	TLNE A,-1		; String pointer?
	 TYPE < %1S>		; Yes, type string
	TLNN A,-1
	 PUSHJ P,0(A)		; No, use as address of routine
	JRST CTRLR1		; Retype command line

; Control-R
CTRLR:	TRNE F,NECHOF		; Echoing off?
	 JRST [	ERROR <>	; Yes, refuse to retype (since
		JRST INWOR1]	;  we might type a password)
CTRLR1:	PRINT EOL		; Print crlf
	PUSHJ P,APPNUL		; Append null to command string
	HRROI A,CMDBUF		; Print contents of command string
	PSOUT
	JRST INWOR1		; On to next char

; More INWORD special characters

; Space or tab
INSPAC:	TDNN D,WRDATR		; Is space a terminator?
	SKIPE WRDLEN		; Yes, is this a leading space?
	 JRST INWOR3		; No, handle normally
	IBP WRDBYT		; Yes, advance start of word
	IDPB A,CMDBYT		; Store the character
	TRNE F,NECHOF		; Echoing off?
	 PRINT 0(A)		; Yes, do the echo ourselves
	JRST INWOR1		; On to next char

; Carriage return
INCR:	PBIN			; Flush following line feed
	JRST INEOL1		; Substitute EOL

; Line feed
INLF:	PRINT 15		; Print carriage return
INEOL1:	MOVEI A,37		; Substitute EOL
	JRST INWOR2		; Treat as if EOL was input

; Escape
INESC:	TDNE D,WRDATR		; Is Escape a terminator?
	 JRST [	ERROR <>	; No, don't allow
		JRST INWOR1]
	TRO F,ESCAPF		; Yes, note Escape hit (for NOISE)
	JRST INWOR3

; Control-Q, control-X
CTRLQ:	TYPE <←←←%/>
	JRST INDEL1

; Delete
INDEL:	TYPE 
INDEL1:	POPJ P,			; Take +1 return

; Initialize editor at beginning of command
; Returns +1
; Clobbers A-C

INIEDT::MOVE A,[POINT 7,CMDBUF]	; Initialize byte pointer
	MOVEM A,CMDBYT
	PUSHJ P,OKECHO		; Normalize terminal parameters
	MOVE B,[BYTE(2) 0,0,1,1,1,1,0,2,0,2,2,1,1,2,1,1,1,0]
	MOVE C,[BYTE(2) 0,1,1,1,0,0,0,1,1,0,1,1,1,3]
	SFCOC			; Set desired control echoing
	POPJ P,


; Back up CMDBYT byte pointer
; Returns +1
; Clobbers A

BAKBYT::MOVE A,CMDBYT		; Get byte pointer
	BKJFN			; Let Tenex back it up
	 PUSHJ P,SCREWUP
	MOVEM A,CMDBYT		; Store it back
	POPJ P,


; Append null to command string without affecting pointer
; Returns +1
; Clobbers A, B

APPNUL::MOVE A,CMDBYT		; Get byte pointer
	SETZ B,			; Append null
	IDPB B,A
	POPJ P,


; Turn echoing off and on
; Returns +1
; Clobbers A, B

NOECHO::TROA F,NECHOF		; Echoing off
OKECHO:: TRZ F,NECHOF		; Echoing on
	MOVEI B,16B23+1B29	; Wakeup on punct & control, Ascii
	TRNN F,NECHOF		; Want echoing?
	 IORI B,2B25		; Yes
	MOVEI A,100		; Set new terminal bits
	SFMOD
	POPJ P,


; Type crlf if not already at left margin
; Returns +1
; Clobbers A, B

CRIF::	MOVEI A,101		; Get current position
	RFPOS
	TRNE B,-1		; At left margin already?
	 PRINT EOL		; No, go there
	POPJ P,


; If last char was Escape, substitute and print a space
; Returns +1
; Clobbers nothing

ESPACE::PUSH P,A
	LDB A,CMDBYT		; Get last char input
	CAIE A,33		; Escape?
	 JRST ESPAC1		; No
	MOVEI A," "		; Yes, substitute space
	DPB A,CMDBYT
	PBOUT			; Print space also
ESPAC1:	POP P,A
	POPJ P,

; Handlers for command-related UUOs


; NOISE 
; Type noise word iff last input was terminated by Escape

%UNOIS::TRNN F,ESCAPF		; Last terminator Escape?
	 POPJ P,		; No
	PUSHJ P,ESPACE		; Yes, ensure turned into space
				; Rest same as PROMPT

; PROMPT 
; Type the given string and also append to command buffer (for ↑R)

%UPROM::PUSHJ P,FORMAT##	; Call formatter
	 MOVE A,CMDBYT		; Setup string ptr to cmd buffer
	 PUSHJ P,%UPRO2		; Completion -- fix ptr, type text
	POPJ P,

%UPRO2:	MOVE B,A		; Copy string ptr
	SETZ C,			; Append null
	IDPB C,B
	EXCH A,CMDBYT		; Store end pointer, recover start
	PSOUT			; Type the text
	POPJ P,

; Character attribute and dispatch table
; Attributes from list in PFUDEF.MAC

DEFINE X(CHAR,ATTR) >
DEFINE XRNG(CHAR1,CHAR2,ATTR) <
RELOC CHRTAB+CHAR1
REPEAT CHAR2-CHAR1+1,
>

CHRTAB::
; Editing characters
	X <"A"-100,"H"-100>,CTRLA
	X <"R"-100>,CTRLR
	X <"V"-100>,CTRLV
	X <"W"-100>,CTRLW
	X <"?">,C.PUNC+INQUES

; Non-editing controls
	X <0>,INWOR1		; Flush nulls
	XRNG "B"-100,"G"-100,C.CTRL
	X <"K"-100>,C.CTRL
	XRNG "N"-100,"P"-100,C.CTRL
	XRNG "S"-100,"U"-100,C.CTRL
	XRNG "Y"-100,"Z"-100,C.CTRL
	XRNG 34,36,C.CTRL

; End-of-line characters
	X <12,14>,C.CEOL+INLF
	X <15>,C.CEOL+INCR
	X <33>,C.ESC+INESC
	X <37>,C.CEOL

; Line delete characters
	X <"Q"-100,"X"-100>,C.CDEL+CTRLQ
	X <177>,C.CDEL+INDEL

; Punctuation characters
	X <"I"-100," ">,C.SPAC+INSPAC
	X <"+","#">,C.PUNC+C.HSTP
	X <"-","/">,C.PUNC+C.HSTC
	X <"!",42,"$","%","&","'","(",")","*">,C.PUNC
	X <54,".",":",";",74,"=",76,"@">,C.PUNC
	XRNG 133,140,C.PUNC
	XRNG 173,176,C.PUNC

; Alphanumeric characters
	XRNG "A","Z",C.ALPH
	XRNG "a","z",C.ALPH+LOWERC
	XRNG "0","9",C.NUM

RELOC CHRTAB+200



; Storage for command processor
LS CMDBUF,CMDLEN	; Command string buffer
LS CMDBYT		; Byte ptr to tail of command string
LS WRDBYT		; Byte ptr to start of current word
LS WRDLEN		; Length of current word
LS WRDATR		; Attributes of legal word constituents
LS HLPDAT		; Data for help message ("?")
LS WRDXIT		; Return pc for last call of INWORD
LS WRDPDP		; Stack level of last call of INWORD


	END