perm filename FTPSRV.MAC[IP,NET]1 blob sn#702353 filedate 1983-02-09 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00046 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00005 00002		TITLE	FTPSRV -- FILE TRANSFER PROTOCOL SERVER
C00011 00003	[96bit]H=	11		HOST TABLE INDEX FOR LOCAL HOST
C00014 00004		SUBTTL	INITIALIZATION
C00017 00005	SEARCH FOR A PTY WE CAN HAVE
C00019 00006	HERE WHEN THERE IS NO TELNET CONNECTION OPEN.  IF FTPSRV IS
C00023 00007	CONTINUATION OF ICP CODE and repeat 0
C00025 00008		SUBTTL	COMMAND TABLES
C00027 00009	ASSEMBLE COMMAND NAMES
C00028 00010	ASSEMBLE COMMAND DISPATCH TABLE
C00029 00011		SUBTTL	FTP COMMAND DECODING AND DISPATCH
C00031 00012	HERE WHEN A MESSAGE ARRIVES FROM THE IMP.  FIRST, READ THE ENTIRE
C00037 00013	HERE WHEN A COMPLETE COMMAND HAS BEEN INPUT.  DECIPHER IT
C00040 00014		SUBTTL	SYSTEM ACCESS COMMANDS
C00041 00015	   PASS <PASSWORD>
C00043 00016	   BYE
C00046 00017		SUBTTL	DATA TRANSFER PARAMETER COMMANDS
C00048 00018	   SOCK <SOCKET>      OR     SOCK <HOST>,<SOCKET>
C00052 00019	repeat 0,<	 con't handle odd types
C00054 00020	repeat 0,<	 not implemented in TCP
C00056 00021	repeat 0,<	 not implemented in this TCP hack
C00058 00022		SUBTTL	FTP DATA TRANSFER FUNCTIONS
C00060 00023	   STOR <PATHNAME>
C00064 00024	     MLFL <PPN>
C00067 00025		SUBTTL	MISCELLANEOUS FTP FUNCTIONS
C00070 00026	   DELE <PATHNAME>
C00073 00027	   STAT	OR	STAT <PATHNAME>
C00076 00028	   HELP
C00079 00029		SUBTTL	NONSTANDARD FUNCTIONS
C00082 00030	    XREP	(REPLAY RECORDED PTY DIALOGUE, FOR DEBUGGING)
C00084 00031		SUBTTL	SUBROUTINES
C00086 00032	ROUTINE TO WAIT FOR COMPLETION OF A DATA TRANSFER FUNCTION
C00088 00033	ROUTINE TO PERFORM A "FREE" FTP LOGIN
C00093 00034	ROUTINE TO LOG THE SUBJOB OUT.
C00096 00035	ROUTINE TO WAIT FOR A RESPONSE FROM THE SUBJOB.
C00101 00036	ROUTINE TO COPY A RESPONSE FROM THE PTY TO THE IMP.
C00104 00037	ROUTINE TO INPUT A DECIMAL NUMBER FROM THE CURRENT INPUT DEVICE
C00107 00038	ROUTINE TO BUFFER PTY OUTPUT SO WE CAN SEND IT SOME DATA
C00110 00039	ROUTINE TO DO WCH OPERATION FOR IMP AND PTY, WHICH WANT TO BREAK
C00113 00040	ROUTINE TO DO THE RCH OPERATION FROM THE IN-CORE IMP BUFFER.
C00116 00041	ROUTINE TO MAKE SURE THE TELNET CONNECTION IS STILL OPEN.
C00121 00042		SUBTTL	INITIAL FILE BLOCKS
C00122 00043	PTY INPUT (SUBJOB'S OUTPUT)
C00123 00044		SUBTTL	LOW-SEGMENT INITIALIZATION DATA
C00124 00045		SUBTTL	OTHER TABLES AND STUFF
C00126 00046		SUBTTL	LOW SEGMENT
C00130 ENDMK
C⊗;
	TITLE	FTPSRV -- FILE TRANSFER PROTOCOL SERVER
	SUBTTL	E.A.TAFT/EW13/EAT/DB33/CFE/drp--  may 80 [96bit]

	TWOSEG
	RELOC	400000

	SEARCH	C,TULIP,IMP	;ACCESS GENERAL PARAMETERS AND IMP STUFF

	VERSION	6,,43,6

; note on IO: all IO to the pty is done via the standard OFile.
; IO to the IMP connection is USUALLY done using the Error UUOs
; (EWsix and EDisix).  it you find it nessecary to change the
; the OFile (via "FoSel ImpObl", for example), make sure to change
; the OFile back when you're done, as the rest of the program
; expects it to go to the PTY.


;[96bit] first, define all the site specific things.


;[96bit] the PPn string that must be passed to login to get
;	 the free login for ftp transfers.  leave undefined if
;	 you do not wish to support free logins
Define FtpLogin<SixPPn(70,70)>	;[96bit] avsail uses 70,70

;[96bit] the octal PPn that FtpSrv should ChgPPN to before trying
;	 to login the free subjob for an Ftp transfer.  leave
;	 undefined if you do not wish the current PPN to be changed.
FtpPPn== 70 ,, 70		;[96bit] avsail uses 70,70

;[96bit] now mail information

;[96bit] define the command that should be issued to the monitor to
;	 accomplish a MLFL (Mail File) command.  The input file must
;	 be "Data:".  the line MUST end with number sign ("#") which
;	 produces a <CRLF>, followed by an exclamation mark ("!")
;	 which represents the end of the sixbit string.  Each percent
;	 sign ("%") in the string causes each successive macro
;	 statement to be executed at that point in the printing of
;	 the string.  for more detailed information, read the
;	 tulip modules.
;	 there is no default.  leave this undefined ONLY if you do
;	 not wish to support the MLFL command.
Define MlFlCommand
<
	Disix	[[SIXBIT\Mail %/IDENTI:%/FILE:DATA:#!\]
		PUSHJ	P,IMPPTY
		PUSHJ	P,HSTPRT]
>

;[96bit] define the MAIL command.  all the notes for the MLFL command
;	 apply here as well, except that this command MUST be defined.
Define MailCommand
<
	Disix	[[SIXBIT\Mail %/IDENTI:%/FILE:TTY:#!\]
		PUSHJ	P,IMPPTY
		PUSHJ	P,HSTPRT]
>

;[96bit] the PPN string that should be passed to login to get the
;	 subjob logged in for MLFL transfers.  Leave undefined if
;	 if MLFL transfers should login in the same way as ftp.
;	 (including the ChgPPn used for ftp.)  if defined, the job
;	 is logged out as soon as the transfer is completed.
;[avsail]Define MailLogin<SixPPN(N900AR0M)>	;[96bit] cmu uses Arpanet.Mail

;[96bit] the octal PPN that FtpSrv should ChgPPN to before trying
;	 to login the subjob for an MLFL transfer.  leave undefined
;	 if you do not wish the current PPN to be changed for mail.
;	 (this is ignored if MailLogin is undefined.)
MailPPn== 33125,,13776			;[96bit] cmu avoids a password


;[96bit] Define the logout routine.  leave undefined if
;	 you just want the standard "Kjob/b".
Define KjFunc
<	; CMU, of course, has to do something different.
	WSix	[SIXBIT\KJOB /F#!\];CMU- SAVE ALL FILES
	PUSHJ	P,CPYRSP	;COPY THIS
	TXNN	F,ERRFLG	;ERROR (OVER QUOTA)
	POPJ	P,		;NOPE ALL IS GOODNESS
	PUSHJ	P,CNCUSR	;STOP HIM
	WSix	[SIXBIT\CORE 0#!\];FREE ALL HIS CORE
	PJRST	PTYFLS		;AND GO AWAY
>	;end of KjFunc

;[96bit] End of site specific information


;[96bit] now clean up a little

IfDef	FtpLogin,<	$FtpLog==-1	>
IfDef	MailLogin,<	$MLogin==-1	>
ND	$FtpLog,0
ND	$MLogin,0
ND	FtpPPn,0
ND	MailPPn,0
ND	FtHarv,0	;code for harvard DIRECT
;[96bit]H=	11		;HOST TABLE INDEX FOR LOCAL HOST

;FLAGS USED IN FTPSRV
	FLAG	(OPNFLG)	;TELNET CONNECTION IS OPEN
	FLAG	(LGIFLG)	;SUBJOB IS LOGGED IN
	FLAG	(USRFLG)	;USER NAME GIVEN BUT NOT PASSWORD
	FLAG	(ERRFLG)	;ERROR MESSAGE ENCOUNTERED IN CPYRSP
	FLAG	(SLGFLG)	;FTPSRV IS A LOGGED-IN JOB
	FLAG	(PTYFLG)	;WE HAVE A PTY
	FLAG	(MAILFG)	;WE'RE IN THE MIDDLE OF A MAIL COMMAND
	FLAG	(WRPFLG)	;PTY DIALOGUE RECORDING HAS WRAPPED AROUND
;[96bit]FLAG	(LGAR0M)	;LOGGED IN AS N900AR0M
	FLAG	(TLogin)	;[96bit] should be logged out after
				;  	the command is done.
	FLAG	(MLFLFG)	;WE'RE IN THE MIDDLE OF A MLFL COMMAND
	Flag	(NlsCom)	;[96bit] processing a NLST command

;MISCELLANEOUS PARAMETERS
	PDLSIZ==100		;SIZE OF STACK
	PTY==	1		;I/O CHANNEL FOR PTY
	IMP==	2		;I/O CHANNEL FOR IMP
	ICPSKT==1		;SOCKET FOR LOCAL ICP
	TLNSKT==↑D64		;TELNET SOCKET FOR LOCAL ICP
	CMDLEN==↑D315		;MAXIMUM LEGAL FTP COMMAND LENGTH
;[CFE] Above line reflects the size of MAIL's TTY input buffer,
;[CFE]  namely about 315 characters as of 3-Jan-1981.
	WATWRN==↑D15		;TIME WE'LL WAIT BEFORE WARNING USER
	WATMAX==↑D20		;TIME WE'LL WAIT BEFORE LOGGING HIM OUT
	RECSIZ==↑D50		;NUMBER OF WORDS FOR RECORDING PTY DIALOGUE

;MACRO TO EXECUTE THE IMPUUO.  DONE AS A DEC-STYLE "CALL" SO AS TO
;   BE TRANSPORTABLE TO CMU.

DEFINE IMPUUO(AC,JUNK) <
	MCALL	AC,[SIXBIT\IMPUUO\]
>

;[96bit] Macro to define the control AC for the impuuo
Define ImpAc(Bits,Funct,Block,TimeOut<0>)
    < [ <Bits>!InSVl.(TimeOut,If.Tim)!InSVl.(Funct,If.Fnc)!<Block> ] >

;[96bit] marco to define a sixbit PPN string for the printing routines
Define SixPPn(Proj,Prog),
    < [
	ifnb <Prog>,< Sixbit \'Proj','Prog'!\ >
	ifb <Prog>,< Sixbit \'Proj'!\ >
      ]
    >
	SUBTTL	INITIALIZATION

FTPSRV:	JFCL			;IN CASE CCL ENTRY
	MOVE	P,[IOWD PDLSIZ,PDL] ;SETUP STACK
	START			;DO INITIALIZATION
	SETZM	ZEROL		;CLEAR ZEROED PART OF LOW SEGMENT
	MOVE	T1,[ZEROL,,ZEROL+1]
	BLT	T1,ZEREND-1
	MOVE	T1,[FILLH,,FILLL] ;INITIALIZE LOW SEGMENT DATA
	BLT	T1,FLLEND-1
	GETPPN	T1,		;GET OUR PPN
	  JFCL			;GETPPN SKIPS IF JACCT
	MOVEM	T1,PRJPRG	;REMEMBER IT
;[96bit] we don't care who we are anymore
;[96bit]MOVE	T1,[.IULHS,,LHOSTP] ;RETURN LOCAL HOST PARAMETERS
;[96bit]IMPUUO	T1,
;[96bit]  PUSHJ	P,Idiocy
;[96bit]HRRZ	T1,.IBHST+LHOSTP ;GET LOCAL HOST NUMBER
;[96bit]MOVSI	H,-NHOSTS	;SEARCH HOST TABLE FOR THIS NUMBER
;[96bit]HLRZ	T2,HSTTAB(H)
;[96bit]CAIE	T1,(T2)
;[96bit]AOBJN	H,.-2
;[96bit]JUMPL	H,.+2		;MAKE SURE WE FOUND ONE, AND REMEMBER INDEX
;[96bit]PUSHJ	P,Idiocy

;[96bit]MOVEI	T1,CONBLK	;SEE IF TELNET CONNECTION IS ALREADY OPEN
	Move	T1,ImpAc(If.New,.IuStt,ConBlk)		;[96bit]
	IMPUUO	T1,
	  JRST	NOTELC		;NO, GO TRY TO OPEN ONE

;HERE WITH TELNET CONNECTION OPEN TO USER
TLNOPN:	TXO	F,OPNFLG	;FLAG CONNECTION OPEN
	MOVEI	T1,IMPOBL	;DIRECT ERRORS TO THE TELNET USER
	MOVEM	T1,EFILE##
	Move	T1,.IBHST+CONBLK ;[96bit] DEFAULT HOST IS this one
	Movem	T1,HstTmp	;[96bit] put where it'll get set up
	move	T1,.IBRMT+CONBLK	; get his socket
	MOVEM	T1,RmtSkt		; and remember it for connections
	sos	t1,.IbLcl+ConBlk	; get our socket minus 1
	MOVEM	T1,LclSkt		; that's where connections go
	FSETUP	IMPIBH		;SETUP IMP I/O BLOCKS
	FSETUP	IMPOBH
	FIGET	IMPIBL		;OPEN IMP CONNECTION FOR I/O

;TYPE THE SIGNON MESSAGE
	MOVEI	T1,4		;FIVE WORDS OF MONITOR NAME
CNFGET:	MOVSI	T2,(T1)		;GET A WORD
	HRRI	T2,.GTCNF
	GETTAB	T2,
	  SETZ	T2,		;OOP......
	MOVEM	T2,SYSNAM(T1)	;STORE IT
	SOJGE	T1,CNFGET	;BACK FOR MORE
	MOVSI	T1,'300'	;OK, START WITH SIGNON MESSAGE
	EDisix	[EXP	SRVMSG
		WSIX	4,T1
		WASC	SYSNAM]
;SEARCH FOR A PTY WE CAN HAVE
	FSETUP	PTYIBH		;SETUP PTY FILE BLOCKS
	FSETUP	PTYOBH
	FoSel	PtyOBl		; start off talking naturally to pty.
	MOVX	T1,%CNPTY	;GET FIRST PTY,,# OF PTY'S
	GETTAB	T1,
NOPTAV:	  EDisix [BYEFR1,,[SIXBIT\401 N&O &PTY&S AVAILABLE.  &T&RY AGAIN LATER.#!\]]
	MOVEI	T1,(T1)		;ISOLATE NUMBER OF PTY'S

;HERE WHEN OPEN FAILS ON A PARTICULAR PTY
PTYTRY:	SOJL	T1,NOPTAV	;JUMP IF THERE AREN'T ANY MORE
	MOVEI	T2,(T1)		;GET NEXT PTY NUMBER
	SETZ	T3,		;CONVERT TO OCTAL DIGITS
	LSHC	T2,-3
	LSH	T3,-3
	TXO	T3, <'0'>B5
	JUMPN	T2,.-3
	HLRM	T3,PTYIBL+FILDEV ;STORE IN RIGHT HALF OF PTY NAME
	HLRM	T3,PTYOBL+FILDEV
	FIGET	PTYIBL		;TRY TO ASSIGN IT.  TO PTYTRY IF FAIL
	TXO	F,PTYFLG	;GOT IT -- SET FLAG
;[96bit]HRRZ	T1,HSTADR	;GET FOREIGN HOST'S ADDRESS
;[96bit]PUSHJ	P,HSTNAM##	;FIND OUT WHAT IT'S NAME IS
;[96bit]  SETZ	T1,		;ERROR, PUNT
;[96bit]MOVEM	T1,SXBHST	;STORE THE RESULTS (MAY BE ZERO)
;[96bit]MOVEM	T2,SXBHST+1
;	Pushj	P,SetNam	;[96bit] get the name, if we can.
; don't delay start up to build host tables: put this off until
; we have a command.
	MOVEI	T1,C.BYE	;GO TO BYE ROUTINE TO LOGOUT SUBJOB
	HRRM	T1,.JBREN##	;on a reenter.
	JRST	COMAND		;BEGIN PROCESSING COMMANDS
;HERE WHEN THERE IS NO TELNET CONNECTION OPEN.  IF FTPSRV IS
;   BEING RUN BY A LOGGED-IN USER, ATTEMPT TO DO AN ICP.
NOTELC:	PJOB	T1,		;GET OUR JOB NUMBER
	MOVN	T1,T1		;NEGATE FOR JOBSTS
	JOBSTS	T1,
	  PUSHJ	P,Idiocy		;SHOULDN'T FAIL
	TXNN	T1,JB.ULI	;ARE WE LOGGED IN?
	DISIX	[DOLOGO,,[SIXBIT\?L&OGIN PLEASE#.!\]]
	WSIX	[SIXBIT\P&RIVATE &FTP& SERVER RUNNING.#&M&ONITORING? !\]
	INCHRW	T1		;ASK FOR RESPONSE FROM TTY
	CAIN	T1,CR		;IF CARRIAGE RETURN
	INCHRW	T1		;  ABSORB LINE FEED
	CAIE	T1,"Y"		;YES IN EITHER UPPER OR LOWER CASE?
	CAIN	T1,"Y"+40
	TXO	F,SLGFLG	;YES, REMEMBER SERVER LOGGED-IN AND MONITORING
	pjob	t1,			; get job again
	LSH	T1,9
	ADDI	T1,ICPSKT	;BUILD LOCAL ICP SOCKET NUMBER
	DISIX	[[SIXBIT\#A&WAITING &ICP& ON SOCKET %#!\]
		WDEC	T1]
;[96bit]MOVE	T1,[7B10+<.IUREQ>B17+ICPCON] ;WAIT FOR ICP REQUEST
	Move	T1,ImpAc(If.New,.IuReq,ConBlk,7)	;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR		;ERROR (MAYBE TIMED OUT)

repeat 0,<	;[tcp] old complex stuff not needed anymore.
;[96bit]HRLI	T1,.IUCON	;OK, CONNECT
	HRLI	T1,.IUCON(If.New)	;[96bit] OK, CONNECT
	IMPUUO	T1,
	  JRST	ICPERR
	MOVE	T1,.IBRMT+ICPCON ;GET HIS SOCKET (INPUT)
	ADDI	T1,3		;STORE HIS CORRECT TELNET OUTPUT SOCKET
	MOVEM	T1,.IBRMT+CONBLK
	MOVE	T1,.IBHST+ICPCON ;GET HOST NUMBER
;[96bit]HRRM	T1,.IBHST+CONBLK ;STORE IN TELNET CONNECTION BLOCK
	Movem	T1,.IBHST+CONBLK ;[96bit] STORE IN TELNET CONNECTION BLOCK
;[96bit]MOVE	T1,[.IULSN,,CONBLK] ;SET TELNET SOCKETS INTO LISTEN STATE
	Move	T1,ImpAc(If.New,.IuLsn,ConBlk)		;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR
	SOS	.IBRMT+CONBLK
	AOS	.IBLCL+CONBLK
	IMPUUO	T1,
	  JRST	ICPERR
	FSETUP	ICPBLH		;OPEN ICP SOCKET FOR OUTPUT
	FOOPEN	ICPBLK
	HRRZ	T1,PRJPRG	;COMPUTE OUR FULL LOCAL SOCKET NUMBER
	LSH	T1,9
	IORI	T1,TLNSKT	;  FOR THE SERVER TELNET CONNECTION
	MOVE	T2,[POINT 8,T1,3] ;UNPACK 8 BITS AT A TIME
	ILDB	T3,T2
	WCHI	(T3)		;STUFF AN 8-BIT BYTE
	TXNE	T2,77B5		;DONE?
	JRST	.-3		;NO, DO MORE
	FOCLOS	ICPBLK		;YES, SEND ICP DATA ON ITS WAY
;[96bit]MOVE	T1,[.IUCLS,,ICPCON] ;  BY CLOSING THE ICP SOCKET
	Move	T1,ImpAc(If.New,.IuCls,ICPCon)		;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR
repeat 0 continues to next page
;CONTINUATION OF ICP CODE and repeat 0

	SETZM	OFILE##		;OUTPUT BACK TO TTY
;[96bit]MOVE	T1,[IF.NWT+<.IUCON>B17+CONBLK] ;CONNECT THE TELNET SOCKETS
	Move	T1,ImpAc(If.Nwt!If.New,.IuCon,ConBlk)	;[96bit]
	IMPUUO	T1,		;DO THE OUTPUT SOCKET FIRST
	  JRST	ICPERR
	AOS	.IBRMT+CONBLK	;NOW THE INPUT SOCKET
	SOS	.IBLCL+CONBLK
;[96bit]HRLI	T1,.IUCON	;WAIT FOR THIS ONE
	HRLI	T1,.IUCON(If.New)	;[96bit] WAIT FOR THIS ONE
	IMPUUO	T1,
	  JRST	ICPERR
	SOS	.IBRMT+CONBLK	;NOW BACK TO LOOK AT THE OUTPUT SIDE
	AOS	.IBLCL+CONBLK
	IMPUUO	T1,		;WAIT FOR SOCKET TO BECOME OPEN
	  JFCL			;PROBABLY ALREADY WAS OPEN
> ;[tcp] end of repeat 0

;[96bit]MOVEI	T1,CONBLK	;GET ITS STATUS
	Move	T1,ImpAc(If.New,.IuStt,ConBlk)		;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR
	LDB	T2,[POINT 6,.IBSTT+CONBLK,35] ;OUTPUT SIDE OPEN NOW?
	CAIN	T2,.ISEst		; established?
	  DISIX	[TLNOPN,,[SIXBIT\ICP &COMPLETED.#!\]]


;HERE WHEN SOMETHING FAILS DURING THE ICP.
ICPERR:	SETZM	OFILE##		;MAKE OUTPUT COME OUT ON THE TTY
	WSIX	[SIXBIT\? S&ERVER &T&ELNET &ICP& FAILED#!\]
;[96bit]MOVE	T1,[IF.NWT+<.IUCLS>B17+ICPBLK] ;CLOSE ICP BLOCK IN CASE OPEN
	Move	T1,ImpAc(If.Nwt!If.New,.IuCls,ICPBlk)	;[96bit]
	IMPUUO	T1,
	  JFCL
	JSP	T4,BYEFRC	;CLOSE CONNECTIONS IF OPEN
	SUBTTL	COMMAND TABLES

;BITS IN LH OF COMMAND DISPATCH ENTRY
	CM.LGI==1B0		;LOGIN REQUIRED FOR THIS COMMAND
	CM.HLP==1B1		;LIST COMMAND IN THE HELP MESSAGE
	CM.LGM==1B2		;[96bit] use mlfl login, and logout
				;	 the job when the transfer's
				;	 done.


DEFINE COMS <

	CC	USER,<HLP>
	CC	PASS,<HLP>
	CC	ACCT,<>
	CC	BYTE,<HLP>
	CC	SOCK,<HLP>
	CC	Pasv,<>		; give "not implemented" for this
	CC	TYPE,<HLP>
	CC	STRU,<HLP>
	CC	MODE,<HLP>
	CC	RETR,<LGI,HLP>
	CC	STOR,<LGI,HLP>
	CC	APPE,<>
	CC	RNFR,<LGI,HLP>
	CC	RNTO,<LGI,HLP>
	CC	DELE,<LGI,HLP>
	CC	LIST,<LGI,HLP>
	CC	NLst,<LGI,HLP>	;[96bit] implement name-list
	CC	ALLO,<>
	CC	REST,<>
	CC	STAT,<HLP>
	CC	ABOR,<>
	CC	BYE ,<HLP>
Ife $MLogin,<	;[96bit] MLFL doesn't need to logout
	CC	MLFL,<LGI,HLP>
>; ife $MLogin
ifn $MLogin,<	;[96bit] MLFL needs to logout
	CC	MLFL,<LGI,LGM,HLP>
>; ifn $MLogin
	CC	MAIL,<HLP>
	CC	HELP,<>
	CC	NoOp,<>		;[96bit] implement NoOp
	CC	XCWD,<LGI,HLP>
	CC	XSRC,<LGI,HLP>
	CC	XTIM,<HLP>
	CC	XREP,<>

>
;ASSEMBLE COMMAND NAMES

DEFINE CC(A,B) <
	<SIXBIT	\A\>
>

	XALL

COMTAB:	COMS

	COMLEN==.-COMTAB	;NUMBER OF COMMANDS IN TABLE
;ASSEMBLE COMMAND DISPATCH TABLE

DEFINE CC(A,B) <
	ZZ==	0
IFNB<B>,<IRP B<
	ZZ==	ZZ+CM.'B
>>
IFDEF C.'A,<
	ZZ +	C.'A
>
IFNDEF C.'A,<
	ZZ +	COMUNI
>>

COMDSP:	COMS

	SALL
	SUBTTL	FTP COMMAND DECODING AND DISPATCH

;HERE WHEN FTPSRV HAS NOTHING BETTER TO DO.  WAIT FOR INPUT FROM
;   EITHER THE IMP OR THE PTY.
COMAND:	PUSHJ	P,IMPCHK	;MAKE SURE TELNET CONNECTION IS STILL OPEN
;[tcp] there's nothing special about FTPSRV IMPs, they are just connected
;[tcp]	to TTYs, and the TTY is what we talk to.  IO.DAT cannot be on for
;[tcp]	a non-imp.
;[tcp]	STATZ	IMP,IO.DAT	;  OR MORE AVAILABLE FROM TELNET CONNECTION?
	skpinl				;[tcp] another command?
	 SKIPle	IMPIBL+FILCTR		;[tcp] perhaps read in already?
	  JRST	IMPGET		;YES, PROCESS IT
	PUSHJ	P,PTYCHK	;NO, HAS ANYTHING COME FROM THE PTY?
	  AOSA	T1,WATCNT	;NO, INCREMENT TIME WE'VE BEEN WAITING
	JRST	PTYGET		;YES, PROCESS IT
	CAIN	T1,WATWRN*↑D60	;TIME TO WARN OUR INACTIVE USER?
	EDisix	[[SIXBIT\030 Y&OU WILL BE LOGGED OFF IN % MINUTES IF YOU CONTINUE TO DO NOTHING.#!\]
		WDECI	WATMAX-WATWRN]
	CAIN	T1,WATMAX*↑D60	;TIME TO GIVE UP ON HIM?
	EDisix	[C.BYE,,[SIXBIT\430 I&NACTIVITY TIMEOUT--GOODBYE.#!\]]
	MOVEI	T1,1		;SLEEP FOR A SECOND
	SLEEP	T1,
	JRST	COMAND		;GO LOOK AGAIN


;HERE WHEN SOMETHING COMES BACK FROM THE PTY.  JUST COPY IT TO THE IMP.
PTYGET:	MOVSI	T1,'050'	;MISC MESSAGE CODE
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO IMP
	JRST	COMAND		;RESUME WAITING
;HERE WHEN A MESSAGE ARRIVES FROM THE IMP.  FIRST, READ THE ENTIRE
;  LINE INTO CORE AND CHECK FOR ILLEGAL CHARACTERS AND IMPROPER TERMINATION.
IMPGET:	HLLZS	WATCNT		;RESET WAIT COUNT
;[CFE]	Clear out CmdBuf before storing into it.  Remember count of
;[CFE]	  characters stored, also; use count reading from buffer.
	setzm	CmdBuf		;[CFE] Clear first word,
	move	t1,[xwd CmdBuf,CmdBuf+1]
	blt	t1,CmdBuf+<CmdLen/5>	;[CFE]  clear the rest.
	MOVE	T1,[POINT 7,CMDBUF] ;POINT TO COMMAND STORAGE BUFFER
	MOVEM	T1,CMDPTR	;STORE FOR LATER USE
	MOVEI	T2,CMDLEN	;MAX LEGAL COMMAND LENGTH
	FISEL	IMPIBL		;INPUT FROM IMP
; IMP output uses Error UUOs.
;	FOSEL	IMPOBL		;OUTPUT POSSIBLE MESSAGES TO IMP

;MAKE SURE THIS IS A REAL MESSAGE COMING AND NOT JUST SOME LEFTOVER NULLS
IMPGE4:	RCHF	P1		;GET A CHAR FROM THE IMP
	JUMPN	P1,IMPGE1	;A REAL CHAR, PROCESS IT
;[tcp]	SKIPG	IMPIBL+FILCTR	;NO, ANY MORE INPUT DATA?
;[tcp]	STATZ	IMP,IO.DAT	;NO, MORE TO GET FROM THE IMP?
;[tcp]	JRST	IMPGE4		;YES, DO IT
	JRST	COMAND		;NO, FORGET IT

;[CFE, 3-Jan-81] If this is MAIL command input, artificially insert
;[CFE] CRLFs to break very-long lines to lengths that MAIL will
;[CFE] handle for us.
IMPGE5:	txnn	F,MAILFG	; Are we doing a MAIL?
	  jrst	IMPGE0		;  Yes: don't test here.
IMPGE7:	caig	T2,2		; More than two spaces left?
	  jrst	IMPGE6		;  No; force a CRLF.
	caig	T2,↑D15		; 15 or fewer spaces left
	 caie	P1," "		;  and this char is a space ( =40 )?
	  jrst	IMPGE0		; No, it's OK: treat ordinarily.
IMPGE6:	movei	P1,15		; Force a CRLF into cmd buffer.
	idpb	P1,T1
	movei	P1,12
	idpb	P1,T1
	subi	T2,2		; Account for spaces used.
	EWSix	[sixbit\051 L&ong &MAIL& line broken into pieces.#!\]
	jrst	CmdFin		; Send buffered text to MAIL subjob.
;[CFE] end of long-line patch

IMPGE0:	RCHF	P1		;GET A CHAR FROM THE IMP
	JUMPE	P1,IMPGE0	;IGNORE NULLS
IMPGE1:	TXNN	F,MAILFG	;MAIL MODE?
	JRST	IMPGE3		;NO, DON'T THROW OUT SPECIAL CHARS
	CAIE	P1,"C"&37	;↑C?
	CAIN	P1,"Z"&37	;OR ↑Z?
	JRST	IMPGE0		;IGNORE SINCE THEY'LL TERMINATE MAIL
;[CFE]	CAIE	P1,33		;CHECK FOR ALL ALTMODES
;[CFE]	CAIL	P1,175		;DON'T WORRY ABOUT LOSING RUBOUTS
	cain	P1,33		;[CFE] Check MAIL's <escape> terminator
	JRST	IMPGE0		;IGNORE...SAME REASON
IMPGE3:	IDPB	P1,T1		;PACK CHARACTER INTO COMMAND BUFFER
	SOJGE	T2,IMPGE2	;COUNT THE CHARACTER
	; more than we can take: load error and go die.
	Movei	T1,[SIXBIT\500 L&AST LINE WAS TOO LONG.#!\]
	JRST	CMDERR
IMPGE2:	TXNE	P2,LETTER!LGLSIX ;LEGAL CHARACTER?
;[CFE]	JRST	IMPGE0		;YES, GO ON TO NEXT
	  jrst	IMPGE5		;[CFE] Check MAIL lines, then go on.
	CAIN	P1,LF		;LINE FEED?
	JRST	CMDFIN		;YES, END OF COMMAND
	TXNE	F,MAILFG	;IN MAIL MODE?
;[CFE]	JRST	IMPGE0		;YES, STORE CHAR WITHOUT FURTHER ADO
	  JRST	IMPGE7		;[CFE] YES, STORE CHAR after size check
	CAIN	P1,CR		;CARRIAGE RETURN?
	RCHF	P1		;YES, GET NEXT
	JUMPE	P1,.-1		;IGNORE NULLS
	CAIN	P1,LF		;IS NEXT LINE FEED?
	JRST	IMPGE1		;YES, FINISH OFF THE LINE
	Movei	T1,[SIXBIT\500 L&AST LINE WAS UNRECOGNIZABLE.#!\]

;HERE WHEN THE COMMAND IS IN ERROR.  error message in T1.
CMDERR:	CAIN	P1,LF		;LINE FEED?
	JRST	CMDER1		;YES
	RCHF	P1		;NO, DISCARD AND GET NEXT
	JRST	CMDERR
CMDER1:	EWSix	(T1)		; send the error message
	JRST	COMAND		;WAIT FOR NEXT COMMAND
;HERE WHEN A COMPLETE COMMAND HAS BEEN INPUT.  DECIPHER IT
CMDFIN:				;THE MAIL FUNCTION ACCEPTS DATA OVER THE
				;TELNET CONNECTION, SO WE HAVE TO CHECK IT
;[CFE]	Set up character count first.
	subi	t2,CmdLen	;[CFE] Get negative character count
	movnm	t2,CmdCnt	;[CFE]  and store for RCHICB.
	TXNN	F,MAILFG	;IN MAIL MODE?
	JRST	CMDIS		;NO, A COMMAND IT IS
	PUSHJ	P,C.MAIX	;HANDLE THIS LINE
	JRST	COMAND		;AND TRY THE NEXT
CMDIS:	FSETUP	IMPCBH		;SETUP IMP INPUT PSEUDO-FILE
	FISEL	IMPCBL		;SELECT IT
	MOVE	T1,[POINT 6,T2]	;PREPARE TO PACK COMMAND NAME
	SETZ	T2,
CMDFN1:	RCHF	P1		;GET A CHAR
	TXNN	P2,LETTER	;IS IT A LETTER?
	JRST	CMDSRC		;NO, END OF COMMAND
	SUBI	P1,40		;CONVERT TO SIXBIT
	TXNE	T1,77B5		;IS THERE ROOM FOR MORE LETTERS?
	IDPB	P1,T1		;YES, STORE IT
	JRST	CMDFN1		;BACK FOR MORE

;HERE WHEN END OF COMMAND NAME REACHED
CMDSRC:	JUMPN	T2,CMDSR1	;JUMP IF NONBLANK
	EWSix	[SIXBIT\500 L&AST LINE WAS UNRECOGNIZABLE.#!\]
	JRST	COMAND		;WAIT FOR NEXT COMMAND
CMDSR1:	CAIE	P1," "		;WAS THE CHAR A SPACE?
	LCHF	P1		;NO, BACK UP OVER IT
	MOVEM	T2,CMDNAM	;REMEMBER COMMAND NAME
	MOVSI	T1,-COMLEN	;NUMBER OF COMMANDS
	CAME	T2,COMTAB(T1)	;SEARCH FOR COMMAND NAME
	AOBJN	T1,.-1
	JUMPGE	T1,CMDNFD	;JUMP IF NOT IN TABLE
	MOVE	P4,COMDSP(T1)	;GET CORRESPONDING DISPATCH ENTRY
	TXNE	P4,CM.LGI	;LOGIN REQUIRED?
	TXNE	F,LGIFLG	;YES, IS SUBJOB LOGGED IN?
	JRST	.+3		;YES, OR NOT REQUIRED
	PUSHJ	P,FRELGI	;NO, ATTEMPT A FREE LOGIN
	  JRST	COMAND		;UNSUCCESSFUL (MSG ALREADY PRINTED)
	Call	SetNam		;[96bit] make sure have set host up.
	PUSHJ	P,(P4)		;DO COMMAND PROCESSING
	JRST	COMAND		;WAIT FOR NEXT COMMAND


;HERE WHEN COMMAND NAME NOT FOUND
CMDNFD:	EDisix	[COMAND,,[SIXBIT\500 % &COMMAND NOT RECOGNIZED.#!\]
		WNAME	CMDNAM]

;HERE WHEN COMMAND IS NOT IMPLEMENTED
COMUNI:	EDisix	[COMAND,,[SIXBIT\506 % &COMMAND NOT IMPLEMENTED.#!\]
		WNAME	CMDNAM]
	SUBTTL	SYSTEM ACCESS COMMANDS

;   USER <USER NAME>

C.USER:	TXZE	F,LGIFLG	;IS USER ALREADY LOGGED IN?
	PUSHJ	P,LGOUSR	;YES, LOG HIM OUT FIRST
	TXOE	F,USRFLG	;USER NAME ALREADY GIVEN?
	PUSHJ	P,CNCUSR	;YES, FORCE SUBJOB TO MONITOR LEVEL
	Disix	[[SIXBIT\LOGIN %#!\] ;SEND LOGIN COMMAND TO SUBJOB
		PUSHJ	P,IMPPTY]
	PUSHJ	P,CHKLGI	;GO TAKE A LOOK AT HOW WE DID
	  PJRST	LGIERR		;DROPPED ON OUR NOSE. TELL USER
	  EDisix [PTYFLS,,[SIXBIT\330 P&ASSWORD, PLEASE.#!\]]
	PJRST	LGIFIN		;NO PSW NEEDED, WELCOME HIM
;   PASS <PASSWORD>

C.PASS:	TXNN	F,USRFLG	;GIVEN USER NAME YET?
	EDisix	[CPOPJ##,,[SIXBIT\504 USER &COMMAND MUST PRECEDE PASSWORD.#!\]]
	PUSHJ	P,IMPPTY	;OK, COPY PASSWORD TO LOGIN
	W2CHI	CRLF		;TERMINATE IT
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	LGIERR		;ERROR
	PUSHJ	P,PTYF1L	;FLUSH LINE OF ASTERISKS
	PUSHJ	P,GETRSP	;CHECK RESPONSE ON NEXT LINE
	  PJRST	LGIERR		;ERROR

;HERE WHEN LOGIN OPERATION FINISHED
LGIFIN:	TXC	F,USRFLG!LGIFLG ;CLEAR USRFLG, SET LGIFLG
	PUSHJ	P,SJBPPN	;FIND OUT THE PPN OF OUR SUBJOB
	MOVEM	T1,PRJPRG	;SAVE FOR LATER USE
	MOVSI	T1,'050'	;COPY RESPONSE TO USER AS SYSTEM INFO
	LCHF	P1		;DON'T MISS FIRST CHAR OF RESPONSE
	PUSHJ	P,CPYRSP
	EWSix	[SIXBIT\230 L&OGIN SUCCESSFUL.#!\]
	POPJ	P,



;   ACCT <ACCOUNT STRING>

C.ACCT:	EWSix	[SIXBIT\200 A&CCOUNTS NOT USED ON THIS SYSTEM.#!\]
	POPJ	P,
;   BYE

C.BYE:	move	p,[iowd PdlSiz,Pdl] ;RESET THE STACK
	txne	f,PtyFlg	;DO WE HAVE A PTY?
	  pushj	p,FreOut	;LOG possible SUBJOB OFF
	EWSix	[SIXBIT\231 B&YE.#!\] ;TRY TO BE FRIENDLY
	Releas	Pty,
	txz	f,PtyFlg	;REMEMBER WE DON'T HAVE A PTY ANY MORE
	jsp	t4,ByeFrc	; Remember how we got here

;HERE TO FORCE BYE COMMAND WHEN WE KNOW THE SUBJOB ISN'T LOGGED IN
BYEFRC:
;[CFE] First, see if the Imp connection is open; don't hang trying
;[CFE]  to send to an absent connection!  Note: this doesn't eliminate
;[CFE]  race conditions between remote-close and this RELEASE, but it
;[CFE]  does narrow the race window.
	pushj	p,ImpChk	;[CFE] One final test.  Will terminate.
	movei	t1,Imp		;[CFE] This is channel to reset, maybe.
	txnn	f,OpnFlg	;[CFE] Is conn still open?
	 ResDv.	t1,		;[CFE]  No; flush the device buffers.
	  jfcl			;[CFE] (ok, we were just trying...)
	pushj	p,ImpChk	;[CFE] Test again.
	RELEASE	IMP,		;FORCE OUT ANY PENDING MESSAGES
;[96bit]MOVE	T1,[IF.NWT+<.IUCLS>B17+CONBLK] ;CLOSE TELNET CONNECTIONS
	Move	T1,ImpAc(If.Nwt!If.New,.IuCls,ConBlk)	;[96bit]
	SETZM	CONBLK+.IBLCL	;INPUT SIDE
	IMPUUO	T1,		;NO WAIT FOR ACTION
	  JFCL
;[tcp]	AOS	CONBLK+.IBLCL	;NOW OUTPUT SIDE
;[tcp]	IMPUUO	T1,
;[tcp]	  JFCL
DOLOGO:	LOGOUT			;GO AWAY.

; Dummy BYEFRC callers for tracing where the hanging comes from.
BYEFR1:	jsp	t4,ByeFrc	; Remember PC
BYEFR2:	jsp	t4,ByeFrc
BYEFR3:	jsp	t4,ByeFrc


FREOUT:
	TXZE	F,USRFLG!MAILFG ;IF IN LOGIN OR MAIL...
	PUSHJ	P,CNCUSR	;FORCE SUBJOB TO COMMAND LEVEL
	TXZE	F,LGIFLG	;IS SUBJOB LOGGED IN?
	PUSHJ	P,LGOUSR	;YES, LOG IT OUT
	pjrst	PTYFLS		;MAKE SURE ALL OUTPUT IS ABSORBED
	SUBTTL	DATA TRANSFER PARAMETER COMMANDS

repeat 0,<	; no byte size in TCP

;   BYTE <BYTE SIZE>

C.BYTE:	PUSHJ	P,GETDEC	;GET BYTE SIZE
	  JRST	BYTERR		;ERROR IN NUMBER
	CAIE	P1,LF		;END OF LINE?
BYTERR:	EDisix	[CPOPJ##,,[SIXBIT\501 B&YTE SIZE SPECIFICATION ERROR.#!\]]
	CAIL	T1,1		;CHECK BYTE SIZE FOR LEGALITY
	CAILE	T1,↑D255
	JRST	BYTERR		;OUT OF RANGE
	CAIE	T1,↑D8		;CHECK FOR BYTE SIZES THAT OUR
	CAIN	T1,↑D36		;  CRUMMY IMPSER CAN HANDLE PROPERLY
	CAIA			;OK
	EDisix	[CPOPJ##,,[SIXBIT\506 B&YTE SIZE % NOT SUPPORTED.#!\]
		WDECI	(T1)]
	MOVEM	T1,BYTSIZ	;OK, STORE BYTE SIZE
	EDisix	[CPOPJ##,,[SIXBIT\200 B&YTE SIZE % ACCEPTED.#!\]
		WDECI	(T1)]

> ; end of repeat 0
;   SOCK <SOCKET>      OR     SOCK <HOST>,<SOCKET>

C.SOCK:	PUSHJ	P,GETDEC	;GET DECIMAL NUMBER
	  JRST	SKTERR		;ERROR
	Caie	P1,"."		;[96bit] <Host>.<Site>?
	 Cain	P1,"/"		;[96bit] or <Host>/<Site>?
	  Jrst	[		;[96bit] one of them: must be host.
		 Move	T2,T1		;[96bit] save host number
		 Pushj	p,GetDec	;[96bit] get the site number
		   Jrst	SockBH		;[96bit] no site: bad format
		 Caie	P1,","		;[96bit] now a socket?
		   Jrst	SktErr		;[96bit] no: not legal.
		 Jrst	Sockt3		;[96bit] ok: go juggle right
		]
	;[96bit] just a straight host or socket number.
	CAIE	P1,","		;COMMA?
	JRST	SOCKT1		;NO, NOT CHANGING HOST
;[96bit]CAIL	T1,1		;YES, CHECK FOR LEGAL HOST NUMBER
	CAILE	T1,↑D255	; does it look like in old format?
	  Jrst	Sockt2		;[96bit] full host: just check and store
	;[96bit] old format: convert to proper format
	LDB	T2,[Point 2,T1,35-6]	;[96bit] host number
	Andi	T1,77			;[96bit] mask out host number
Sockt3:	Dpb	T2,[Pointr (T1,Ih.Hst)]	;[96bit] host in place

Sockt2:	Txnn	T1,Ih.Imp	;[96bit] is there a site?
	  Jrst	SockBH		;[96bit] no: illegal host
	Movem	T1,HstTmp	;[96bit] save the host number
	PUSHJ	P,GETDEC	;GET SOCKET NUMBER
	  JRST	SKTERR		;ERROR
SOCKT1:	CAIN	P1,LF		;CHECK FOR LEGAL FORMAT
	TLNE	T1,(-1←↑D32)	;AND FOR LEGAL SOCKET NUMBER
	  Jrst	SktErr		; out of range
	MOVE	T2,T1		;OK, COPY SOCKET NUMBER
	ANDCAI	T2,1		;HIS INPUT IS OUR OUTPUT, SO COMPLEMENT
	MOVEM	T1,RmtSkt	;STORE NEW REMOTE INPUT OR OUTPUT SOCKET
	Call	SetNam		;[96bit] store HstTmp, and get new name.
				;	 (saves T1 & T2)
	EDisix	[CPOPJ##,,[SIXBIT\200 S&OCKET % AT HOST % (%) ACCEPTED.#!\]
		WDEC	T1
;[96bit]	WDEC	HSTADR
		Call	HstPrt	;[96bit] print host name
		Call	HstNoo	;[96bit] and print number, to make
				;	 clear how we interpreted
		]
SockBH:	EDisix	[CPOPJ##,,[SIXBIT\501 H&OST NUMBER SPECIFICATION ERROR.#!\]]
SKTERR:	Clearm	HstTmp		;[96bit] clear potential new host adr
	EWSix	[SIXBIT\501 S&OCKET NUMBER SPECIFICATION ERROR.#!\]
	Return
repeat 0,<	; con't handle odd types

;   TYPE <TYPE CODE>

C.TYPE:	PUSHJ	P,SPNOR		;IGNORE SPACES
	MOVSI	T1,-TYPLEN	;PREPARE TO SEARCH TYPE TABLE
	HLRZ	T2,TYPCOD(T1)	;GET TYPE CODE
	CAIE	T2,(P1)		;IS THIS IT?
	AOBJN	T1,.-2		;NO, TRY NEXT
	JUMPGE	T1,.+3		;JUMP IF NOT FOUND
	PUSHJ	P,SPNOR1	;OK, CHECK FOR LEGAL FORMAT
	CAIE	P1,LF
	EDisix	[CPOPJ##,,[SIXBIT\501 D&ATA TYPE SPECIFICATION ERROR.#!\]]
	MOVE	T1,TYPCOD(T1)	;FETCH TYPE DESCRIPTOR
	TRNE	T1,400000	;IMPLEMENTED?
	EDisix	[CPOPJ##,,[SIXBIT\506 T&YPE % NOT IMPLEMENTED.#!\]
		WCHI	(T2)]	;NO
	MOVEM	T1,XFRTYP	;YES, STORE NEW TYPE DESCRIPTOR
	EDisix	[CPOPJ##,,[SIXBIT\200 T&YPE % ACCEPTED.#!\]
		WCHI	(T2)]


;TYPE TABLE
TYPCOD:	"A" ,,	0		;ASCII
	"I" ,,	1		;IMAGE
	"L" ,,	-1		;LOCAL BYTE (NOT IMPLEMENTED)
	"P" ,,	-1		;PRINT FILE (NOT IMPLEMENTED)
	"E" ,,	-1		;EBCDIC PRINT FILE (NOT IMPLEMENTED)

	TYPLEN==.-TYPCOD	;NUMBER OF DIFFERENT KNOWN TYPE CODES

> ; end of repeat 0
repeat 0,<	; not implemented in TCP

;   STRU <STRUCTURE CODE>

C.STRU:	PUSHJ	P,SPNOR		;IGNORE SPACES
	MOVSI	T1,-STRLEN	;PREPARE TO SEARCH STRUCTURE TABLE
	HLRZ	T2,STRCOD(T1)	;GET AN ENTRY
	CAIE	T2,(P1)		;IS THIS IT?
	AOBJN	T1,.-2		;NO
	JUMPGE	T1,.+3		;JUMP IF NOT FOUND
	PUSHJ	P,SPNOR1	;CHECK SYNTAX
	CAIE	P1,LF		;DID EOL IMMEDIATELY FOLLOW?
	EDisix	[CPOPJ##,,[SIXBIT\501 S&TRUCTURE SPECIFICATION ERROR.#!\]]
	MOVE	T1,STRCOD(T1)	;OK, GET SPECIFIER WORD
	TRNE	T1,400000	;IS IT IMPLEMENTED?
	EDisix	[CPOPJ##,,[SIXBIT\506 S&TRUCTURE % NOT IMPLEMENTED.#!\]
		WCHI	(T2)]
	MOVEM	T1,STRTYP	;OK, STORE STRUCTURE SPECIFIER
	EDisix	[CPOPJ##,,[SIXBIT\200 S&TRUCTURE % ACCEPTED.#!\]
		WCHI	(T2)]


STRCOD:	"F" ,,	0		;FILE (NO RECORD STRUCTURES)
	"R" ,,	-1		;RECORD (NOT IMPLEMENTED)

	STRLEN==.-STRCOD

> ; end of repeat 0
repeat 0,<	; not implemented in this TCP hack

;   MODE <MODE CODE>

C.MODE:	PUSHJ	P,SPNOR		;IGNORE SPACES
	MOVSI	T1,-MODLEN	;SEARCH MODE TABLE
	HLRZ	T2,MODCOD(T1)
	CAIE	T2,(P1)		;IS THIS IT?
	AOBJN	T1,.-2		;NO, TRY NEXT
	JUMPGE	T1,.+3		;JUMP IF NOT FOUDN
	PUSHJ	P,SPNOR1	;CHECK FOR LEGAL SYNTAX
	CAIE	P1,LF
	EDisix	[CPOPJ##,,[SIXBIT\501 M&ODE SPECIFICATION ERROR.#!\]]
	MOVE	T1,MODCOD(T1)	;OK, FETCH MODE SPECIFIER
	TRNE	T1,400000	;IMPLEMENTED?
	EDisix	[CPOPJ##,,[SIXBIT\506 M&ODE % NOT IMPLEMENTED.#!\]
		WCHI	(T2)]
	MOVEM	T1,MODTYP	;OK, SAVE MODE SPECIFIER
	EDisix	[CPOPJ##,,[SIXBIT\200 M&ODE % ACCEPTED.#!\]
		WCHI	(T2)]


MODCOD:	"S" ,,	0		;STREAM
	"B" ,,	-1		;BLOCK (NOT IMPLEMENTED)
	"T" ,,	-1		;TEXT (NOT IMPLEMENTED)
	"H" ,,	-1		;HASP (NOT IMPLEMENTED)

	MODLEN==.-MODCOD

> ; end of repeat 0
	SUBTTL	FTP DATA TRANSFER FUNCTIONS

;   RETR <PATHNAME>

C.RETR:	MOVE	T1,[SIXBIT\DATA\] ;LOGICAL NAME FOR IMP DEVICE
	HRRZ	T2,XFRTYP	;DATA TYPE FOR TRANSFER
	PUSHJ	P,DoOpen	;OPEN SUBJOB'S IMP OUTPUT CONNECTION
	  POPJ	P,		;ERROR--MESSAGE ALREADY PRINTED
	WSix	[SIXBIT\R PIP#!\] ;START SUBJOB RUNNING PIP
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  JRST	XFRERR		;ERROR??
	HRRZ	T1,XFRTYP	;GET TRANSFER TYPE
	Disix	[[SIXBIT\DATA: = %#!\] ;ENTER PIP COMMAND
		PUSHJ	P,IMPPTY]
RtrEnd:	;[96bit] end a RETR or LIST
	PUSHJ	P,XFRCHK	;CHECK FOR SUCCESSFUL COMPLETION
	  POPJ	P,		;ERROR, MESSAGE ALREADY PRINTED
	PUSHJ	P,CNCUSR	;FORCE SUBJOB TO COMMAND LEVEL
	WSix	[SIXBIT\IMP CLOSE DATA:#!\] ;CLOSE DATA CONNECTION
	MOVSI	T1,'452'	;CODE TO USE IF ERROR
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR--COPY MESSAGE TO USE4R AND QUIT
	EWSix	[SIXBIT\252 T&RANSFER COMPLETED.#!\]
	PJRST	PTYFLS		;FLUSH PTY OUTPUT AND RETURN
;   STOR <PATHNAME>

C.STOR:	MOVE	T1,[SIXBIT\DATA\] ;LOGICAL NAME FOR IMP DEVICE
	HRRZ	T2,XFRTYP	;DATA TYPE FOR TRANSFER
	PUSHJ	P,DoOpen	;OPEN SUBJOB'S IMP INPUT CONNECTION
	  POPJ	P,		;ERROR--MESSAGE ALREADY PRINTED
	WSix	[SIXBIT\R PIP#!\] ;START SUBJOB RUNNING PIP
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  JRST	XFRERR		;COULDN'T START PIP
	HRRZ	T1,XFRTYP	;FETCH TRANSFER TYPE
	Disix	[[SIXBIT\% = DATA:#!\] ;ENTER PIP TRANSFER COMMAND
		PUSHJ	P,IMPPTY
		]
;[tcp]	PUSHJ	P,XFRCHK	;WAIT FOR SUCCESSFUL COMPLETION
;[tcp]	  POPJ	P,		;ERROR, MESSAGE ALREADY PRINTED
;[tcp]	EDisix	[CNCUSR,,[SIXBIT\252 T&RANSFER COMPLETED.#!\]]
	jrst	RtrEnd			;[tcp] standard out


ife FtHarv,<	;[96bit] harvard DIRECT does not support /InDir
;   Nlst <PathName>		[96bit]

C.Nlst:	TXO	F,NlsCom	;[96bit] remember we're doing NLST
;	Jrst	C.List		;[96bit] fall into LIST command
>	;end of IFE FtHarv


;   LIST <PATHNAME>

C.LIST:
;[96bit]WSix	[SIXBIT\ASSIGN IMP LPT#!\] ;KLUDGE TO DIRECT OUTPUT FROM
;[96bit]PUSHJ	P,GETRSP	;  HARVARD DIRECT TO AN IMP DEVICE.
;[96bit]  EDisix [PTYFLS,,[SIXBIT\454 N&O &IMP&S AVAILABLE.#!\]]
;[96bit]PUSHJ	P,PTYFLS	;FLUSH "IMPN ASSIGNED" MESSAGE
;[96bit]MOVSI	T1,'LPT'	;LOGICAL DEVICE NAME
	MOVE	T1,[Sixbit \Data\]	;[96bit] normal logical name
	MOVEI	T2,0		;ASCII DATA TYPE
	PUSHJ	P,DoOpen	;OPEN DATA CONNECTION FOR OUTPUT
	  POPJ	P,		;ERROR, MSG ALREADY PRINTED
Ife FTHarv,<	;[96bit] harvard DIRECT doesn't support /InDirect
	TXZE	F,NlsCom	;[96bit] an NLST? (Clear flag if on)
	  SKIPA	T1,[Sixbit \/Indir\]	;[96bit] yes: do indirect
	SETZ	T1,		;[96bit] LIST command: don't do indirect
;[96bit]Disix	[[SIXBIT\DIRECT /L %#!\]
	Disix	[RtrEnd,,[SIXBIT\DIRECT Data:=% %#!\]
		WNAME	T1	;[96bit] give the /I if it's there
		PUSHJ	P,IMPPTY]
>	;end of IFE FtHarv
ifn FtHarv,<	;[96bit] harvard DIRECT is "non-standard"
	Disix	[RtrEnd,,[SIXBIT\DIRECT %/FILE=Data:#!\]
		PUSHJ	P,IMPPTY]
>	;end of IFN FtHarv
;[96bit]PUSHJ	P,XFRCHK	;WAIT FOR COMPLETION OF DATA TRANSFER
;[96bit]  POPJ	P,		;ERROR--MESSAGE ALREADY PRINTED
;[96bit]PUSHJ	P,PTYFLS	;GET RID OF ANY GARBAGE FROM DIRECT
;[96bit]WSix	[SIXBIT\IMP CLOSE LPT:#!\] ;CLOSE DATA CONNECTION
;[96bit]MOVSI	T1,'452'	;ERROR CODE TO USE IF ERROR
;[96bit]PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
;[96bit]  PJRST	CPYRSP		;ERROR, CPY RESPONSE TO USER
;[96bit]EWSix	[SIXBIT\252 T&RANSFER COMPLETED.#!\]
;[96bit]PJRST	PTYFLS		;FLUSH REMAINING PTY OUTPUT
;     MLFL <PPN>

IfDef MlFlCommand,<	;[96bit] if we are supporting Mail File commands
			;	 then define this, else leave undefined
			;	 and let the command macro sort it out.
C.MLFL:	MOVE	T1,[SIXBIT/DATA/];THE LOGICAL NAME WE WANT TO USE
	MOVEI	T2,0		;TRANSFER IN ASCII MODE
	PUSHJ	P,DoOpen	;TRY TO GET IMP
	  PJRST ML.ERR		;FAILED..GIVE UP
	TXO	F,MLFLFG	;SET INSIDE MLFL FLAG
;[96bit]Disix	[[SIXBIT\MAIL /ZVRF/ZXC/TO:%/IDENTI:%/FILE:DATA:#!\]
;[96bit]	PUSHJ	P,IMPPTY
;[96bit]	PUSHJ	P,HSTPRT]
	MlFlCommand		; do the right mail file command
	PUSHJ	P,XFRCHK	;WAIT TIL THINGS FINISH UP
	  PJRST ML.ERR		;SOMETHING DIED ALONG THE WAY
	MOVSI	T1,'051'	;GENERAL FTP COMMENTARY
	LCHF	P1			;GET FIRST CHAR
	PUSHJ	P,CPYRSP	;COPY ALL RESPONSES FROM MAIL
	;[96bit] assume no trouble
	Movei	T2,[SIXBIT/252 MAIL &TRANSFER COMPLETED.#!/]
	TXNE	F,ERRFLG	;ANY ERRORS IN RESPONSES?
	  Movei	T2,[SIXBIT/454 MLFL &FAILED.#!/]  ;[96bit] trouble.
	EWSix	(T2)		;[96bit] give the error message
	TXZ	F,MlFlFg	;[96bit] clear mail flag
;[96bit]TXZE	F,LGAR0M	;DID WE LOGIN AS AR0M?
	TXZE	F,TLogin	;[96bit] want to undo login?
	PJRST	FREOUT		;DO A LOGOUT AND RETURN
	PJRST	PTYFLS		;GET RID OF EXTRA PTY TRASH

ML.ERR:	TXZ	F,MLFLFG
;[96bit]TXZE	F,LGAR0M
	TXZE	F,TLogin		;[96bit] undo login?
	PUSHJ	P,FREOUT
	POPJ	P,

>	; end IfDef MlFlCommand
	SUBTTL	MISCELLANEOUS FTP FUNCTIONS

;   MAIL <PPN>

C.MAIL:
;[96bit]Disix	[[SIXBIT\MAIL /ZVRF/ZXC/TO:%/IDENTI:%/FILE:TTY:#!\]
;[96bit]	PUSHJ	P,IMPPTY
;[96bit]	PUSHJ	P,HSTPRT]
	MailCommand		;[96bit] do the right mail command
;[CFE]	MOVSI	T1,'507'	;A GENERAL ERROR CODE
	MOVSI	T1,'454'	;[CFE] A temporary-failure code.
	TXO	F,MAILFG	;[CFE] Let GETRSP make badness into
				;[CFE]   permanent-failure type codes.
	PUSHJ	P,GETRSP	;SEE HOW IT GOES
;[CFE]	  PJRST	CPYRSP		;NOT WELL
	  PJRST	[TXZ	F,MAILFG	;[CFE] Clear this state first
		 PJRST	CPYRSP]		;NOT WELL
;[CFE]	TXO	F,MAILFG	;TELL COMAND TO COME HERE FOR A WHILE
	EWSix	[SIXBIT\350 E&NTER MAIL, ENDED BY A LINE WITH JUST A '.'#!\]
	PJRST	PTYFLS		;FORGET ANYTHING ELSE MAIL SAID

C.MAIX:	MOVE	T1,CMDPTR	;HERE WHEN A TELNET LINE COMES IN WHILE IN MAIL
	ILDB	T2,T1		;SEE IF IT IS ONLY A .<CR>
	CAIE	T2,"."		;WHICH IS THE MAIL TERMINATION CHARACTER
	JRST	MAIX1		;WELL, NOT YET
	ILDB	T2,T1		;IS THE NEXT A <CR>?
	CAIE	T2,CR
	JRST	MAIX1		;NO, SEND IT ALL OFF TO THE PTY
	FOSEL	PTYOBL		;IT IS. FINISH UP MAIL
	W2CHI	<"Z"-100>B28+LF	;AND GIVE IT THE CTRL-Z IT WANTS
				;+ A LF TO FORCE OUT THE BUFFER
	PUSHJ	P,XFRCK1	;WAIT TILL THINGS FINISH UP
		POPJ	P,	;SOMETHING WENT WRONG
	MOVSI	T1,'051'	;GENERAL RESPONSE CODE
	LCHF	P1		;GET FIRST CHAR
	PUSHJ	P,CPYRSP	;COPY RESPONSES LOOKING FOR ERRORS
	;[96bit] assume no trouble
	Movei	T2,[SIXBIT/256 MAIL &COMPLETED.#!/]
	TXNE	F,ERRFLG	;ANY ERRORS IN RESPONSES?
	  Movei	T2,[SIXBIT/454 MAIL &FAILED.#!/]  ;[96bit] trouble.
	EWSix	(T2)		;[96bit] give the error message
	TXZ	F,MAILFG	;CLEAR THIS
	PJRST	PTYFLS		;THROW OUT ANY GARBAGE

MAIX1:	Disix	[[SIXBIT\%#!\]
		PUSHJ	P,IMPPTY]
	POPJ	P,		;FINISHED THIS LINE, TRY ANOTHER
;   DELE <PATHNAME>

C.DELE:	Disix	[[SIXBIT\DELETE %#!\]
		PUSHJ	P,IMPPTY]
	MOVSI	T1,'501'	;ONLY POSSIBLE ERROR IS SYNTAX ERROR
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR, PRINT MESSAGE
	LCHF	P1		;BACK OVER FIRST CHAR OF RESPONSE
	MOVSI	T1,'050'	;GENERAL FTP COMMENTARY
	PUSHJ	P,CPYRSP	;COPY DELETE RESPONSE TO USER
	TXNN	F,ERRFLG	;WERE THERE ANY ERRORS?
	EDisix	[CPOPJ##,,[SIXBIT\254 D&ELETE COMPLETED.#!\]]
	EDisix	[CPOPJ##,,[SIXBIT\451 D&ELETE UNSUCCESSFUL.#!\]]



;   ALLO <DECIMAL INTEGER>

C.ALLO:	EWSix	[SIXBIT\200 A&LLOCATION NOT REQUIRED ON THIS SYSTEM.#!\]
	POPJ	P,



;   RNFR <PATHNAME>

C.RNFR:	HLLZ	T1,CMDPTR	;GET LH OF CURRENT BYTE PTR
	HRRI	T1,RNFBUF	;BUILD POINTER TO "RENAME FROM" BUFFER
	MOVEM	T1,RNFPTR	;SAVE IT
	HRL	T1,CMDPTR	;COPY "FROM" PATHNAME TO TEMP BUFFER
	BLT	T1,RNFBUF+CMDLEN/5
;[CFE] Also copy character count.
	move	t1,CmdCnt	;[CFE] From CMD buffer
	movem	t1,RnFCnt	;[CFE]  to RNF buffer.
	EDisix	[CPOPJ##,,[SIXBIT\200 RNFR &PATHNAME STORED.#!\]]

;   RNTO <PATHNAME>

C.RNTO:	SKIPN	T1,RNFPTR	;CHECK FOR PRECEDING RNFR
	EDisix	[CPOPJ##,,[SIXBIT\504 RNFR &COMMAND MUST PRECEDE &RNTO& COMMAND.#!\]]
	move	t2,RnFCnt	;[CFE] Also load character count
	Disix	[[SIXBIT\RENAME % = %%%#!\]
		PUSHJ	P,IMPPTY	;COPY NEW PATHNAME TO PTY
		MOVEM	T1,CMDPTR
		movem	t2,CmdCnt	;[CFE] Copy count, also
		PUSHJ	P,IMPPTY]	;NOW OLD PATHNAME
	SETZM	RNFPTR		;CLEAR OLD POINTER
	MOVSI	T1,'501'	;ERROR IN FIRST LINE IS PROBABLY SYNTAX
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR, COPY RESPONSE AND QUIT
	LCHF	P1		;OK, BACKUP OVER FIRST CHAR
	MOVSI	T1,'050'	;FTP COMMENTARY
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER
	TXNN	F,ERRFLG	;WERE THERE ANY ERRORS?
	EDisix	[CPOPJ##,,[SIXBIT\253 R&ENAME COMPLETED.#!\]]
	EDisix	[CPOPJ##,,[SIXBIT\451 R&ENAME UNSUCCESSFUL.#!\]]
;   STAT	OR	STAT <PATHNAME>

C.STAT:	PUSHJ	P,SPNOR1	;IGNORE BLANKS
	CAIE	P1,LF		;END OF LINE?
	JRST	STATDR		;NO, GO PROCESS PATHNAME
	MOVSI	T1,'050'
	EDisix	[EXP	SRVMSG
		WSIX	4,T1
		WASC	SYSNAM]
	EDisix	[[SIXBIT\100-C&URRENT PARAMETERS:#∨
    &H&OST: %   &L&ocal &S&OCKET: %   &R&emote &S&OCKET: %#!\]
		PUSHJ	P,HstPrt	;[96bit] print name
		WDEC	LclSkt
		WDEC	RmtSkt]

repeat 0,<	; these are implmeneted
	HLRZ	T1,XFRTYP
	HLRZ	T2,STRTYP
	HLRZ	T3,MODTYP
	EDisix	[[SIXBIT\    B&YTE SIZE: %   &T&YPE: %   &S&TRUCTURE: %   &M&ODE: %#!\]
		WDEC	BYTSIZ
		WCHI	(T1)
		WCHI	(T2)
		WCHI	(T3)]
> ; end of repeat 0

	TXNE	F,LGIFLG	;LOGGED IN?
	EDisix	[[SIXBIT\    S&ERVER JOB LOGGED IN UNDER [%]#!\]
		PUSHJ	P,PPNPRT]
	TXNE	F,USRFLG	;PASSWORD EXPECTED?
	EWSix	[SIXBIT\    P&ASSWORD EXPECTED#!\]
	EWSix	[Sixbit \100 E&nd of status.#!\]	;[96bit]
	POPJ	P,

;HERE TO DO STAT <PATHNAME>, I.E. DIRECTORY LISTING.
STATDR:	TXNE	F,LGIFLG	;LOGGED IN?
	JRST	.+3		;YES, PROCEED
	PUSHJ	P,FRELGI	;NO, ATTEMPT FREE LOGIN
	  POPJ	P,		;FAILED (MSG ALREADY TYPED)
	LCHF	P1		;OK, BACKUP OVER FIRST CHAR OF PATHNAME
	Disix	[[SIXBIT\DIRECT %#!\] ;OUTPUT COMMAND TO PTY
		PUSHJ	P,IMPPTY]
	MOVSI	T1,'501'	;ERROR IS PROBABLY A SYNTAX ERROR
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR--COPY RESPONSE TO USER
	MOVSI	T1,'151'	;DIRECTORY LISTING REPLY
	LCHF	P1		;BACK UP OVER FIRST CHAR
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER
	EWSIX	[SIXBIT\200 D&IRECTORY LISTING COMPLETED.#!\]
	POPJ	P,
;   HELP
;[96bit] messages changed slightly to agree with protocol.

C.HELP:	EDisix	[Cpopj##,,HlpMsg
		  Call	HlpLst
		]

; help message.  note the percent sign at the end of the first line.
HlpMsg:	SIXBIT\200-T&HE FOLLOWING &FTP& FUNCTIONS ARE IMPLEMENTED:%#∨
    &O&NLY &ASCII& AND 36-BIT IMAGE TRANSFERS.#∨
    &STAT, LIST, NLST, DELE, RNFR, RNTO& ACCEPT WILDCARD SPECIFICATIONS.#∨
    &N&ONSTANDARD COMMANDS:#∨
      &XCWD  C&HANGE WORKING DIRECTORY.#∨
      &XSRC  C&HANGE DISK SEARCH LIST.#∨
      &XTIM  D&ISABLE INACTIVITY TIMEOUT.#∨
200 &E&nd of &HELP&.#!\

; prints out all the commands which should be printed for help.
; only called from inside EDisix, so the EFile in standard output.
HlpLst:	MOVSI	T1,-COMLEN	;CHECK EACH ONE
	SETZ	T3,		;RESET NUMBER OF ITEMS SO FAR
HELP1:	MOVE	T2,COMDSP(T1)	;GET DISPATCH WORD FOR THIS COMMAND
	TXNN	T2,CM.HLP	;WANT COMMAND LISTED?
	JRST	HELP2		;NO, SKIP IT
	SOJG	T3,.+3		;JUMP IF STILL ROOM ON THE LINE
	WSIX	[SIXBIT\#    !\] ;NO, START ANOTHER
	MOVEI	T3,↑D10		;RESET COUNTER
	WSIX	6,COMTAB(T1)	;LIST THE COMMAND
HELP2:	AOBJN	T1,HELP1	;LOOP FOR REST
Ife $FtpLog,<	;[96bit] tell if we don't allow not logged in access
	WSIX	[SIXBIT	\#    U&SER COMMAND REQUIRED TO ACCESS ANY FILES.\]
>
	Return			; now go back and print the rest.
	SUBTTL	NONSTANDARD FUNCTIONS

;    XTIM

C.XTIM:	HRROS	WATCNT		;DISABLE INACTIVITY TIMEOUT
	PJRST	COMACK		;ACKNOWLEDGE COMMAND


;    XSRC <SETSRC-STYLE SEARCH LIST>

C.XSRC:	WSix	[SIXBIT\R SETSRC#!\] ;CALL THE STANDARD DEC CUSP
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	COMNAK		;ERROR, COMPLAIN
	PUSHJ	P,PTYFLS	;FLUSH PROMPT, HELP MSG, ETC.
	Disix	[[SIXBIT\C %#!\] ;CREATE NEW SEARCH LIST AS SPECIFIED
		PUSHJ	P,IMPPTY]
	PJRST	XCMRSP		;WAIT FOR WINNING OR LOSING RESPONSE


;    XCWD <DIRECTORY>     OR    XCWD [<DIRECTORY>]

C.XCWD:	WSix	[SIXBIT\R SETSRC#!\] ;RUN SETSRC TO DO THE WORK
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	COMNAK		;CAN'T DO SETSRC STUFF
	PUSHJ	P,PTYFLS	;FLUSH RESPONSE
	FISEL	IMPCBL		;GET INPUT FROM IMP AGAIN
	CCHF	P1
	PUSHJ	P,SPNOR		;SKIP BLANKS
	CAIE	P1,"["		;DID USER TYPE SQUARE BRACKETS?
	LCHF	P1		;NO, BACKUP (SINCE IMPPTY DOES RCHF)
	;[96bit] NOTE: do NOT add a close bracket to the following
	;	 line.  it makes "XCWD [342,231]" illegal.
	Disix	[[SIXBIT\CP [%#!\] ;ENTER SETSRC COMMAND
		PUSHJ	P,IMPPTY]
XCMRSP:	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	COMNAK		;LOSES, SAY WHY
	PUSHJ	P,CNCUSR	;WINS, FORCE TO COMMAND LEVEL
				;AND FALL INTO COMACK


;ROUTINE TO REPLY FOR A SUCCESSFUL MISCELLANEOUS COMMAND

C.NoOp:	;[96bit]	No-Op just acknowledges command
COMACK:	EDisix	[CPOPJ##,,[SIXBIT\200 % &COMMAND ACCEPTED.#!\]
		WNAME	CMDNAM]

;ROUTINE TO COMPLAIN ABOUT AN ERROR IN A NONSTANDARD COMMAND

COMNAK:	MOVSI	T1,'507'	;CATCHALL ERROR REPLY CODE
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER FROM PTY
	EDisix	[CNCUSR,,[SIXBIT\507 % &COMMAND NOT ACCEPTED.#!\]
		WNAME	CMDNAM]
;    XREP	(REPLAY RECORDED PTY DIALOGUE, FOR DEBUGGING)

C.XREP:	EDisix	[Cpopj##,,[SIXBIT\050-R&EPLAY OF RECORDED &PTY& DIALOGUE:#∨
%∨
050 &E&nd of replay.#∨
200 &R&EPLAY COMPLETED.#!\]
	 Call	Replay			; do the replay
	]

Replay:	SKIPGE	T1,RECPTR	;IS ANYTHING THERE?
	  Return		; no, forget it.
	TXNN	F,WRPFLG	;YES, DOES IT WRAP AROUND?
	MOVE	T1,RECPT0	;NO, START AT BEGINNING OF BUFFER
XREP1:	CAMN	T1,RECPTZ	;AT END?
	MOVE	T1,RECPT0	;YES, GO BACK TO BEGINNING
	ILDB	T2,T1		;GET A CHAR
	WCHI	(T2)		;SEND IT TO IMP
	CAME	T1,RECPTR	;BACK WHERE WE STARTED?
	JRST	XREP1		;NO, CONTINUE
	CAIE	T2,LF		;YES, WERE WE AT EOL?
	W2CHI	CRLF		;NO, START FRESH LINE
	Return			; all done: go back and print the ending
	SUBTTL	SUBROUTINES

;ROUTINE TO OPEN THE SUBJOB'S IMP DATA CONNECTION.
;	MOVE	T1,[SIXBIT IMP LOGICAL DEVICE NAME TO BE USED]
;	MOVE	T2,[TYPE INDEX -- 0=ASCII, 1=IMAGE]
;	PUSHJ	P,DoOpen
;	  ERROR RETURN--MESSAGE ALREADY TYPED
;	OK RETURN

DoOpen:
	EDisix	[[SIXBIT\255 SOCK %#!\] ;STANDARD MESSAGE
		WDEC	LCLSkt]
	Disix	[[SIXBIT\IMP CONNECT %: % /LOCAL:%/Absolute/REMOTE:%#!\]
		WNAME	T1
		Pushj	P,HstNoo	;[96bit] print host number
		WDEC	LCLSKT
		WDEC	RmtSkt
		]
	MOVSI	T1,'454'	;MESSAGE CODE IN CASE ERROR
	PUSHJ	P,GETRSP	;EAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR, COPY MESSAGE TO USER AND QUIT
	PUSHJ	P,PTYFLS	;OK, FLUSH OUTPUT
	JRST	CPOPJ1##		;TAKE GOOD RETURN
;ROUTINE TO WAIT FOR COMPLETION OF A DATA TRANSFER FUNCTION
;	PUSHJ	P,XFRCHK
;	  ERROR--MESSAGE ALREADY PRINTED AND CONNECTION CLOSED
;	OK--NOTHING PRINTED, CONNECTION NOT CLOSED, OUTPUT NOT FLUSHED

XFRCHK:	MOVEI	T1,1		;WAIT ONE SECOND FOR THINGS TO GET STARTED
	SLEEP	T1,
	PUSHJ	P,PTYCHK	;HAS ANYTHING COME BACK FROM THE SUBJOB?
	  EDisix [XFRCK1,,[SIXBIT\250 % &STARTED.#!\]
		WNAME	CMDNAM]
	PUSHJ	P,GETRSP	;YES, SEE WHAT IT WAS
	  JRST	XFRERR		;AN ERROR, GO COMPLAIN
	EDisix	[CPOPJ1##,,[SIXBIT\250 % &STARTED.#!\]
		WNAME	CMDNAM]

;HERE IF NO RESPONSE IN THE FIRST SECOND
XFRCK1:	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  JRST	XFRERR		;ERROR, GO COMPLAIN
	JRST	CPOPJ1##		;OK, SKIP RETURN

;HERE ON ERROR RESPONSE DURING DATA TRANSFER
XFRERR:
	MOVSI	T3,'507'	;if code is 507 don't change to 454
	CAME	T3,T1
	MOVSI	T1,'454'	;CATCHALL ERROR MESSAGE
	PUSHJ	P,CPYRSP	;COPY ERROR MESSAGE TO USER
	PUSHJ	P,CNCUSR	;FORCE TO COMMAND LEVEL
	WSix	[SIXBIT\IMP CLOSE/SELF#!\] ;CLOSE OPEN CONNECTION(S)
	PJRST	PTYFLS		;FLUSH ANYTHING THAT COMES BACK UP
;ROUTINE TO PERFORM A "FREE" FTP LOGIN
;	PUSHJ	P,FRELGI
;	  ERROR--MESSAGE ALREADY PRINTED
;	OK--LGIFLG HAS BEEN SET

FRELGI:
Ife $MLogin ! $FtpLog,<	;[96bit] if no free logins, complain and return
	EDisix [Cpopj,,[SIXBIT\504 USER &COMMAND REQUIRED BEFORE %.#!\]
		WNAME	CMDNAM]
>	;end ife ftfree
Ifn $MLogin ! $FtpLog,<	;[96bit] want free logins of some kind?
	TXZE	F,USRFLG	;LEFTOVER USER NAME?
	PUSHJ	P,CNCUSR	;YES, FLUSH IT
Ifn $MLogin,<	;[96bit] any special mail stuff?
	TXNN	P4,CM.LGM	;WANT FREE LOGIN FOR MLFL
	JRST	FRELG1		;NO
Ifn MailPPn,<	;[96bit] need to chgppn?
	MovX	T1,MailPPn		;[96bit] change the current ppn
	CHGPPN	T1,
	   JFCL
>	;end ifn MailPPn
	HRRZI	T1,MailLogin	;[96bit] set up the proper ppn
	TXO	F,TLogin	;[96bit] remember to log this out
	JRST	FRELG2
FRELG1:
>	;end ifn $Mlogin
Ife $FtpLog,<	;[96bit] if not allowing normal FTPs without USER,
		;	 then complain and return
	EDisix [Cpopj,,[SIXBIT\504 USER &COMMAND REQUIRED BEFORE %.#!\]
		WNAME	CMDNAM]
>
Ifn $FtpLog,<	;[96bit] logging in for ftp?
Ifn FtpPPn,<	;[96bit] want a chgppn for ftp?
	MovX	T1,FtpPPn	;[96bit] get the PPn to change to
	CHGPPN	T1,		;YES, DO IT
	  JFCL			;DON'T CARE IF FAILS
>
	HRRZI	T1,FtpLogin	;[96bit] get name of free account
>	;end ifn $ftplog
FRELG2:	Disix	[[SIXBIT\LOGIN %#!\] ;ATTEMPT TO LOGIN
		WSIX	(T1)]
	PUSHJ	P,CHKLGI	;SEE HOW IT DID
	  PJRST	[		; totally invalid
		 TXZ F,TLogin	;[96bit] not logged in
		 PJRST LGIERF
		]
	  EDisix [CNCUSR,,[SIXBIT\504 USER &COMMAND REQUIRED BEFORE %.#!\]
		  WNAME	CMDNAM]
	PUSHJ	P,SJBPPN	;WE DID, GET SUBJOB PPN
	MOVEM	T1,PRJPRG	;STORE IT
	TXO	F,LGIFLG	;REMEMBER LOGIN SUCCESS
	LCHF	P1		;RETAIN FIRST CHAR OF RESPONSE
	MOVSI	T1,'050'	;CODE FOR GENERAL FTP INFO
	PUSHJ	P,CPYRSP	;COPY LOGIN MESSAGES TO USER
	FISEL	IMPCBL		;POINT TO INPUT FILE BLOCK AGAIN
	JRST	CPOPJ1##		;TAKE SUCCESS RETURN
>	;end Ifn $MLogin ! $FtpLog


;ROUTINES TO HANDLE LOGIN FAILURE AND PRINT MESSAGE
;	PUSHJ	P,LGIERR OR LGIERF
;	ALWAYS RETURN HERE, MESSAGE PRINTED, PTY OUTPUT FLUSHED
;  LGIERR USES CODE 431, LGIERF USES 504.
;[CFE]	LGIERF now uses 436 since it's just a temporary error condition!

LGIERR:	MOVSI	T1,'431'	;ERROR CODE FOR NORMAL LOGIN ATTEMPT
	TXZA	F,USRFLG	;CLEAR USER-NAME-GIVEN FLAG
;[CFE] LGIERF:	MOVSI	T1,'504'	;ERROR CODE FOR FREE LOGIN ATTEMPT
LGIERF:	MOVSI	T1,'436'	;[CFE] ERROR CODE FOR FREE LOGIN ATTEMPT
	RCHF	P1		;GET FIRST CHAR AFTER QUESTION MARK
	CAIE	P1,"("		;ERROR NUMBER IN PARENTHESES?
	JRST	.+4		;NO
	RCHF	P1		;YES, FLUSH LEFT PAREN
	RCHF	P1		;FLUSH ERROR CODE
	JRST	.+2		;CAUSE RIGHT PAREN TO BE FLUSHED
	LCHF	P1		;BACKUP IF DIDN'T SEE "("
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER
	PJRST	CNCUSR		;FORCE SUBJOB TO COMMAND LEVEL.
;ROUTINE TO LOG THE SUBJOB OUT.
;	PUSHJ	P,LGOUSR
;	RETURN HERE AFTER SUBJOB LOGGED OUT

LGOUSR:	PUSHJ	P,CNCUSR	;FORCE TO MONITOR LEVEL
	MOVSI	T1,'050'	;TREAT REPLIES AS COMMENTARY
IfDef KjFunc,<	;[96bit] is there a brain damaged logout?
	KjFunc			;[96bit] yes: use it.
>
IfNDef KjFunc,<	;[96bit] no: use k/b
	WSix	[SIXBIT\KJOB /B#!\] ;PRESERVE ANY FILES POSSIBLE
	PJRST	CPYRSP		;COPY RESPONSE TO USER IF HE'S STILL THERE
>

Repeat 0,<	;[96bit] do this with macros now
	HRRZ	T2,BYEDSP(H)	;GET DISPATCH FOR DESIRED LOGOUT PROTOCOL
	JRST	(T2)		;  FOR THIS HOST

KJOB.F:	WSix	[SIXBIT\KJOB /F#!\];CMU- SAVE ALL FILES
	PUSHJ	P,CPYRSP	;COPY THIS
	TXNN	F,ERRFLG	;ERROR (OVER QUOTA)
	POPJ	P,		;NOPE ALL IS GOODNESS
	PUSHJ	P,CNCUSR	;STOP HIM
	WSix	[SIXBIT/CORE 0#!/];FREE ALL HIS CORE
	PJRST	PTYFLS		;AND GO AWAY

KJOB.B:	WSix	[SIXBIT\KJOB /W/B#!\] ;PRESERVE ANY FILES POSSIBLE
	PJRST	CPYRSP		;COPY RESPONSE TO USER IF HE'S STILL THERE
>	;end of repeat 0


;ROUTINE TO SEND CONTROL-C'S TO THE SUBJOB AND FLUSH ALL RESULTING
;   OUTPUT.
;	PUSHJ	P,CNCUSR
;	ALWAYS RETURN HERE

CNCUSR:	FOSEL	PTYOBL		;SELECT INPUT AND OUTPUT PTY
	FISEL	PTYIBL
	W2CHI	3B28+3		;SEND 2 ↑C'S
	WCHI	LF		;MAKE BUFFER BE FORCED OUT
				;FALL INTO PTYFLS


;ROUTINE TO FLUSH ALL PTY OUTPUT UNTIL IT GOES INTO INPUT WAIT.
;	PUSHJ	P,PTYFLS
;	ALWAYS RETURN HERE

PTYFLS:	FISEL	PTYIBL		;SELECT PTY FOR INPUT
PtyFl1:	RCHF	P1		;GET A CHAR
	JUMPN	P1,PtyFl1	;TRY AGAIN IF GOT ANYTHING
	POPJ	P,		;RETURN WHEN NOTHING MORE


;ROUTINE TO FLUSH PTY OUTPUT UNTIL EITHER A LINE FEED IS ENCOUNTERED
;   OR THE SUBJOB GOES INTO TTY INPUT WAIT.
;	PUSHJ	P,PTYF1L
;	ALWAYS RETURN HERE

PTYF1L:	FISEL	PTYIBL		;SELECT PTY FOR INPUT
PtyF11:	RCHF	P1		;GET A CHAR
	CAIE	P1,LF		;LINE FEED?
	JUMPN	P1,PtyF11	;NO, FLUSH IF NOT END OF OUTPUT
	POPJ	P,
;ROUTINE TO WAIT FOR A RESPONSE FROM THE SUBJOB.
;	PUSHJ	P,GETRSP
;	  ERROR--RESPONSE LINE BEGAN WITH "?"
;	OK RETURN, FIRST CHAR OF RESPONSE IN P1
;   GETRSP FLUSHES BLANK LINES WHILE SEARCHING FOR ITS RESPONSE.

GETRSP:	FISEL	PTYIBL		;SELECT PTY INPUT
GETRS1:	RCHF	P1		;GET A CHAR
	JUMPE	P1,CPOPJ1##	;SKIP RETURN IF GOT NONE
	CAIE	P1,CR		;CARRIAGE RETURN?
	CAIN	P1,LF		;LINE FEED?
	JRST	GETRS1		;YES, FLUSH
	CAIE	P1,"?"		;ERROR RESPONSE?
	JRST	CPOPJ1##		;NO, SKIP RETURN
	;[96bit] check for "?%", which we interpret as
	;	 "user not found" type errors: completely fatal.
	TXNN	F,MAILFG!MLFLFG	;inside mail or mlfl?
	POPJ	P,		;no normal error return
	RCHF	P1		;yes, get next char
	CAIN	P1,"%"		;unknown user type error?
	MOVSI	T1,'507'	;yes, special error code
;[CFE, 16 Apr 81] Make sure a legitimate first character gets through.
	CAIE	P1,"%"		;[CFE] Unless a "%",
	  LCHF	P1		;[CFE]  save it for diagnostic msg.
	POPJ	P,


;ROUTINE TO CHECK WHETHER THE SUBJOB HAS BEEN SUCCESSFULLY LOGGED IN
;AFTER THE LOGIN COMMAND WAS SENT TO IT.
;	Disix	[[SIXBIT\LOGIN %#!\]
;		 PUSHJ	P,WHATEVER]
;	PUSHJ	P,CHKLGI
;	  SOMETHING VERY WRONG, LOGIN GAVE ERROR
;	  NEEDS PASSWORD STILL.
;	SUBJOB LOGGED IN; JOBSTS BITS IN T1

CHKLGI:	PUSHJ	P,GETRSP	;GET RESPONSE FROM LOGIN
	  POPJ	P,		;NOT GOOD, LET CALLER HANDLE
	PUSHJ	P,PTYF1L	;IGNORE THIS LINE (JOB #, TTY#, ETC.)
	jumpn	p1,chklgi	; if there are more chars in the buffer,
				; continue to check for errors.

	; now check to see where we stand
CHKLG1:	MOVEI	T1,PTY		;TAKE A LOOK AT PTY STATUS
	JOBSTS	T1,		;TO CHECK LOGGED IN BIT.
	  PUSHJ	P,Idiocy	;DAMN IT, I JUST HAD ONE!
	txne	t1,jb.uoa	; more output available?
	  jrst	ChkLgi		; yes: go back to error checking
;[CFE]	txne	t1,jb.uli	; well, is it logged in?
;[CFE]	  pjrst	cpopj2		; yes: give an excellent return
;[CFE] Wait for logged-in *and* input wait.
	txnn	t1,Jb.ULI	;[CFE] Logged in?
	  jrst	ChkLg2		;[CFE]  No; skip ahead.
	txnn	t1,Jb.UDI	;[CFE] Awaiting input?
	  jrst	ChkLg3		;[CFE]  no; wait for this bit.
CPopj2:	aos	(p)		; Double-skip (excellent) return.
	jrst	CPopj1##
ChkLg2:
;[CFE]	txne	t1,jb.udi	; input wait?
;[CFE]	  pjrst	cpopj1##	; yes: must want a password
;[CFE]	txne	t1,jb.uml	; at monitor level (and NOT logged in!)
;[CFE]	  pushj	p,idiocy	; this situation should be looked at
;[CFE] No, JB.UDI can happen in monitor mode, also.
	txnn	t1,Jb.UDI	;[CFE] Awaiting input?
	  jrst	ChkLg3		;[CFE]  No, wait for another event.
	txne	t1,Jb.UML	;[CFE] Are we in monitor mode?
	  popj	p,		;[CFE] Yes; something went badly wrong.
	jrst	CPopj1##	;[CFE] No; we must await a password.
ChkLg3:	MOVEI	T1,1		;NONE. WAIT AWHILE
	SLEEP	T1,		; FOR LOGIN TO DO ITS THING
	pushj	p,ImpChk	;[CFE] Check this while we wait
	JRST	CHKLG1		;AND LOOK AGAIN


;ROUTINE TO RETURN THE SUBJOB'S PPN
;	PUSHJ 	P,SJBPPN
;	ALWAYS RETURN HERE WITH PPN IN T1

SJBPPN:	MOVEI	T1,PTY		;PTY CHANNEL
	JOBSTS	T1,		;GET CONTROLLED JOB NUMBER
	  PUSHJ	P,Idiocy
	MOVSI	T1,(T1)		;GET PPN FOR THAT JOB
	HRRI	T1,.GTPPN
	GETTAB	T1,
	  PUSHJ	P,Idiocy
	POPJ	P,
;ROUTINE TO COPY A RESPONSE FROM THE PTY TO THE IMP.
;	MOVE	T1,[4-CHARACTER SIXBIT RESPONSE CODE]
;	PUSHJ	P,CPYRSP
;	ALWAYS RETURN HERE

CPYRSP:	FISEL	PTYIBL		;SELECT PTY INPUT
	FOSEL	IMPOBL		;IMP OUTPUT
	TXZ	F,ERRFLG	;CLEAR ERROR FLAG
CPYRS1:	RCHF	P1		;GET A CHAR
	JUMPE	P1,CpyRs4	;RETURN IF NO MORE
	CAIE	P1,CR		;BLANK LINE?
	CAIN	P1,LF
	JRST	CPYRS1		;YES, FLUSH
;[CFE] Flush double-"." after a MAIL command; ignore leading "."s.
	cain	p1,"."		;[CFE] Is it a monitor dot?
	  jrst	CpyRs1		;[CFE]  yes; ignore it.
	CAIN	P1,"?"		;AN ERROR?
	TXO	F,ERRFLG	;YES, REMEMBER IT
	MOVEI	T2,(P1)		;SAVE THE FIRST CHAR
CpyRsX:	RCHF	P1		;GET NEXT CHAR
	JUMPE	P1,CpyRs4	;QUIT IF NONE (CHAR WAS A PROMPT)
	CAIN	P1,4		;CONTROL-D?
	JRST	CpyRsX		;YES (LOGIN HACK ON SOME ERRORS)
	WSIX	4,T1		;OUTPUT MESSAGE CODE
	WCH	T2		;OUTPUT FIRST CHARACTER
	SKIPA			;KEEP RESPNSE CODE FOR ALL LINES
CPYRS2:	RCHF	P1		;GET A CHAR
	JUMPE	P1,CPYRS3	;JUMP IF ENDED IN MIDDLE OF LINE
	WCH	P1		;OUTPUT CHAR TO IMP
	CAIE	P1,LF		;END OF LINE?
	JRST	CPYRS2		;NO, KEEP COPYING
	JRST	CPYRS1		;YES, START NEW LINE

;HERE IF ENDED IN MIDDLE OF LINE (SHOULDNT)
CPYRS3:	W2CHI	CRLF		;CAUSE LINE TO GO OUT TO IMP ANYWAY
CpyRs4:	FoSel	PtyObl		; return to pty output.
	POPJ	P,


;ROUTINE TO COPY A LINE OF TEXT FROM THE IMP TO THE PTY.
;   THE CRLF AT THE END IS NOT INCLUDED
;	PUSHJ	P,IMPPTY
;	ALWAYS RETURN HERE

IMPPTY:	FISEL	IMPCBL		;SELECT COMMAND BUFFER INPUT
	FOSEL	PTYOBL		;SELECT PTY OUTPUT
IMPPT1:	RCHF	P1		;GET A CHAR
	CAIE	P1,CR		;RETURN OR LINEFEED?
	CAIN	P1,LF
	POPJ	P,		;YES, DONE
	WCH	P1		;NO, SEND TO PTY
	JRST	IMPPT1		;BACK FOR MORE
;ROUTINE TO INPUT A DECIMAL NUMBER FROM THE CURRENT INPUT DEVICE
;   AND RETURN IT IN T1.
;	PUSHJ	P,GETDEC
;	  ERROR--FIRST CHAR NOT A DIGIT
;	OK--NUMBER IN T1

GETDEC:	PUSHJ	P,SPNOR1	;GET FIRST CHAR AND IGNORE SPACES
	TXNN	P2,DIGIT	;IS FIRST CHAR A DIGIT?
	POPJ	P,		;NO--ERROR
	SETZ	T1,		;YES, INITIALIZE NUMBER
GETDE1:	IMULI	T1,↑D10		;ACCUMULATE DIGIT
	ADDI	T1,-"0"(P1)
	RCHF	P1		;GET NEXT
	TRNE	P2,DIGIT	;ALSO A DIGIT?
	JRST	GETDE1		;YES, USE IT
	PUSHJ	P,SPNOR		;NO, IGNORE TRAILING BLANKS
	JRST	CPOPJ1##		;SKIP RETURN


;ROUTINE TO IGNORE BLANKS
;	PUSHJ	P,SPNOR		;USES CURRENT P1
;	PUSHJ	P,SPNOR1	;FETCHES  NEW CHAR BEFORE TESTING

SPNOR1:	RCHF	P1		;FETCH A CHARACTER
SPNOR:	CAIE	P1," "		;BLANK?
	CAIN	P1,CR		;CARRIAGE RETURN (WHICH WE IGNORE)
	JRST	SPNOR1		;YES, FLUSH IT
	POPJ	P,		;NO, RETURN


;ROUTINE TO CHECK FOR PTY OUTPUT
;	PUSHJ	P,PTYCHK
;	  NO OUTPUT AVAILABLE
;	OUTPUT IS AVAILABLE
;   T1 CONTAINS JOBSTS BITS ON EITHER RETURN AND IS THE ONLY AC CLOBBERED

PTYCHK:	MOVE	T1,PTSPNT	;ALSO, SEE IF ANYTHING BUFFERED (NORMALLY WON'T BE)
	CAME	T1,PTRPNT	;MEANING RETRIEVE AND STORE POINTERS ARE DIFFERENT
	JRST	CPOPJ1##		;YES, SKIP RETURN

;ROUTINE TO SEE IF PTY BUFFERS HAVE DATA TO READ IN
PTBCHK:	MOVEI	T1,PTY		;SET PTY CHANNEL
	JOBSTS	T1,		;CHECK STATE OF SUBJOB
	  PUSHJ	P,Idiocy		;HMMM...
	TXNE	T1,JB.UOA	;SUBJOB OUTPUT AVAILABLE?
	AOS	(P)		;THEY ARE...DATA
	POPJ	P,		;NOPE, PTY QUIET
;ROUTINE TO BUFFER PTY OUTPUT SO WE CAN SEND IT SOME DATA

PTYSAV:	PUSH	P,U2		;SAVE CURRENT IO BLOCK
	MOVEI	U2,PTYIBL	;AND POINT TO PTY
PTYS1:	PUSHJ	P,PTYBUF	;GET A CHARACTER FROM PTY
	JUMPE	U1,PTYS2	;0 SAYS END
	SOSLE	PTSCNT		;ROOM TO SAVE THIS ONE?
	IDPB	U1,PTSPNT	;YEP, HE LUCKS OUT
	JRST	PTYS1		;AND TRY FOR ANOTHER, OVERFLOW WILL BE LOST

PTYS2:	POP	P,U2		;RESTORE
	POPJ	P,		;AND RETURN


;ROUTINE TO DO THE RCH OPERATION FOR THE PTY.

PTYRCH:	MOVE	U3,PTRPNT	;PICKUP PTY RETRIEVAL POINTER
	CAMN	U3,PTSPNT	;IS IT THE SAME AS THE STUFF POINTER?
	JRST	PTYBUF		;YES, THEREFORE NO DATA SAVED TO READ, GET FROM BUFFER
	ILDB	U1,U3		;GET NEXT CHAR TO PROCESS
	CAME	U3,PTSPNT	;NOW ARE WE EQUAL?
	JRST	[MOVEM	U3,PTRPNT;NO, SAVE POINTER FOR NEXT TIME
		 POPJ	P,]
	MOVE	U3,[PTYRSH,,PTYRSL];SAME, REINITIALIZE AREA
	BLT	U3,PTYRSE-1	;FOR THE NEXT DATA WE HAVE TO BUFFER
	POPJ	P,		;MEANWHILE, LET THE LAST SAVED CHAR BE PROCESSED

PTYBUF:;ROUTINE TO READ NEXT CHARACTER FROM PTY BUFFERS

	SKIPLE	FILCTR(U2)	;IS THERE ANY BUFFERED DATA?
	JRST	PTYRC1		;YES, GET IT NOW
	MOVE	U1,T1		;NO, SAVE T1
	PUSHJ	P,PTBCHK	;SEE IF PTY HAS ANY MORE OUTPUT DATA
	  JRST	PTYRC2		;IT DOESN'T
	MOVE	T1,U1		;IT DOES.  RESTORE T1 AND PROCESS IT

;HERE WHEN DATA IS AVAILABLE
PTYRC1:	PUSHJ	P,I1BYTE##	;CALL STANDARD BYTE ROUTINE
	JUMPE	U1,PTYBUF	;FLUSH NULLS
	PJRST	RECPUT		;PRINT AND/OR RECORD THE CHAR

;HERE WHEN NO DATA IS AVAILABLE
PTYRC2:	EXCH	U1,T1		;RESTORE T1, PUT JOBSTS BITS IN U1
	TXNE	U1,JB.UDI	;SUBJOB WAITING FOR INPUT?
	TDZA	U1,U1		;YES
	MOVEI	U1,1		;NO, SET SLEEP TIME
	JUMPE	U1,CPOPJ##	;RETURN WITH NULL IF NO MORE OUTPUT
	SLEEP	U1,		;SLEEP ONE SECOND
	PUSHJ	P,IMPCHK	;MAKE SURE TELNET CONNECTION STILL OPEN
	JRST	PTYBUF		;TRY AGAIN
;ROUTINE TO DO WCH OPERATION FOR IMP AND PTY, WHICH WANT TO BREAK
;   ON END-OF-LINE.

IMPWCH:	TXNN	F,OPNFLG	;TELNET CONNECTION OPEN?
	POPJ	P,		;NO, FLUSH IMP OUTPUT
PTYWCH:	PUSHJ	P,O1BYTE##	;CALL STANDARD BYTE OUTPUT ROUTINE
	CAIN	U2,PTYOBL	;PTY OUTPUT?
	PUSHJ	P,RECPUT	;YES, PRINT AND/OR RECORD THE CHAR
	MOVEI	U3,(U1)	;COPY CHARACTER JUST OUTPUT
	ANDI	U3,177		;7 BITS ONLY
	CAIE	U3,LF		;REACHED END OF LINE?
	POPJ	P,		;NO
	CAIE	U2,PTYOBL	;GOING OUT TO PTY?
	JRST	PTYW2		;NO, CAN DO OUTPUT
PTYW1:	MOVEI	U3,PTY		;LET'S SEE IF PTY WANTS DATA
	JOBSTS	U3,
	  JRST	PTYW2		;FAILED? SHOULDN'T HAVE
	TXNE	U3,JB.UOA	;ANY OUTPUT FROM PTY THAT WE MUST STORE FIRST?
	JRST	[PUSHJ P,PTYSAV	;YES, GO BUFFER EVERYTHING IN SIGHT
		 JRST	PTYW1]	;AND SEE IF WE CAN OUTPUT NOW
	TXNE	U3,JB.UDI	;OKAY TO OUTPUT TO?
	JRST	PTYW2		;YES, DO SO
	MOVX	U3,HB.RWJ!HB.RPT!↑D1000;WAIT FOR PTY ACTIVITY
	HIBER	U3,		;DO SO
	  JRST	[MOVEI	U3,1	;FAILED (10/40) SLEEP A SECOND
		 SLEEP	U3,
		 pushj	p,ImpChk ;[CFE] Check IMP connection
		 JRST	PTYW1]	;AND TRY AGAIN
	pushj	p,ImpChk	;[CFE] Ensure connection still there
	JRST	PTYW1		;TRY AGAIN FROM HIBERNATE

PTYW2:	PUSHJ	P,UXCT2##	;YES, CAUSE OUTPUT TO BE SENT
	  OUT
	  POPJ	P,		;OK
	MOVE	U1,FILER2(U2)	;ERROR, TAKE ERROR DISPATCH
	PJRST	UERXIT##


;ROUTINE TO MONITOR AND/OR RECORD CHARACTER IN U1 FOR LATER PLAYBACK.
;	MOVE	U1,[ASCII CHARACTER]
;	PUSHJ	P,RECPUT
;	ALWAYS RETURN HERE, ALL AC'S PRESERVED

RECPUT:	TXNE	F,SLGFLG	;MONITORING?
	OUTCHR	U1		;YES, PRINT THE CHARACTER
	EXCH	U2,RECPTR	;GET CURRENT RECORDING POINTER
	CAME	U2,RECPTZ	;AT END OF BUFFER?
	JRST	.+3		;NO
	TXO	F,WRPFLG	;YES, REMEMBER WE WRAPPED AROUND
	MOVE	U2,RECPT0	;RESET POINTER TO START
	IDPB	U1,U2		;STORE CHAR IN BUFFER
	EXCH	U2,RECPTR	;RESTORE U2 AND STORE NEW POINTER
	POPJ	P,		;RETURN

RECPTZ:	POINT	7,RECBUF+RECSIZ-1,34	;POINTER TO LAST CHAR OF BUFFER
;ROUTINE TO DO THE RCH OPERATION FROM THE IN-CORE IMP BUFFER.

RCHICB:
;[CFE] Provide overflow-safe character processing: obey a count of
;[CFE]  the number of characters saved in the buffer.  Return LFs
;[CFE]  when we're at end of buffer.
	sosge	CmdCnt		;[CFE] Decr and test count
	  jrst	[movei	u1,12	;[CFE] Out of chars!  Return a LF.
		 popj	p,]	;[CFE]
	ILDB	U1,CMDPTR	;GET A CHAR
	CAIL	U1,"A"+40	;LOWER CASE?
	CAILE	U1,"Z"+40
	POPJ	P,		;NO
	TXNN	F,MAILFG	;AND NOT MAIL?
	SUBI	U1,40		;YES, MAKE UPPER
	POPJ	P,


Repeat 0,<	; remove these, and their UUOs (SixImp, SixPty,
		; DSxPty, DSxImp), and replace them with error
		; channel for imp output, normal output for pty output

;VARIOUS SPECIAL UUO HANDLERS

UDSXPT::MOVEI	U2,PTYOBL	;DISIX OPERATION TO PTY
	JRST	.+2

UDSXIM::MOVEI	U2,IMPOBL	;DISIX OPERATION TO IMP
	MOVEM	U2,OFILE##	;STORE CORRECT POINTER TO FILE BLOCK
	PJRST	UDISIX##

USIXPT::MOVEI	U2,PTYOBL	;WSIX OPERATION TO PTY
	JRST	.+2

USIXIM::MOVEI	U2,IMPOBL	;WSIX OPERATION TO IMP
	MOVEM	U2,OFILE##	;STORE CORRECT FILE BLOCK POINTER
	SETZ	U3,		;ONLY INDEFINITE WSIX ALLOWED!
	PJRST	UWSIX##		;DO OPERATION

>;	end of Repeat 0


;ROUTINE TO HANDLE IMPOSSIBLE ERRORS

Idiocy:	SOS	T1,(P)		;GET ERROR ADDRESS
	EDisix	[C.BYE,,[SIXBIT\435 A&N IMPOSSIBLE ERROR HAS OCCURRED AT LOCATION %#!\]
		WOCTI	(T1)]
;ROUTINE TO MAKE SURE THE TELNET CONNECTION IS STILL OPEN.
;	PUSHJ	P,IMPCHK
;	RETURN HERE IF STILL OPEN
;   INITIATES "BYE" COMMAND IF CONNECTION HAS CLOSED
;   NO AC'S CLOBBERED

IMPCHK:	TXNN	F,OPNFLG	;DO WE THINK IT'S OPEN NOW?
	POPJ	P,		;NO, JUST FLUSHING JOB OR SOMETHING
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,CONBLK	;DO STATUS OPERATION
	IMPUUO	P1,
	  JRST	ImpEro		;CONNECTION MUST HAVE GONE AWAY
	LDB	P1,[POINT 6,.IBSTT+CONBLK,35] ;GET STATE
	CAIg	P1,.ISEst	; established or working on it?
	  POPJ	P,		;YES, RETURN
;HERE ON IMP ERROR (PROBABLY CONNECTION CLOSED)
ImpEro:	TXZ	F,OPNFLG	;CLEAR IMP OPEN FLAG
	JRST	C.BYE		;FORCE A BYE COMMAND


;HERE ON ERROR FROM THE PTY. TELL USER WHAT HAPPENED, THEN CLOSE
PTYERR:	pushj	p,ImpChk	;[CFE] Check IMP before write, also.
	EDisix	[C.BYE,,[SIXBIT\435 %#!\]
		 ERROUT	PTYOBL]	;REPORT PTY ERROR AND BREAK CONNECTION


;ROUTINE TO PRINT C(PRJPRG) AS REGULAR PPN OR CMUPPN
;	PUSHJ	P,PPNPRT
;	ALWAYS RETURN HERE

PPNPRT:	WPPN	PRJPRG		;PRINT PPN THE REGULAR WAY
	POPJ	P,


;ROUTINE TO PRINT THE NAME OR NUMBER OF THE FOREIGN HOST
; uses currently selected output, which will be the IMP if called
; from "inside" a EDisix.
;	PUSHJ	P,HSTPRT
;	ALWAYS RETURN HERE

HSTPRT:
;[96bit]SKIPE	SXBHST		;DO WE KNOW WHO HE IS?
;[96bit]DISIX	[CPOPJ##,,[SIXBIT\%-%!\]
;[96bit]	WNAME	SXBHST
;[96bit]	WNAME	SXBHST+1]
;[96bit]WDEC	HSTADR		;NO, JUST PRINT IN DECIMAL
	Skipg	HsName		;[96bit] know the name?
	  Jrst	HstNoo		;[96bit] no: print the number
	WASC	@HsName		;[96bit] print the name
	Popj	p,		;[96bit] and return

HstNoo:	;[96bit] subroutine to print host number in new format
	pushj	p,save3##		;[tcp] get P1-P3
	move	p2,HstAdr		;[tcp] get host address
	lsh	p2,4			;[tcp] left justify it
	movei	p3,4			;[tcp] set counter
HstLoo:	setz	p1,			;[tcp] clear target reg
	lshc	p1,↑d8			;[tcp] shift next 8 bits up
	wdec	p1			;[tcp] and print it
	sojle	p3,cpopj##		;[tcp] count and return if done
	wchi	"."			;[tcp] print separator
	jrst	HstLoo			;[tcp] and loop


;[96bit] subroutine to set a new host address.  checks HstTmp:
;	 if non-zero, moves value into HstAdr, and looks up the
;	 name and puts it in HsName.  if can't find name, HsName
;	 gets -1.
;NOTE: this routine CANNOT be called from withing a LUUO, like
;      in the instruction list for a EDisix, for example.
SetNam:	Push	P,T1		;[96bit] save a reg
	Skipn	T1,HstTmp	;[96bit] new address?
	  Jrst	Tpopj		;[96bit] no: just return
	Movem	T1,HstAdr	;[96bit] save new address
	Clearm	HstTmp		;[96bit] if it's new, forget newness.
	Setom	HsName		;[96bit] assume we're going to fail
	Push	P,T2		;[96bit] save reg from nasty HstNum
	PUSHJ	P,HstNum##	;FIND OUT WHAT IT'S NAME IS
	  Jfcl			; couldn't get tables.
	  Jrst	T2Popj		; couldn't find entry.  flag is set
	hrrzm	T1,HsName	; remember
T2Popj:	Pop	P,T2		;[96bit] restore T2
TPopj:	Pop	P,T1		;[96bit] restore T1
	Popj	P,		;[96bit] return
	SUBTTL	INITIAL FILE BLOCKS

	XALL

;ICP OUTPUT
ICPBLH:	FILE	IMP,O,ICPBLK,<DEV(ICP),STAT(6)>

;IMP INPUT OVER TELNET CONNECTION
IMPIBH:	FILE	IMP,I,IMPIBL,<DEV(TTY),STAT(.IOASC),OPEN(BYEFR2)
		,INPUT(ImpEro),EOF(ImpEro),OTHER(IMPOBL)>

;IMP OUTPUT OVER TELNET CONNECTION
IMPOBH:	FILE	IMP,O,IMPOBL,<DEV(FTPSRV),STAT(.IOASC),OPEN(BYEFR3)
		,OUTPUT(ImpEro),OTHER(IMPIBL),<INST(<PUSHJ P,IMPWCH>)>>
;PTY INPUT (SUBJOB'S OUTPUT)
PTYIBH:	FILE	PTY,I,PTYIBL,<DEV(PTY),STAT(.IOASC),OPEN(PTYTRY)
		,INPUT(PTYERR),EOF(PTYERR),OTHER(PTYOBL)
		,<INST(<PUSHJ P,PTYRCH>)>>

;PTY OUTPUT (SUBJOB'S INPUT)
PTYOBH:	FILE	PTY,O,PTYOBL,<DEV(PTY),STAT(.IOASC),OPEN(PTYTRY)
		,OUTPUT(PTYERR),OTHER(PTYIBL),<INST(<PUSHJ P,PTYWCH>)>>

;INPUT FROM IMP COMMAND BUFFER
IMPCBH:	PFILE	IMPCBL,<PUSHJ P,RCHICB>
	SUBTTL	LOW-SEGMENT INITIALIZATION DATA

FILLH:

; CONBLK (TELNET CONNECTION BLOCK)
	SIXBIT	\FTPSRV\
	0
	EXP	TLNSKT
	0			;[96bit]
	0

;DEFAULT FTP TRANSFER PARAMETERS
	EXP	↑D8		;BYTE SIZE
	"A" ,,	0		;TRANSFER TYPE (ASCII)
	"F" ,,	0		;STRUCTURE (FILE)
	"S" ,,	0		;MODE (STREAM)

;MISCELLANEOUS
RECPT0:	POINT	7,RECBUF	;POINTER TO FIRST CHAR -1 OF PTY DIALOGUE
				;  RECORDING BUFFER
PTYRSH:				;ADDRESS OF DATA TO REINIT PTY SAVE AREA
	POINT	7,PTYHID	;FIRST-1 CHAR OF BUFFER
	POINT	7,PTYHID
	RECSIZ*5		;# OF BYTES WE CAN STORE
	SUBTTL	OTHER TABLES AND STUFF

;SIGNON STRING

DEFINE XX(V,U,E,W) <
IFE W,<
SRVMSG:	SIXBIT	\%% FTP S&ERVER& V'U(E)#!\
>
IFN W,<
SRVMSG:	SIXBIT	\%% FTP S&ERVER& V'U(E)-W#!\
>>
	VERSTR

;DISPATCH TABLES FOR HOST-DEPENDENT HANDLING

MailCm:	MailCommand		;[96bit] monitor command for mailing

repeat 0,<	;[96bit] forget the tables
HSTTAB:				;HOST NUMBER IN LH, FREE ACCOUNT STRING IN RH

FREACT:	↑D9	,, [SIXBIT\62,"#!\]
	↑D14	,, [SIXBIT\N900AR00!\]
	↑D78	,, [SIXBIT\N900AR00!\]
	↑D142	,, [SIXBIT\N900AR00!\]

	NHOSTS==.-HSTTAB	;NUMBER OF HOSTS IN TABLE

PPNCHG:	0			;PPN TO CHANGE TO WHEN DOING FREE LOGIN
	33125	,, 13750	; N900AR00 (CMUPPN)
	33125	,, 13750
	33125	,, 13750

BYEDSP:	0	,, KJOB.B	;RH IS DISPATCH FOR BYE HANDLING
	0	,, KJOB.F
	0	,, KJOB.F
	0	,, KJOB.F
>
	SUBTTL	LOW SEGMENT
	RELOC	0

ZEROL:		;BEGINNING OF AREA TO ZERO DURING INITIALIZATION

PDL:	BLOCK	PDLSIZ		;STACK
PRJPRG:	BLOCK	1		;PPN OF SUBJOB WHILE LOGGED IN
HSTADR:	BLOCK	1		;HOST TO USE IN DATA TRANSFERS
HstTmp:	Block	1		; place to put a potential new host adr.
HsName:	block	1		; pointer to asciz string of host name
RmtSkt:	BLOCK	1		;REMOTE SOCKET FOR data OPERATIONs
LclSkt:	block	1		; our socket number for data connections
SYSNAM:	BLOCK	5		;LOCAL MONITOR NAME GETS PUT HERE
CMDBUF:	BLOCK	CMDLEN/5+1	;INPUT FTP COMMAND BUFFER
CMDPTR:	BLOCK	1		;POINTER INTO CMDBUF
CmdCnt:	block	1		;[CFE] Count of chars in CmdBuf
RNFBUF:	BLOCK	CMDLEN/5+1	;AREA TO SAVE "RNFR" PATHNAME UNTIL "RNTO"
RNFPTR:	BLOCK	1		;POINTER INTO RNFBUF
RnFCnt:	block	1		;[CFE] Count of chars in RnFBuf
CMDNAM:	BLOCK	1		;NAME OF FTP COMMAND BEING EXECUTED
WATCNT:	BLOCK	1		; # SECONDS WAITED FOR USER TO DO SOMETHING
LHOSTP:	BLOCK	.IBSIZ		;LOCAL HOST PARAMETERS
RECBUF:	BLOCK	RECSIZ		;REGION FOR RECORDING PTY DIALOGUE
PTYHID:	BLOCK	RECSIZ		;REGION FOR SAVING PTY OUTPUT

ICPBLK:				;FILE BLOCK FOR DOING ICP
IMPIBL:	BLOCK	FBSIZE		;IMP TELNET INPUT BLOCK
IMPOBL:	BLOCK	FBSIZE		;IMP TELNET OUTPUT BLOCK
PTYIBL:	BLOCK	FBSIZE		;PTY INPUT (SUBJOB OUTPUT) BLOCK
PTYOBL:	BLOCK	FBSIZE		;PTY OUTPUT (SUBJOB INPUT) BLOCK
IMPCBL:	BLOCK	PBSIZE		;FTP COMMAND PSEUDO-FILE BLOCK

ZEREND:		;END OF AREA TO ZERO DURING INITIALIZATION
FILLL:		;BEGINNING OF AREA TO FILL WITH NONZERO DATA

CONBLK:	BLOCK	.IBSIZ		;TELNET CONNECTION BLOCK

BYTSIZ:	BLOCK	1		;DATA CONNECTION BYTE SIZE
XFRTYP:	BLOCK	1		;DATA TRANSFER TYPE
STRTYP:	BLOCK	1		;DATA TRANSFER STRUCTURE
MODTYP:	BLOCK	1		;DATA TRANSFER MODE

RECPTR:	BLOCK	1		;BYTE POINTER FOR RECORDING PTY DIALOGUE
PTYRSL:				;ADDR TO BLT TO TO REINIT PTY SAVE REGION
PTSPNT:	BLOCK	1		;POINTER FOR STUFFING CHARACTERS
PTRPNT:	BLOCK	1		;POINTER FOR PICKING UP CHARACTERS
PTSCNT:	BLOCK	1		;# OF CHARS LEFT TO FILL IN BUFFER
PTYRSE:				;ADDR+1 TO FINISH REINIT

FLLEND:		;END OF AREA TO SETUP DURING INITIALIZATION

	RELOC
	END	FTPSRV