;-*-  Mode: MIDAS -*-
TITLE	FTPU - NEW FTP USER FOR ITS

.SYMTAB 5001.,7000.

SUBTTL	Basic Definitions

;Accumulators

F=0		; Flags
A=1		;Standard ACs
B=2
C=3
D=4
E=5
T=6
TT=7
R=10
; =11		;Not used
OC=12		; Output package
U1=13		;UUO accs
U2=14
U3=15
U4=16
P=17		;Stack pointer.

;Channels

ICPCH==0	;ICPCH must be 0 for socket table hackery to win!!
TMPC==1		;temp
NETI==2		;Net input ch (telnet connection)
NETO==3		;Net output ch (telnet connection)
NETDI==4	;Net data input channel
NETDO==5	;Net data output channel
STRC==6		;UUO string output channel
DC==7		;general purpose dsk channel (in and out)
TYIC==10	;TTY input
TYOC==11	;TTY output
ERRC==12	;ERR device
SCRIPC==13	;Script file out
COMC==14	;Command file in

; Flags
%LTCP==1	; LH 1 = Using TCP
%TMP==1		; RH temporary flag

TIMOUT=30.*120.	;Timeout value is two minutes.

;Subroutine Packages.
DEFINE %%.CRLF C,ARG
IFSN ARG,, .ERR non-null argument "ARG" after CRLF in FWRITE
	CRLF C,
TERMIN

$$OUT==1
$$OFLT==1
UAREAS==1	; Assemble UUO areas
USTRGS==1	; and string hackery.
UFLOAT==1	; and floating point typeout
.INSRT KSC;NUUOS >
		;and wonderful filename parser
.INSRT KSC;NFNPAR >

$$HST3==1
$$SYMLOOK==1	;and miraculous network routines
$$HSTMAP==1	;and routines for the host names table file
$$HOSTNM==1
$$OWNHST==1
$$ICP==1	;and network connection routines
$$CONNECT==1	;and tcp net connection routines
$$ANALYZE==1	;include network error analysis routine
$$ARPA==1	;handle Arpanet (only, for now)
$$TCP==1	;handle Internet host parsing.
USETCP:	-1		;For the hostname parser (default)
USENCP:	0		;For the hostname parser.
.INSRT SYSENG;NETWRK >

FTPSKT==3	;standard ICP socket #
FTPORT==25	;TCP FTP port #


SUBTTL Random locations

TMPLOC 41, <JSR 1,UUOH>	;"1," means illegal instructions cause fatal interrupts.
TMPLOC 42, JSR TSINT

PAT:
PATCH:	BLOCK 100
PDLLEN==100
PDL:	-PDLLEN,,.
	BLOCK PDLLEN

POPTJ:	POP P,T
	POPJ P,

POPBA1:	AOS -2(P)
POPBAJ:	POP P,B
	POP P,A
	POPJ P,
POPJ1:	AOSA (P)
POP1J:	 POP P,JUNK
CPOPJ:	POPJ P,

POPAJ1:	AOS -1(P)
POPAJ:	POP P,A
APOPJ:	POPJ P,

POPCBJ:	POP P,C
POPBJ:	POP P,B
	POPJ P,


JUNK:	0	;for random useless writes
VERSHN:	.FNAM2
	VERSION==.FNAM2

SCRIPT:	0	;nonzero if writing script file.
READIN:	0	;nonzero while rubout processing - inhibits script file output.
COMFIL:	0	;nonzero if reading command file.
TYITTY:	0	;nonzero if TTY can be read from.
TYOTTY:	0	;nonzero if TTY can be written on.
TRITTY:	0	;nonzero if TTY input was translated (not really device TTY)
TROTTY:	0	;similar, for TTY output.
DISTTY:	0	;nonzero if TTY is display (can rub out)
HDXTTY:	0	;nonzero if half duplex TTY and shouldn't echo.
SILENT:	0	;nonzero if shouldn't type on TTY (cleared if try to read tty).
INHIDE:	0	;nonzero if TYILIN shouldn't echo input. (eg for pwd)
DEBUG:	0	;nonzero inhibits death, disowning, etc.
PRREP:	0	; -1 if printing server replies.

IFNDEF BUFFL,BUFFL==4000
BUFFER:	BLOCK BUFFL	;Buffer for the actual file transfer.
XFRBFL==:BUFFL
XFR8BL==<<<XFRBFL*9>+7>/8.>	; # words in 8-bit byte buffer
TMPBUF:	BLOCK XFR8BL		; Buffer for packing 8-bit bytes (TCP)

XFRLBV:	0	;length of buffer except for one word, in characters, 
		; (used by XFRASC.)
XFRLWP:	0	;Address of last word in used part of buffer
XFRDIR:	0	;-1 if writing to net
XFRBPW:	0	;Bytes per word
DCTYPE:	0	;Transfer Type.  0=ASCII, -1=Image, 1=Local.
		; These are what we think server believes,
DCBYTE:	0	;Byte size. One of 8., 32., 36.
		; not what user has said he prefers. If user wants to
		; change it, we ask the server before setting these.
DCSENT:	0	;-1 => The server knows DCTYPE and DCBYTE, either because they
		;      are the default (TYPE A, BYTE 8) or because we sent them
		;      and they were accepted.
		;0 => The server doesn't know them, they must be TYPE I,BYTE 36
		;     so try sending those before next data transfer operation.
		;	For TCP the desired settings are TYPE L 36.
DKIOSW:	0	;0 Disk input, -1 Disk output
NTIOSW:	0	;0 Net input, -1 Net output
ICPSOC:	0	;frn skt to ICP to
LPORT:	0	;lcl port we listen for data connection on
LDSOC:	0	;lcl skt for data skt open
FDSOC:	0	;frn skt for data skt open
FDHST:	0	;frn hst for data skt open
OWNHST:	0	; # of own site.
MACHNM:	0	; Machine name (AI,MC,ML,DM)
ITSVER:	0	; ITS version # in sixbit
OWNNAM:	0	; Addr of ASCIZ string for own site-name.

CNECTD:	0	;nonzero => connected to a foreign host

NBITS:	0	;# bits transferred
NTIME:	0	;starting time in 30ths

FILDEV:	SIXBIT /DSK/
FILDIR:	0
FILFN1:	0
FILFN2:	SIXBIT />/

SCRIPF:	SIXBIT /DSK/
SCRIPS:	0
SCRIP1:	SIXBIT /FTPOUT/
SCRIP2:	SIXBIT />/

COMDEV:	SIXBIT /DSK/
COMDIR:	0
COMFN1:	SIXBIT /FTPCOM/
COMFN2:	SIXBIT />/

FTPOF1:	SIXBIT /_FTPU_/
FTPOF2:	SIXBIT /OUTPUT/

THOSTB:	BLOCK 10	;Block for THOSTN to store its host name in.

IFNDEF JCLBFL,JCLBFL==50
JCLFLG:	0		;-1 => executing command that came from DDT
SUICID:	0		;-1 => commit suicide after this command if it wins.
JCLTRN:	0		;-1 => this is implicit TRAN from JCL.
			;      (Suppresses "N bits in M seconds".)
JCLBF1:	ASCII/TRAN /
JCLBUF:	BLOCK JCLBFL	;Command Line (read from DDT or from TTY) as ASCIZ string.
	BLOCK 400
JCLBFE:	,,-1		;Nonzero to stop DDT's xfer of JCL.  Top byte 0 to end JCL.
JCLBFP:	440700,,JCLBUF	;Addr of block to store JCL in.  These 2 words bound
JCLP:	440700,,JCLBUF	;Pointer to read JCL out of.	 if must "push JCLBUF".
JCLBF2:	BLOCK JCLBFL	;Alternate block to read tty input into.
ARGBUF:	BLOCK JCLBFL	;ARG reads stuff from JCLBUF, making ASCIZ sting in ARGBUF.
ARGCT:	0		;These two words are a string variable containing ARGBUF.
ARGPT:	440700,,ARGBUF
ALTARG:	BLOCK JCLBFL	;Used to save a way one arg while reading another.

NCPJNM:	SIXBIT /NCPFTP/	;Name of job to do old-style (NCP based) FTPing.


SUBTTL Initialization and Main Loop

