Refill some long/short copyright headers.
[bpt/emacs.git] / lisp / net / ange-ftp.el
index 72c6c03..f0d3645 100644 (file)
@@ -1,8 +1,6 @@
 ;;; ange-ftp.el --- transparent FTP support for GNU Emacs
 
-;; Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
-;;   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1989-1996, 1998, 2000-2011  Free Software Foundation, Inc.
 
 ;; Author: Andy Norman (ange@hplb.hpl.hp.com)
 ;; Maintainer: FSF
 ;;; Code:
 
 (require 'comint)
-;; Silence compiler:
-(eval-when-compile
-  (require 'dired)
-  (defvar comint-last-output-start nil)
-  (defvar comint-last-input-start nil)
-  (defvar comint-last-input-end nil))
 
 ;;;; ------------------------------------------------------------
 ;;;; User customization variables.
   "Accessing remote files and directories using FTP
    made as simple and transparent as possible."
   :group 'files
+  :group 'comm
   :prefix "ange-ftp-")
 
 (defcustom ange-ftp-name-format
@@ -726,7 +719,8 @@ parenthesized expressions in REGEXP for the components (in that order)."
          "^Connected \\|^$\\|^Remote system\\|^Using\\|^ \\|Password:\\|"
          "^Data connection \\|"
          "^local:\\|^Trying\\|^125 \\|^550-\\|^221 .*oodbye\\|"
-          "^500 .*AUTH \\(KERBEROS\\|GSSAPI\\)\\|^KERBEROS\\|"
+          "^500 .*AUTH\\|^KERBEROS\\|"
+          "^504 Unknown security mechanism\\|"
          "^530 Please login with USER and PASS\\|" ; non kerberised vsFTPd
          "^534 Kerberos Authentication not enabled\\|"
          "^22[789] .*[Pp]assive\\|^200 EPRT\\|^500 .*EPRT")
@@ -1176,7 +1170,7 @@ only return the directory part of FILE."
 ;;;; ------------------------------------------------------------
 
 (defmacro ange-ftp-generate-passwd-key (host user)
-  `(concat (downcase ,host) "/" ,user))
+  `(and (stringp ,host) (stringp ,user) (concat (downcase ,host) "/" ,user)))
 
 (defmacro ange-ftp-lookup-passwd (host user)
   `(gethash (ange-ftp-generate-passwd-key ,host ,user)
@@ -1739,7 +1733,10 @@ good, skip, fatal, or unknown."
                      ange-ftp-gateway-tmp-name-template
                    ange-ftp-tmp-name-template)))
 
-(defalias 'ange-ftp-del-tmp-name 'delete-file)
+(defun ange-ftp-del-tmp-name (filename)
+  "Force to delete temporary file."
+  (delete-file filename))
+
 \f
 ;;;; ------------------------------------------------------------
 ;;;; Interactive gateway program support.
@@ -2332,7 +2329,7 @@ and NOWAIT."
 
      ;; Second argument is the remote name
      ((or (memq cmd0 '(append put chmod))
-          (and (eq cmd0 'quote) (string= cmd1 "mdtm")))
+          (and (eq cmd0 'quote) (member cmd1 '("mdtm" "size"))))
       (setq cmd2 (funcall fix-name-func cmd2)))
      ;; Both arguments are remote names
      ((eq cmd0 'rename)
@@ -2678,10 +2675,11 @@ The main reason for this alist is to deal with file versions in VMS.")
 
 (defmacro ange-ftp-parse-filename ()
   ;;Extract the filename from the current line of a dired-like listing.
-  `(let ((eol (progn (end-of-line) (point))))
-     (beginning-of-line)
-     (if (re-search-forward directory-listing-before-filename-regexp eol t)
-        (buffer-substring (point) eol))))
+  `(save-match-data
+     (let ((eol (progn (end-of-line) (point))))
+       (beginning-of-line)
+       (if (re-search-forward directory-listing-before-filename-regexp eol t)
+          (buffer-substring (point) eol)))))
 
 ;; This deals with the F switch. Should also do something about
 ;; unquoting names obtained with the SysV b switch and the GNU Q
@@ -2971,6 +2969,8 @@ this also returns nil."
 
 (defun ange-ftp-set-binary-mode (host user)
   "Tell the FTP process for the given HOST & USER to switch to binary mode."
+  ;; FIXME: We should keep track of the current mode, so as to avoid
+  ;; unnecessary roundtrips.
   (let ((result (ange-ftp-send-cmd host user '(type "binary"))))
     (if (not (car result))
        (ange-ftp-error host user (concat "BINARY failed: " (cdr result)))
@@ -2981,6 +2981,8 @@ this also returns nil."
 
 (defun ange-ftp-set-ascii-mode (host user)
   "Tell the FTP process for the given HOST & USER to switch to ASCII mode."
+  ;; FIXME: We should keep track of the current mode, so as to avoid
+  ;; unnecessary roundtrips.
   (let ((result (ange-ftp-send-cmd host user '(type "ascii"))))
     (if (not (car result))
        (ange-ftp-error host user (concat "ASCII failed: " (cdr result)))
@@ -3423,6 +3425,17 @@ system TYPE.")
          (nreverse files)))
     (apply 'ange-ftp-real-directory-files directory full match v19-args)))
 
+(defun ange-ftp-directory-files-and-attributes
+  (directory &optional full match nosort id-format)
+  (setq directory (expand-file-name directory))
+  (if (ange-ftp-ftp-name directory)
+      (mapcar
+       (lambda (file)
+        (cons file (file-attributes (expand-file-name file directory))))
+       (ange-ftp-directory-files directory full match nosort))
+    (ange-ftp-real-directory-files-and-attributes
+     directory full match nosort id-format)))
+
 (defun ange-ftp-file-attributes (file &optional id-format)
   (setq file (expand-file-name file))
   (let ((parsed (ange-ftp-ftp-name file)))
@@ -3449,7 +3462,7 @@ system TYPE.")
                      '(0 0)            ;4 atime
                      (ange-ftp-file-modtime file) ;5 mtime
                      '(0 0)            ;6 ctime
-                     -1                ;7 size
+                     (ange-ftp-file-size file) ;7 size
                      (concat (if (stringp dirp) "l" (if dirp "d" "-"))
                              "?????????") ;8 mode
                      nil               ;9 gid weird
@@ -3493,8 +3506,9 @@ system TYPE.")
        (file-exists-p file)
       (ange-ftp-real-file-executable-p file))))
 
-(defun ange-ftp-delete-file (file)
-  (interactive "fDelete file: ")
+(defun ange-ftp-delete-file (file &optional trash)
+  (interactive (list (read-file-name "Delete file: " nil default-directory)
+                    (null current-prefix-arg)))
   (setq file (expand-file-name file))
   (let ((parsed (ange-ftp-ftp-name file)))
     (if parsed
@@ -3512,7 +3526,7 @@ system TYPE.")
                       (format "FTP Error: \"%s\"" (cdr result))
                       file)))
          (ange-ftp-delete-file-entry file))
-      (ange-ftp-real-delete-file file))))
+      (ange-ftp-real-delete-file file trash))))
 
 (defun ange-ftp-file-modtime (file)
   "Return the modification time of remote file FILE.
@@ -3551,6 +3565,33 @@ Value is (0 0) if the modification time cannot be determined."
           (or (zerop (car file-mdtm))
               (<= (float-time file-mdtm) (float-time buf-mdtm))))
       (ange-ftp-real-verify-visited-file-modtime buf))))
+
+(defun ange-ftp-file-size (file &optional ascii-mode)
+  "Return the size of remote file FILE. Return -1 if can't get it.
+If ascii-mode is non-nil, return the size with the extra octets that
+need to be inserted, one at the end of each line, to provide correct
+end-of-line semantics for a transfer using TYPE=A. The default is nil,
+so return the size on the remote host exactly. See RFC 3659."
+  (let* ((parsed (ange-ftp-ftp-name file))
+        (host (nth 0 parsed))
+        (user (nth 1 parsed))
+        (name (ange-ftp-quote-string (nth 2 parsed)))
+        ;; At least one FTP server (wu-ftpd) can return a "226
+        ;; Transfer complete" before the "213 SIZE".  Let's skip
+        ;; that.
+        (ange-ftp-skip-msgs (concat ange-ftp-skip-msgs "\\|^226"))
+        (res (unwind-protect
+                  (progn
+                    (unless ascii-mode
+                      (ange-ftp-set-binary-mode host user))
+                    (ange-ftp-send-cmd host user (list 'quote "size" name)))
+               (unless ascii-mode
+                 (ange-ftp-set-ascii-mode host user))))
+        (line (cdr res)))
+    (if (string-match "^213 \\([0-9]+\\)$" line)
+       (string-to-number (match-string 1 line))
+      -1)))
+
 \f
 ;;;; ------------------------------------------------------------
 ;;;; File copying support... totally re-written 6/24/92.
@@ -3789,7 +3830,8 @@ Value is (0 0) if the modification time cannot be determined."
     (ange-ftp-call-cont cont result line)))
 
 (defun ange-ftp-copy-file (filename newname &optional ok-if-already-exists
-                                   keep-date preserve-uid-gid)
+                                   keep-date preserve-uid-gid
+                                   preserve-selinux-context)
   (interactive "fCopy file: \nFCopy %s to file: \np")
   (ange-ftp-copy-file-internal filename
                               newname
@@ -3797,7 +3839,7 @@ Value is (0 0) if the modification time cannot be determined."
                               keep-date
                               nil
                               nil
-                              (interactive-p)))
+                              (called-interactively-p 'interactive)))
 
 (defun ange-ftp-copy-files-async (okay-p line verbose-p files)
   "Copy some files in the background.
@@ -4067,6 +4109,15 @@ directory, so that Emacs will know its current contents."
 (defun ange-ftp-delete-directory (dir &optional recursive)
   (if (file-directory-p dir)
       (let ((parsed (ange-ftp-ftp-name dir)))
+       (if recursive
+           (mapc
+            (lambda (file)
+              (if (file-directory-p file)
+                  (ange-ftp-delete-directory file recursive)
+                (delete-file file)))
+            ;; We do not want to delete "." and "..".
+            (directory-files
+             dir 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")))
        (if parsed
            (let* ((host (nth 0 parsed))
                   (user (nth 1 parsed))
@@ -4083,11 +4134,14 @@ directory, so that Emacs will know its current contents."
                            (ange-ftp-real-file-name-as-directory
                             (nth 2 parsed)))))
                   (abbr (ange-ftp-abbreviate-filename dir))
-                  ;; TODO: handle RECURSIVE.
-                  (result (ange-ftp-send-cmd host user
-                                             (list 'rmdir name)
-                                             (format "Removing directory %s"
-                                                     abbr))))
+                  (result
+                   (progn
+                     ;; CWD must not in this directory.
+                     (ange-ftp-cd host user "/" 'noerror)
+                     (ange-ftp-send-cmd host user
+                                        (list 'rmdir name)
+                                        (format "Removing directory %s"
+                                                abbr)))))
              (or (car result)
                  (ange-ftp-error host user
                                  (format "Could not remove directory %s: %s"
@@ -4322,6 +4376,8 @@ NEWNAME should be the name to give the new compressed or uncompressed file.")
 (put 'delete-directory 'ange-ftp 'ange-ftp-delete-directory)
 (put 'insert-file-contents 'ange-ftp 'ange-ftp-insert-file-contents)
 (put 'directory-files 'ange-ftp 'ange-ftp-directory-files)
+(put 'directory-files-and-attributes 'ange-ftp
+     'ange-ftp-directory-files-and-attributes)
 (put 'file-directory-p 'ange-ftp 'ange-ftp-file-directory-p)
 (put 'file-writable-p 'ange-ftp 'ange-ftp-file-writable-p)
 (put 'file-readable-p 'ange-ftp 'ange-ftp-file-readable-p)
@@ -4399,6 +4455,8 @@ NEWNAME should be the name to give the new compressed or uncompressed file.")
   (ange-ftp-run-real-handler 'insert-file-contents args))
 (defun ange-ftp-real-directory-files (&rest args)
   (ange-ftp-run-real-handler 'directory-files args))
+(defun ange-ftp-real-directory-files-and-attributes (&rest args)
+  (ange-ftp-run-real-handler 'directory-files-and-attributes args))
 (defun ange-ftp-real-file-directory-p (&rest args)
   (ange-ftp-run-real-handler 'file-directory-p args))
 (defun ange-ftp-real-file-writable-p (&rest args)
@@ -4464,32 +4522,54 @@ NEWNAME should be the name to give the new compressed or uncompressed file.")
     ;; because some FTP servers react to "ls foo" by listing the symlink foo
     ;; rather than the directory it points to.  Now that ange-ftp-ls uses
     ;; "cd foo; ls" instead, this is not necesssary any more.
-    (insert
-     (cond
-      (wildcard
-       (let ((default-directory (file-name-directory file)))
-         (ange-ftp-ls (file-name-nondirectory file) switches nil nil t)))
-      (full
-       (ange-ftp-ls file switches 'parse))
-      (t
-       ;; If `full' is nil we're going to do `ls' for a single file.
-       ;; Problem is that for various reasons, ange-ftp-ls needs to cd and
-       ;; then do an ls of current dir, which obviously won't work if we
-       ;; want to ls a file.  So instead, we get a full listing of the
-       ;; parent directory and extract the line corresponding to `file'.
-       (when (string-match "-?d\\'" switches)
-         ;; Remove "d" which dired added to `switches'.
-         (setq switches (substring switches 0 (match-beginning 0))))
-       (let* ((dirlist (ange-ftp-ls (or (file-name-directory file) ".")
-                                    switches nil))
-              (filename (file-name-nondirectory (directory-file-name file)))
-              (case-fold-search nil))
-         ;; FIXME: This presumes a particular output format, which is
-         ;; basically Unix.
-         (if (string-match (concat "^.+[^ ] " (regexp-quote filename)
-                                   "\\( -> .*\\)?[@/*=]?\n") dirlist)
-             (match-string 0 dirlist)
-           "")))))))
+    (let ((beg (point))
+         (end (point-marker)))
+      (set-marker-insertion-type end t)
+      (insert
+       (cond
+       (wildcard
+        (let ((default-directory (file-name-directory file)))
+          (ange-ftp-ls (file-name-nondirectory file) switches nil nil t)))
+       (full
+        (ange-ftp-ls file switches 'parse))
+       (t
+        ;; If `full' is nil we're going to do `ls' for a single file.
+        ;; Problem is that for various reasons, ange-ftp-ls needs to cd and
+        ;; then do an ls of current dir, which obviously won't work if we
+        ;; want to ls a file.  So instead, we get a full listing of the
+        ;; parent directory and extract the line corresponding to `file'.
+        (when (string-match "-?d\\'" switches)
+          ;; Remove "d" which dired added to `switches'.
+          (setq switches (substring switches 0 (match-beginning 0))))
+        (setq file (directory-file-name file))
+        (let* ((dirlist (ange-ftp-ls (or (file-name-directory file) ".")
+                                     switches 'parse))
+               (filename (file-name-nondirectory file))
+               (case-fold-search nil))
+          ;; FIXME: This presumes a particular output format, which is
+          ;; basically Unix.
+          (if (string-match (concat "^.+[^ ] " (regexp-quote filename)
+                                    "\\( -> .*\\)?[@/*=]?\n") dirlist)
+              (match-string 0 dirlist)
+            "")))))
+
+      ;; Insert "  " for dired's alignment sanity.
+      (goto-char beg)
+      (while (re-search-forward "^\\(\\S-\\)" end 'move)
+       (replace-match "  \\1"))
+
+      ;; The inserted file could be from somewhere else.
+      (when (and (not wildcard) (not full)
+                (search-backward
+                 (if (zerop (length (file-name-nondirectory
+                                     (expand-file-name file))))
+                     "."
+                   (file-name-nondirectory file))
+                 nil 'noerror))
+       (replace-match (file-relative-name (expand-file-name file)) t)
+       (goto-char end))
+
+      (set-marker end nil))))
 
 (defun ange-ftp-dired-uncache (dir)
   (if (ange-ftp-ftp-name (expand-file-name dir))
@@ -4537,7 +4617,8 @@ NEWNAME should be the name to give the new compressed or uncompressed file.")
       ;; Can't use ange-ftp-dired-host-type here because the current
       ;; buffer is *dired-check-process output*
       (condition-case oops
-         (cond ((equal dired-chmod-program program)
+         (cond ((equal (or (bound-and-true-p dired-chmod-program) "chmod")
+                       program)
                 (ange-ftp-call-chmod arguments))
                ;; ((equal "chgrp" program))
                ;; ((equal dired-chown-program program))
@@ -4818,7 +4899,7 @@ NEWNAME should be the name to give the new compressed or uncompressed file.")
 ;;  ;; This is the Unix dl version.
 ;;  (let ((opoint (point))
 ;;     case-fold-search hidden)
-;;    (or eol (setq eol (save-excursion (end-of-line) (point))))
+;;    (or eol (setq eol (line-end-position)))
 ;;    (setq hidden (and selective-display
 ;;                    (save-excursion
 ;;                      (search-forward "\r" eol t))))
@@ -5217,7 +5298,7 @@ Other orders of $ and _ seem to all work just fine.")
 ;;  ;; This is the VMS version.
 ;;  (let (opoint hidden case-fold-search)
 ;;    (setq opoint (point))
-;;    (or eol (setq eol (save-excursion (end-of-line) (point))))
+;;    (or eol (setq eol (line-end-position)))
 ;;    (setq hidden (and selective-display
 ;;                   (save-excursion (search-forward "\r" eol t))))
 ;;    (if hidden
@@ -5575,7 +5656,7 @@ Other orders of $ and _ seem to all work just fine.")
 ;;  ;; This is the MTS version.
 ;;  (let (opoint hidden case-fold-search)
 ;;    (setq opoint (point)
-;;       eol (save-excursion (end-of-line) (point))
+;;       eol (line-end-position)
 ;;       hidden (and selective-display
 ;;                   (save-excursion (search-forward "\r" eol t))))
 ;;    (if hidden
@@ -5796,7 +5877,7 @@ Other orders of $ and _ seem to all work just fine.")
 ;;  ;; This is the CMS version.
 ;;  (let ((opoint (point))
 ;;     case-fold-search hidden)
-;;    (or eol (setq eol (save-excursion (end-of-line) (point))))
+;;    (or eol (setq eol (line-end-position)))
 ;;    (setq hidden (and selective-display
 ;;                   (save-excursion
 ;;                     (search-forward "\r" eol t))))
@@ -6070,5 +6151,4 @@ be recognized automatically (they are all valid BS2000 hosts too)."
 
 (provide 'ange-ftp)
 
-;; arch-tag: 2987ef88-cb56-4ec1-87a9-79132572e316
 ;;; ange-ftp.el ends here