;;; -*- Mode:Lisp; Readtable:CL; Package:USER; Base:10; Patch-File:T -*-
;;; Patch file for System version 123.76
;;; Reason:
;;;  Enhancements to (ftp:ftp)
;;;    - "ls" command's argument is now optional
;;;    - "lcd" command now does something useful -- sets local pathname defaults
;;;      so you don't need to specify host and directory when you "get" and "put"
;;;    - set transfer mode to ASCII before "ls", if not that already
;;;    - when you use the "16bit" command before a "get" or a "put", open the local
;;;      file in that mode and make sure that proper 8-to-16 or 16-to-8 bit
;;;      translation is done betwwen file and network.
;;;    - (change to server) default version is :wild for directory list and
;;;      :newest for file transfer -- "ls" now lists all versions, not just newest
;;; Written 27-Oct-87 15:59:58 by pld at site LMI Cambridge
;;; while running on Jack Flanders from band 2
;;; with Experimental System 123.75, Experimental Local-File 73.0, Experimental FILE-Server 22.0, Experimental Unix-Interface 11.0, Experimental Tape 18.0, Experimental KERMIT 34.0, Experimental ZMail 71.0, Experimental Lambda-Diag 15.0, microcode 1754, SDU Boot Tape 3.12, SDU ROM 8.



; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 16:19:22
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defmethod (8b-to-16b-translating-output-stream :after :close) (&optional mode)
  (send output :close mode))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 16:39:41
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defflavor 16b-to-8b-translating-output-stream
	 ((output nil)
	  (bytes 0))
	 (si:buffered-output-stream)
  (:initable-instance-variables output))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 16:39:48
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defmethod (16b-to-8b-translating-output-stream :send-output-buffer) (array end)
  (let ((byte-count (* end 2)))
    (incf bytes byte-count)
    (send output
	  :string-out
	  (make-array byte-count :element-type 'string-char :displaced-to array)
	  0
	  byte-count))
  (global:deallocate-resource 'fs:simple-art-16b-buffer array))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 16:39:55
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defmethod (16b-to-8b-translating-output-stream :after :close) (&optional mode)
  (send output :close mode))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 16:40:00
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defmethod (16b-to-8b-translating-output-stream :bytes) ()
  bytes)

))

; From modified file DJ: L.NETWORK.IP-TCP.USER; FTP.LISP#30 at 27-Oct-87 16:52:36
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; USER; FTP  "

(defun sendrequest (cmd local remote)
  (prog (read_reply start stop bytes dout)
	(setq bytes 0)
	(unwind-protect
	    (progn (if (initconn :output) (go bad))
		   (setq read_reply t)
		   (unless (= (command "~A ~A" cmd remote) (sym prelim))
		     (setq read_reply nil)
		     (go bad))
		   (setq dout (dataconn :output))
		   (if (null dout) (go bad))
		   (when (eq *type* '16bit)
		     (setq dout (make-16b-to-8b-translating-output-stream dout)))
		   (with-open-stream (fin (if (eq *type* '16bit)
					      (open local :byte-size 16 :characters nil)
					    (open local)))
		     (setq start (time:time))
		     (global:stream-copy-until-eof fin dout)
		     (send dout :force-output)
		     (setq stop (time:time))
		     (setq bytes (send dout :bytes))))
	  (unless stop
	    (setq stop (time:time))
	    (when *data*
	      (close *data*)
	      (setq *data* nil))
	    (if dout (setq bytes (send dout :bytes)))))
	(close dout)
	(getreply nil)
     done
	(if (and (> bytes 0) *verbose*)
	    (ptransfer "sent" bytes start stop))
	(return nil)
     bad
	(if read_reply (getreply nil))
	(go done)))

))

; From modified file DJ: L.NETWORK.IP-TCP.USER; FTP.LISP#30 at 27-Oct-87 17:27:41
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; USER; FTP  "