GO:	MOVE P,PDL		;Set up PDL and TTY
	SETOM TYITTY
	SETOM TYOTTY
	SETZM TRITTY
	SETZM TROTTY
	SETZM DISTTY
	SETZM HDXTTY
	SYSCAL OPEN,[%CLBIT,,.UAI ? %CLIMM,,TYIC ? [SIXBIT /TTY/]]
	 SETZM TYITTY		;Sometimes we dont have a TTY to read from.
	SYSCAL RFNAME,[%CLIMM,,TYIC ? %CLOUT,,B]
	 .LOSE %LSFIL
	CAME B,['TTY,,]		;If the device name is not "TTY"
	 SETOM TRITTY		; there must be an input translation.
	SYSCAL TTYGET,[%CLIMM,,TYIC ? %CLOUT,,B ? %CLOUT,,C]
	 JRST GO1
	ANDCM B,[606060606060]	;If TYIC really has a TTY, turn off echo.
	ANDCM C,[606060606060]
	TLO C,010000		;Enable ^G/^S interrupts
	SYSCAL TTYSET,[%CLIMM,,TYIC ? B ? C]
	 .LOSE 1400
GO1:	SYSCAL OPEN,[%CLBIT,,<.UAO\%TJDIS> ? %CLIMM,,TYOC ? [SIXBIT /TTY/]]
	 SETZM TYOTTY		;No typeout is maybe OK, if have script XFILE.
	SYSCAL RFNAME,[%CLIMM,,TYOC ? %CLOUT,,B]
	 .LOSE %LSFIL
	CAME B,['TTY,,]		;If the device name is not "TTY"
	 SETOM TROTTY		; there must be an output translation.
	SETZM DISTTY
	SYSCAL CNSGET,[%CLIMM,,TYOC ? REPEAT 4,[%CLOUT,,JUNK ?] %CLOUT,,A]
	 JRST GO2
	TLNE A,%TOOVR		;DISTTY on also for glass TTY's
	 TLNE A,%TOERS		;DISTTY gets -1 if we are on a display TTY.
	  SETOM DISTTY
	TLNE A,%TOHDX		;HDXTTY gets -1 if half duplex (shouldn't echo).
	 SETOM HDXTTY
GO2:	.SUSET [.RXJNAM,,B]	;Look at our job name.
	TLO F,%LTCP		;We usually do TCP based FTPing.
	CAMN B,NCPJNM		;But if our jname is the magic one
	 JRST [	TLZ F,%LTCP	; we wont use the Internet FTP.
		SETZM USETCP
		SETZM USENCP
		JRST .+1]
	MOVEI A,FTPORT	    	;TCP means we use this port.
	TLNN F,%LTCP
	 MOVEI A,FTPSKT		;NCP means this is the ICP socket
	MOVEM A,ICPSOC	    	;Remember which port or socket to use.

	MOVEI A,LSTPAG
	MOVEI B,0
	PUSHJ P,NETWRK"HSTMAP	;Read in HOSTS3 at LSTPAG
	 .LOSE

	MOVE B,A	    ;Then make ARPAGS point at all pages left above it.
	SUBI B,400	    ;HSTMAP leaves A pointing to first unused page.
	HRL A,B
	MOVEM A,ARPAGS
	UARINIT ARPAGS	    	;initialize core
	STRINIT			;initialize strings
	OUTOPN TYOC,[$UCXCT,,[PUSHJ P,UUOTYO]]	
	OUTOPN SCRIPC,		;allow uuos to type out

	MOVE A,[NETWRK"NW%ARP]	;Get own host number, on Arpanet
	PUSHJ P,NETWRK"OWNHST
	 .LOSE			;Not connected to Arpanet?
	MOVEM A,OWNHST
	.SUSET [.RSNAME,,FILDIR]
	MOVE A,FILDIR
	MOVEM A,COMDIR
	MOVEM A,SCRIPS

	.SUSET [.ROPTION,,TT]	;Get JCL if present
	TLZN TT,OPTCMD
	 JRST NOJCL
	SETZM JCLBUF
	MOVE A,[JCLBUF,,JCLBUF+1]
	BLT A,JCLBFE
	.BREAK 12,[5,,JCLBUF]
	MOVE A,JCLP		;If JCL contains an underscore, an implied TRAN
	SETOM JCLFLG
TRNCH1:	ILDB B,A
	CAIE B,^C
	 CAIN B,^M
	  JRST TRNCH2
	JUMPE B,TRNCH2
	CAIE B,"_
	 CAIN B,"
	  JRST TRNCH3
	CAIE B,"=
	 JRST TRNCH1
TRNCH3:	SOS JCLP		;insert TRAN<sp> in front of JCL
	SETOM SUICID		;and exit after completing the transfer.
	SETOM JCLTRN		;Suppress the "n bits in m seconds" printout.
TRNCH2:	SKIPN JCLBUF
NOJCL:	 SETZM JCLP
	.SUSET [.ROPTION,,TT]
	TLO TT,%OPOPC
	.SUSET [.SOPTION,,TT]
	.SUSET [.SMASK,,[%PIIOC]]	;Enable IOC interrupts
	SKIPN TRITTY
	 .SUSET [.SMSK2,,[1_TYIC]]	;Enable ^G/^S interrupts
	JRST MAIN

SUBTTL	Main loop.  

;Come here after finishing a command, or after an error.
MAIN:	PUSHJ P,STILOPN		;If connection was closed by other side, tell user.
	PUSHJ P,GRATU		;Check for gratuitous reply from server
	TLNE F,%LTCP		;If using Internet FTP,
	 JRST [	.CLOSE NETDI,	;  we use a new PORT each time.
		.CLOSE NETDO,
		JRST .+1 ]
	SKIPE JCLFLG		;Prompt for input unless supplied by superior.
	 JRST MAIN2
	SKIPN CNECTD
	 OUTI TYOC,"$		;Prompt with $ if connected, $$ if not.
	OUTI TYOC,"$
MAIN2:	PUSHJ P,ARGSPC
	    [ASCIZ //]
	LDB A,[350700,,ARGBUF]
	JUMPE A,MAIN1		;Null command is OK.  Do nothing.
	MOVEI A,ARGBUF
	MOVE E,[-COMTBL,,COMTAB]
	PUSHJ P,NETWRK"SYMLOOK	;Gobble Command
	 JRST MAIN3		; Not a command - try it as a host name.
	CAMN B,[-1]		;Numbers are not allowed as commands.
	 JRST ERRCOM
	HLRZ A,(B)
	PUSHJ P,(A)		;get address out of table and call the command.
	 JRST ERRTR1		;No skip => failed;  flush typeahead of all sorts.
MAIN1:	SKIPE SUICID
	 JRST QUIT
	JRST ERRTR2

MAIN3:	MOVE P,PDL
	MOVEI A,ARGBUF		;Couldn't parse command as command name => rescan it
	PUSHJ P,NHOSTN		;and try it as a host name.
	 JRST ERRCOM		; Doesn't make sense as either?
	PUSHJ P,CONN		;Command is host number, connect to it
	 JRST ERRTR1		;Failure to conect is an error.
	JRST MAIN1

ERRCOM:	OUTZ TYOC,[ASCIZ /Invalid command or host name?
/]
	JRST ERRTR1
ERRCTX:	OUTZ TYOC,[ASCIZ /Command not supported under TCP
/]
	JRST ERRTR1
ERRHST:	OUTZ TYOC,[ASCIZ/Invalid host name?
/]
ERRTR1:	SKIPE TYITTY
	 .RESET TYIC,
	SKIPE COMFIL
	 OUTZ TYOC,[ASCIZ /Closing Command File
/]
	.CLOSE COMC,
	SETZM COMFIL
	SETZM JCLBUF
	MOVE A,[440700,,JCLBUF]
	MOVEM A,JCLP
	SETZM JCLTRN
	SETZM SUICID
ERRTR2:	MOVE P,PDL		;Fix up PDL before re-entering main loop.
	MOVE A,JCLP		;If there's no JCL, next ARG would find out,
	ILDB A,A		;but check now so that we prompt before next ARG.
	SKIPN A
	 SETZM JCLFLG
	JRST MAIN		;Now go reenter main loop.


SUBTTL Command Tables

COMTAB:

DEFINE CMD NAME,LOC,HELP/
IFNB [HELP] TMPLOC HLPTAB+.-COMTAB, [ASCIZ\HELP\]
	LOC,,[ASCIZ\NAME\]
TERMIN
IF1 HLPTAB==0			;loc of HLPTAB not known until size of COMTAB known

	CMD ?,AHELP
	CMD NCP,DONCP,Switch to NCP mode.
	CMD TCP,DOTCP,Switch to TCP mode.
	CMD ACCT,AACCT,Send Account Name
	CMD ALLOCATE,AALLOC,Give Size of File to be STOREd
	CMD APPEND,AAPPE,Append Local File to Foreign File
	CMD ASCII,ATEXT,Transfer File as ASCII Text (TYPE A, BYTE 8)
	CMD BYE,ABYE,Disconnect, Giving Server Warning
	CMD BYTE,ABYTE,Set Byte Size
	CMD CONNECT,ACONN,Connect to Host
	CMD CWD,ACWD,Change Foreign Working Directory
	CMD DBGET,ADBGET,Debugging get file
	CMD DBPUT,ADBPUT,Debugging put file
	CMD DEBUG,ADBUG,Toggle Whether to Print Server Replies
	CMD DEFAULTS,ADEFA,Set Local Filename Defaults
	CMD DELETE,ADELE,Delete File
	CMD DIRECTORY,ALSTF,List Foreign File Directory
	CMD DISCONNECT,ADISC,Disconnect from Host
	CMD DISOWN,ADISOW,Run disowned
	CMD ESCRIPT,AESCRIP,Close Script File
	CMD GET,AGET,Get File from Foreign Host
	CMD HELP,AHELP,List Commands and What They Do
	CMD HOSTS,AHSTS,List Hosts
	CMD ICPSOCKET,AICPS,Set ICP Socket Number
	CMD LISTB,ALSTB,List Foreign Directory Briefly (filenames only)
	CMD LISTF,ALSTF,List Foreign File Directory
	CMD LISTL,ALSTL,List Local File Directory
	CMD LOGIN,ALOGIN,Login to Foreign Host
	CMD PASS,APASS,Send Password
	CMD PRINT,APRIN,Print File on ML TPL
	CMD PROCEED,APROCD,Run without the TTY
	CMD PUT,APUT,Put File onto Foreign Host
	CMD Q,AQUIT,Leave FTP
	CMD QUIT,AQUIT,Leave FTP
	CMD QUOTE,AQUOT,Send Arbitrary Command to Server
	CMD RENAME,ARENAM,Rename a File at Foreign Host
	CMD RETRIEVE,AGET,Get File from Foreign Host
	CMD SCRIPT,ASCRIP,Open Script File (gets all typeout)
	CMD SEND,APUT,Put File onto Foreign Host
	CMD SILENT,ASILNT,Stop typing on the TTY
	CMD SOAK,ASOAK,Wait for and Print One Reply From Server
	CMD STATUS,ASTAT,Print Server Status
	CMD STORE,APUT,Put File onto Foreign Host
	CMD TEN,ATEN,Transfer File in Most Efficient Mode (If Both Hosts PDP10s)
	CMD TEXT,ATEXT,Transfer File as ASCII Text (TYPE A, BYTE 8)
	CMD TRANSFER,ATRAN,Transfer host1filespec_host2filespec
	CMD TYPE,ATYPE,Specify Type of Transfer
	CMD VALRET,AVALR,Return to superior, not suicidally
	CMD XFILE,AXFILE,Read Commands From File

COMTBL==.-COMTAB

IF1 EXPUNGE HLPTAB
HLPTAB:	BLOCK COMTBL

SUBTTL Routines called by NETWRK

;PUTCHR Routine.  Writes character from T, clobbers no ACs, never skips.
;Called by NETWRK's error analysis routine ANALYZE.
PUTCHR:

;Output to script file if any.
;Output to TTY unless have both com file and script file.
TYO:	SKIPE SCRIPT		;Write to script file if we have one.
	 SKIPE READIN		; Except inside TYILIN.
	  CAIA
	   .IOT SCRIPC,T
	SKIPE SILENT
	 POPJ P,
	SKIPN SCRIPT
	 SKIPE TYOTTY
	  CAIA
	   JRST DIE		;Nothing to type out on???
	SKIPE COMFIL
	 SKIPN SCRIPT
	  SKIPN TYOTTY
	   POPJ P,
	.IOT TYOC,T		;Type on TTY if we can
	POPJ P,			;unless we have an XFILE and a script.

;IO UUOs on channel "TYOC" XCT UUOTYO for each char.
UUOTYO:	PUSH P,T
	MOVE T,U1
	PUSHJ P,TYO
	POP P,T
	POPJ P,

;Call "HSTLOOK to translate asciz string <- A into a host number.
;Then set bit 4.9 in A if the host is a PDP10.
NHOSTN:	PUSHJ P,NETWRK"HSTLOOK
	 POPJ P,
	MOVE B,A		;Host number in B
	PUSHJ P,NETWRK"HSTSRC	;Get SITES table entry addr in D.
	 JRST [	MOVE A,B ? JRST POPJ1]
	MOVE A,B
	HLRZ T,NETWRK"STLSYS(D)	;Find host's operating system.
	ADD T,NETWRK"HSTADR	;If we guess it's a PDP10,
	MOVE T,(T)		; we will be able to use 36 bit mode.
	MOVSI TT,-NTENS
NHOST1:	CAMN T,TENS(TT)
	 TLO A,400000		;Set bit 4.9 in A if machine is a PDP10.
	AOBJN TT,NHOST1
	JRST POPJ1

;;; Well-known PDP-10 operating systems (machine types are too varied).

TENS:	ASCII /ITS/		   ;Incompatible Timesharing System
	ASCII /WAITS/		   ;Wise-assed incompatible timesharing system
	ASCII /TOPS1/		   ;TOPS-10
	ASCII /TOPS2/		   ;TOPS-20
	ASCII /TENEX/		   ;BBN TENEX
	ASCII /FOONE/		   ;Foonly
	ASCII /AUGUS/		   ;Foonly
	ASCII /TYCOM/		   ;Foonly
NTENS==.-TENS


INCHR:	ILDB T,JCLP		;Get next JCL char or 0 if exhausted.
	JUMPN T,CPOPJ
	;If exhausted, read in another line and return its 1st char.
	PUSHJ P,TYILIN
	JRST INCHR



;Read an argument from the terminal.  If there is any of a previous line left,
;we use that;  otherwise we read a new line, prompting first.  The call should
;be followed by a pointer to the prompt string. Leading and trailing spaces
;are discarded, as is the CR at the end.
;On return, A points at ARGCT, which looks like a string var containing the
;argument, which is an ASCIZ string in ARGBUF.
ARGCON:	PUSHJ P,CONTST	;Read arg, but first establish connection if nec.
ARG:	SETZM TRANFL'
	CAIA
ARGTRN:	 SETOM TRANFL		;Here to read arg for TRAN command - 
				;Stop on = or _ or .
	SETZM SPCFL'
	CAIA
ARGSPC:	 SETOM SPCFL		;Here to read an arg that a space can terminate.
	PUSH P,B
	PUSH P,T
	MOVE B,JCLP		;Is there any input already?
	ILDB B,B
	MOVE T,@-2(P)
	SKIPN B
	 OUTZ TYOC,(T)		;If not, prompt the user for some.
	MOVE B,[440700,,ARGBUF]
	MOVEM B,ARGPT
	SETZM ARGCT
ARG1:	PUSHJ P,INCHR
	CAIN T,40
	 JRST ARG1
ARGLP:	CAIN T,^M
	 JRST ARGFIN
	CAIN T,^C
	 SETOM SUICID
	CAIE T,^C		;Can appear in JCL, not snarfed by TYILIN.
	 CAIN T,^N
	  JRST ARGFIN
	CAIN T,40		;In ARGSPC, space terminates once arg is non-null.
	 SKIPN SPCFL
	  CAIA
	   JRST ARGFIN
	SKIPN TRANFL		;In TRAN, filenames are terminated by _ or = or 
	 JRST ARGNRM
	CAIE T,"=		;so we must be able to ^Q those characters.
	 CAIN T,"_
	  JRST ARGFIN
	CAIN T,"
	 JRST ARGFIN
	CAIN T,^Q
	 JRST ARGCTQ
ARGNRM:	IDPB T,B
	AOS ARGCT
	PUSHJ P,INCHR
	JRST ARGLP

ARGCTQ:	IDPB T,B		;Here for "^Q - put it in the string, then don't
				; look for _ or  or = in next char.
	AOS ARGCT		
	PUSHJ P,INCHR
	CAIE T,"=		;^Q followed by a _, , = or ^Q =>
	 CAIN T,"_		; store the quoted character only.
	  JRST ARGNRM
	CAIN T,"
	 JRST ARGNRM
	CAIN T,^Q
	 JRST ARGNRM
	CAIN T,^M		;CR isn't suppressed by ^Q.
	 JRST ARGFIN
	PUSH P,T		;^Q followed by anything else.
				;Store both the ^Q and it.
	MOVEI T,^Q
	IDPB T,B
	AOS ARGCT
	POP P,T
	JRST ARGNRM

ARGFIN:	LDB T,B			;Here for a CR "( or _ or  or = in TRAN command),
	CAIE T,40		; to end the arg.
	 JRST ARGFI2		;Flush trailing spaces.
	D7BPT B
	SOS ARGCT
	JRST ARGFIN

ARGFI2:	SETZ T,			;Make sure the arg is an ASCIZ string.
	IDPB T,B
	POP P,T
	POP P,B
	MOVEI A,ARGCT		;Return a pointer to a string var
				; containing our stuff.
	JRST POPJ1

;Throw away rest of line if already read, but if no buffered input don't read any.
FLSLIN:	PUSH P,T
	MOVE T,JCLP		;If last char read was "EOL",
				;we have nothing to flush.
	LDB T,T 
	CAIE T,^M		;This happens when you type, 
				;eg, PROCEED<cr> with no space.
	 CAIN T,^N
	  JRST POPTJ
	CAIN T,^C
	 JRST POPTJ
FLSLI1:	PUSHJ P,INCHR
	CAIN T,^C
	 SETOM SUICID
	CAIN T,^N
	 JRST POPTJ
	CAIE T,^M
	 CAIN T,^C
	  JRST POPTJ
	JRST FLSLI1

SUBTTL Connect/Disconnect commands

;CONNECT command.
ACONN:	PUSHJ P,ARG
	    [ASCIZ /host: /]
	MOVEI A,ARGBUF
	PUSHJ P,NHOSTN
	 JRST ERRHST
	JRST CONN

;Connect to host specified in bits 4.8-1.1 of A.
;For NCP, sign(A) should be 1 to try to use image mode.
;For TCP, we only know how to do ASCII for now.

CONN:	JUMPE A,ERRHST		;Meaningless host name.
	PUSH P,A
	PUSHJ P,BYE		;Flush any existing connection.
	 JFCL
	POP P,A			;A has host number.
	SETZM DCTYPE		;Start out assuming ASCII mode
	MOVEI B,8.
	MOVEM B,DCBYTE		;8-bit bytes.
	SETOM DCSENT		;And we are probably content with that too.
	TLZE A,(SETZ)		;If it's a PDP10, try to use 36-bit Image.
	 JRST [	SETZM DCSENT
		SKIPE JCLTRN
		 JRST .+1	;No message if doing JCL tran command
		FWRITE TYOC,[[Will use 36-bit image transfer (TEXT command gives 8-bit ascii mode)],CRLF,,]
		JRST .+1 ]
	TLNN F,%LTCP
	 JRST NCPCON	; Dispatch for NCP/TCP processing

TCPCON:	MOVEM A,FDHST		;Remember foreign host number.
	MOVE B,FDHST
	MOVEI A,NETI
	MOVE C,ICPSOC
	PUSHJ P,NETWRK"TCPCON
	 JRST NETERR
	SETOM CNECTD		;We are now connected sompleace.
	JRST ACONN1

NCPCON:	MOVE B,A		;Host #
	MOVEM B,FDHST
	MOVEI A,ICPCH		;Pin #
	MOVE C,ICPSOC		;Foreign socket
	MOVEI T,FTPSKT
	MOVEM T,ICPSOC
	MOVE D,[40+.UAI,,40+.UAO]	;Modes
	PUSHJ P,NETWRK"ARPICP	;Connect it up
	 JRST NETERR		;Failed => return error message.
	SETOM CNECTD
	SYSCAL RCHST,[MOVEI NETI
		MOVEM JUNK	;NET
		MOVEM LDSOC	;Local socket
		MOVEM FDSOC ]	;Foreign socket
	 .LOSE 1000
	MOVEI A,2
	ADDM A,LDSOC	    	;Relocate to data socket base
	AOS FDSOC
ACONN1:	OUTOPN NETO,		;Prepare for using output UUOs on NETO.
	MOVEI A,300.		;Expect 300 Initial Greeting
	TLNE F,%LTCP	    	;If using TCP
	 MOVEI A,220.		;  the greeting is 220.
	PUSHJ P,REPLY		;Get reply, skip if winning
	 JRST ERDISC		;Reply says connection no good => disconnect.
	PUSHJ P,REPDIS		;Print the text of the greeting message.
	JRST POPJ1

ERDISC:	PUSHJ P,DISC
	 JFCL
	JRST ERRTR1

ADISC:
ABYE:	PUSHJ P,FLSLIN		;BYE command.  No-op if we have no server.
BYE:	PUSHJ P,STILOPN
	PUSHJ P,GRATU		;Check for gratuitous reply from server.
	SKIPN CNECTD
	 JRST POPJ1
	OUTZ NETO,[ASCIZ /QUIT
/]
	MOVEI A,221.		;Wait for acknowledgement, then disconnect.
	PUSHJ P,REPLY
	 JFCL			;You don't want to disconnect?
				;Too bad!  We will anyway.
DISC:	SKIPN CNECTD		;Disconnect.  No-op if not connected.
	 JRST POPJ1
	SETZM CNECTD
	.CLOSE NETO,
	.CLOSE NETI,
	JRST POPJ1

SUBTTL A few simple commands

AHELP:	PUSHJ P,FLSLIN
	MOVSI A,-COMTBL
AHELP1:	SKIPN HLPTAB(A)
	 JRST AHELP2
	FWRITE TYOC,[CRLF,,TZ$,COMTAB(A),TI,^I,TZ$,HLPTAB(A)]
AHELP2:	AOBJN A,AHELP1
	OUTZ TYOC,[ASCIZ\
You may also give just a host name or number if not already connected.	

Terminate input with CR or LF.  Use rubout to delete last character typed.
Commands and Host names may be abbreviated (if the abbreviation is
unambiguous).
\]
	JRST POPJ1

AHSTS:	PUSHJ P,FLSLIN		;HOSTS command - list all hosts. (Arpanet only)
	MOVE A,NETWRK"HSTADR
	MOVE B,NETWRK"NAMPTR(A)
	ADD B,A			;Get addr of HOSTS3 file NAMES table.
	MOVE C,(B)		;C gets number of entries.
	MOVE D,1(B)		;D gets words per entry
	ADDI B,2		;B -> first entry
AHSTS1:	HLRZ A,NETWRK"NMLSIT(B)
	ADD A,NETWRK"HSTADR	;A gets addr of SITES table entry for this host.
	HLRZ TT,NETWRK"STLNAM(A)	;Get relative address of the official name
	HRRZ T,NETWRK"NMRNAM(B)	;Get relative address of this name
	CAME T,TT
	 JRST AHSTS2		;Don't mention nicknames
	HLRZ TT,NETWRK"STLSYS(A)
	ADD TT,NETWRK"HSTADR	;A gets addr of string containing type of system
	MOVE TT,(TT)
	CAME TT,[ASCIZ /TIP/]	;Dont mention TIPs.  Can't FTP to them.
	 SKIPL NETWRK"STLFLG(A)	;Only mention servers
	  JRST AHSTS2
	HRRZ A,NETWRK"STRADR(A)	;See if this guy is on Internet
AHSTS3:	ADD A,NETWRK"HSTADR
	MOVE TT,NETWRK"ADDADR(A)
	TLNN TT,(NETWRK"NE%UNT)	; Skip if not an internet address.
	 JRST AHSTS4
	HRRZ A,NETWRK"ADRCDR(A)
	JUMPN A,AHSTS3
	JRST AHSTS2

AHSTS4:	ADD T,NETWRK"HSTADR
	FWRITE TYOC,[TZ$,T,CRLF,,]
AHSTS2:	ADD B,D			;B -> next entry
	SOJG C,AHSTS1
	JRST POPJ1

AQUOT:	PUSHJ P,ARGCON
	    [ASCIZ /FTP protocol command to give to server:  /]
	JRST SNDARG

ASTAT:	PUSHJ P,ARGCON
	    [ASCIZ /Server-dependent status-type:  /]
	OUTZ NETO,[ASCIZ /STAT/]
	HRRZ D,(A)	   ;Get length of arg.
	SKIPE D		   ;If user said anything,
	 OUTZ NETO,[ASCIZ / /]	   ;delimit cmd and arg.
	JRST SNDARG

AICPS:	PUSHJ P,ARG
	    [ASCIZ /Socket number:  /]
	PUSHJ P,RDOCT
	MOVEM A,ICPSOC
	JRST POPJ1

ASOAK:	PUSHJ P,FLSLIN
ASOAK0:	SETO A,			;Read in and print one reply from server.
	PUSH P,PRREP
	SETOM PRREP
	PUSHJ P,REPLY
	 JFCL
	POP P,PRREP
	JRST POPJ1

ADBUG:	PUSHJ P,FLSLIN
	SETCMM PRREP		;DEBUG - complement printing of server replies.
	SKIPN PRREP
	 OUTZ TYOC,[ASCIZ\not \]
	OUTZ TYOC,[ASCIZ\printing server replies.\]
	CRLF TYOC,
	JRST POPJ1

DONCP:	PUSHJ P,ABYE		;Can't change mode with conns open.
	 JFCL
	OUTZ TYOC,[ASCIZ /Switching to NCP mode, which probably do anything good./]
	CRLF TYOC,
	TLZ F,%LTCP		;Say not doing TCP.
	MOVEI A,FTPSKT		;NCP means this is the ICP socket
	MOVEM A,ICPSOC	    	;Remember which port or socket to use.
	MOVE A,[NETWRK"NW%ARP]	;Get own host number, on Arpanet
	PUSHJ P,NETWRK"OWNHST
	 .LOSE			;Not connected to Arpanet?
	MOVEM A,OWNHST
	JRST POPJ1


DOTCP:	PUSHJ P,ABYE		;Can't change mode with conns open.
	 JFCL
	OUTZ TYOC,[ASCIZ /Switching to TCP mode./]
	CRLF TYOC,
	TLO F,%LTCP		;Say doing TCP.
	MOVEI A,FTPORT	    	;TCP means we use this port.
	MOVEM A,ICPSOC	    	;Remember which port or socket to use.
	MOVE A,[SQUOZE 0,/IMPUS3/]
	.EVAL A,	;If TCP, need the HOSTS3 format address instead.
	 .LOSE %LSSYS
	MOVEM A,OWNHST
	JRST POPJ1


AQUIT:	PUSHJ P,FLSLIN
QUIT:	PUSHJ P,BYE
	 JFCL
DIE:	.CLOSE SCRIPC,
	SKIPE DEBUG
	 .VALUE
	.LOGOUT 1,

AVALR:	PUSHJ P,FLSLIN		;Flush typed-ahead commands.
	.BREAK 16,100000	;Return to superior.  Can be $P'd or $G'd.
	JRST POPJ1

APROCD:	SKIPA C,[[ASCIZ //]]	;Proceed command.
ADISOW:	 MOVEI C,[ASCIZ / /];Disown command - disown self, keep running.
	PUSHJ P,FLSLIN
	MOVE A,JCLP
	ILDB A,A		;If have input already buffered, it might
				;contain an XFILE and SCRIPT, so we can't be
	JUMPN A,ADISO1		;sure user is losing.
	SKIPN TRITTY		;If TTY input is translated, 
	 SKIPE COMFIL		;or redirected with an XFILE, then input is OK.
	  CAIA
	   JRST ADISO2
	SKIPN TROTTY		;If TTY output is translated
	 SKIPE SCRIPT		; or redirected with a SCRIPT,
	  CAIA			;  OK.
ADISO2:	   JRST [ OUTZ TYOC,[ASCIZ /Can't DISOWN or PROCEED if using the TTY
/]
		  JRST ERRTR1]
ADISO1:	SKIPE DEBUG
	 .VALUE
	.VALUE (C)
	SKIPN TROTTY		;If TTY output isn't translated, 
	 SETOM SILENT		; we mustn't use it any more.
	JRST POPJ1

ASILNT:	PUSHJ P,FLSLIN
	SETOM SILENT
	JRST POPJ1

ALOGIN:	PUSHJ P,ARGCON
	    [ASCIZ /as User name:  /]
	OUTZ NETO,[ASCIZ /USER /]	;Send the command name.
	JRST SNDARG		;Send the arg, handle reply.

APASS:	PUSHJ P,ARGCON
	    [ASCIZ/password:  /]
	OUTZ NETO,[ASCIZ /PASS /]
SNDARG:	FWRITE NETO,[TZ,ARGBUF,CRLF,,]
	MOVEI A,200.
	JRST REPLY

AACCT:	PUSHJ P,ARGCON
	    [ASCIZ /account number to log in under:  /]
	OUTZ NETO,[ASCIZ /ACCT /]	;Send the command name.
	JRST SNDARG		;Send the arg, handle reply.

ACWD:	PUSHJ P,ARGCON
	    [ASCIZ/to Directory:  /]
	OUTZ NETO,[ASCIZ /CWD /]	;Send the command name.
	JRST SNDARG		;Send the arg, handle reply.

ADEFA:	PUSHJ P,ARG		;DEFAULTS - just set local filename.
	    [ASCIZ /filename defaults:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR
   	JRST POPJ1

AALLOC:	PUSHJ P,ARGCON		;ALLOC - send size for file to be STOREd.
	    [ASCIZ /# bytes:  /]
	OUTZ NETO,[ASCIZ /ALLO /]
	JRST SNDARG

;Delete command.
ADELE:	PUSHJ P,ARGCON
	    [ASCIZ /file:  /]
	OUTZ NETO,[ASCIZ /DELE /]
	JRST SNDARG

;Rename command.
ARENAM:	PUSHJ P,ARGCON
	    [ASCIZ /old file:  /]
	FWRITE NETO,[[RNFR ],TZ,ARGBUF,CRLF,,]	;Send the old name.
	MOVEI A,350.
	PUSHJ P,REPLY		;Wait for acceptance.
	 POPJ P,
	PUSHJ P,ARG
	    [ASCIZ /to new name:  /]
	FWRITE NETO,[[RNTO ],TZ,ARGBUF,CRLF,,]
	MOVEI A,250.
	JRST REPLY

;PRINT command - print file on ML's TPL.
APRIN:	MOVE B,FDHST
	CAIN B,306
	 SKIPN CNECTD
	  CAIA
	   JRST APRIN2
	MOVE A,[SETZ 306]
	PUSHJ P,CONN		;First connect to ML (if not already connected).
	 POPJ P,
APRIN2:	PUSHJ P,ARG		;Then ask for and open the disk file.
	    [ASCIZ/local file:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR
	PUSHJ P,DCSEND		;Tell ML to use image mode.
	SKIPLE TT,DCTYPE
	 SETO TT,		; Assume local-byte means image.
	SYSCAL OPEN,[	(TT)1+[.BII,,DC ? .UAI,,DC]
			FILDEV ? FILFN1 ? FILFN2 ? FILDIR ]
	 JRST FILERR
	PUSHJ P,NETWLS
	OUTZ NETO,[ASCIZ /STOR TPL:
/]
	JRST APRIN1		;Then go do a PUT to TPL:.

SUBTTL	Connection Checking

;Commands that need to be connected before they can work call here
;If we aren't connected to a server, ask user which host and try to connect now.

CONTST:	SKIPN CNECTD		;If no connection, ask for host name.
	 JRST CONTS1
	PUSHJ P,STILOPN		;If have a server, check that it's really there.
	SKIPE CNECTD		;If it is, we win.
	 POPJ P,		;Otherwise, ask for host to connect to.	
	SKIPE TYITTY		;In this case, any type-ahead
	 .RESET TYIC,		;must be intended for something else.
CONTS1:	PUSHAE P,[A,JCLBFP,JCLP]	;? *******
	MOVE A,[440700,,JCLBF2]	;Read host name using alternate buffer.
	MOVEM A,JCLBFP
	MOVEM A,JCLP
	SETZM JCLBF2
	PUSHJ P,ACONN	
	 JRST ERRTR1
	POPAE P,[JCLP,JCLBFP,A]
	POPJ P,


SUBTTL	Error Handling

;Handle File Error
FILERR:	.OPEN ERRC,[.UAI,,'ERR ? 1]
	 .VALUE
FILER1:	.IOT ERRC,TT
	CAIL TT,40
	 JRST [ OUTI TYOC,(TT) ? JRST FILER1 ]
	FWRITE TYOC,[[ - ],6F,FILDEV,[: ],6F,FILDIR,[; ],6F,FILFN1,[ ],6F,FILFN2,CRLF,,]
	JRST ERRTR1

NOTTY:	OUTZ TYOC,[ASCIZ /No TTY or Script file to read from
/]
	JRST QUIT

;Here for error making data connection.  We need to reset.
DCBORE:	OUT(TYOC,("Timed out waiting for server to make data connection."),EOL)
	JRST NETCLS

;Here for error making data connection.  A server reply is expected
;and should be soaked up.
DCNERR:	.LOGOUT 0,
	PUSHJ P,NETWRK"ANALYZ
	 .VALUE
	CRLF TYOC,
	PUSHJ P,STILOPN
	SKIPE CNECTD
	 PUSHJ P,ASOAK0
	  JFCL
	JRST NETCLS

;Here is call to CONNEC or ICP loses.
NTERDI:	MOVEI A,NETDI
NETERR:	.LOGOUT 0,	    	;Die if no one to complain to.
	PUSHJ P,NETWRK"ANALYZ	;Else print an error message.
	 .VALUE			;  Sigh?
	CRLF TYOC,
	PUSHJ P,STILOPN		;See if TELNET connection seems to be closed.

;Here if lose inside GET or PUT.  Close the files.
NETCLS:	.CLOSE NETDO,
	.CLOSE DC,
	.CLOSE NETDI,
	JRST ERRTR1		;Go fix up stuff and safely restart main loop.


;Do nothing if we are not supposedly connected, or if we really
;are still connected.  If supposedly connected but connection
;broken, make it official and tell the user.

STILOPN:SKIPN CNECTD		;Do nothing if we know there's no connection.
	 POPJ P,
	SYSCAL WHYINT,[%CLIMM,,NETI ? %CLOUT,,A ? %CLOUT,,B]
	 JRST STILLS		;If channel is closed
	ANDI B,-1		;or socket state is closed
	JUMPN B,CPOPJ		; then connection is broken.
STILLS:	PUSHJ P,DISC		;So close channels, zero CNECTD, etc.
	 JFCL
	OUTZ TYOC,[ASCIZ /Connection to Server has broken.
/]
	POPJ P,

;If there is gratuitous input from the server, say so
GRATU:	SKIPN CNECTD
	 POPJ P,
	PUSHAE P,[A,B,C]
	SYSCAL WHYINT,[%CLIMM,,NETI ? MOVEM A ? MOVEM B ? MOVEM C ]
	 JRST GRATU9
	CAIN A,%WYNET
	 CAIG C,0		;Have any input?
	  JRST GRATU9
	OUTZ TYOC,[ASCIZ/
Note: gratuitous response from server:
/]
GRATU1:	.IOT NETI,A
	.IOT TYOC,A
	SOJG C,GRATU1
	CRLF TYOC,
GRATU9:	POPAE P,[C,B,A]
	POPJ P,

;Give final bullshit about bits per second
FRATE:	SKIPE JCLTRN
	 JRST QUIT		;Skip this printout for implicit TRAN's from DDT.
	SKIPN SCRIPT		;If we are disowned with no file to write in,
	 SKIPE TROTTY		;log out now rather than hanging up.
	  CAIA
	   .LOGOUT
	.RDTIME T,
	SUB T,NTIME
	PUSH P,T
	IMULI T,100.
	IDIVI T,30.		;Time in hundredths of a second.
	IDIVI T,100.		;T has time in seconds, TT has extra hundredths.
	FWRITE TYOC,[N9,NBITS,[ bits in ],N10,T,N9,TT,[ seconds (]]
	POP P,T
	FSC T,233
	FDVRI T,(30.0)
	MOVE A,NBITS
	FSC A,233
	FDVRI A,(1000.0)	;kilobits
	FDVR A,T
	FWRITE TYOC,[NFL,A,[ kbps).],CRLF,,]
	POPJ P,


SUBTTL	NCP data transfer commands  

AAPPE:	TLNE F,%LTCP
	 JRST ERRCTX
	PUSHJ P,APUT1
	MOVEI A,[ASCIZ/APPE /]
	JRST APUT2

APUT:	TLNE F,%LTCP
	 JRST PUTTCP	;TCP is different.
	PUSHJ P,APUT1
	MOVEI A,[ASCIZ /STOR /]
APUT2:	PUSH P,A
	PUSHJ P,ARG
	    [ASCIZ/to foreign file:  /]
	PUSHJ P,NETWLS
	POP P,B
	FWRITE NETO,[TZ,(B),TS,(A),CRLF,,]
APRIN1:	PUSHJ P,SOCK	;Look for 255 SOCK mumble
	 JRST NETCLS
	MOVEI A,NETDO
	PUSHJ P,NETWRK"CONFIN
	 JRST DCNERR	;lossage
	.NETAC NETDO,
	 JRST DCNERR	;lossage
	MOVEI A,250.	;Look for 250 socket to me
	TLNE F,%LTCP
	 MOVEI A,125.
	PUSHJ P,REPLY
	 JRST NETCLS	;lossage
	PUSHJ P,REPDIS	;tell user transfer started successfully
	SETZM NBITS
	.RDTIME TT,
	MOVEM TT,NTIME
	MOVEI A,DC
	MOVEI B,NETDO
	SETOM XFRDIR
	PUSHJ P,XFR	;Do the transfer (looks at the transfer mode).
	SYSCAL FINISH,[MOVEI NETDO]
	 JFCL
	.CLOSE NETDO,
	MOVEI A,252.	;look for 252 finis
	TLNE F,%LTCP
	 MOVEI A,226.
	PUSHJ P,REPLY
	 JRST NETCLS	;lossage
	PUSHJ P,FRATE
	JRST POPJ1	;winnage

;Do a listen on the output data socket.
NETWLS:	PUSH P,A
	SKIPE DCTYPE	;Ascii?
	 JRST NETWL1	;Image.
	MOVEI D,160+.UAO
	MOVE A,DCBYTE
	CAIN A,8
	 JRST NETWL2
	FWRITE TYOC,[[Byte size ],N9,A,[ illegal with TYPE A; use BYTE 8],CRLF,,]
	JRST ERRTR1

NETWL1:	MOVE A,DCBYTE
	MOVEI D,44160+.BIO
	CAIN A,36.
	 JRST NETWL2
	PUSHJ P,BSETUP
	IORI D,160+.UIO
NETWL2:	MOVEI A,NETDO	;Listen data connection
	MOVE B,FDHST
	HRLI D,400000
	PUSHJ P,NETWRK"ARPCON
	 JRST NETERR	;lossage
	POP P,A
	POPJ P,

;Set up for doing image-mode transfer with strange byte size
BSETUP:	CAIL A,1
	 CAILE A,36.
	  JRST [ FWRITE TYOC,[[Byte size ],N9,A,[ illegal with TYPE I, not between 1 and 36],CRLF,,]
		 JRST ERRTR1 ]
	MOVE D,A
	MOVEI A,36.
	IDIV A,D
	FWRITE TYOC,[[Will transfer ],N9,DCBYTE,[-bit bytes packed ],N9,A,[ per pdp-10 word, left-justified.],CRLF,,]
	LSH D,9
	POPJ P,

APUT1:	PUSHJ P,CONTST		;Make sure we have a server set up.
	PUSHJ P,DCSEND		;Make sure server knows about data type etc.
	PUSHJ P,ARG
	    [ASCIZ/local file:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR
	SKIPLE TT,DCTYPE
	 JRST [	MOVE TT,DCBYTE	; If logical-byte being used,
		CAIE TT,36.	; Then if not 36-bit bytes
		 TDZA TT,TT	; we must use unit-mode for SIOT
		  SETO TT,	; otherwise hack block image for 36-bit xfers
		JRST .+1]
	SYSCAL OPEN,[	(TT)1+[.BII,,DC ? .UAI,,DC]
			FILDEV ? FILFN1 ? FILFN2 ? FILDIR ]
	 JRST FILERR
	POPJ P,

;Debugging version of PUT.
ADBPUT:	TLNE F,%LTCP
	 JRST ERRCTX
	PUSHJ P,APUT1
	PUSHJ P,ARG
	    [ASCIZ/protocol command:  /]
	PUSHJ P,NETWLS
	FWRITE NETO,[TZ,ARGBUF,CRLF,,]
	JRST APRIN1


;PUT command for TCP
PUTTCP:	PUSHJ P,CONTST		;Make sure we have a server set up.
	PUSHJ P,DCSEND		;Make sure server knows about data type etc.
	PUSHJ P,ARG
		[ASCIZ/From local file:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR		;Read desired source file name.
	SKIPLE TT,DCTYPE
	 JRST [	MOVE TT,DCBYTE	; If logical-byte being used,
		CAIE TT,36.	; Then if not 36-bit bytes
		 TDZA TT,TT	; we must use unit-mode for SIOT
		  SETO TT,	; otherwise hack block image for 36-bit xfers
		JRST .+1]
	SYSCAL OPEN,[(TT)1+[.BII,,DC ? .UAI,,DC]
		     FILDEV ? FILFN1 ? FILFN2 ? FILDIR]
	 JRST FILERR		;Eh? cant open source file?
	PUSHJ P,ARG
		[ASCIZ/to foreign file:  /]
	PUSHJ P,TCPLSN	    	;Do a listen on the data socket.
	FWRITE NETO,[[STOR ],TS,(A),CRLF,,]
	.NETS NETO,		;Send the transfer command.
	PUSHJ P,TCPWRI	    	;Transfer the file.
	PUSHJ P,FRATE		;Print out statistics.
	JRST POPJ1


;Send file over data socket (for TCP).

;This routine assumes we are listening for a data connection, and that
;we have given the storage command. Waits for the server to connect to
;us, and writes the file from channel DC to channel NETDO.

TCPWRI:	PUSH P,A
	PUSH P,B
	MOVEI A,125.		;Make sure he is ready to send receive it.
	PUSHJ P,REPLY	    	;This should be a "Socket to me".
	 JRST NETCLS
	MOVEI A,%NSLSN		;Initial state to hang on.
	MOVEI T,TIMOUT
TCPWR0:	JUMPE T,DCBORE
	SYSCAL NETBLK,[%CLIMM,,NETDO ? A ? T ? %CLOUT,,A ? %CLOUT,,TT]
	 JRST DCNERR
	MOVE T,TT
	CAIN A,%NSRFC		; If in SYN-RECEIVED state
	 JRST TCPWR0		; then it's OK to keep waiting.
	CAIE A,%NSOPN		; Else should be open now.
	 CAIN A,%NSRFN
	  CAIA
	   JRST TCPWR0
	PUSHJ P,REPDIS		;Tell user transfer started successfully.
	SETZM NBITS
	.RDTIME TT,
	MOVEM TT,NTIME
	SETOM XFRDIR		; Outputting to network
	MOVEI A,DC	    	;Source file open on this channel.
	MOVEI B,NETDO	   	;Data connection open on this channel.
	PUSHJ P,XFR		;Do the transfer.
TCPWR9:	.CLOSE DC,		;Close file.
	SYSCAL FINISH,[MOVEI NETDO]
	 JFCL
	.CLOSE NETDO,		;Close data socket.
	.CLOSE NETDI,		; Close unused reverse direction channel
	MOVEI A,226.		;Look for 226 Finis reply.
	PUSHJ P,REPLY
	 JRST NETCLS
	POP P,B
	POP P,A
	POPJ P,

;GET command
AGET:	PUSHJ P,CONTST		;Make sure we have a server set up.
	PUSHJ P,DCSEND		;Make sure server knows about data type etc.
	PUSHJ P,ARG
		[ASCIZ/from foreign file:  /]
	MOVE A,[ARGBUF,,ALTARG]
	BLT A,ALTARG+JCLBFL-1	;Save it away.
	PUSHJ P,ARG
		[ASCIZ/into local file:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR		;Read desired local name.
	SKIPLE TT,DCTYPE
	 JRST [	MOVE TT,DCBYTE	; If logical-byte being used,
		CAIE TT,36.	; Then if not 36-bit bytes
		 TDZA TT,TT	; we must use unit-mode for SIOT
		  SETO TT,	; otherwise hack block image for 36-bit xfers
		JRST .+1]
	SYSCAL OPEN,[(TT)1+[.BIO,,DC ? .UAO,,DC]
		     FILDEV ? FTPOF1 ? FTPOF2 ? FILDIR]
	 JRST FILERR		;Eh? cant open temporary output file?
	PUSHJ P,NETRLS		;Do a listen on the data socket.
ATRGET:	FWRITE NETO,[[RETR ],TZ,ALTARG,CRLF,,]
ADBGT1:	.NETS NETO,
	MOVEI B,DC		;For GET, send output to Disk.
	PUSHJ P,NETREAD		;Read the whole file and write it to DC.
	MOVE B,FILDEV
	CAMN B,[SIXBIT/TTY/]	;If outputting to the TTY
	 JRST AGET90		;  do not renaming.
	SYSCAL RENMWO,[%CLIMM,,DC ? FILFN1 ? FILFN2]
	 JRST FILERR
AGET90:	.CLOSE DC,		;Close file.
	PUSHJ P,FRATE		;Print out statistics.
	JRST POPJ1


;Listen for data connection we want to receieve.

;For NCP, do a listen on our input data socket. 
;For TCP, use the PORT command for the "Quiet-Time" SYN hackery first.
;For both, we complete and accept the connection later. 
;This routine returns always.

NETRLS:	PUSH P,A
	PUSH P,B
	TLNN F,%LTCP
	 JRST NCPRLS	;If NCP FTPing, things are not necessarily ASCII.
TCPRLS:	PUSHJ P,TCPLSN		;Offer and listen for a data connection.
	JRST RLSDUN		;We are ready for the transferring command.

NCPRLS:	SKIPE DCTYPE		;Ascii?
	 JRST NETRL1		;Image or logical-byte
	MOVEI D,160+.UAI	;THIS ROUTINE SMASHES D!
	MOVE A,DCBYTE
	CAIN A,8
	 JRST NETRL2
	FWRITE TYOC,[[Byte size ],N9,A,[ illegal with TYPE A; use BYTE 8],CRLF,,]
	JRST ERRTR1
NETRL1:	MOVE A,DCBYTE
	MOVEI D,44160+.BII
	CAIN A,36.
	 JRST NETRL2
	PUSHJ P,BSETUP
	IORI D,160+.UII
NETRL2:	MOVEI A,NETDI
	MOVE B,FDHST
	HRLI D,400000
	PUSHJ P,NETWRK"ARPCON
	 JRST NETERR		;lossage

RLSDUN:	POP P,B
	POP P,A
	POPJ P,



SUBTTL	TCP Listen for data connection 

;Start listening for a data connection from the foreign server on some port.
;When we have receieved the reply to the PORT command, return.

TCPLSN:	PUSH P,A
	MOVE A,ICPSOC	    ;Take the FTP server port number.
	SUBI A,1	    ;Subtract one.
	SYSCAL TCPOPN,[%CLBIT,,%NOLSN	;Say we want to listen.
			%CLIMM,,NETDI	;Data Receive channel.
			%CLIMM,,NETDO	;Data Transmit channel.
			[-1]		;Gensym a local listening port
			A   		;Try this foreign port.
			FDHST]		;Frn host to listen for.
	 JRST NTERDI
	SYSCAL RFNAME,[	%CLIMM,,NETDI ? %CLOUT,,JUNK
			%CLOUT,,LPORT]	;This port is where we are listening.
	 .LOSE %LSSYS
	OUT(NETO,("PORT "))	;Need to tell server where to connect to.
	MOVSI TT,-4		;There are four fields in the address.
	MOVE T,[401000,,OWNHST]	;Each field is 8. bits long.
TCPLS1:	ILDB A,T		;Get the field.
	OUT(NETO,D(A),(","))	;Print that part as a decimal number.
	AOBJN TT,TCPLS1		;Print the entire address this way.
	MOVE T,[201000,,LPORT]	;Now we want the port number.
	ILDB A,T		;Get high eight bits of the port number.
	OUT(NETO,D(A),(","))	;Print them.
	ILDB A,T		;Get low eight bits of th port number.
	OUT(NETO,D(A),EOL)	;Print them, and wrap up the command.
	.NETS NETO,
	PUSHJ P,SOCK		;Wait for a reply that we are winning.
	 JRST [	OUT(TYOC,("Negative reply to PORT command."),CRLF)
		JRST NETCLS]
	POP P,A
	POPJ P,			;Return, ready for transferring command.

SUBTTL	Wait for 255 SOCK COMMAND.

;Skip return on success.

SOCK:	PUSH P,A
	PUSH P,B
	PUSH P,C
	MOVEI A,255.		;If NCP, look for 255 "SOCK mumble".
	TLNE F,%LTCP		;If TCP, 
	 MOVEI A,200.		;  Look for 200 "PORT Ok".
	PUSHJ P,REPLY
	 JRST [ POP P,C ? POP P,B ? POP P,A ? JRST CPOPJ ]
	POP P,C
	POP P,B
	POP P,A
	JRST POPJ1

;We used to store the argument to the SOCK reply into FDSOC; kludge,kludge.
IFN 0,[	MOVEI A,REPLYS
	MOVEI B,40
	PUSHJ P,PRSWRD		;255
	MOVEI B,40
	PUSHJ P,PRSWRD		;SOCK
	HRRZ C,(B)
	CAIE C,4
	 JRST SCKLOS
	IRPC CH,,SOCK
	 ILDB C,1(B)
	 CAIE C,"CH
	  JRST SCKLOS
	 TERMIN
	MOVEI B,0		;Now get the decimal number
SOCK1:	ILDB C,REPLYS+1
	CAIL C,"0
	 CAILE C,"9
	  JRST SOCK2
	IMULI B,10.
	ADDI B,-"0(C)
	JRST SOCK1

SOCK2:	MOVEM B,FDSOC
	POPJ P,
SCKLOS:	OUTZ TYOC,[ASCIZ/Host did not send proper "255 SOCK nnnn" reply.
/]
	JRST ERRTR1
] ;End IFN 0.



SUBTTL	Read file over data socket

;This routine assumes we are listening on NETDI for a data connection,
;and that we have given the retreival command.  Waits for the server to
;connect to us, and writes the file to the channel in B.
;Closes the data connection when done, and returns.

NETREAD:PUSH P,A	    ;Save an AC.
	PUSH P,B	    ;Save channel to write to here.
	TLNN F,%LTCP
	 JRST NETRE1    ;If NCP, go do ICP.

	MOVEI A,125.		;For TCP, wait for positive "Here Comes"
	PUSHJ P,REPLY	    	;reply before doing the listen,
	 JRST NETCLS		;   since the file might not be there.
	MOVEI A,%NSLSN		; Initial state to hang on.
	MOVEI T,TIMOUT
TCPRD0:	JUMPE T,DCBORE
	SYSCAL NETBLK,[%CLIMM,,NETDO ? A ? T ? %CLOUT,,A ? %CLOUT,,TT]
	 JRST DCNERR
	MOVE T,TT		; Boredom sets in eventually.
	CAIN A,%NSRFC		; If in SYN-RECEIVED state
	 JRST TCPRD0		; then it's OK to keep waiting.
	CAIE A,%NSOPN		; Else should be open now.
	 CAIN A,%NSRFN
	  CAIA
	   JRST TCPRD0		;If not OPEN or RFNM, keep waiting.
	JRST NETRE2		;When connected, go read data.


NETRE1:	PUSHJ P,SOCK		;NCP needs a 255 SOCK mumble.
	 JRST [	OUT(TYOC,("Unexpected reply, should have been SOCK."),EOL)
		JRST NETCLS ]
	MOVEI A,NETDI
	PUSHJ P,NETWRK"CONFIN	;NCP needs messy ICP too!
	 JRST DCNERR		;  lossage
	.NETAC NETDI,		;Accept the connection we were listening for.
	 JRST DCNERR
	MOVEI A,250.
	TLNE F,%LTCP
	 MOVEI A,125.		;Look for 125. Here it comes.
	PUSHJ P,REPLY
	 JRST NETCLS

NETRE2:	SETZM NBITS
	.RDTIME TT,
	MOVEM TT,NTIME
	MOVEI A,NETDI
	POP P,B			;Get back channel to write to (DC or TYOC).
	SETZM XFRDIR
	PUSHJ P,XFR		;Do the transfer (looks at DCTYPE, closes NETDI).
	.CLOSE NETDO,		; TCP: make sure reverse chan closed too.
	MOVEI A,252.		;If NCP, look for 252 "FINIS".
	TLNE F,%LTCP		;If TCP,
	 MOVEI A,226.		;  look for 226 "Transfer Complete".
	PUSHJ P,REPLY
	 JRST [	OUT(TYOC,("Unexpected reply, should have been FINIS."),EOL)
		JRST .+1 ]
	POP P,A		    	;Pop saved ACs back off. (B already popped).
	POPJ P,

;Debugging version of GET.
ADBGET:	TLNE F,%LTCP
	 JRST ERRCTX
	PUSHJ P,CONTST
	PUSHJ P,ARG
	    [ASCIZ/into local file:  /]
	MOVE B,[FILDEV,,FILDEV]	;Read desired local filename
	PUSHJ P,FILPAR
	MOVE TT,FILDEV		;Output to TTY means use text mode
	CAMN TT,[SIXBIT/TTY/]
	 JRST [	SKIPE DCSENT	;If server knows that we are in image mode
		 SKIPN DCTYPE
		  JRST [SETOM DCSENT
			JRST .+1 ]
		PUSHJ P,ATEXT	;then tell it to use ascii
		 JFCL
		JRST .+1 ]
	PUSHJ P,DCSEND
	SKIPLE TT,DCTYPE
	 JRST [	MOVE TT,DCBYTE	; If logical-byte being used,
		CAIE TT,36.	; Then if not 36-bit bytes
		 TDZA TT,TT	; we must use unit-mode for SIOT
		  SETO TT,	; otherwise hack block image for 36-bit xfers
		JRST .+1]
	SYSCAL OPEN,[	(TT)1+[.BIO,,DC ? .UAO,,DC]
			FILDEV ? FTPOF1 ? FTPOF2 ? FILDIR ]
	 JRST FILERR
	PUSHJ P,ARG
	    [ASCIZ/protocol command:  /]
	PUSHJ P,NETRLS ;Do listen on data socket before sending the command.
	FWRITE NETO,[TZ,ARGBUF,CRLF,,]
	MOVE B,FILDEV		;Output to TTY is special case
	CAME B,[SIXBIT/TTY/]
	 JRST ADBGT1		;Go join regular GET command
	.CLOSE DC,
	MOVEI B,TYOC
	PUSHJ P,NETREAD
	JRST POPJ1


SUBTTL TRAN Command

ATRAN:	MOVE B,JCLP
	ILDB B,B
	SKIPN B			;If there's no input on same line as TRAN, prompt.
	 OUTZ TYOC,[ASCIZ /to-hostfile = from-hostfile:
/]
	PUSHJ P,THOSTN		;Read the To-host.
	 JRST ERRTR1
	LDB B,[4300,,A]		;All but sign
	CAME B,OWNHST		;If to-host is us, this TRAN is a GET.
	 JRST ATRPUT
	PUSHJ P,ARGTRN		;So read in the to-filenames,
	    [ASCIZ /To-filename:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR		;and parse them
	PUSHJ P,THOSTN		;Read the From-host.
	 JRST ERRTR1
	LDB B,[4300,,A]		;All but sign
	SKIPE CNECTD
	 CAME B,FDHST		;If not already connected to it, connect.
	  CAIA
	   JRST ATRGE1
	PUSHJ P,CONN
	 JRST ERRTR1
ATRGE1:	PUSHJ P,DCSEND
	MOVEI A,ARGCT
	MOVE B,JCLP
	ILDB B,B
	CAIN B,^M		;If the from-file is null, default to the to-file.
	 JRST [	PUSHJ P,FLSLIN	;READ PAST THE CR.
		JRST ATRGE2]
	MOVE C,ARGCT
	PUSHJ P,ARG		;Else read the from-file.
	    [ASCIZ/from foreign file:  /]
	MOVE B,[FILDEV,,FILDEV]
	SKIPN C			;If the to-file was null, default to from-file.
	 PUSHJ P,FILPAR
ATRGE2:	SKIPLE TT,DCTYPE	;Now open the local (to-) file.
	 JRST [	MOVE TT,DCBYTE	; If logical-byte being used,
		CAIE TT,36.	; Then if not 36-bit bytes
		 TDZA TT,TT	; we must use unit-mode for SIOT
		  SETO TT,	; otherwise hack block image for 36-bit xfers
		JRST .+1]
	SYSCAL OPEN,[	(TT)1+[.BIO,,DC ? .UAO,,DC]
			FILDEV ? FTPOF1 ? FTPOF2 ? FILDIR ]
	 JRST FILERR
	FWRITE NETO,[[RETR ],TS,(A),CRLF,,]


ATRPUT:	SKIPE CNECTD		;Here if to-host isn't us.  This TRAN is a PUT.
	 CAME B,FDHST		;If not already connected to it, connect.
	  CAIA
	   JRST ATRPU1
	PUSHJ P,CONN
	 JRST ERRTR1
ATRPU1:	PUSHJ P,DCSEND		;If it's a PDP10, switch to TYPE I BYTE 36.
	PUSHJ P,ARGTRN		;Read in the To-filename.
	    [ASCIZ /to foreign file:  /]
	MOVE A,[ARGBUF,,ALTARG]
	BLT A,ALTARG+JCLBFL-1	;Save it away.
				;Will send after we read rest of command.
	PUSHJ P,THOSTN		;Read the From-host.
	 JRST ERRTR1
	LDB B,[4300,,A]		;All but sign
	CAME B,OWNHST		;It must be us, if this is to be a PUT.
	 JRST [	OUTZ TYOC,[ASCIZ /TRAN must be either to or from the local host.
/]
		JRST ERRTR1]
	MOVE B,JCLP
	ILDB B,B
	MOVEI A,ARGCT
	CAIN B,^M		;If the from-filename is null, default it to the
	 JRST [	PUSHJ P,FLSLIN
		JRST ATRPU2]	; to-filename.
	MOVE C,ARGCT
	PUSHJ P,ARG
	    [ASCIZ/from local file:  /]
	JUMPN C,ATRPU2		;If the to-file was null, use the from-file for it.
	MOVE C,[ARGBUF,,ALTARG]
	BLT C,ALTARG+JCLBFL-1	;Save it away.
				;Will send after we read rest of command.
ATRPU2:	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR
	SKIPLE TT,DCTYPE
	 JRST [	MOVE TT,DCBYTE	; If logical-byte being used,
		CAIE TT,36.	; Then if not 36-bit bytes
		 TDZA TT,TT	; we must use unit-mode for SIOT
		  SETO TT,	; otherwise hack block image for 36-bit xfers
		JRST .+1]
	SYSCAL OPEN,[	(TT)1+[.BII,,DC ? .UAI,,DC]
			FILDEV ? FILFN1 ? FILFN2 ? FILDIR ]
	 JRST FILERR
	PUSHJ P,NETWLS
	FWRITE NETO,[[STOR ],TZ,ALTARG,CRLF,,]
	JRST APRIN1

;HOSTNM for TRAN command - if there is no Altmode, we assume the host name
;was not specified, and default it to the local host.
;For TCP, I think this breaks.
THOSTN:	MOVE B,JCLP	    ;Look ahead. See if next filespec preceded by altmode.
	MOVE A,[440700,,THOSTB]
TSYMGL:	ILDB T,B
	CAIN T,^Q
	 JRST TSYMGQ	    ;^Q'd _'s, 's, and ='s don't count.
	CAIE T,"=
	 CAIN T,"_	    ;Reached the end of the next filename with no altmode
	  JRST TSYMDF	    ; => default the hostname.
	CAIN T,"
	 JRST TSYMDF
TSYMG1:	CAIE T,^C	    ;If end of jcl, stop
	 CAIN T,^M
	  JRST TSYMDF
	CAIN T,^_	    ;I bet you didn't know this ends JCL
	 JRST TSYMDF
	JUMPE T,TSYMDF
	CAIE T,33	    ;Reached an altmode => host name explicitly spec'd,
	 JRST [	IDPB T,A
		JRST TSYMGL]
	MOVEM B,JCLP	    ;Mark it (and altmode) gobbled so filename doesn't
	SETZ T,		    ;include them.
	IDPB T,A
	MOVEI A,THOSTB
	JRST NHOSTN	    ;Read the host name and convert to number.

TSYMGQ:	ILDB T,B
	JRST TSYMG1

TSYMDF:	MOVE A,OWNHST
	TLO A,400000		;Can lose if not running on a PDP10!
	JRST POPJ1

SUBTTL Directory Listing Commands

ALSTL:	PUSHJ P,ARG		;LISTL - list local directory
	    [ASCIZ /Directory:  /]
	MOVE B,[FILDEV,,FILDEV]
	PUSHJ P,FILPAR		;Parse the directory specified.  
				;It becomes our default too.
	SYSCAL OPEN,[ [.UAI,,DC]
		      FILDEV ? ['.FILE.] ? [SIXBIT /(DIR)/] ? FILDIR]
	 JRST FILERR
	MOVEI A,DC
	MOVEI B,TYOC
	PUSHJ P,XFRASC		;Read whole file off DC and write to TYOC,
				;flushing padding.
	JRST POPJ1

;LISTF - list foreign directory
ALSTF:	SKIPA A,[[ASCIZ /LIST /]]
ALSTB:	 MOVEI A,[ASCIZ /NLST /] ;LISTB - brief (names only) listing of directory.
	PUSH P,DCBYTE
	PUSH P,DCTYPE
	PUSH P,DCSENT		;Save current connection status
	PUSH P,A		;Save away the command type (FTP command to use).
	PUSHJ P,CONTST		;If not connected yet, ask for host and connect.
	MOVE A,DCBYTE
	CAIN A,8		;Switch to TYPE A BYTE 8 (if not there already).
	 SKIPE DCTYPE
	  JRST [ PUSHJ P,ATEXT
		  JRST ERRTR1
		 JRST .+1]
	PUSHJ P,ARG		;Read in directory name, send to server.
	    [ASCIZ /Directory:  /]
	POP P,B			;Recover FTP command (LIST or NLST).
	PUSHJ P,NETRLS
	FWRITE NETO,[TZ,(B),TS,(A),CRLF,,]
	.NETS NETO,
	MOVEI B,TYOC
	PUSHJ P,NETREAD	    ;Read what the LIST command sends us, and type on TTY.
	POP P,C		    ;Now restore the old status.
	JUMPE C,[ MOVEM C,DCSENT
		  SUB P,[2,,2] ;If DCSENT was 0, just restore it - thats all!
		  JRST POPJ1]
	POP P,B		    ;Otherwise restore the TYPE, then the BYTE.
	MOVE A,(P)	    ;If restoring to TYPE A, BYTE 8, don't bother
	CAIN A,8
	 JUMPE B,[ MOVEM A,DCBYTE
		   MOVEM B,DCTYPE
		   SETOM DCSENT
		   POP P,A
		   JRST POPJ1 ]
	MOVEI D,[ASCIZ /A/]
	SKIPGE B
	 MOVEI D,[ASCIZ /I/]
	SKIPLE B
	 JRST [	MOVEI D,[ASCIZ /L 36/]
		CAIE A,36.
		 MOVEI D,[ASCIZ /L 8/]
		JRST .+1]
	PUSHJ P,ATYPE3
	 JRST ERRTR1
	POP P,A
	TLNE F,%LTCP
	 JRST POPJ1
	PUSHJ P,ABYTE2
	 JFCL
	JRST POPJ1

SUBTTL Type and Byte Commands.

;If have just connected, to a PDP10, try negotiating 36 bit image mode.
DCSEND:	SKIPE DCSENT
	 POPJ P,
	PUSHJ P,ATEN		;Specify TYPE I, BYTE 36.
	 POPJ P,		;He doesn't like them => proceed, using ASCII mode.
	POPJ P,

;Set the transfer TYPE, and possibly the byte size.
ATYPE:	TLNE F,%LTCP
	 JRST [	CALL ARGCON
		 [ASCIZ /A for ASCII, or L <size> for LOGICAL: /]
		JRST ATYPE1 ]
	PUSHJ P,ARGCON
	    [ASCIZ /A for ASCII, I for IMAGE: /]
ATYPE1:	HRRZ D,(A)
	MOVE D,1(A)
	ILDB D,D		;Check the type he specified.
	CAIL D,140		;Uppercasify.
	 SUBI D,40
	CAIE D,"L		;Logical type is hairy.
	 JRST ATYPE2
	TLNN F,%LTCP		;Only allowed if doing TCP.
	 JRST ATYPEL
	MOVE D,[ASCIZ "L 36"]
	MOVEM D,ALTARG
	MOVEI D,ALTARG	   	;Actual argument string for server.
	HRLZI T,260700
	HRR T,D			;Bp to byte size in it.
	MOVE B,1(A)		;Bp to user's string.
ATYPL1:	ILDB C,B
	JUMPE C,[ OUT(TYOC,("Defaulting logical byte size to 36 bits."),EOL)
		  JRST ATYPE3 ]
	CAIL C,60		;Look for a number.
	 CAILE C,71
	  JRST ATYPL1
	IDPB C,T		;Deposit first digit.
	ILDB C,B		;There may be a second digit.
	JUMPE C,ATYPL2
	CAIL C,60
	 CAILE C,71
ATYPL2:	  SETZ C,
	IDPB C,T		;Deposit second (and last) digit.
	JRST ATYPE3

ATYPE2:	CAIN D,"A
	 JRST [	MOVEI D,[ASCIZ /A/]
		JRST ATYPE3 ]
	CAIN D,"I
	 JRST [	TLNE F,%LTCP	;If doing NCP, allow type "I".
		 JRST ATYPEL
		MOVEI D,[ASCIZ /I/]
		JRST ATYPE3 ]
ATYPEL:	FWRITE TYOC,[[? "],TS,(A),[" is not a type that I understand.],CRLF,,]
	JRST ERRTR1

;Select TYPE according to ASCIZ string in D (e.g. "L 36").
ATYPE3:	SETOM DCSENT		;Override defaults now.
	FWRITE NETO,[[TYPE ],TZ,(D),CRLF,,]
	PUSH P,D
	MOVEI A,200.		;See whether server it likes it.
	PUSHJ P,REPLY
	 JRST POPAJ
	POP P,D			;Accepted.
	SETOM DCTYPE		;DCTYPE gets 0 for ASCII, -1 for image.
	HRLI D,350700
	LDB T,D			;Get 1st char
	CAIN T,"A
	 SETZM DCTYPE
	CAIN T,"L
	 MOVMS DCTYPE		;Logical byte, set 1
	JRST POPJ1

;Set data connection byte size.  We only handle 8, 32 or 36-bit bytes.
ABYTE:	TLNN F,%LTCP
	 JRST [	CALL ARGCON
		  [ASCIZ /Byte size (8 or 36): /]
		CALL RDDEC
		JRST ABYTE1]
	PUSHJ P,ARGCON
	    [ASCIZ /Byte Size (8, 32 or 36):  /]
	PUSHJ P,RDDEC		;Now convert arg to binary in A.
	CAIN A,32.
	 JRST ABYTE2		;Is the byte size ok with us?
ABYTE1:	CAIE A,8.
	 CAIN A,36.
	  JRST ABYTE2
ABYTEL:	FWRITE TYOC,[[? "],N9,A,[" is not a byte size I can handle.],CRLF,,]
	JRST ERRTR1

	;Select BYTE size in A.
ABYTE2:	TLNE F,%LTCP
	 JRST [	FWRITE NETO,[[TYPE L ],N9,A,CRLF,,]
		JRST ABYTE3 ]	 
	FWRITE NETO,[[BYTE ],N9,A,CRLF,,]
ABYTE3:	SETOM DCSENT		;No need to send him our defaults - they are overridden now.
	PUSH P,A
	MOVEI A,200.		;Yes, send it to server and see if ok with him.
	PUSHJ P,REPLY
	 JRST POPAJ
	POP P,DCBYTE		;If so, remember it as the one we are using.
	JRST POPJ1


;Select 36-bit Image mode.
ATEN:	PUSHJ P,CONTST
	MOVEI D,[ASCIZ /L 36/]	;TCP says it this way.
	TLNN F,%LTCP
	 MOVEI D,[ASCIZ /I/]	;NCP says it this way.
	PUSHJ P,ATYPE3
	 POPJ P,
	TLNE F,%LTCP
	 JRST [	MOVEI A,36.
		MOVEM A,DCBYTE
		JRST POPJ1]
	MOVEI A,36.		;If it wins, do the BYTE 36.
	JRST ABYTE2

;Select 8-bit ASCII mode.  Shorthand for TYPE A, BYTE 8.
ATEXT:	PUSHJ P,CONTST
	MOVEI D,[ASCIZ /A/]
	PUSHJ P,ATYPE3		;Try the TYPE A.
	 POPJ P,
	TLNE F,%LTCP
	 JRST POPJ1
	MOVEI A,8
	JRST ABYTE2		;If it wins, do the BYTE 8.

SUBTTL Script Files and Command Files

ASCRIP:	PUSHJ P,ARG
	    [ASCIZ/Script file:  /]
	MOVE B,[SCRIPF,,SCRIPF]
	PUSHJ P,FILPAR
	SETZM SCRIPT
	SYSCAL OPEN,[	[.UAO,,SCRIPC]
			SCRIPF ? SCRIP1 ? SCRIP2 ? SCRIPS]
	 JRST FILERR
	SETOM SCRIPT
	JRST POPJ1

AESCRI:	PUSHJ P,FLSLIN		;ESCRIPT - close script file.  Flush the rest of the line.
	SKIPN TYOTTY		;Closing script and can't type on tty =>
	 OUTZ TYOC,[ASCIZ /Note: committing suicide since can't type on TTY
/]
	.CLOSE SCRIPC,
	SETZM SCRIPT 
	JRST POPJ1

AXFILE:	PUSHJ P,ARG
	    [ASCIZ/Command file:  /]
	MOVE B,[COMDEV,,COMDEV]
	PUSHJ P,FILPAR
	SETZM COMFIL
	SYSCAL OPEN,[	[.UAI,,COMC]
			COMDEV ? COMFN1 ? COMFN2 ? COMDIR]
	 JRST FILERR
	SETOM COMFIL
	JRST POPJ1

SUBTTL Reply Processing

;Call here with A containing the decimal number of expected reply.
;Skips if success reply seen, non-skip return if error reply seen,
;Handles intermediate conditions, requests for password, printing of replies, etc.
;Returns with B containing reply code found.
;The reply will be in the string variable REPLYS

REPLY:	.NETS NETO,		;just in case
REPLY0:	BCONC
	PUSHJ P,REPLIN		;get a line, number prefix in RH(B), hyphen flag in SIGN(B)
	JUMPGE B,REPLY2		;jump if single-line
	HRRZ C,B
	AOJE B,[ECONC REPLYS	;Line with no reply code!  Show it to user
		SKIPN JCLTRN
		 OUTS TYOC,REPLYS
		JRST REPLY0]	;and then ignore it.
	PUSH P,C		;multi-line, gobble rest of it, concatenating all together.
REPLY1:	PUSHJ P,REPLIN
	JUMPL B,REPLY1		;no number, get more
	POP P,B
REPLY2:	ECONC REPLYS		;REPLYS := reply string
	SKIPE PRREP
	 OUTS TYOC,REPLYS
	CAMN B,A		;expected reply?
	 JRST POPJ1		;Yes, winning
	CAILE B,999.		;range check reply code
	 JRST REPTY9		;Anything over 999 is considered to be in the 900's
	MOVE C,B		;No, get type of reply
	IDIVI C,100.		; which is the hundreds digit
REPLY3:	JRST @.+1(C)		;Jump to reply handler based on code first digit.
	 REPTY0
	 REPTY1
	 REPTY2
	 REPTY3
	 REPTY4
	 REPTY5
	 REPTY6
	 REPTY7
	 REPTY8
	 REPTY9

;0xx useless information.  [Old protocol only.]  Might be interesting, type it out.
REPTY0:	PUSHJ P,REPDIS
	JRST REPLY

;1xx positive preliminary reply.  Might be interesting, type it out.
REPTY1:	PUSHJ P,REPDIS
	CAIL A,100.		; If expected reply was in 1xx range,
	 CAILE A,199.
	  JRST REPLY
	JRST POPJ1		; then take win return anyway...

;2xx positive completion reply.
;Indicates winnage, except not same code as expected so print.
REPTY2:	PUSHJ P,REPDIS
	JRST POPJ1

;4xx temporary error, 5xx permanent error, 6xx, 7xx, 8xx, 9xx undefined.
REPTY4: REPTY5: REPTY6: REPTY7: REPTY8: REPTY9:
REPTYE:	SKIPN PRREP		;If PRREP, we already printed it, so don't do it again.
	 OUTS TYOC,REPLYS	;error message - print it including reply code.
	POPJ P,			;lose

;3xx User action required.
;Special cases are 330 give password, 331 give account, 332 login please
;Others handled by caller saying e.g. 354 is what I expect.
REPTY3:	TLNE F,%LTCP
	 JRST [	CAIN B,331.
		 JRST REP3PA
		CAIN B,332.
		 JRST REP3AC
		JRST REPTYE]
	CAIN B,330.
	 JRST REP3PA
	CAIN B,331.
	 JRST REP3AC
	CAIN B,332.
	 JRST REP3LO
	JRST REPTYE		;Unexpected 300 code - treat it as an error.

REP3PA:	SETOM INHIDE		; Don't echo next input line.  TYILIN resets.
	JSP E,REP3GT
	    [ASCIZ/Password (safety of this password not guaranteed):  /]
	    [ASCIZ/PASS/]

REP3AC:	JSP E,REP3GT
	    [ASCIZ/Account:  /]
	    [ASCIZ/ACCT/]

REP3LO:	JSP E,REP3GT
	    [ASCIZ/Login Name:  /]
	    [ASCIZ/USER/]

REP3GT:	OUTZ TYOC,@(E)
	PUSHJ P,ARG		;read the password or whatever.
	 [0]
	OUTZ NETO,@1(E)		;send command the server wanted
	OUTI NETO,40
	OUTS NETO,(A)		;with the arg we read.
	CRLF NETO,
	JRST REPLY		;try again

;Routine to display a non-error reply.  Doesn't print the reply code.
REPDIS:	SKIPN JCLTRN		;Don't show it for :FTP FOO=BAR.
	 SKIPE PRREP		;or if it was already typed out.
	  POPJ P,
	PUSHAE P,[A,B]
	HRRZ A,REPLYS		;byte count
	MOVE B,REPLYS+1		;byte pointer
REPDS0:	SOJL A,REPDS9
	ILDB T,B		;flush reply code number and space or hyphen following it
	CAIL T,"0
	 CAILE T,"9
	  CAIA
	   JRST REPDS0
	CAIE T,40
	 CAIN T,"-
	  CAIA
REPDS1:	   PUSHJ P,TYO
	CAIN T,^J
	 JRST REPDS0
	SOJL A,REPDS9
	ILDB T,B
	JRST REPDS1

REPDS9:	POPAE P,[B,A]
	POPJ P,

;Routine to read in a line of reply.
;Returns in B: -1 if no reply code, reply code for single-line reply,
;400000,,reply code for first line of multi-line reply.
;If connection gets closed, returns via POP1J with -1 in B
REPLIN:	SETO B,
REPLN0:	.IOT NETI,TT
	JUMPL TT,POP1J		;EOF
	CAIN TT,^G		;rumor that BBN sends bells
	 JRST REPLN0
	CAIN TT,177	    	;Ignore random rubouts from Multics
	 JRST REPLN0
	CAIN TT,377
	 JRST [	.IOT NETI,TT	;Ignore new-TELNET control codes from Multics.
		JRST REPLN0]
	CAIL TT,"0
	 CAILE TT,"9
	  JRST REPLN1
	SKIPGE B
	 TDZA B,B
	  IMULI B,10.
	ADDI B,-"0(TT)
	OUTI STRC,(TT)
	JRST REPLN0

REPLN1:	CAIN TT,"-
	 HRLI B,(SETZ)
REPLN2:	OUTI STRC,(TT)
	JUMPL TT,POP1J		;EOF
	CAIN TT,^J
	 POPJ P,
	.IOT NETI,TT
	JRST REPLN2

SUBTTL TTY Line Input

;Reads a line from the TTY or command file into JCLBUF (or wherever JCLBFP points).
;Clobbers no ACs

TYILIN:	PUSHAE P,[A,B,C,E,T]
	PUSH P,READIN		;For now, echo only on the TTY.  After rubout processing
	SETOM READIN		;is finished, we echo the edited command line on script file.
TYIOVR:	SKIPE COMFIL
	 JRST TYILN4
	SYSCAL RCPOS,[MOVEI TYOC ? MOVEM E]
	 .LOSE 1000
TYILN4:	MOVE B,JCLBFP
	MOVEM B,JCLP		;B is B.P. to store chars through.
TYILN0:	SKIPN COMFIL
	 JRST TYILN2
	.IOT COMC,A		;IF HAVE COMMAND FILE, READ FROM IT,
	JUMPGE A,TYILN3
	OUTZ TYOC,[ASCIZ /End of Command file
/]
	.CLOSE COMC,		;(EOF => IT'S NOT OPEN ANY MORE)
	SETZM COMFIL
	SKIPN TRITTY		;If disowned and tty not input-translated, we can't read anything.
	 .LOGOUT
	JRST TYILN9		;Now pretend to read a null command, so we make the main loop prompt.

TYILN2:	SETZM SILENT
	SKIPN TYITTY		;ELSE IF HAVE TTY INPUT, READ FROM IT, ELSE BARF.
	 JRST NOTTY
	.IOT TYIC,A
TYILN3:	CAIE A,177
	 CAIGE A,40
	  JRST TYIRUB		;Jump if control
TYILN1:	MOVE T,B
	SUB T,JCLBFP
	HRRES T
	CAIL T,JCLBFL-1
	 JRST [	OUTI TYOC,^G	;Buffer full?  Complain, don't store character.
		JRST TYILN0]
	SKIPE INHIDE		; Hiding input?
	 JRST TYILND		; Yes, don't echo.
	SKIPE HDXTTY
	 SKIPE COMFIL
	  OUTI TYOC,(A)		;echo, if reading from TTY and it is full duplex.
TYILND:	IDPB A,B		;stash away.
	JRST TYILN0

TYIRUB:	SKIPN COMFIL		;Don't recognize Rubout from command files.
	 CAIE A,177
	  JRST TYICTL
	MOVE A,B
	MOVE C,JCLBFP
	IBP A
	IBP C
	CAMN A,C		;Rubout when buffer empty types a CRLF.
	 JRST [ CRLF TYOC, ? JRST TYIOVR ]
	LDB A,B			;char getting rubbed
	D7BPT B			;officially remove it from buffer.
	SKIPE INHIDE		; If hiding input,
	 JRST TYILN0		; needn't hack cursor.
	SKIPN DISTTY
	 JRST TYIRUP		;jump if printing terminal
	CAIL A,40
	 JRST [ OUTI TYOC,^P ? OUTI TYOC,"X ? JRST TYILN0 ]
TYIRDS:	OUTI TYOC,^P
	OUTI TYOC,"H
	OUTI TYOC,10(E)
	HLRZ A,E
	OUTI TYOC,^P
	OUTI TYOC,"V
	OUTI TYOC,10(A)
	OUTI TYOC,^P
	OUTI TYOC,"L
TYILN8:	SETZ A,
	MOVE T,B
	IDPB A,T		;Make the string ASCIZ.
	SKIPN INHIDE		; Don't output if hiding it.
	 OUTZ TYOC,@JCLBFP
	JRST TYILN0

TYIRUP:	SKIPN INHIDE
	 .IOT TYOC,A
	JRST TYILN0

TYICTL:	CAIE A,33		;altmode and tab are printing characters
	 CAIN A,11
	  JRST TYILN1
	CAIN A,10		;so is backspace
	 JRST TYILN1
	CAIN A,^Q		;^Q is needed for quoting chars in filenames.
	 JRST TYILN1
	CAIE A,^N		;^N also ends line (for JCL from DDT).
	 CAIN A,15		;CR ends the line
	  JRST TYILN9
	CAIN A,^J		;Ignore line feed
	 JRST TYILN0
	SKIPE COMFIL		;Input editing controls not recognized in command files.
	 JRST [	CAIN A,^C
		 JRST TYILN0	;Ignore ^C in command files.
		JRST TYILN1]
	CAIN A,^L		;^L redisplays input
	 JRST TYIFF
	CAIN A,^U
	 JRST TYIKIL
	CAIE A,^G		;^D and ^G flush input buffer
	 CAIN A,^D
	  JRST TYIKIL
	CAIN A,^C		;^C ends input and requests suicide after command.
	 JRST TYICTC
	JRST TYILN1		;Other controls taken as ordinary characters.  This really sucks,
				;the right way to do this is to make ^Q quote the next character,
				;unfortunately this code is too bad for me to fix it easily.
				;This mainly for Tenex control-V.

TYIKIL:	SKIPN DISTTY		; if printing tty,
	 JRST [ MOVE B,JCLP ? JRST TYIRS0 ]	; flush buffer, reprompt
	MOVE A,B		; else, display terminal, wipe all chars
	MOVE C,JCLBFP		; one at a time
	IBP A
	IBP C
	CAMN A,C
	 JRST TYILN0
	LDB A,B			;char getting rubbed
	D7BPT B			;officially remove it from buffer.
	SKIPE INHIDE		; If hiding input,
	 JRST TYIKIL		; needn't hack cursor.
	CAIL A,40		; else erase char from screen
	 JRST [ OUTI TYOC,^P ? OUTI TYOC,"X ? JRST .+1 ]
	JRST TYIKIL


TYIRST:	MOVE B,JCLP		;flush input we got so far.
TYIFF:	SKIPE DISTTY
	 JRST [ OUTI TYOC,^P	;On display tty,
		OUTI TYOC,"C	; set horzontial position to
		MOVEI E,0	;Change display-point to top-left corner
		JRST TYIRDS ]	;and redisplay input
TYIRS0:	SKIPN HDXTTY
	 .IOT TYOC,A		;echo the mumble
	CRLF TYOC,		;redisplay on printing tty
	JRST TYILN8

TYICTC:	SETOM SUICID		;^C - say we should suicide soon.
	SKIPN HDXTTY
	 .IOT TYOC,A		;Echo the ^C, then end the command.
TYILN9:	SETZ A,			;Make input line end with null, for sake of outputting to SCRIPC.
	IDPB A,B
	POP P,READIN
	SKIPN INHIDE
	 JRST [	SKIPE SCRIPT	;Output the line to the script file if any.
		 OUTPZ SCRIPC,JCLP
		JRST .+1]
	CRLF TYOC,		;This CRLF goes to TTY and to script file.
	MOVEI A,^M		;Now make input line end properly, with CR-Null
	DPB A,B
	SETZ A,
	IDPB A,B
	SETZM INHIDE		; Crock, always reset after each call.
	POPAE P,[T,E,C,B,A]
	POPJ P,

SUBTTL Interrupt Level

TSINT:	0 ? 0
	PUSHAE P,[T,TT,A]
	SKIPGE A,TSINT
	 JRST TSINT2		;I/O interrupts
	TRNN A,%PIIOC
	 .VALUE			;non-enabled interrupt
	.SUSET [.RBCHN,,A]
	CAIL A,NETI
	 CAILE A,NETDO
	  JRST TSINT1		;Not network
	.DISMIS [NETERR]	;Network, inform user

TSINT1:	HRLZ A,TSINT+1		;Give error to DDT
	HRRI A,1+.LZ %PIIOC	;and allow it to be continued
	MOVEM A,TSINT
	POPAE P,[A,TT,T]
	.CALL [ SETZ ? 'DISMIS ? MOVEI ? TSINT+1 ? MOVEI ? MOVEI ? SETZ TSINT ]
	 .LOSE %LSSYS

TSINT2:	TRNN A,1_TYIC
	 .VALUE
	MOVEI A,TYIC
	.ITYIC A,
	 JRST TSINT3
	CAIE A,^G
	 JRST TSINT3
	OUTZ TYOC,[ASCIZ/
QUIT
/]
	.DISMISS [ERRTR1]

TSINT3:	POPAE P,[A,TT,T]
	.DISMISS TSINT+1

SUBTTL Data Transfer Routines

;Transfer the file open on the channel in A to the channel in B,
;using whichever mode is appropriate.  Closes the input channel,
;but not the output.  If the output channel specified is TYOC, we
;output via TYO to the TTY and/or script file.
; TCP transfers always use 8-bit bytes.  The storage format to use
; is determined by DCBYTE and DCTYPE and the server is told which
; to use with the TYPE command.
;    Svr cmd	DCTYPE	DCBYTE	Description
;    TYPE A	0	8	ASCII text.  User stores as 7-bit bytes
;    TYPE I	-1	8, 36	Image.  User stores as packed 36-bit words
;    TYPE L 8	1	8	Logical byte 8.  User stores as 8-bit bytes.
;    TYPE L 36	1	36	Logical byte 36.  Same as Image, but the right
;				thing for PDP-10 binary file xfers.

XFR:	SETZM NBITS
	SETZM BUFFER	;Clear buffer - else, an ascii xfr after an image one
	MOVE C,[BUFFER,,BUFFER+1] ;might set some low bits.
	BLT C,BUFFER+BUFFL-1
	SKIPE DCTYPE
	 JRST XFRIMG

;Transfer the file open on channel in A to the channel in B, in ASCII mode.
XFRASC:	MOVEI D,BUFFL*5-5	;Compute buffer size
	CAIN B,TYOC
	 MOVEI D,20.
	MOVEM D,XFRLBV
	MOVEI D,BUFFER+BUFFL-1	;And last-word pointer
	CAIN B,TYOC
	 MOVEI D,BUFFER+4
	MOVEM D,XFRLWP
	MOVE C,[440700,,BUFFER]
	MOVEI D,5		;First read ahead one word.
	SYSCAL SIOT,[A ? C ? D]	;When we output the buffer we always save 1 word,
	 .LOSE 1400		;so that we can always flush up to 5 chars
				;of padding (^C's or ^@'s).
	JUMPG D,XFRAS2		;Didn't get even 1 word => at EOF.
XFRASL:	MOVE C,[440700,,BUFFER+1]	;Try to fill up buffer.
	MOVE D,XFRLBV
	SYSCAL SIOT,[A ? C ? D]
	 .LOSE 1400
	JUMPG D,XFRAS1	    ;Didn't fill it all up => at EOF, flush some padding.
	MOVE C,[440700,,BUFFER]
	MOVE D,XFRLBV		;Did fill it => output it, but save the last word,
	PUSHJ P,XFRASO
	MOVEI D,<BUFFL-1>*40.;8-bit ASCII, remember?  1 word is 5 bytes = 40 bits.
	CAIN B,TYOC
	 MOVEI D,20.*8
	ADDM D,NBITS
	MOVE C,@XFRLWP		;Move last word down into first word.
	MOVEM C,BUFFER
	JRST XFRASL

XFRAS2:	ADD D,XFRLBV
XFRAS1:	MOVNS D
	ADD D,XFRLBV
	ADDI D,5		;# chars we have in buffer now.
	SYSCAL CLOSE,[A]	; Close empty input chan
	 .LOSE %LSFIL
	SETZ A,
	PTSKIP A,C		; Make BP canonical (can be 440700 from SIOT...)
XFRAS4:	JUMPE D,CPOPJ		;Discard any number of ^@'s or ^C's, then one ^L.
	LDB T,C
	CAIE T,^C
	 JUMPN T,XFRAS3
	D7BPT C
	SOJA D,XFRAS4

XFRAS3:	CAIE T,^L
	 JRST XFRAS5
	D7BPT C
	SOJE D,CPOPJ
XFRAS5:	MOVE C,[440700,,BUFFER]	;Output what's left after flushing padding.
	MOVE T,D
	IMULI T,8
	ADDM T,NBITS
;Output c(D) chars from b.p. in C to channel in B, handling TYOC specially.
XFRASO:	CAIN B,TYOC
	 JRST XFRTTY
	SYSCAL SIOT,[B ? C ? D]
	 .LOSE 1400
	POPJ P,

XFRTTY:	ILDB T,C
	PUSHJ P,TYO
	SOJG D,XFRTTY
	POPJ P,

; Image transfer from channel in A to channel in B.
XFRIMG:	TLNE F,%LTCP
	 JRST XFRIT			; Hack TCP local/image transfer
	SKIPLE DCTYPE
	 JRST XFRLCL			; Logical-byte transfer
	MOVE C,DCBYTE
	CAIE C,36.
	 JRST XFRIM1			; Go transfer bytes, not words
XFRIM4:	MOVE C,[-BUFFL,,BUFFER]		; Read a bufferfull.
	SYSCAL IOT,[A ? C]
	 .LOSE %LSFIL
	JUMPGE C,[MOVE C,[-BUFFL,,BUFFER]	; Restore AOBJN ptr
		SYSCAL IOT,[B ? C]	; And use to output buffer.
		 .LOSE %LSFIL
		MOVEI C,36.*BUFFL
		ADDM C,NBITS
		JRST XFRIM4]
	HRLOI C,-BUFFER-1(C)	; Put <#wds-1> in LH, -1 in RH
	EQVI C,BUFFER		; And convert to AOBJN pointing to buffer.
	MOVE D,C
	SYSCAL IOT,[B ? C]	; And output rest of stuff.
	 .LOSE %LSFIL
	SUBI C,(D)		; C gets number of words IOTed
	IMULI C,36.
	ADDM C,NBITS
XFRIM9:	SYSCAL CLOSE,[A]	; Aha, got it all. Close empty input chan.
	 .LOSE %LSFIL
	POPJ P,

XFRIM1:	MOVEI C,36.		;Get bytes per word
	IDIV C,DCBYTE
	MOVEM C,XFRBPW
	MOVE T,DCBYTE		;Get byte pointer to buffer
	MOVE D,[440000,,BUFFER]
	DPB T,[300600,,D]
	SKIPL XFRDIR
	 JRST XFRIM2		; Reading from net
	MOVE C,[-BUFFL,,BUFFER]	; Read a bufferfull from disk
	SYSCAL IOT,[A ? C]
	 .LOSE %LSFIL
	MOVEI C,-BUFFER(C)	; Number of words read
	IMUL C,XFRBPW		; Number of bytes to send
	JUMPE C,XFRIM9		; EOF
	IMUL T,C
	ADDM T,NBITS
	SYSCAL SIOT,[B ? D ? C]	;Output them to net
	 .LOSE %LSFIL
	JRST XFRIM1

XFRIM2:	MOVEI C,BUFFL		;Read a bufferfull from net
	IMUL C,XFRBPW
	MOVE E,C
	SYSCAL SIOT,[A ? D ? C]
	 .LOSE %LSFIL
	SUBB E,C		;Number of bytes read
	JUMPE E,XFRIM9		;EOF
	IMUL T,E
	ADDM T,NBITS
	IDIV C,XFRBPW
	SKIPE D
	 ADDI C,1		;Part word possible at EOF
	MOVNS C
	HRLZS C
	HRRI C,BUFFER
	SYSCAL IOT,[B ? C]	;Write to disk
	 .LOSE %LSFIL
	JRST XFRIM1

; Local-byte Transfer.
XFRLCL:	PUSHAE P,[C,D,E]
	MOVEI C,BUFFER
	MOVE D,DCBYTE			; Get byte-size
	DPB D,[$SFLD,,C]		; Stick into size field of BP
	TLO C,440000			; Start at beg of word
	MOVEI D,36.
	IDIV D,DCBYTE		; Find # bytes in a word
	MOVEM D,XFRBPW		; Save
	IMULI D,XFRBFL		; Find # bytes in buffer
	MOVE E,D		; Save cnt
	PUSH P,C		; Save BP
XFRLC2:	MOVE D,E		; Get # bytes max to read
	MOVE C,(P)		; Restore BP
	SKIPE XFRDIR
	 JRST [	MOVEI D,XFRBFL	; Nope, DSK... set # wds
		HRLI C,444400	; and use word-size bytes.
		JRST .+1]	;
	SYSCAL SIOT,[A ? C ? D]	; Slurp up
	 JSR AUTPSY
	SKIPE XFRDIR
	 IMUL D,XFRBPW		; convert count to # bytes.
	SUBM E,D		; Get # bytes read in D
	MOVE T,D		; Stat cruft
	IMUL T,DCBYTE
	ADDM T,NBITS
	JUMPLE D,XFRLC9
	SKIPN XFRDIR
	 JRST [	PUSH P,E	; Pad out.
		IDIV D,XFRBPW	; Find # words
		CAILE E,	; Round up
		 AOS D
		CAILE E,	; Pad out with zeros
		 PUSHJ P,[PUSH P,D
			  SETZ D,
			  IDPB D,C
			  POP P,D
			  SOS (P)
			  SOS (P)	; Call again til done.
			  SOJA E,APOPJ ]
		POP P,E
		JRST .+1]
	MOVE C,(P)		; Restore BP
	SKIPN XFRDIR
	 HRLI C,444400		; use word-size bytes.
	SYSCAL SIOT,[B ? C ? D]	; Output them
	 JSR AUTPSY
	JRST XFRLC2
XFRLC9:	POP P,C
	POPAE P,[E,D,C]
	POPJ P,


; XFRIT - TCP Image transfer.  Network 8-bit bytes are packed into
;	disk 36-bit words, and vice versa.
;	A/ input channel
;	B/ output channel
;	XFRDIR/ -1 if outputting to net

XFRIT:	SKIPLE DCTYPE		; Do a test of logical byte mode
	 JRST [	MOVE T,DCBYTE
		CAIN T,36.	; Currently must be 36
		 JRST .+1
		CAIN T,8.
		 JRST XFRLCL	; Hmm, try to hack this bytesize (shd be 8)
		OUT(TYOC,("Cannot handle TYPE L "),D(DCBYTE),(", using TYPE L 8."),EOL)
		JRST XFRLCL]
	PUSHAE P,[C,D,E,R]
;	TRNE F,%NTDIR
	SKIPE XFRDIR
	 JRST XFRITO		; Output, from disk to net.

	; TCP Image Input, network 8-bit bytes must be packed into
	; 36-bit words.
XFR8BL==<<<XFRBFL*9>+7>/8.>	; # words in 8-bit byte buffer
;	UAROPN [%ARTZM,,BUFFAR ? [XFRBFL]]
;	UAROPN [%ARTZM,,TMPAR ? [XFR8BL]]
;	MOVE E,$ARLOC+BUFFAR
	MOVEI E,BUFFER
	HRLI E,-XFRBFL		; Set up initial AOBJN to word buffer
	MOVEI R,0		; Point to beginning of cycle

XFRIT2:	MOVEI D,4*XFR8BL	; # bytes to slurp from net
	PUSH P,D
;	MOVE C,$ARLOC+TMPAR
	MOVEI C,TMPBUF
	HRLI C,441000
	SYSCAL SIOT,[A ? C ? D]	; Get input
	 JSR AUTPSY
;	AOS ALIVEC		; Say we're still active
	TRZ F,%TMP
	CAILE D,
	 TRO F,%TMP		; Set flag if last slurp.
	POP P,C			; Restore # bytes we asked for
	SUBI C,(D)		; Find # bytes we got

	MOVE D,C		; Stat cruft
	LSH D,3
	ADDM D,NBITS

	IDIVI C,4		; Get # words (rem in D)
	JUMPE C,[
;		HRR C,$ARLOC+TMPAR	; If no full words, skip stuff.
		HRRI C,TMPBUF
		JRST XFRIT4]
	MOVN C,C
	HRLZS C
;	HRR C,$ARLOC+TMPAR	; Now have AOBJN to the fullwords we got
	HRRI C,TMPBUF
	JRST @XFITCT(R)		; Re-enter cycle at right place

	; C has AOBJN to full words we received (4 8-bit bytes)
	; D has # remaining bytes in last word
	; E has AOBJN to disk output buffer
	; R has # nibbles needed to fill out word in T.  If 0, nothing in T.
XFITC0:
	MOVE T,(C)		; Get word with 4 bytes left justified
	LSH T,-4		; Right-justify it.
	AOBJP C,[MOVEI R,1		; Jump if no more words
		JRST XFRIT4]		; Handle wrapup stuff
IRP CNT,,[1,2,3,4,5,6,7,8]
XFITC!CNT:	MOVE TT,(C)		; Get next one
		LSHC T,CNT*4		; Shift in to fill up word in T
		MOVEM T,(E)		; Deposit word in buff
		AOBJN E,.+2		; Increment ptr, skip unless full
		 CALL XFITCB		; Force out word buff, reset E
IFN CNT-8,[	LSHC T,<32.-<CNT*4>>	; Shift in unused portion
		AOBJP C,[MOVEI R,CNT+1		; Jump if no more input
			JRST XFRIT4]		; Go handle wrapup stuff
] ; all but last subcycle
TERMIN
	AOBJN C,XFITC0		; Back to start of cycle
	MOVEI R,0		; No more data.  Say no nibbles needed
				; Drop through

	; No more 32-bit full words from input buffer.  
	; T contains partial data, right-justified.
	; TT is empty, awaiting the next input word.
	; D contains the # of bytes in the next input word.
	; R has the # of nibbles left to fill out word in T (0-8)

XFRIT4:	JUMPE D,XFRIT5	; If no remaining data, just get new entry point!
	MOVE TT,(C)	; Get last data word
	LSH D,1		; Turn # bytes into # nibbles
	JUMPE R,XFRIT3	; If nothing currently in T, must skip some stuff.
	MOVEI C,(R)	; Assume enough data to fill last word
	CAIG D,(R)
	 MOVEI C,(D)	; Not enough data nibbles, just shift in all
	LSH C,2		; 4 bits per nibble
	LSHC T,(C)	; Shift in desired amount
	CAIGE D,(R)	; Did we have enough to fill out the word?
	 JRST XFRIT3	; Nope, don't deposit anything.
	MOVEM T,(E)	; Have full word for deposit
	AOBJN E,.+2
	 CALL XFITCB	; Output buff full, force out.

	; Now shift in unused portion of data
XFRIT3:	SUBI R,(D)	; Get new # nibbles needed
	JUMPGE R,XFRIT5	; If zero or positive, easy to set up.
	MOVE C,R	; Negative, has # of data nibbles left in TT
	IMUL C,[-4]	; 4 bits per nibble (make positive)
	LSHC T,(C)	; Right-justify remaining data in T
	ADDI R,9.	; Find # nibbles needed to fill out word

XFRIT5:	TRNN F,%TMP		; Last slurp?
	 JRST XFRIT2		; Nope, go get another slurp.

	; Last slurp, so must left-justify any remaining data and deposit it.
	; This code applies a heuristic to determine whether the remaining
	; data should actually be written or not.  Normally if the user
	; FTP isn't buggy, R will either be 0 (no nibbles left) or
	; 8 (1 nibble left over, since end fell in middle of an octet).
	; If R isn't one of these, there was at least one full data byte
	; that shouldn't have been sent.  In that case we pad out the word
	; and write it anyway.
	JUMPN R,[
		CAIN R,8.	; If only 1 nibble left (partial byte)
		 JRST .+1	; then ignore and assume all's well.
		LSH R,2		; 4 bits per nibble
		LSH T,(R)	; Note pad with zeros!
		MOVEM T,(E)	; Store last (partial) word.
		AOBJN E,.+1
		JRST .+1]
	CALL XFITCB	; Always force out.
	JRST XFRIT9

XFITCT:	XFITC0 ? XFITC1 ? XFITC2 ? XFITC3
	XFITC4 ? XFITC5 ? XFITC6 ? XFITC7 ? XFITC8


	; Force out word buffer, and reset write pointer in E
XFITCB:	HRRZS E
;	SUB E,$ARLOC+BUFFAR	; Find # words deposited
	SUBI E,BUFFER
	MOVNS E
	HRLZS E
;	HRR E,$ARLOC+BUFFAR	; Make it an AOBJN pointer
	HRRI E,BUFFER
	SYSCAL IOT,[B ? E]	; Image output (E has AOBJN)
	 JSR AUTPSY
;	MOVE E,$ARLOC+BUFFAR	; Now initialize write ptr again
	MOVEI E,BUFFER
	HRLI E,-XFRBFL
	RET


	; XFR TCP Image Output, Disk to Net
XFRITO:
;	UAROPN [%ARTZM,,BUFFAR ? [XFRBFL]]
;	UAROPN [%ARTZM,,TMPAR ? [XFR8BL]]
	
XFRIT6:	MOVSI C,-XFRBFL
;	HRR C,$ARLOC+BUFFAR
	HRRI C,BUFFER
	MOVE D,C
	SYSCAL IOT,[A ? C]	; Slurp stuff up
	 JSR AUTPSY
;	AOS ALIVEC
	TRZ F,%TMP
	CAIGE C,
	 TRO F,%TMP		; Not counted out, this is last slurp.
	HLRES C
	HRLOI C,XFRBFL-1(C)
	EQVI C,(D)		; Now have AOBJN to words we read in.
	JUMPGE C,XFRIT9

	HLRE D,C
	IMUL D,[-9.]		; Find # of 4-bit nibbles
	ADDI D,1
	IDIVI D,2.		; Find # of 8-bit bytes.

	MOVE T,D		; Stat cruft
	LSH T,3
	ADDM T,NBITS

	PUSH P,D
;	HRRZ E,$ARLOC+TMPAR
	MOVEI E,TMPBUF
	SUBI E,1		; Allow for increment of first PUSH
	TLO E,(SETZ)		; Make PDL ptr

XFRIT7:	MOVE TT,(C)		; Get word 0
	LSHC T,32.		; Get 1st 32 bits right justified
	LSH T,4.		; Left justify it
	PUSH E,T		; Store bytes 0-3
	AOBJP C,XFRIT8	; If counted out, must store last 4 bits.

REPEAT 7,[
	LSHC T,4*<.RPCNT+1>	; Get low 4 bits of wd 0
	MOVE TT,(C)		; Glom onto wd 1
	LSHC T,<32.-<4*<.RPCNT+1>>> ; Fill out to 32 bits from wd 1
	LSH T,4			; Make left justified
	PUSH E,T		; Store bytes 4-7
	AOBJP C,XFRIT8	; If counted out, store last 8 bits.
]	
	PUSH E,TT	; Reached alignment, store last wd directly
	JRST XFRIT7	; then repeat the cycle

XFRIT8:	PUSH E,TT
	POP P,D
;	MOVE C,$ARLOC+TMPAR
	MOVEI C,TMPBUF
	HRLI C,441000
	SYSCAL SIOT,[B ? C ? D]
	 JSR AUTPSY

	TRNN F,%TMP
	 JRST XFRIT6
	
XFRIT9:
;	UARCLS TMPAR
	SYSCAL CLOSE,[A]	; Close empty input chan
	 .LOSE %LSFIL
;	UARCLS BUFFAR
	POPAE P,[R,E,D,C]
	RET

SUBTTL String hacking rtns

UUODEF EQUSTR,UEQSTR		;extra UUO for easy string comparision

UEQSTR:	MOVE U1,40
	LDB U2,[$ACFLD,,U1]
	MOVE U2,(U2)		;get addr of string AC points to
	HRRZ U3,(U1)		;GET CNT 1
	HRRZ U4,(U2)		;AND 2
	CAIE U3,(U4)
	 JRST UUORET		;NOT EQUAL, DIFFERENT LENGTHS
	MOVE U1,1(U1)
	MOVE U2,1(U2)
	PUSH P,U3		; Save cnt on stack.
UEQST2:	SOSGE (P)
	 JRST UEQST5
	ILDB U3,U1
	ILDB U4,U2
	CAIN U3,(U4)
	 JRST UEQST2
	SUB P,[1,,1]
	JRST UUORET
UEQST5:	SUB P,[1,,1]
	AOS UUOH
	JRST UUORET


; Parse a word off string pointed to by A, leaves ptr to word in
; B and updates string read from.  B furnishes char to break on.
PRSWRD:	PUSHAE P,[C,D]
	MOVE D,B		; Save desired break char in D.
	BCONC
	HRRZ C,(A)		;make sure something there
	JUMPG C,PRSW5
	JRST PRSW6
PRSW2:	ILDB B,1(A)
	CAIN B,(D)
	 JRST PRSW6
	OUTI STRC,(B)		; Collect string.
PRSW5:	SOJGE C,PRSW2
	SETZ C,
PRSW6:	ECONC WRDSTR
	HRRM C,(A)
	MOVEI B,WRDSTR
	POPAE P,[D,C]
	POPJ P,

; CVSIX - converts a string in A to 6bit wd in A
;	stops when reach 0 or get 6 chars, or hit blank and previous
;	chars were nonblank

CVSIX:	PUSHAE P,[B,C,D,E]
	MOVE C,1(A)
	HRRZ B,(A)
	CAILE B,6
	 MOVEI B,6
CVT760:	SETZ A,
	MOVE D,[440600,,A]
CVT761:	ILDB E,C
	CAIN E,40
	 JUMPN A,CVT762	;if hit blank, stop only if something already accumulated
	JUMPE E,CVT762
	CAIL E,141		;convert to uppercase
	 CAILE E,172
	  CAIA
	   SUBI E,40
	SUBI E,40		;convert to 6bit
	IDPB E,D
	SOJG B,CVT761
CVT762:	POPAE P,[E,D,C,B]
	POPJ P,

CVSUPR:	PUSHAE P,[B,C,D]
	MOVE B,1(A)
	HRRZ C,(A)
	JUMPG C,CVSUP5
	JRST CVSUP7
CVSUP2:	ILDB D,B
	CAIL D,"a
	 CAILE D,"z
	  JRST CVSUP5
	SUBI D,40
	DPB D,B
CVSUP5:	SOJGE C,CVSUP2
CVSUP7:	POPAE P,[D,C,B]
	POPJ P,


;Convert the decimal digit string ARG to a number in A.
;Clobbers B and T and TT.
RDDEC:	SKIPA TT,[10.]
;Similar, but reads an octal number.
RDOCT:	 MOVEI TT,10
	SETZ A,
	MOVE B,ARGPT
RDDEC2:	ILDB T,B
	CAIL T,"0
	 CAILE T,"9
	  POPJ P,
	IMUL A,TT
	ADDI A,-"0(T)
	JRST RDDEC2

; A - ptr to string descriptor
; B - [default file block],,[result file block]
; However, default FN2 is always > if only a FN1 was given.

FILPAR:	PUSHAE P,[(A),1(A),A,B,C,D,E]
	HRRZ E,B		;get result addr
	BLT B,3(E)		;zap default values into result block
	PUSHJ P,FNPARD		;parse string as filename, DDT style
	CAIE A,
	 MOVEM A,(E)		;device
	CAIE B,
	 MOVEM B,1(E)		;dir
	CAIN E,FILDEV
	 .SUSET [.SSNAM,,B]	;If main default SNAME changed, show it in who-line.
	CAIE C,
	 MOVEM C,2(E)		;fn1
	MOVSI B,(SIXBIT/>/)
	CAIN C,
	 MOVEM B,3(E)		;Default FN2 is > if no FN1.
	CAIE D,
	 MOVEM D,3(E)		;fn2
	POPAE P,[E,D,C,B,A,1(A),(A)]
	POPJ P,


;;; String Vars

IFE @, REPLYS: WRDSTR:
STRNAM REPLYS			;Reply from server
STRNAM WRDSTR			;Used by PRSWRD

STRNGS:	SBLOCK
	NSTRS==<.-STRNGS>/2

CONSTANTS
VARIABLES

ARPAGS:	<LSTPAG-400>,,LSTPAG	; Define free area to be everything above this.
				; Note that we gobble from here to call HSTMAP
				; before we initialize the storage allocator.
LSTPAG==<.+1777>/2000

END GO
