;<PUP>PFUUTL.MAC;4 4-NOV-79 12:40:18 EDIT BY TAFT
; Restore "Sender" property
;<PUP>PFUUTL.MAC;3 2-SEP-79 15:57:50 EDIT BY TAFT
;<PUP>PFUUTL.MAC;2 3-JUN-77 11:03:04 EDIT BY TAFT
; Remove dummy "Sender" and "Distribution" property parsers
;<PUP>PFUUTL.MAC;1 2-JUN-77 21:35:25 EDIT BY TAFT
; Split out from PUPFTP.MAC
; Copyright 1979 by Xerox Corporation
TITLE PFUUTL -- PUP FTP USER UTILITIES
SUBTTL E. A. Taft / June, 1977
SEARCH PUPDEF,PFUDEF,STENEX
USEVAR FTPVAR,FTPPVR
; Do JFNS with output suitable for inclusion in property list
; B/ File JFN
; C/ JFNS flags
; Returns +1:
; B/ String ptr to JFNS text
; Clobbers A-C
DOJFNS::MOVE A,[POINT 7,TEMP##] ; Where to put temp text
JFNS ; Convert to string
MOVE A,[POINT 7,TEMP##] ; Source string
MOVE B,[POINT 7,TEMP##+100] ; Destination string
DOJFN1: ILDB C,A ; Get a char
CAIN C,"V"-100 ; Tenex filename quote?
JRST DOJFN1 ; Yes, flush it
CAIE C,"(" ; Character need to be quoted?
CAIN C,")"
JRST .+3 ; Yes
CAIE C,PQUOTE
JRST DOJFN2 ; No
MOVEI C,PQUOTE ; Yes, insert quote character
IDPB C,B
LDB C,A ; Recover character
DOJFN2: IDPB C,B ; Store the character
JUMPN C,DOJFN1 ; Repeat if not at end
MOVE B,[POINT 7,TEMP##+100] ; Where result string is now
POPJ P, ; Done
; Send login parameters (previously buffered in USRNAM, etc.)
; A/ Destination designator
; Returns +1
; A/ Destination designator (updated if string ptr)
; Clobbers B
SNDLGN::SKIPN USRNAM## ; Is there a user name?
JRST SNDLG1 ; No, skip this
HRROI B,USRNAM## ; Yes, make string ptr to it
WRITE <(User-Name %2S)> ; Send it off
HRROI B,USRPSW## ; Make string ptr to password
SKIPE USRPSW## ; Is there one?
WRITE <(User-Password %2S)>; Yes, send it
HRROI B,USRACT## ; Make string ptr to account
SKIPE USRACT## ; Is there one?
WRITE <(User-Account %2S)>; Yes, send it
SNDLG1: SKIPN CONNAM## ; Is there a connect name?
JRST SNDLG2 ; No, skip this
HRROI B,CONNAM## ; Yes, make string ptr to it
WRITE <(Connect-Name %2S)>; Send it off
HRROI B,CONPSW## ; Make string ptr to password
SKIPE CONPSW## ; Is there one?
WRITE <(Connect-Password %2S)>; Yes, send it
SNDLG2: HRROI B,DEFPRP##+P.DIRE ; String ptr to default directory
SKIPE DEFPRP##+P.DIRE ; Is there one?
WRITE <(Directory %2S)>; Yes, send it
POPJ P, ; Done
; Fix up filename strings for Retrieve, Store, etc.
; A/ Property list pointer
; Specifically, if a Server-Filename was not supplied, construct
; one from Device, Directory, Name-Body, and Version properties.
; Returns +1
; Clobbers B, C
FIXNAM::SKIPE P.SFIL(A) ; Server-Filename specified?
POPJ P, ; Yes, nothing to do
HRROI B,P.SFIL(A) ; No, start string pointer to cell
HRROI C,P.DEVI(A) ; Get Device string ptr
SKIPE P.DEVI(A) ; Device specified?
WRITE B,<%3S:> ; Yes, prefix it
SKIPE C,P.DIRE(A) ; Directory specified?
WRITE B,<<%3U>> ; Yes, expand into string
HRROI C,P.NAMB(A) ; Append Name-Body string
WRITE B,<%3S>
SKIPE C,P.VERS(A) ; Version specified?
WRITE B,<;%3D> ; Yes, append it
POPJ P,
; Generate transfer properties (Type, bytesize, eol convention)
; A/ Destination designator
; B/ Property list pointer
; Returns +1 always
; A/ Destination designator (updated if string ptr)
; Clobbers C
GNTPAR::HRRZ C,P.TYPE(B) ; Get type
JRST @[ CPOPJ## ; Unspecified
GNTTXT ; Text
GNTBIN ; Binary
GNTPAG](C) ; Paged
GNTTXT: WRITE <(Type Text)>
SKIPE C,P.EOLC(B) ; Output eol convention if given
XCT [ WRITE <(End-of-Line-Convention CRLF)>
WRITE <(End-of-Line-Convention Transparent)>
]-1(C)
POPJ P,
GNTBIN: WRITE <(Type Binary)>
SKIPE C,P.BYTE(B) ; Output byte size if given
WRITE <(Byte-Size %3D)>
POPJ P,
GNTPAG: WRITE <(Type Tenex-Paged)>
POPJ P,
; Procedure called from PUPXFR -- not used in PUPFTP user
SETWDT::POPJ P,
; Dummy parsers for mail-related properties (should never happen)
PPMLBX::
PPSNDR::
FTPM(NO,10,<Property out of context>,1)
; -----------------------------------------------------------------
; File system utilities
; -----------------------------------------------------------------
; Check for device disk
; A/ JFN for file being retrieved or stored
; Returns +1 always
; Sets DSKDVF flag appropriately
; Clobbers B, C
CHKDSK::PUSH P,A ; Save JFN
DVCHR ; Get device characteristics
POP P,A ; Restore JFN
TLNN B,377 ; Check device type
TROA F,DSKDVF ; Device is a disk
TRZ F,DSKDVF ; Device is not a disk
POPJ P,
; "Kill" destination file, i.e. delete all its pages and
; try very hard to make it go away (works only for disk).
; Assumes DSTJFN contains open output JFN and that DSKDVF
; has already been set appropriately.
; Returns +1 always
; Closes and releases JFN
; Clobbers A-D
KILFIL::HRRZ A,DSTJFN## ; Get destination JFN
DELF ; First attempt to delete file
JRST KILFI5 ; Non-directory or no access, stop
TRNN F,DSKDVF ; Output to disk?
JRST KILFI5 ; No, just close file
MOVE B,[1,,1] ; Yes, get FDBCTL word
MOVEI C,C
GTFDB
TLNN C,(1B4) ; FDBNXF set?
JRST KILFI5 ; No, file previously existed
PUSHJ P,DELPGS ; Yes, delete all pages in file
HRRZ A,DSTJFN## ; Close the file
HRLI A,400000 ; but don't release JFN
CLOSF
POPJ P, ; Failed? give up
HRLI A,1 ; Now set FDBNXF to make the
MOVSI B,(1B4) ; file look invisible
MOVSI C,(1B4)
CHFDB
HRRZ A,DSTJFN## ; Release JFN
RLJFN
CAI
POPJ P, ; Done
; Here to just close file
KILFI5: HRRZ A,DSTJFN##
CLOSF
CAI
POPJ P,
; Delete all pages in a disk file
; Assumes file open for writing
; A/ JFN
; Returns +1 always
; Clobbers A, B
DELPGS: HRLZ A,A ; Make designator for file page 0
SETO B, ; Set arg for deleting pages
DELPG1: FFUFP ; Find next existing page
POPJ P, ; No more, done
EXCH A,B ; Found one, delete it
PMAP
EXCH A,B
AOJA A,DELPG1 ; On to next
; Do GTJFN using current command word and capturing terminator
; A/ LH bits for GTJFN (long mode)
; Returns +1: GTJFN failed, A/ error code
; +2: Succeeded, A/ JFN
; In both cases, D/ Attribute bits for terminator
; In the success case, the filename and the terminating
; character are appended to the command string.
; Clobbers A-D
GETJFN::MOVEM A,TEMP## ; Store bits in long GTJFN block
MOVE A,[100,,101] ; I/O to terminal
MOVEM A,TEMP##+1
SETZM TEMP##+2 ; No other defaults
MOVE A,[TEMP##+2,,TEMP##+3]
BLT A,TEMP##+7
MOVEI A,TEMP## ; Set pointer to block
MOVE B,WRDBYT## ; Use last word as main string
GTJFN ; Do it
JRST GETTRM ; Failed, get terminator, return +1
MOVE B,WRDBYT## ; Succeeded, get ptr to word start
WRITE B,<%1F> ; Overwrite with complete filename
PUSHJ P,GETTRM ; Get terminator
IDPB C,B ; Append it
MOVEM B,CMDBYT## ; Update current byte ptr
JRST SKPRET## ; Return +2
; Get terminator character (for GTJFN)
; Returns +1: C/ character, D/ attributes
; If char is Escape, it is replaced by space and ESCAPF set
; If a line-delete character, the appropriate response printed
; Clobbers C and D only
GETTRM::MOVE C,A ; Preserve A
MOVEI A,100 ; Backup terminal input
BKJFN
PUSHJ P,SCREWUP##
PBIN ; Get the terminator
EXCH C,A ; Terminator to C, restore A
CAIN C,33 ; Escape?
TRO F,ESCAPF ; Yes, remember so
MOVE D,CHRTAB##(C) ; Return attributes of terminator
TLNN D,(C.CDEL) ; Command delete char?
POPJ P, ; No
CAIN C,177 ; Yes, print appropriate response
TYPE <XXX%/>
CAIE C,177
TYPE <←←←%/>
POPJ P,
; Routines to print error messages for OPENF
; A/ Error code
; Assume JFN is in SRCJFN or DSTJFN as appropriate
; Return +1
; Clobber C
; Here to handle OPENF errors peculiar to "Retrieve"
ROPNER::HRRZ C,DSTJFN## ; Setup JFN for reply message
JRST OPNERR ; Join common code
; Here to handle OPENF errors peculiar to "Store"
SOPNER::HRRZ C,SRCJFN## ; Setup JFN for message
; OPENF failure code common to "Retrieve" and "Store"
OPNERR: CAIN A,OPNX3
ERROR <Read access denied to file %3F%/>,1
CAIN A,OPNX4
ERROR <Write access denied to file %3F%/>,1
CAIN A,OPNX6
ERROR <Append access denied to file %3F%/>,1
CAIE A,OPNX5
CAIN A,OPNX13
ERROR <Access denied to file %3F%/>,1
CAIN A,OPNX23
ERROR <Access denied to directory containing %3F%/>,1
CAIN A,OPNX9
ERROR <File %3F busy%/>,1
CAIN A,OPNX10
ERROR <No room for file %3F%/>,1
CAIN A,SFBSX2
ERROR <Illegal byte size for file %3F%/>,1
ERROR <File open error: %1J for file %3F%/>,1 ; Other
; -----------------------------------------------------------------
; Subroutines
; -----------------------------------------------------------------
; Get "Yes" or "No" response
; Returns +1: End received
; +2: EOC encountered
; +3: "Yes"
; +4: "No"
; On +3 and +4 returns, A/ Mark type, B/ Subcommand byte,
; D/ String pointer to text
; All other responses are processed internally
; The text of a "No" is printed out
; Clobbers A-D
GYESNO::PUSHJ P,GETRSP ; Get response
POPJ P, ; End
JRST SKPRET## ; EOC
CAIN A,MKYES ; Yes?
JRST SK2RET## ; Give +3 return
CAIN A,MKNO ; No?
JRST [ TLNN F,(DEBUGF) ; Already typed if debugging
TYPE( <%4S%/) ; Type text of "No" reply
JRST SK3RET##] ; Return +4
HRRO C,MRKNAM##(A) ; Other, give error message
TYPE <Improper server response "[%3S] %4S" while Yes/No expected%/>
JRST GYESNO ; Ignore, get another
; Process "No" response for file transfer operations.
; Gets the necessary parameters to retry operation, if possible.
; B/ The "No" subcommand byte
; Returns +1: The attempt should be abandoned, B/ unchanged
; +2: The attempt should be retried
; Clobbers A-D
PROCNO::CAIL B,20 ; User name or password problem?
CAILE B,22
POPJ P, ; No, fail
HRLM B,0(P) ; Yes, save error code
PROCN1: PUSHJ P,CRIF## ; Make sure at left margin
PUSHJ P,INIEDT## ; Initialize editor
PROMPT < LOGIN > ; Prompt for login
MOVSI D,(C.CEOL) ; Pretend EOL terminated last input
PUSHJ P,C.LOGI## ; Call "Login" command
JRST [ TLNN D,(C.CDEL) ; Failed, line delete?
JRST PROCN1 ; Other error, try again
HLRZ B,0(P) ; Delete, recover code and fail
POPJ P,]
JRST SKPRET## ; Successful, return +2
; Scan for and flush EOC
; Returns +1: End received
; +2: Normal
; Clobbers A-D
FLSEOC::PUSHJ P,GETRSP ; Get response
POPJ P, ; End
JRST SKPRET## ; EOC, return +2
HRRO C,MRKNAM##(A) ; Other, give error message
TYPE <Improper server response "[%3S] %4S" when EOC expected%/>
JRST FLSEOC ; Ignore, get another
; Get response from previous command
; Returns +1: End received
; +2: EOC encountered
; +3: Normal, A/ Mark type, B/ Subcommand byte (if any)
; D/ String pointer to text
; "Comment" responses are processed internally
; Clobbers A-D
GETRSP::PUSHJ P,GETCMD ; Get next command from server
POPJ P, ; End
CAIN A,MKEOC ; EOC?
JRST SKPRET## ; Yes, return +2
CAIN A,MKCOMM ; Comment?
JRST [ TLNN F,(DEBUGF) ; Already typed if debugging
TYPE( <%4S%/) ; Type text of comment
JRST GETRSP] ; Ignore, back for next
JRST SK2RET## ; No, return +3
; Get next command (i.e. reply from server)
; Returns +1: End received
; +2: Ok, A/ Mark type, B/ Subcommand byte (if any)
; D/ String pointer to text
; Clobbers A-D
; Check status to distinguish between Mark and End
GETCMD::HLRZ A,CONJFN## ; Get input JFN
SETZ C, ; Don't want address stuff
GDSTS ; Get status
TLNE B,(1B5) ; End received?
POPJ P, ; Yes, fail return
TLZN B,(1B4) ; Mark received?
JRST GETCM4 ; No, go flush extraneous data
; Got Mark, prepare to process command
SDSTS ; Clear flag
MOVEI B,23 ; Get the Mark byte
MTOPR
CAIGE C,NMARKS ; Mark byte in range?
SKIPN D,MRKNAM##(C) ; Yes, fetch name pointer
JRST GETCME ; No or undefined
PUSH P,C ; Save Mark byte
TLNE D,(NFETCH) ; Want to pre-fetch command text?
JRST GETCM2 ; No
HRROI B,NETBUF## ; Yes, buffer as 7-bit ASCII
MOVNI C,5000 ; Max # bytes in buffer
SIN
SETZ A,
SKIPGE C ; Unless completely filled buffer,
IDPB A,B ; Put null on end
SKIPA D,[POINT 7,NETBUF##] ; Init string ptr
GETCM2: SETZB D,NETBUF## ; Here if no text
POP P,A ; Recover Mark byte
HRRO C,MRKNAM##(A) ; Get name string
SKIPGE MRKNAM##(A) ; Does command have subcommands?
JRST GETCM3 ; Yes
DTYPE <S: [%3S] %4S%/> ; No, print command if debugging
JRST SKPRET## ; Return +2
GETCM3: ILDB B,D ; Get subcommand
DTYPE <S: [%3S] <%2O> %4S%/>; Print command if debugging
JRST SKPRET## ; Return +2
; Here if command undefined
GETCME: TYPE <Undefined command [%3O]%/>
; Flush byte stream data to next Mark
GETCM4: HLRZ A,CONJFN## ; Get input JFN
MOVE B,[POINT 8,NETBUF##] ; Byte ptr to buffer
MOVNI C,4000 ; # bytes in buffer
SIN ; Suck bytes from net
JUMPGE C,GETCM4 ; Repeat if didn't get it all
JRST GETCMD ; Go look again for Mark
; Refill the command buffer if necessary
; A/ used string pointer into NETBUF
; Returns +1:
; A/ updated string pointer
; Clobbers B-D
REFILL::TRNN A,400 ; Have we read half the buffer?
POPJ P, ; No, nothing to do
MOVE B,[NETBUF##+400,,NETBUF##] ; Yes, move upper half down
BLT B,NETBUF##+377
SUBI A,400 ; Fix pointer
PUSH P,A ; Save it
HLRZ A,CONJFN## ; Get net input JFN
HRROI B,NETBUF##+400 ; Where to put more input
MOVNI C,400*5 ; Max # chars
SIN ; Get more input
SETZ A,
SKIPGE C ; Unless buffer filled,
IDPB A,B ; Put null on end
POP P,A ; Restore byte ptr
POPJ P,
; Send a command
; A/ Command number (Mark type)
; B/ String ptr to command text (0 => none)
; C/ Subcommand (iff command requires one)
; Returns +1
; Clobbers A-D
SNDCMD::PUSHJ P,BEGCMD ; Do the work
JRST ENDCMD ; Force transmission
; Begin command, i.e. do all the output but don't force
; transmission. Calling sequence same as SNDCMD
BEGCMD::HRRO D,MRKNAM##(A) ; Get string ptr to command name
SKIPL MRKNAM##(A) ; Does command have subcommands?
DTYPE <U: [%4S] %2S%/> ; No
SKIPGE MRKNAM##(A)
DTYPE <U: [%4S] <%3O> %2S%/>; Yes
MOVE D,B ; Save string ptr
HRLM C,0(P) ; Save subcommand if any
MOVE C,A ; Copy command number
HRRZ A,CONJFN## ; Get output JFN
MOVEI B,3 ; Send Mark
MTOPR
HLRZ B,0(P) ; Get subcommand if any
SKIPGE MRKNAM##(C) ; Does command have subcommands?
BOUT ; Yes, send subcommand code
SETZ C,
SKIPE B,D ; Is there a string?
SOUT ; Yes, send it
POPJ P, ; Done
; End command by forcing the byte stream
; Returns +1
; Clobbers A, B
ENDCMD::HRRZ A,CONJFN## ; Get output JFN
MOVEI B,21 ; Force transmission
MTOPR
POPJ P,
; FTPM (mark type, sub-code, <string>, pop count)
; UUO to generate FTP reply message
%UFTPM::PUSH P,@40 ; Preserve control word
AOS 40 ; Advance to start of string
PUSHJ P,FORMAT## ; Call UUO output formatter
HRROI A,TEMP##+600 ; Setup -- buffer reply here
PUSHJ P,UFTPM2 ; Completion -- send off reply
HRLS 0(P) ; Put pop count (+1) in both halves
SUB P,0(P) ; Pop stack appropriately
POPJ P, ; Return from UUO (or from caller)
; FTPM completion
UFTPM2: SETZ B, ; Terminate string with null
IDPB B,A
LDB A,[POINT 8,-6(P),7] ; Get Mark type
LDB C,[POINT 8,-6(P),15] ; Get subcommand code if any
HRROI B,TEMP##+600 ; Point to buffered reply
PUSHJ P,SNDCMD ; Send off the command
MOVEI A,MKEOC ; Set to append EOC
SETZB B,C
MOVE D,-6(P) ; Want to terminate with EOC?
TLNE D,(1B16)
PUSHJ P,SNDCMD ; Yes, do so
POPJ P,
END