(defcmd cmd-lcd (&optional local-directory)
  "change local working directory"
  (let* ((defaults (fs:make-pathname-defaults))
	 (colon (string-search-char #\: local-directory))
	 (host (if colon (si:parse-host (substring local-directory 0 colon) t) si:local-host))
	 (pathname (fs:parse-pathname local-directory host defaults (if colon (1+ colon) 0))))
    (fs:set-default-pathname pathname)
    (format t "~&Local pathname default now ~A.~%" pathname)
    pathname))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 17:39:23
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defun ftp-parse-pathname (state name &optional directory-p)
  (global:condition-case-if ftp-catch-errors (sig)
      (let ((fs:*defaults-are-per-host* nil)
	    (fs:*always-merge-type-and-version* nil)
	    (fs:*name-specified-default-type* :lisp)
	    (parsed (fs:parse-pathname name
				       (cond ((global:get-site-option :ftp-disallow-nonlocal-access)
					      si:local-host)
					     ((string-search ":" name)
					      nil)
					     (t
					      (send (ftpstate-homedir-pn state) :host))))))
	(fs:merge-pathname-components parsed
				      (ftpstate-pn-defaults state)
				      :default-name :wild
				      :default-type :wild
				      :default-version (if directory-p :wild :newest)))
    (error sig)))

))

; From modified file DJ: L.NETWORK.IP-TCP.SERVER; FTP.LISP#67 at 27-Oct-87 17:39:39
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; SERVER; FTP  "

(defun ftp-retrieve (state name directory-p)
  "Get a file from the local TCP host (modulo chaosnet file servers)
   and send it to the ftp user.  This is also used for getting directory listings."
  (if (not name) 
      (if directory-p
	  (setq name (fs:default-pathname (ftpstate-pn-defaults state)))
	(error "FTP-RETRIEVE called with NULL filename.~%")))
  (unless (and (not (pathnamep name))
	       (ftp-pathname-error-reply state (setq name (ftp-parse-pathname state name directory-p))))
    (ftp-file-error-reply state (ftp-file-operation state #'ftp-retrieve-1
							    state name directory-p))))

))

; From modified file DJ: L.NETWORK.IP-TCP.USER; FTP.LISP#31 at 27-Oct-87 17:44:42
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; USER; FTP  "

(defun recvrequest (cmd local remote)
  "local is a string describing the local place to put the
requested data. remote is a remote description."
  (prog (read_reply start stop bytes din fout)
	(setq bytes 0)
	(unwind-protect
	    (progn (if (initconn :input) (go bad))
		   (setq read_reply t)
		   (let ((x (if remote (command "~A ~A" cmd remote) (command cmd))))
		     (cond ((not (= x (sym prelim)))
			    (setq read_reply nil)
			    (go bad))))
		   (setq din (dataconn :input))
		   (if (not din) (go bad))
		   (setq fout (if local
				  (if (eq *type* '16bit)
				      (make-8b-to-16b-translating-output-stream
					(open local :direction :output :byte-size 16 :characters nil))
				    (open local :direction :output))
				*standard-output*))
		   (setq start (time:time))
		   (global:stream-copy-until-eof din fout)
		   (setq stop (time:time))
		   (setq bytes (send din :bytes)))
	  (unless stop
	    (setq stop (time:time))
	    (when *data*
	      (close *data*)
	      (setq *data* nil))
	    (if din (setq bytes (send din :bytes))))
	  (when fout
	    (unless (eq fout *standard-output*)
	      (close fout))))
	(getreply nil)
     done
	(if (and (> bytes 0) *verbose*)
	    (ptransfer "received" bytes start stop))
	(return nil)
     bad
	(if read_reply (getreply nil))
	(go done)))

))

; From modified file DJ: L.NETWORK.IP-TCP.USER; FTP.LISP#31 at 27-Oct-87 17:44:56
#10R FTP#: 
(COMPILER-LET ((*PACKAGE* (GLOBAL:PKG-FIND-PACKAGE "FTP")))
  (COMPILER::PATCH-SOURCE-FILE "SYS: NETWORK; IP-TCP; USER; FTP  "

(defcmd (cmd-ls cmd-dir) (&optional remote-directory local-file)
  "list contents of remote directory"
  (unless (eq *type* 'ascii)
    (cmd-ascii))
  (recvrequest "LIST" local-file remote-directory))

))
