Mercurial > emacs
changeset 48973:09acf3f65bb5
* net/tramp*.el: Sync with upstream version 2.0.28. Bugfixes.
* net/tramp-ftp.el: Glue code with Ange-FTP, broken out of
tramp.el. From Michael Albinus.
* net/tramp-smb.el: New file for using smbclient to access
Windows shares with Tramp. From Michael Albinus.
author | Kai Großjohann <kgrossjo@eu.uu.net> |
---|---|
date | Thu, 26 Dec 2002 20:47:51 +0000 |
parents | 2d4e1ccc9f01 |
children | d29e4e3d4f5d |
files | lisp/ChangeLog lisp/net/tramp-ftp.el lisp/net/tramp-smb.el lisp/net/tramp-util.el lisp/net/tramp-uu.el lisp/net/tramp-vc.el lisp/net/tramp.el |
diffstat | 7 files changed, 1562 insertions(+), 339 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/ChangeLog Thu Dec 26 17:29:06 2002 +0000 +++ b/lisp/ChangeLog Thu Dec 26 20:47:51 2002 +0000 @@ -1,3 +1,11 @@ +2002-12-26 Kai Gro,A_(Bjohann <kai.grossjohann@uni-duisburg.de> + + * net/tramp*.el: Sync with upstream version 2.0.28. Bugfixes. + * net/tramp-ftp.el: Glue code with Ange-FTP, broken out of + tramp.el. From Michael Albinus. + * net/tramp-smb.el: New file for using smbclient to access + Windows shares with Tramp. From Michael Albinus. + 2002-12-26 Andreas Schwab <schwab@suse.de> * international/mule-cmds.el (select-safe-coding-system): Fix
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/net/tramp-ftp.el Thu Dec 26 20:47:51 2002 +0000 @@ -0,0 +1,136 @@ +;;; tramp-ftp.el --- Tramp convenience functions for Ange-FTP and EFS -*- coding: iso-8859-1; -*- + +;; Copyright (C) 2002 Free Software Foundation, Inc. + +;; Author: Michael Albinus <Michael.Albinus@alcatel.de> +;; Keywords: comm, processes + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Convenience functions for calling Ange-FTP (and maybe EFS, later on) +;; from Tramp. Most of them are displaced from tramp.el + +;;; Code: + +(require 'tramp) + +(eval-when-compile + (require 'cl) + (require 'custom) + ;; Emacs 19.34 compatibility hack -- is this needed? + (or (>= emacs-major-version 20) + (load "cl-seq"))) + +;; Disable Ange-FTP from file-name-handler-alist. +;; To handle EFS, the following functions need to be dealt with: +;; +;; * dired-before-readin-hook contains efs-dired-before-readin +;; * file-name-handler-alist contains efs-file-handler-function +;; and efs-root-handler-function and efs-sifn-handler-function +;; * find-file-hooks contains efs-set-buffer-mode +;; +;; But it won't happen for EFS since the XEmacs maintainers +;; don't want to use a unified filename syntax. +(defun tramp-disable-ange-ftp () + "Turn Ange-FTP off. +This is useful for unified remoting. See +`tramp-file-name-structure-unified' and +`tramp-file-name-structure-separate' for details. Requests suitable +for Ange-FTP will be forwarded to Ange-FTP. Also see the variables +`tramp-ftp-method', `tramp-default-method', and +`tramp-default-method-alist'. + +This function is not needed in Emacsen which include Tramp, but is +present for backward compatibility." + (let ((a1 (rassq 'ange-ftp-hook-function file-name-handler-alist)) + (a2 (rassq 'ange-ftp-completion-hook-function file-name-handler-alist))) + (setq file-name-handler-alist + (delete a1 (delete a2 file-name-handler-alist))))) +(tramp-disable-ange-ftp) + +;; Define FTP method ... +(defcustom tramp-ftp-method "ftp" + "*When this method name is used, forward all calls to Ange-FTP." + :group 'tramp + :type 'string) + +;; ... and add it to the method list. +(add-to-list 'tramp-methods (cons tramp-ftp-method nil)) + +;; Add some defaults for `tramp-default-method-alist' +(add-to-list 'tramp-default-method-alist + '("\\`ftp\\." "" tramp-ftp-method)) +(add-to-list 'tramp-default-method-alist + '("" "\\`\\(anonymous\\|ftp\\)\\'" tramp-ftp-method)) + +;; Add completion function for FTP method. +(unless (memq system-type '(windows-nt)) + (tramp-set-completion-function + tramp-ftp-method + '((tramp-parse-netrc "~/.netrc")))) + +(defun tramp-ftp-file-name-handler (operation &rest args) + "Invoke the Ange-FTP handler for OPERATION. +First arg specifies the OPERATION, second arg is a list of arguments to +pass to the OPERATION." + (save-match-data + (or (boundp 'ange-ftp-name-format) + (and (require 'ange-ftp) + (tramp-disable-ange-ftp))) + (let* ((ange-ftp-name-format + (list (nth 0 tramp-file-name-structure) + (nth 3 tramp-file-name-structure) + (nth 2 tramp-file-name-structure) + (nth 4 tramp-file-name-structure))) + (inhibit-file-name-handlers + (list 'tramp-file-name-handler + 'tramp-completion-file-name-handler + (and (eq inhibit-file-name-operation operation) + inhibit-file-name-handlers))) + (inhibit-file-name-operation operation)) + (apply 'ange-ftp-hook-function operation args)))) + +(defun tramp-ftp-file-name-p (filename) + "Check if it's a filename that should be forwarded to Ange-FTP." + (let ((v (tramp-dissect-file-name filename))) + (string= + (tramp-find-method + (tramp-file-name-multi-method v) + (tramp-file-name-method v) + (tramp-file-name-user v) + (tramp-file-name-host v)) + tramp-ftp-method))) + +(add-to-list 'tramp-foreign-file-name-handler-alist + (cons 'tramp-ftp-file-name-p 'tramp-ftp-file-name-handler)) + +(provide 'tramp-ftp) + +;;; TODO: + +;; * In case of "/ftp:host:file" this works only for functions which +;; are defined in `tramp-file-name-handler-alist'. Call has to be +;; pretended in `tramp-file-name-handler' otherwise. Looks like +;; `ange-ftp-completion-hook-function' and `ange-ftp-hook-function' +;; are active temporarily in `file-name-handler-alist'. +;; Furthermore, there are no backup files on FTP hosts this case. +;; Worth further investigations. + +;;; tramp-ftp.el ends here
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/net/tramp-smb.el Thu Dec 26 20:47:51 2002 +0000 @@ -0,0 +1,1102 @@ +;;; tramp-smb.el --- Tramp access functions for SMB servers -*- coding: iso-8859-1; -*- + +;; Copyright (C) 2002 Free Software Foundation, Inc. + +;; Author: Michael Albinus <Michael.Albinus@alcatel.de> +;; Keywords: comm, processes + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Access functions for SMB servers like SAMBA or M$ Windows from Tramp. + +;;; Code: + +(require 'tramp) + +;; Pacify byte-compiler +(eval-when-compile + (require 'cl) + (require 'custom) + ;; Emacs 19.34 compatibility hack -- is this needed? + (or (>= emacs-major-version 20) + (load "cl-seq"))) + +;; Define SMB method ... +(defcustom tramp-smb-method "smb" + "*Method to connect SAMBA and M$ SMB servers." + :group 'tramp + :type 'string) + +;; ... and add it to the method list. +(add-to-list 'tramp-methods (cons tramp-smb-method nil)) + +;; Add a default for `tramp-default-method-alist'. Rule: If there is +;; a domain in USER, it must be the SMB method. +(add-to-list 'tramp-default-method-alist + '("%" "" tramp-smb-method)) + +;; Add completion function for SMB method. +(tramp-set-completion-function + tramp-smb-method + '((tramp-parse-netrc "~/.netrc"))) + +(defcustom tramp-smb-program "smbclient" + "*Name of SMB client to run." + :group 'tramp + :type 'string) + +(defconst tramp-smb-prompt "^smb: \\S-+> " + "Regexp used as prompt in smbclient.") + +(defconst tramp-smb-errors + (mapconcat + 'identity + '(; Connection error + "Connection to \\S-+ failed" + ; Samba + "ERRSRV" + "ERRDOS" + "ERRbadfile" + "ERRbadpw" + "ERRfilexists" + "ERRnoaccess" + "ERRnomem" + "ERRnosuchshare" + ; Windows NT 4.0, Windows 5.0 (Windows 2000), Windows 5.1 (Windows XP) + "NT_STATUS_ACCESS_DENIED" + "NT_STATUS_BAD_NETWORK_NAME" + "NT_STATUS_CANNOT_DELETE" + "NT_STATUS_LOGON_FAILURE" + "NT_STATUS_NO_SUCH_FILE" + "NT_STATUS_OBJECT_NAME_INVALID" + "NT_STATUS_OBJECT_NAME_NOT_FOUND" + "NT_STATUS_SHARING_VIOLATION") + "\\|") + "Regexp for possible error strings of SMB servers. +Used instead of analyzing error codes of commands.") + +(defvar tramp-smb-share nil + "Holds the share name for the current buffer. +This variable is local to each buffer.") +(make-variable-buffer-local 'tramp-smb-share) + +(defvar tramp-smb-share-cache nil + "Caches the share names accessible to host related to the current buffer. +This variable is local to each buffer.") +(make-variable-buffer-local 'tramp-smb-share-cache) + +(defvar tramp-smb-process-running nil + "Flag whether a corresponding process is still running. +Will be changed by corresponding `process-sentinel'. +This variable is local to each buffer.") +(make-variable-buffer-local 'tramp-smb-process-running) + +;; New handlers should be added here. +(defconst tramp-smb-file-name-handler-alist + '( + ;; `access-file' performed by default handler + (add-name-to-file . tramp-smb-handle-copy-file) ;; we're on Windows, honey. + ;; `byte-compiler-base-file-name' performed by default handler + (copy-file . tramp-smb-handle-copy-file) + (delete-directory . tramp-smb-handle-delete-directory) + (delete-file . tramp-smb-handle-delete-file) + ;; `diff-latest-backup-file' performed by default handler + ;; `directory-file-name' performed by default handler + (directory-files . tramp-smb-handle-directory-files) + (directory-files-and-attributes . tramp-smb-handle-directory-files-and-attributes) + (dired-call-process . tramp-smb-not-handled) + (dired-compress-file . tramp-smb-not-handled) + ;; `dired-uncache' performed by default handler + ;; `expand-file-name' not necessary because we cannot expand "~/" + (file-accessible-directory-p . tramp-smb-handle-file-directory-p) + (file-attributes . tramp-smb-handle-file-attributes) + (file-directory-p . tramp-smb-handle-file-directory-p) + (file-executable-p . tramp-smb-handle-file-exists-p) + (file-exists-p . tramp-smb-handle-file-exists-p) + (file-local-copy . tramp-smb-handle-file-local-copy) + (file-modes . tramp-handle-file-modes) + (file-name-all-completions . tramp-smb-handle-file-name-all-completions) + ;; `file-name-as-directory' performed by default handler + (file-name-completion . tramp-handle-file-name-completion) + (file-name-directory . tramp-handle-file-name-directory) + (file-name-nondirectory . tramp-handle-file-name-nondirectory) + ;; `file-name-sans-versions' performed by default handler + (file-newer-than-file-p . tramp-smb-handle-file-newer-than-file-p) + (file-ownership-preserved-p . tramp-smb-not-handled) + (file-readable-p . tramp-smb-handle-file-exists-p) + (file-regular-p . tramp-handle-file-regular-p) + (file-symlink-p . tramp-smb-not-handled) + ;; `file-truename' performed by default handler + (file-writable-p . tramp-smb-handle-file-writable-p) + ;; `find-backup-file-name' performed by default handler + ;; `find-file-noselect' performed by default handler + ;; `get-file-buffer' performed by default handler + (insert-directory . tramp-smb-handle-insert-directory) + (insert-file-contents . tramp-handle-insert-file-contents) + (load . tramp-handle-load) + (make-directory . tramp-smb-handle-make-directory) + (make-directory-internal . tramp-smb-handle-make-directory-internal) + (make-symbolic-link . tramp-smb-not-handled) + (rename-file . tramp-smb-handle-rename-file) + (set-file-modes . tramp-smb-not-handled) + (set-visited-file-modtime . tramp-smb-not-handled) + (shell-command . tramp-smb-not-handled) + ;; `substitute-in-file-name' performed by default handler + (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) + (vc-registered . tramp-smb-not-handled) + (verify-visited-file-modtime . tramp-smb-not-handled) + (write-region . tramp-smb-handle-write-region) +) + "Alist of handler functions for Tramp SMB method. +Operations not mentioned here will be handled by the default Emacs primitives.") + +(defun tramp-smb-file-name-p (filename) + "Check if it's a filename for SMB servers." + (let ((v (tramp-dissect-file-name filename))) + (string= + (tramp-find-method + (tramp-file-name-multi-method v) + (tramp-file-name-method v) + (tramp-file-name-user v) + (tramp-file-name-host v)) + tramp-smb-method))) + +(defun tramp-smb-file-name-handler (operation &rest args) + "Invoke the SMB related OPERATION. +First arg specifies the OPERATION, second arg is a list of arguments to +pass to the OPERATION." + (let ((fn (assoc operation tramp-smb-file-name-handler-alist))) + (if fn + (if (eq (cdr fn) 'tramp-smb-not-handled) + (apply (cdr fn) operation args) + (save-match-data (apply (cdr fn) args))) + (tramp-run-real-handler operation args)))) + +(add-to-list 'tramp-foreign-file-name-handler-alist + (cons 'tramp-smb-file-name-p 'tramp-smb-file-name-handler)) + + +;; File name primitives + +(defun tramp-smb-not-handled (operation &rest args) + "Default handler for all functions which are disrecarded." + (tramp-message 10 "Won't be handled: %s %s" operation args) + nil) + +(defun tramp-smb-handle-copy-file + (filename newname &optional ok-if-already-exists keep-date) + "Like `copy-file' for tramp files. +KEEP-DATE is not handled in case NEWNAME resides on an SMB server." + (setq filename (expand-file-name filename) + newname (expand-file-name newname)) + + (let ((tmpfile (file-local-copy filename))) + + (if tmpfile + ;; remote filename + (rename-file tmpfile newname ok-if-already-exists) + + ;; remote newname + (when (file-directory-p newname) + (setq newname (expand-file-name + (file-name-nondirectory filename) newname))) + (when (and (not ok-if-already-exists) + (file-exists-p newname)) + (error "copy-file: file %s already exists" newname)) + +; (with-parsed-tramp-file-name newname nil + (let (user host path) + (with-parsed-tramp-file-name newname l + (setq user l-user host l-host path l-path)) + (save-excursion + (let ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path t))) + (unless share + (error "Target `%s' must contain a share name" filename)) + (tramp-smb-maybe-open-connection user host share) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Copying file %s to file %s..." filename newname) + (if (tramp-smb-send-command + user host (format "put %s \"%s\"" filename file)) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Copying file %s to file %s...done" filename newname) + (error "Cannot copy `%s'" filename)))))))) + +(defun tramp-smb-handle-delete-directory (directory) + "Like `delete-directory' for tramp files." + (setq directory (directory-file-name (expand-file-name directory))) + (unless (file-exists-p directory) + (error "Cannot delete non-existing directory `%s'" directory)) +; (with-parsed-tramp-file-name directory nil + (let (user host path) + (with-parsed-tramp-file-name directory l + (setq user l-user host l-host path l-path)) + (save-excursion + (let ((share (tramp-smb-get-share path)) + (dir (tramp-smb-get-path (file-name-directory path) t)) + (file (file-name-nondirectory path))) + (tramp-smb-maybe-open-connection user host share) + (if (and + (tramp-smb-send-command user host (format "cd \"%s\"" dir)) + (tramp-smb-send-command user host (format "rmdir \"%s\"" file))) + ;; Go Home + (tramp-smb-send-command user host (format "cd \\")) + ;; Error + (tramp-smb-send-command user host (format "cd \\")) + (error "Cannot delete directory `%s'" directory)))))) + +(defun tramp-smb-handle-delete-file (filename) + "Like `delete-file' for tramp files." + (setq filename (expand-file-name filename)) + (unless (file-exists-p filename) + (error "Cannot delete non-existing file `%s'" filename)) +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-excursion + (let ((share (tramp-smb-get-share path)) + (dir (tramp-smb-get-path (file-name-directory path) t)) + (file (file-name-nondirectory path))) + (unless (file-exists-p filename) + (error "Cannot delete non-existing file `%s'" filename)) + (tramp-smb-maybe-open-connection user host share) + (if (and + (tramp-smb-send-command user host (format "cd \"%s\"" dir)) + (tramp-smb-send-command user host (format "rm \"%s\"" file))) + ;; Go Home + (tramp-smb-send-command user host (format "cd \\")) + ;; Error + (tramp-smb-send-command user host (format "cd \\")) + (error "Cannot delete file `%s'" directory)))))) + +(defun tramp-smb-handle-directory-files + (directory &optional full match nosort) + "Like `directory-files' for tramp files." + (setq directory (directory-file-name (expand-file-name directory))) +; (with-parsed-tramp-file-name directory nil + (let (user host path) + (with-parsed-tramp-file-name directory l + (setq user l-user host l-host path l-path)) + (save-excursion + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file))) + ;; Just the file names are needed + (setq entries (mapcar 'car entries)) + ;; Discriminate with regexp + (when match + (setq entries + (delete nil + (mapcar (lambda (x) (when (string-match match x) x)) + entries)))) + ;; Make absolute paths if necessary + (when full + (setq entries + (mapcar (lambda (x) + (concat (file-name-as-directory directory) x)) + entries))) + ;; Sort them if necessary + (unless nosort (setq entries (sort entries 'string-lessp))) + ;; That's it + entries)))) + +(defun tramp-smb-handle-directory-files-and-attributes + (directory &optional full match nosort) + "Like `directory-files-and-attributes' for tramp files." + (mapcar + (lambda (x) + (cons x (file-attributes + (if full x (concat (file-name-as-directory directory) x))))) + (directory-files directory full match nosort))) + +(defun tramp-smb-handle-file-attributes (filename &optional nonnumeric) + "Like `file-attributes' for tramp files. +Optional argument NONNUMERIC means return user and group name +rather than as numbers." +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-excursion + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file)) + (entry (and entries + (assoc (file-name-nondirectory file) entries)))) + ; check result + (when entry + (list (and (string-match "d" (nth 1 entry)) + t) ;0 file type + -1 ;1 link count + -1 ;2 uid + -1 ;3 gid + (nth 3 entry) ;4 atime + (nth 3 entry) ;5 mtime + (nth 3 entry) ;6 ctime + (nth 2 entry) ;7 size + (nth 1 entry) ;8 mode + nil ;9 gid weird + -1 ;10 inode number + -1)))))) ;11 file system number + +(defun tramp-smb-handle-file-directory-p (filename) + "Like `file-directory-p' for tramp files." +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-excursion + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file)) + (entry (and entries + (assoc (file-name-nondirectory file) entries)))) + (and entry + (string-match "d" (nth 1 entry)) + t))))) + +(defun tramp-smb-handle-file-exists-p (filename) + "Like `file-exists-p' for tramp files." +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-excursion + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file))) + (and entries + (member (file-name-nondirectory file) (mapcar 'car entries)) + t))))) + +(defun tramp-smb-handle-file-local-copy (filename) + "Like `file-local-copy' for tramp files." + (with-parsed-tramp-file-name filename nil + (save-excursion + (let ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path t)) + (tmpfil (tramp-make-temp-file))) + (unless (file-exists-p filename) + (error "Cannot make local copy of non-existing file `%s'" filename)) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Fetching %s to tmp file %s..." filename tmpfil) + (tramp-smb-maybe-open-connection user host share) + (if (tramp-smb-send-command + user host (format "get \"%s\" %s" file tmpfil)) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Fetching %s to tmp file %s...done" filename tmpfil) + (error "Cannot make local copy of file `%s'" filename)) + tmpfil)))) + +;; This function should return "foo/" for directories and "bar" for +;; files. +(defun tramp-smb-handle-file-name-all-completions (filename directory) + "Like `file-name-all-completions' for tramp files." +; (with-parsed-tramp-file-name directory nil + (let (user host path) + (with-parsed-tramp-file-name directory l + (setq user l-user host l-host path l-path)) + (save-match-data + (save-excursion + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file))) + + (all-completions + filename + (mapcar + (lambda (x) + (list + (if (string-match "d" (nth 1 x)) + (file-name-as-directory (nth 0 x)) + (nth 0 x)))) + entries))))))) + +(defun tramp-smb-handle-file-newer-than-file-p (file1 file2) + "Like `file-newer-than-file-p' for tramp files." + (cond + ((not (file-exists-p file1)) nil) + ((not (file-exists-p file2)) t) + (t (tramp-smb-time-less-p (file-attributes file2) + (file-attributes file1))))) + +(defun tramp-smb-handle-file-writable-p (filename) + "Like `file-writable-p' for tramp files." +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-excursion + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file)) + (entry (and entries + (assoc (file-name-nondirectory file) entries)))) + (and entry + (string-match "w" (nth 1 entry)) + t))))) + +(defun tramp-smb-handle-insert-directory + (filename switches &optional wildcard full-directory-p) + "Like `insert-directory' for tramp files. +WILDCARD and FULL-DIRECTORY-P are not handled." + (setq filename (expand-file-name filename)) + (when (file-directory-p filename) + ;; This check is a little bit strange, but in `dired-add-entry' + ;; this function is called with a non-directory ... + (setq filename (file-name-as-directory filename))) +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-match-data + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil)) + (entries (tramp-smb-get-file-entries user host share file))) + + ;; Delete dummy "" entry, useless entries + (setq entries + (if (file-directory-p filename) + (delq (assoc "" entries) entries) + ;; We just need the only and only entry FILENAME. + (list (assoc (file-name-nondirectory filename) entries)))) + + ;; Sort entries + (setq entries + (sort + entries + (lambda (x y) + (if (string-match "t" switches) + ; sort by date + (tramp-smb-time-less-p (nth 3 y) (nth 3 x)) + ; sort by name + (string-lessp (nth 0 x) (nth 0 y)))))) + + ;; Print entries + (mapcar + (lambda (x) + (insert + (format + "%10s %3d %-8s %-8s %8s %s %s\n" + (nth 1 x) ; mode + 1 "nobody" "nogroup" + (nth 2 x) ; size + (format-time-string + (if (tramp-smb-time-less-p + (tramp-smb-time-subtract (current-time) (nth 3 x)) + tramp-smb-half-a-year) + "%b %e %R" + "%b %e %Y") + (nth 3 x)) ; date + (nth 0 x))) ; file name + (forward-line) + (beginning-of-line)) + entries))))) + +(defun tramp-smb-handle-make-directory (dir &optional parents) + "Like `make-directory' for tramp files." + (setq dir (directory-file-name (expand-file-name dir))) + (unless (file-name-absolute-p dir) + (setq dir (concat default-directory dir))) +; (with-parsed-tramp-file-name dir nil + (let (user host path) + (with-parsed-tramp-file-name dir l + (setq user l-user host l-host path l-path)) + (save-match-data + (let* ((share (tramp-smb-get-share path)) + (ldir (file-name-directory dir))) + ;; Make missing directory parts + (when (and parents share (not (file-directory-p ldir))) + (make-directory ldir parents)) + ;; Just do it + (when (file-directory-p ldir) + (tramp-smb-handle-make-directory-internal dir)) + (unless (file-directory-p dir) + (error "Couldn't make directory %s" dir)))))) + +(defun tramp-smb-handle-make-directory-internal (directory) + "Like `make-directory-internal' for tramp files." + (setq directory (directory-file-name (expand-file-name directory))) + (unless (file-name-absolute-p directory) + (setq ldir (concat default-directory directory))) +; (with-parsed-tramp-file-name directory nil + (let (user host path) + (with-parsed-tramp-file-name directory l + (setq user l-user host l-host path l-path)) + (save-match-data + (let* ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path nil))) + (when (file-directory-p (file-name-directory directory)) + (tramp-smb-maybe-open-connection user host share) + (tramp-smb-send-command user host (format "mkdir \"%s\"" file))) + (unless (file-directory-p directory) + (error "Couldn't make directory %s" directory)))))) + +(defun tramp-smb-handle-rename-file + (filename newname &optional ok-if-already-exists) + "Like `rename-file' for tramp files." + (setq filename (expand-file-name filename) + newname (expand-file-name newname)) + + (let ((tmpfile (file-local-copy filename))) + + (if tmpfile + ;; remote filename + (rename-file tmpfile newname ok-if-already-exists) + + ;; remote newname + (when (file-directory-p newname) + (setq newname (expand-file-name + (file-name-nondirectory filename) newname))) + (when (and (not ok-if-already-exists) + (file-exists-p newname)) + (error "rename-file: file %s already exists" newname)) + +; (with-parsed-tramp-file-name newname nil + (let (user host path) + (with-parsed-tramp-file-name newname l + (setq user l-user host l-host path l-path)) + (save-excursion + (let ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path t))) + (tramp-smb-maybe-open-connection user host share) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Copying file %s to file %s..." filename newname) + (if (tramp-smb-send-command + user host (format "put %s \"%s\"" filename file)) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Copying file %s to file %s...done" filename newname) + (error "Cannot rename `%s'" filename))))))) + + (delete-file filename)) + +(defun tramp-smb-handle-write-region + (start end filename &optional append visit lockname confirm) + "Like `write-region' for tramp files." + (unless (eq append nil) + (error "Cannot append to file using tramp (`%s')" filename)) + (setq filename (expand-file-name filename)) + ;; XEmacs takes a coding system as the seventh argument, not `confirm' + (when (and (not (featurep 'xemacs)) + confirm (file-exists-p filename)) + (unless (y-or-n-p (format "File %s exists; overwrite anyway? " + filename)) + (error "File not overwritten"))) +; (with-parsed-tramp-file-name filename nil + (let (user host path) + (with-parsed-tramp-file-name filename l + (setq user l-user host l-host path l-path)) + (save-excursion + (let ((share (tramp-smb-get-share path)) + (file (tramp-smb-get-path path t)) + (curbuf (current-buffer)) + ;; We use this to save the value of `last-coding-system-used' + ;; after writing the tmp file. At the end of the function, + ;; we set `last-coding-system-used' to this saved value. + ;; This way, any intermediary coding systems used while + ;; talking to the remote shell or suchlike won't hose this + ;; variable. This approach was snarfed from ange-ftp.el. + coding-system-used + tmpfil) + ;; Write region into a tmp file. + (setq tmpfil (tramp-make-temp-file)) + ;; We say `no-message' here because we don't want the visited file + ;; modtime data to be clobbered from the temp file. We call + ;; `set-visited-file-modtime' ourselves later on. + (tramp-run-real-handler + 'write-region + (if confirm ; don't pass this arg unless defined for backward compat. + (list start end tmpfil append 'no-message lockname confirm) + (list start end tmpfil append 'no-message lockname))) + ;; Now, `last-coding-system-used' has the right value. Remember it. + (when (boundp 'last-coding-system-used) + (setq coding-system-used last-coding-system-used)) + + (tramp-smb-maybe-open-connection user host share) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Writing tmp file %s to file %s..." tmpfil filename) + (if (tramp-smb-send-command + user host (format "put %s \"%s\"" tmpfil file)) + (tramp-message-for-buffer + nil tramp-smb-method user host + 5 "Writing tmp file %s to file %s...done" tmpfil filename) + (error "Cannot write `%s'" filename)) + + (delete-file tmpfil) + (unless (equal curbuf (current-buffer)) + (error "Buffer has changed from `%s' to `%s'" + curbuf (current-buffer))) + (when (eq visit t) + (set-visited-file-modtime)) + ;; Make `last-coding-system-used' have the right value. + (when (boundp 'last-coding-system-used) + (setq last-coding-system-used coding-system-used)))))) + + +;; Internal file name functions + +(defun tramp-smb-get-share (path) + "Returns the share name of PATH." + (save-match-data + (when (string-match "^/?\\([^/]+\\)/" path) + (match-string 1 path)))) + +(defun tramp-smb-get-path (path convert) + "Returns the file name of PATH. +If CONVERT is non-nil exchange \"/\" by \"\\\\\"." + (save-match-data + (let ((res path)) + + (setq + res (if (string-match "^/?[^/]+/\\(.*\\)" res) + (if convert + (mapconcat + (lambda (x) (if (equal x ?/) "\\" (char-to-string x))) + (match-string 1 res) "") + (match-string 1 res)) + (if (string-match "^/?\\([^/]+\\)$" res) + (match-string 1 res) + ""))) + + ;; Sometimes we have discarded `substitute-in-file-name' + (when (string-match "\\(\\$\\$\\)\\(/\\|$\\)" res) + (setq res (replace-match "$" nil nil res 1))) + + res))) + +;; Share names of a host are cached. It is very unlikely that the +;; shares do change during connection. +(defun tramp-smb-get-file-entries (user host share path) + "Read entries which match PATH. +Either the shares are listed, or the `dir' command is executed. +Only entries matching the path are returned. +Result is a list of (PATH MODE SIZE MONTH DAY TIME YEAR)." + (save-excursion + (save-match-data + (let ((base (or (and (> (length path) 0) + (string-match "\\([^/]+\\)$" path) + (regexp-quote (match-string 1 path))) + "")) + res entry) + (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) + (if (and (not share) tramp-smb-share-cache) + ;; Return cached shares + (setq res tramp-smb-share-cache) + ;; Read entries + (tramp-smb-maybe-open-connection user host share) + (when share + (tramp-smb-send-command + user host + (format "dir %s" + (if (zerop (length path)) "" (concat "\"" path "*\""))))) + (goto-char (point-min)) + ;; Loop the listing + (unless (re-search-forward tramp-smb-errors nil t) + (while (not (eobp)) + (setq entry (tramp-smb-read-file-entry share)) + (forward-line) + (when entry (add-to-list 'res entry)))) + (unless share + ;; Cache share entries + (setq tramp-smb-share-cache res))) + + + ;; Add directory itself + (add-to-list 'res '("" "dr-xr-xr-x" 0 (0 0))) + + ;; Check for matching entries + (delq nil (mapcar + (lambda (x) (and (string-match base (nth 0 x)) x)) + res)))))) + +;; Return either a share name (if SHARE is nil), or a file name +;; +;; If shares are listed, the following format is expected +;; +;; \s-\{8,8} - leading spaces +;; \S-\(.*\S-\)\s-* - share name, 14 char +;; \s- - space delimeter +;; \S-+\s-* - type, 8 char, "Disk " expected +;; \(\s-\{2,2\}.*\)? - space delimeter, comment +;; +;; Entries provided by smbclient DIR aren't fully regular. +;; They should have the format +;; +;; \s-\{2,2} - leading spaces +;; \S-\(.*\S-\)\s-* - file name, 32 chars, left bound +;; \s- - space delimeter +;; \s-*[ADHRS]* - permissions, 5 chars, right bound +;; \s- - space delimeter +;; \s-*[0-9]+ - size, 8 (Samba) or 7 (Windows) +;; chars, right bound +;; \s-\{2,2\} - space delimeter +;; \w\{3,3\} - weekday +;; \s- - space delimeter +;; [ 19][0-9] - day +;; \s- - space delimeter +;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time +;; \s- - space delimeter +;; [0-9]\{4,4\} - year +;; +;; Problems: +;; * Modern regexp constructs, like spy groups and counted repetitions, aren't +;; available in older Emacsen. +;; * The length of constructs (file name, size) might exceed the default. +;; * File names might contain spaces. +;; * Permissions might be empty. +;; +;; So we try to analyze backwards. +(defun tramp-smb-read-file-entry (share) + "Parse entry in SMB output buffer. +If SHARE is result, entries are of type dir. Otherwise, shares are listed. +Result is the list (PATH MODE SIZE MTIME)." + (let ((line (buffer-substring (point) (tramp-point-at-eol))) + path mode size month day hour min sec year mtime) + + (if (not share) + + ; Read share entries + (when (string-match "^\\s-+\\(\\S-+\\)\\s-+Disk" line) + (setq path (match-string 1 line) + mode "dr-xr-xr-x" + size 0)) + + ; Real listing + (block nil + + ;; year + (if (string-match "\\([0-9]+\\)$" line) + (setq year (string-to-number (match-string 1 line)) + line (substring line 0 -5)) + (return)) + + ;; time + (if (string-match "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)$" line) + (setq hour (string-to-number (match-string 1 line)) + min (string-to-number (match-string 2 line)) + sec (string-to-number (match-string 3 line)) + line (substring line 0 -9)) + (return)) + + ;; day + (if (string-match "\\([0-9]+\\)$" line) + (setq day (string-to-number (match-string 1 line)) + line (substring line 0 -3)) + (return)) + + ;; month + (if (string-match "\\(\\w+\\)$" line) + (setq month (match-string 1 line) + line (substring line 0 -4)) + (return)) + + ;; weekday + (if (string-match "\\(\\w+\\)$" line) + (setq line (substring line 0 -5)) + (return)) + + ;; size + (if (string-match "\\([0-9]+\\)$" line) + (setq size (match-string 1 line) + line (substring line 0 (- (max 8 (1+ (length size)))))) + (return)) + + ;; mode + (if (string-match "\\(\\([ADHRS]+\\)?\\s-?\\)$" line) + (setq + mode (or (match-string 2 line) "") + mode (save-match-data (format + "%s%s" + (if (string-match "D" mode) "d" "-") + (mapconcat + (lambda (x) "") " " + (concat "r" (if (string-match "R" mode) "-" "w") "x")))) + line (substring line 0 (- (1+ (length (match-string 2 line)))))) + (return)) + + ;; path + (if (string-match "^\\s-+\\(\\S-\\(.*\\S-\\)?\\)\\s-+$" line) + (setq path (match-string 1 line)) + (return)))) + + (when (and path mode size) + (setq mtime + (if (and sec min hour day month year) + (encode-time + sec min hour day + (cdr (assoc (downcase month) tramp-smb-parse-time-months)) + year) + '(0 0))) + (list path mode size mtime)))) + + +;; Connection functions + +(defun tramp-smb-send-command (user host command) + "Send the COMMAND to USER at HOST (logged into an SMB session). +Erases temporary buffer before sending the command. Returns nil if +there has been an error message from smbclient." + (save-excursion + (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) + (erase-buffer) + (tramp-send-command nil tramp-smb-method user host command nil t) + (tramp-smb-wait-for-output user host))) + +(defun tramp-smb-maybe-open-connection (user host share) + "Maybe open a connection to HOST, logging in as USER, using `tramp-smb-program'. +Does not do anything if a connection is already open, but re-opens the +connection if a previous connection has died for some reason." + (let ((p (get-buffer-process + (tramp-get-buffer nil tramp-smb-method user host)))) + (save-excursion + (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) + ;; Check whether it is still the same share + (unless (and p (processp p) (string-equal tramp-smb-share share)) + (when (and p (processp p)) + (delete-process p) + (setq p nil))) + ;; If too much time has passed since last command was sent, look + ;; whether process is still alive. If it isn't, kill it. + (when (and tramp-last-cmd-time + (> (tramp-time-diff (current-time) tramp-last-cmd-time) 60) + p (processp p) (memq (process-status p) '(run open))) + (unless (and p (processp p) (memq (process-status p) '(run open))) + (delete-process p) + (setq p nil)))) + (unless (and p (processp p) (memq (process-status p) '(run open))) + (when (and p (processp p)) + (delete-process p)) + (tramp-smb-open-connection user host share)))) + +(defun tramp-smb-open-connection (user host share) + "Open a connection using `tramp-smb-program'. +This starts the command `smbclient //HOST/SHARE -U USER', then waits +for a remote password prompt. It queries the user for the password, +then sends the password to the remote host. + +Domain names in USER and port numbers in HOST are acknowledged." + + (save-match-data + (let* ((buffer (tramp-get-buffer nil tramp-smb-method user host)) + (real-user user) + (real-host host) + domain port args) + + ; Check for domain ("user%domain") and port ("host#port") + (when (and user (string-match "\\(.+\\)%\\(.+\\)" user)) + (setq real-user (or (match-string 1 user) user) + domain (match-string 2 user))) + + (when (and host (string-match "\\(.+\\)#\\(.+\\)" host)) + (setq real-host (or (match-string 1 host) host) + port (match-string 2 host))) + + (if share + (setq args (list (concat "//" real-host "/" share))) + (setq args (list "-L" real-host ))) + + (if real-user + (setq args (append args (list "-U" real-user))) + (setq args (append args (list "-N")))) + + (when domain (setq args (append args (list "-W" domain)))) + (when port (setq args (append args (list "-p" port)))) + + ; OK, let's go + (tramp-pre-connection nil tramp-smb-method user host) + (tramp-message 7 "Opening connection for //%s@%s/%s..." + user host (or share "")) + + (let* ((default-directory (tramp-temporary-file-directory)) + ;; If we omit the conditional here, then we would use + ;; `undecided-dos' in some cases. With the conditional, + ;; we use nil in these cases. Which one is right? + (coding-system-for-read (unless (and (not (featurep 'xemacs)) + (> emacs-major-version 20)) + tramp-dos-coding-system)) + (p (apply #'start-process (buffer-name buffer) buffer + tramp-smb-program args))) + + (tramp-message 9 "Started process %s" (process-command p)) + (process-kill-without-query p) + (set-buffer buffer) + (set-process-sentinel + p (lambda (proc str) (setq tramp-smb-process-running nil))) + ; If no share is given, the process will terminate + (setq tramp-smb-process-running share + tramp-smb-share share) + + ; send password + (when real-user + (let ((pw-prompt "Password:")) + (tramp-message 9 "Sending password") + (tramp-enter-password p pw-prompt))) + + (unless (tramp-smb-wait-for-output user host) + (error "Cannot open connection //%s@%s/%s" + user host (or share ""))))))) + +;; We don't use timeouts. If needed, the caller shall wrap around. +(defun tramp-smb-wait-for-output (user host) + "Wait for output from smbclient command. +Sets position to begin of buffer. +Returns nil if an error message has appeared." + (save-excursion + (let ((proc (get-buffer-process (current-buffer))) + (found (progn (goto-char (point-max)) + (beginning-of-line) + (looking-at tramp-smb-prompt))) + err) + (save-match-data + ;; Algorithm: get waiting output. See if last line contains + ;; tramp-smb-prompt sentinel, or process has exited. + ;; If not, wait a bit and again get waiting output. + (while (and (not found) tramp-smb-process-running) + (accept-process-output proc) + (goto-char (point-max)) + (beginning-of-line) + (setq found (looking-at tramp-smb-prompt))) + + ;; There might be pending output. If tramp-smb-prompt sentinel + ;; hasn't been found, the process has died already. We should + ;; give it a chance. + (when (not found) (accept-process-output nil 1)) + + ;; Search for errors. + (goto-char (point-min)) + (setq err (re-search-forward tramp-smb-errors nil t))) + + ;; Add output to debug buffer if appropriate. + (when tramp-debug-buffer + (append-to-buffer + (tramp-get-debug-buffer nil tramp-smb-method user host) + (point-min) (point-max)) + (when (and (not found) tramp-smb-process-running) + (save-excursion + (set-buffer + (tramp-get-debug-buffer nil tramp-smb-method user host)) + (goto-char (point-max)) + (insert (format "[[Remote prompt `%s' not found]]\n" + tramp-smb-prompt))))) + (goto-char (point-min)) + ;; Return value is whether no error message has appeared. + (not err)))) + + +;; Snarfed code from time-date.el and parse-time.el + +(defconst tramp-smb-half-a-year '(241 17024) +"Evaluated by \"(days-to-time 183)\".") + +(defconst tramp-smb-parse-time-months '(("jan" . 1) ("feb" . 2) ("mar" . 3) + ("apr" . 4) ("may" . 5) ("jun" . 6) + ("jul" . 7) ("aug" . 8) ("sep" . 9) + ("oct" . 10) ("nov" . 11) ("dec" . 12)) +"Alist mapping month names to integers.") + +(defun tramp-smb-time-less-p (t1 t2) + "Say whether time value T1 is less than time value T2." + (unless t1 (setq t1 '(0 0))) + (unless t2 (setq t2 '(0 0))) + (or (< (car t1) (car t2)) + (and (= (car t1) (car t2)) + (< (nth 1 t1) (nth 1 t2))))) + +(defun tramp-smb-time-subtract (t1 t2) + "Subtract two time values. +Return the difference in the format of a time value." + (unless t1 (setq t1 '(0 0))) + (unless t2 (setq t2 '(0 0))) + (let ((borrow (< (cadr t1) (cadr t2)))) + (list (- (car t1) (car t2) (if borrow 1 0)) + (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2))))) + + +;; `PC-do-completion' touches the returning "$$" by `substitute-in-file-name'. +;; Must be corrected. + +(defadvice PC-do-completion (around tramp-smb-advice-PC-do-completion activate) + "Changes \"$\" back to \"$$\" in minibuffer." + (if (funcall PC-completion-as-file-name-predicate) + + (progn + ;; Substitute file names + (let* ((beg (or (and (functionp 'minibuffer-prompt-end) ; Emacs 21 + (funcall 'minibuffer-prompt-end)) + (point-min))) + (end (point-max)) + (str (substitute-in-file-name (buffer-substring beg end)))) + (delete-region beg end) + (insert str) + (ad-set-arg 2 (point))) + + ;; Do `PC-do-completion' without substitution + (let* (save) + (fset 'save (symbol-function 'substitute-in-file-name)) + (fset 'substitute-in-file-name (symbol-function 'identity)) + ad-do-it + (fset 'substitute-in-file-name (symbol-function 'save))) + + ;; Expand "$" + (let* ((beg (or (and (functionp 'minibuffer-prompt-end) ; Emacs 21 + (funcall 'minibuffer-prompt-end)) + (point-min))) + (end (point-max)) + (str (buffer-substring beg end))) + (delete-region beg end) + (insert (if (string-match "\\(\\$\\)\\(/\\|$\\)" str) + (replace-match "$$" nil nil str 1) + str)))) + + ;; No file names. Behave unchanged. + ad-do-it)) + +(provide 'tramp-smb) + +;;; TODO: + +;; * Provide a local smb.conf. The default one might not be readable. +;; * Error handling in case password is wrong. +;; * Read password from "~/.netrc". +;; * Use different buffers for different shares. By this, the password +;; won't be requested again when changing shares on the same host. +;; * Return more comprehensive file permission string. Think whether it is +;; possible to implement `set-file-modes'. +;; * Handle WILDCARD and FULL-DIRECTORY-P in +;; `tramp-smb-handle-insert-directory'. +;; * Handle links (FILENAME.LNK). +;; * Maybe local tmp files should have the same extension like the original +;; files. Strange behaviour with jka-compr otherwise? +;; * Copy files in dired from SMB to another method doesn't work. +;; * Try to remove the inclusion of dummy "" directory. Seems to be at +;; several places, especially in `tramp-smb-handle-insert-directory'. +;; * Provide variables for debug. +;; * (RMS) Use unwind-protect to clean up the state so as to make the state +;; regular again. + +;;; tramp-smb.el ends here
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/net/tramp-util.el Thu Dec 26 20:47:51 2002 +0000 @@ -0,0 +1,54 @@ +;;; tramp-util.el --- Misc utility functions to use with Tramp + +;; Copyright (C) 2001 Free Software Foundation, Inc. + +;; Author: Kai Großjohann <Kai.Grossjohann@CS.Uni-Dortmund.DE> +;; Keywords: comm, extensions, processes + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Some misc. utility functions that might go nicely with Tramp. +;; Mostly, these are kluges awaiting real solutions later on. + +;;; Code: + +(eval-when-compile (require 'cl)) +(require 'compile) +(require 'tramp) + +(defun tramp-compile (command) + "Compile on remote host." + (interactive + (if (or compilation-read-command current-prefix-arg) + (list (read-from-minibuffer "Compile command: " + compile-command nil nil + '(compile-history . 1))) + (list compile-command))) + (setq compile-command command) + (save-some-buffers (not compilation-ask-about-save) nil) + (let ((d default-directory)) + (save-excursion + (pop-to-buffer (get-buffer-create "*Compilation*") t) + (erase-buffer) + (setq default-directory d))) + (tramp-handle-shell-command command (get-buffer "*Compilation*")) + (pop-to-buffer (get-buffer "*Compilation*")) + (compilation-minor-mode 1)) + +(provide 'tramp-util) +;;; tramp-util.el ends here
--- a/lisp/net/tramp-uu.el Thu Dec 26 17:29:06 2002 +0000 +++ b/lisp/net/tramp-uu.el Thu Dec 26 20:47:51 2002 +0000 @@ -36,7 +36,7 @@ (mapcar (lambda (c) (prog1 (cons c i) - (incf i))) + (setq i (1+ i)))) tramp-uu-b64-alphabet)) "Alist of mapping from base64 character to its byte.") @@ -65,7 +65,7 @@ ;; "=" means padding. Insert "`" instead. (insert "`") (insert (tramp-uu-byte-to-uu-char (tramp-uu-b64-char-to-byte c)))) - (incf i) + (setq i (1+ i)) ;; Every 60 characters, add "M" at beginning of line (as ;; length byte) and insert a newline. (when (zerop (% i 60))
--- a/lisp/net/tramp-vc.el Thu Dec 26 17:29:06 2002 +0000 +++ b/lisp/net/tramp-vc.el Thu Dec 26 20:47:51 2002 +0000 @@ -136,7 +136,8 @@ (goto-char (point-max)) (set-buffer-modified-p nil) (forward-line -1) - (if (or (not (integerp status)) (and okstatus (< okstatus status))) + (if (or (not (integerp status)) + (and (integerp okstatus) (< okstatus status))) (progn (pop-to-buffer buffer) (goto-char (point-min)) @@ -174,14 +175,16 @@ (path (when file (tramp-file-name-path v)))) (setq squeezed (delq nil (copy-sequence flags))) (when file - (setq squeezed (append squeezed (list path)))) + (setq squeezed (append squeezed (list (file-relative-name + file default-directory))))) (let ((w32-quote-process-args t)) (when (eq okstatus 'async) (message "Tramp doesn't do async commands, running synchronously.")) (setq status (tramp-handle-shell-command (mapconcat 'tramp-shell-quote-argument (cons command squeezed) " ") t)) - (when (or (not (integerp status)) (and okstatus (< okstatus status))) + (when (or (not (integerp status)) + (and (integerp okstatus) (< okstatus status))) (pop-to-buffer (current-buffer)) (goto-char (point-min)) (shrink-window-if-larger-than-buffer) @@ -281,6 +284,9 @@ (setq exec-status (read (current-buffer))) (message "Command %s returned status %d." command exec-status))) + ;; Maybe okstatus can be `async' here. But then, maybe the + ;; async thing is new in Emacs 21, but this function is only + ;; used in Emacs 20. (cond ((> exec-status okstatus) (switch-to-buffer (get-file-buffer file)) (shrink-window-if-larger-than-buffer
--- a/lisp/net/tramp.el Thu Dec 26 17:29:06 2002 +0000 +++ b/lisp/net/tramp.el Thu Dec 26 20:47:51 2002 +0000 @@ -1,6 +1,6 @@ ;;; tramp.el --- Transparent Remote Access, Multiple Protocol -*- coding: iso-8859-1; -*- -;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. +;; Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. ;; Author: Kai.Grossjohann@CS.Uni-Dortmund.DE ;; Keywords: comm, processes @@ -72,7 +72,7 @@ ;; In the Tramp CVS repository, the version numer is auto-frobbed from ;; the Makefile, so you should edit the top-level Makefile to change ;; the version number. -(defconst tramp-version "2.0.25" +(defconst tramp-version "2.0.28" "This version of tramp.") (defconst tramp-bug-report-address "tramp-devel@mail.freesoftware.fsf.org" @@ -99,6 +99,22 @@ ;; (when (fboundp 'efs-file-handler-function) ;; (require 'efs)) +;; Load foreign methods. Because they do require Tramp internally, this +;; must be done with the `eval-after-load' trick. + +;; tramp-ftp supports Ange-FTP only. Not suited for XEmacs therefore. +(unless (featurep 'xemacs) + (eval-after-load "tramp" + '(require 'tramp-ftp))) + +;; tramp-smb uses "smbclient" from Samba. +;; Not available under Cygwin and Windows, because they don't offer +;; "smbclient". And even not necessary there, because Emacs supports +;; UNC file names like "//host/share/path". +(unless (memq system-type '(cygwin windows-nt)) + (eval-after-load "tramp" + '(require 'tramp-smb))) + (eval-when-compile (require 'cl) (require 'custom) @@ -618,20 +634,12 @@ (defcustom tramp-default-method "ssh" "*Default method to use for transferring files. See `tramp-methods' for possibilities. -Also see `tramp-default-method-alist'. - -Emacs uses a unified filename syntax for Tramp and Ange-FTP. -For backward compatibility, the default value of this variable -is \"ftp\" on Emacs. But XEmacs uses a separate filename syntax -for Tramp and EFS, so there the default method is \"sm\"." +Also see `tramp-default-method-alist'." :group 'tramp :type 'string) (defcustom tramp-default-method-alist - (when tramp-unified-filenames - '(("\\`ftp\\." "" "ftp") - ("" "\\`\\(anonymous\\|ftp\\)\\'" "ftp") - ("\\`localhost\\'" "\\`root\\'" "su"))) + '(("\\`localhost\\'" "\\`root\\'" "su")) "*Default method to use for specific user/host pairs. This is an alist of items (HOST USER METHOD). The first matching item specifies the method to use for a file name which does not specify a @@ -648,11 +656,6 @@ (regexp :tag "User regexp") (string :tag "Method")))) -(defcustom tramp-ftp-method "ftp" - "*When this method name is used, forward all calls to Ange-FTP." - :group 'tramp - :type 'string) - ;; Default values for non-Unices seeked (defconst tramp-completion-function-alist-rsh (unless (memq system-type '(windows-nt)) @@ -687,13 +690,6 @@ "Default list of (FUNCTION FILE) pairs to be examined for su methods." ) -;; Default values for non-Unices seeked -(defconst tramp-completion-function-alist-ftp - (unless (memq system-type '(windows-nt)) - '((tramp-parse-netrc "~/.netrc"))) - "Default list of (FUNCTION FILE) pairs to be examined for ftp methods." -) - (defcustom tramp-completion-function-alist (list (cons "rcp" tramp-completion-function-alist-rsh) (cons "scp" tramp-completion-function-alist-ssh) @@ -718,7 +714,6 @@ (cons "plink" tramp-completion-function-alist-ssh) (cons "pscp" tramp-completion-function-alist-ssh) (cons "fcp" tramp-completion-function-alist-ssh) - (cons "ftp" tramp-completion-function-alist-ftp) ) "*Alist of methods for remote files. This is a list of entries of the form (NAME PAIR1 PAIR2 ...). @@ -730,7 +725,7 @@ * `tramp-parse-shosts' for \"ssh_known_hosts\" like files, * `tramp-parse-hosts' for \"/etc/hosts\" like files, and * `tramp-parse-passwd' for \"/etc/passwd\" like files. - * `tramp-parse-netrc ' for \".netrc\" like files. + * `tramp-parse-netrc' for \".netrc\" like files. FUNCTION can also see a customer defined function. For more details see the info pages." @@ -870,7 +865,7 @@ :group 'tramp :type 'boolean) -(defcustom tramp-sh-extra-args '(("/bash\\'" . "--norc")) +(defcustom tramp-sh-extra-args '(("/bash\\'" . "-norc -noprofile")) "*Alist specifying extra arguments to pass to the remote shell. Entries are (REGEXP . ARGS) where REGEXP is a regular expression matching the shell file name and ARGS is a string specifying the @@ -1254,7 +1249,30 @@ the visited file modtime.") (make-variable-buffer-local 'tramp-buffer-file-attributes) -(defvar tramp-end-of-output "/////" +(defvar tramp-md5-function + (cond ((fboundp 'md5) 'md5) + ((and (require 'md5) (fboundp 'md5-encode)) 'md5-encode) + (t (error "Coulnd't find an `md5' function"))) + "Function to call for running the MD5 algorithm.") + +(defvar tramp-end-of-output + (concat "///" + (funcall tramp-md5-function + (concat + (prin1-to-string process-environment) + (current-time-string) +;; (prin1-to-string +;; (if (fboundp 'directory-files-and-attributes) +;; (funcall 'directory-files-and-attributes +;; (or (getenv "HOME") +;; (tramp-temporary-file-directory))) +;; (mapcar +;; (lambda (x) +;; (cons x (file-attributes x))) +;; (directory-files (or (getenv "HOME") +;; (tramp-temporary-file-directory)) +;; t)))) + ))) "String used to recognize end of output.") (defvar tramp-connection-function nil @@ -1622,6 +1640,12 @@ mentioned here will be handled by `tramp-file-name-handler-alist' or the normal Emacs functions.") +;; Handlers for foreign methods, like FTP or SMB, shall be plugged here. +(defvar tramp-foreign-file-name-handler-alist nil + "Alist of elements (FUNCTION . HANDLER) for foreign methods handled specially. +If (FUNCTION FILENAME) returns non-nil, then all I/O on that file is done by +calling HANDLER.") + ;;; Internal functions which must come first. (defsubst tramp-message (level fmt-string &rest args) @@ -1711,7 +1735,9 @@ (tramp-parse-shosts \"~/.ssh/known_hosts\")))" (let ((v (cdr (assoc method tramp-completion-function-alist)))) - (when v (setcdr v function-list)))) + (if v (setcdr v function-list) + (add-to-list 'tramp-completion-function-alist + (cons method function-list))))) (defun tramp-get-completion-function (method) "Returns list of completion functions for METHOD. @@ -1732,9 +1758,6 @@ this can give surprising results if the user/host for the source and target of the symlink differ." (with-parsed-tramp-file-name linkname l - (when (tramp-ange-ftp-file-name-p l-multi-method l-method l-user l-host) - (tramp-invoke-ange-ftp 'make-symbolic-link - filename linkname ok-if-already-exists)) (let ((ln (tramp-get-remote-ln l-multi-method l-method l-user l-host)) (cwd (file-name-directory l-path))) (unless ln @@ -1778,9 +1801,6 @@ (unless (file-name-absolute-p file) (error "Tramp cannot `load' files without absolute path name")) (with-parsed-tramp-file-name file nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'load - file noerror nomessage nosuffix must-suffix)) (unless nosuffix (cond ((file-exists-p (concat file ".elc")) (setq file (concat file ".elc"))) @@ -1813,8 +1833,6 @@ "Like `file-name-directory' but aware of TRAMP files." ;; everything except the last filename thing is the directory (with-parsed-tramp-file-name file nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-name-directory file)) ;; For the following condition, two possibilities should be tried: ;; (1) (string= path "") ;; (2) (or (string= path "") (string= path "/")) @@ -1839,18 +1857,11 @@ (defun tramp-handle-file-name-nondirectory (file) "Like `file-name-nondirectory' but aware of TRAMP files." (with-parsed-tramp-file-name file nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-name-nondirectory file)) (file-name-nondirectory path))) (defun tramp-handle-file-truename (filename &optional counter prev-dirs) "Like `file-truename' for tramp files." (with-parsed-tramp-file-name filename nil - ;; Ange-FTP does not support truename processing, but for - ;; convenience we pretend it did and forward the call to Ange-FTP - ;; anyway. Ange-FTP then just invokes `identity'. - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-truename filename)) (let* ((steps (tramp-split-string path "/")) (pathdir (let ((directory-sep-char ?/)) (file-name-as-directory path))) @@ -1926,8 +1937,6 @@ (defun tramp-handle-file-exists-p (filename) "Like `file-exists-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-exists-p filename)) (save-excursion (zerop (tramp-send-command-and-check multi-method method user host @@ -1944,8 +1953,6 @@ rather than as numbers." (let (result) (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-attributes filename)) (when (tramp-handle-file-exists-p filename) ;; file exists, find out stuff (save-excursion @@ -2074,15 +2081,6 @@ (let ((f (buffer-file-name)) (coding-system-used nil)) (with-parsed-tramp-file-name f nil - ;; This operation is not handled by Ange-FTP! Compare this - ;; behavior with `file-truename' which Ange-FTP does not really - ;; handle, either, but at least it pretends to. I wonder if - ;; Ange-FTP should also pretend to grok - ;; `set-visited-file-modtime', for consistency? - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (throw 'tramp-forward-to-ange-ftp - (tramp-run-real-handler 'set-visited-file-modtime - (list time-list)))) (let* ((attr (file-attributes f)) (modtime (nth 5 attr))) ;; We use '(0 0) as a don't-know value. See also @@ -2114,12 +2112,6 @@ (with-current-buffer buf (let ((f (buffer-file-name))) (with-parsed-tramp-file-name f nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - ;; This one requires a hack since the file name is not passed - ;; on the arg list. - (let ((buffer-file-name (tramp-make-ange-ftp-file-name - user host path))) - (tramp-invoke-ange-ftp 'verify-visited-file-modtime buf))) (let* ((attr (file-attributes f)) (modtime (nth 5 attr))) (cond ((and attr (not (equal modtime '(0 0)))) @@ -2153,8 +2145,6 @@ (defun tramp-handle-set-file-modes (filename mode) "Like `set-file-modes' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'set-file-modes mode filename)) (save-excursion (unless (zerop (tramp-send-command-and-check multi-method method user host @@ -2172,22 +2162,16 @@ (defun tramp-handle-file-executable-p (filename) "Like `file-executable-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-executable-p filename)) (zerop (tramp-run-test "-x" filename)))) (defun tramp-handle-file-readable-p (filename) "Like `file-readable-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-readable-p filename)) (zerop (tramp-run-test "-r" filename)))) (defun tramp-handle-file-accessible-directory-p (filename) "Like `file-accessible-directory-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-accessible-directory-p filename)) (and (zerop (tramp-run-test "-d" filename)) (zerop (tramp-run-test "-r" filename)) (zerop (tramp-run-test "-x" filename))))) @@ -2213,7 +2197,7 @@ (fa2 (file-attributes file2))) (if (and (not (equal (nth 5 fa1) '(0 0))) (not (equal (nth 5 fa2) '(0 0)))) - (> 0 (car (subtract-time (nth 5 fa1) (nth 5 fa2)))) + (> 0 (car (tramp-time-diff (nth 5 fa1) (nth 5 fa2)))) ;; If one of them is the dont-know value, then we can ;; still try to run a shell command on the remote host. ;; However, this only works if both files are Tramp @@ -2228,12 +2212,6 @@ file1 file2))) (with-parsed-tramp-file-name file1 v1 (with-parsed-tramp-file-name file2 v2 - (when (and (tramp-ange-ftp-file-name-p - v1-multi-method v1-method v1-user v1-host) - (tramp-ange-ftp-file-name-p - v2-multi-method v2-method v2-user v2-host)) - (tramp-invoke-ange-ftp 'file-newer-than-file-p - file1 file2)) (unless (and (equal v1-multi-method v2-multi-method) (equal v1-method v2-method) (equal v1-user v2-user) @@ -2257,11 +2235,9 @@ (defun tramp-handle-file-modes (filename) "Like `file-modes' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-modes filename)) (when (file-exists-p filename) (tramp-mode-string-to-int - (nth 8 (tramp-handle-file-attributes filename)))))) + (nth 8 (file-attributes filename)))))) (defun tramp-handle-file-directory-p (filename) "Like `file-directory-p' for tramp files." @@ -2274,8 +2250,6 @@ ;; ;; Alternatives: `cd %s', `test -d %s' (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-directory-p filename)) (save-excursion (zerop (tramp-send-command-and-check @@ -2287,24 +2261,18 @@ (defun tramp-handle-file-regular-p (filename) "Like `file-regular-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-regular-p filename)) (and (tramp-handle-file-exists-p filename) (eq ?- (aref (nth 8 (tramp-handle-file-attributes filename)) 0))))) (defun tramp-handle-file-symlink-p (filename) "Like `file-symlink-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-symlink-p filename)) (let ((x (car (tramp-handle-file-attributes filename)))) (when (stringp x) x)))) (defun tramp-handle-file-writable-p (filename) "Like `file-writable-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-writable-p filename)) (if (tramp-handle-file-exists-p filename) ;; Existing files must be writable. (zerop (tramp-run-test "-w" filename)) @@ -2317,8 +2285,6 @@ (defun tramp-handle-file-ownership-preserved-p (filename) "Like `file-ownership-preserved-p' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-ownership-preserved-p filename)) (or (not (tramp-handle-file-exists-p filename)) ;; Existing files must be writable. (zerop (tramp-run-test "-O" filename))))) @@ -2337,8 +2303,6 @@ (defun tramp-handle-directory-file-name (directory) "Like `directory-file-name' for tramp files." (with-parsed-tramp-file-name directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'directory-file-name directory)) (let ((directory-length-1 (1- (length directory)))) (save-match-data (if (and (eq (aref directory directory-length-1) ?/) @@ -2353,9 +2317,6 @@ &optional full match nosort files-only) "Like `directory-files' for tramp files." (with-parsed-tramp-file-name directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'directory-files - directory full match nosort files-only)) (let (result x) (save-excursion (tramp-barf-unless-okay @@ -2410,9 +2371,6 @@ (defun tramp-handle-file-name-all-completions (filename directory) "Like `file-name-all-completions' for tramp files." (with-parsed-tramp-file-name directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-name-all-completions - filename directory)) (unless (save-match-data (string-match "/" filename)) (let* ((nowild tramp-completion-without-shell-p) result) @@ -2463,13 +2421,10 @@ "tramp-handle-file-name-completion invoked on non-tramp directory `%s'" directory)) (with-parsed-tramp-file-name directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-name-completion - filename directory)) (try-completion filename (mapcar (lambda (x) (cons x nil)) - (tramp-handle-file-name-all-completions filename directory))))) + (file-name-all-completions filename directory))))) ;; cp, mv and ln @@ -2487,16 +2442,6 @@ (equal v1-host v2-host)) (error "add-name-to-file: %s" "only implemented for same method, same user, same host")) - (when (and (tramp-ange-ftp-file-name-p v1-multi-method v1-method v1-user v1-host) - (tramp-ange-ftp-file-name-p v2-multi-method v2-method v2-user v2-host)) - (tramp-invoke-ange-ftp 'add-name-to-file - filename newname ok-if-already-exists)) - (when (tramp-ange-ftp-file-name-p v1-multi-method v1-method v1-user v1-host) - (tramp-invoke-ange-ftp 'add-name-to-file - filename newname ok-if-already-exists)) - (when (tramp-ange-ftp-file-name-p v2-multi-method v2-method v2-user v2-host) - (tramp-invoke-ange-ftp 'add-name-to-file - filename newname ok-if-already-exists)) (when (and (not ok-if-already-exists) (file-exists-p newname) (not (numberp ok-if-already-exists)) @@ -2571,14 +2516,6 @@ ;; Both are Tramp files. (with-parsed-tramp-file-name filename v1 (with-parsed-tramp-file-name newname v2 - ;; Possibly invoke Ange-FTP. - (when (and (tramp-ange-ftp-file-name-p v1-multi-method v1-method v1-user v1-host) - (tramp-ange-ftp-file-name-p v2-multi-method v2-method v2-user v2-host)) - (if (eq op 'copy) - (tramp-invoke-ange-ftp - 'copy-file filename newname ok-if-already-exists keep-date) - (tramp-invoke-ange-ftp - 'rename-file filename newname ok-if-already-exists))) ;; Check if we can use a shortcut. (if (and (equal v1-multi-method v2-multi-method) (equal v1-method v2-method) @@ -2663,8 +2600,6 @@ "Like `make-directory' for tramp files." (setq dir (expand-file-name dir)) (with-parsed-tramp-file-name dir nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'make-directory dir parents)) (save-excursion (tramp-barf-unless-okay multi-method method user host @@ -2679,8 +2614,6 @@ "Like `delete-directory' for tramp files." (setq directory (expand-file-name directory)) (with-parsed-tramp-file-name directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'delete-directory directory)) (save-excursion (tramp-send-command multi-method method user host @@ -2692,8 +2625,6 @@ "Like `delete-file' for tramp files." (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'delete-file filename)) (save-excursion (unless (zerop (tramp-send-command-and-check multi-method method user host @@ -2709,9 +2640,6 @@ "Recursively delete the directory given. This is like `dired-recursive-delete-directory' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'dired-recursive-delete-directory - filename)) ;; run a shell command 'rm -r <path>' ;; Code shamelessly stolen for the dired implementation and, um, hacked :) (or (tramp-handle-file-exists-p filename) @@ -2732,11 +2660,6 @@ (defun tramp-handle-dired-call-process (program discard &rest arguments) "Like `dired-call-process' for tramp files." (with-parsed-tramp-file-name default-directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (let ((default-directory - (tramp-make-ange-ftp-file-name user host path))) - (tramp-invoke-ange-ftp 'dired-call-process - program discard arguments))) (save-excursion (tramp-barf-unless-okay multi-method method user host @@ -2779,9 +2702,6 @@ (setq switches (replace-match "" nil t switches))) (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'insert-directory - filename switches wildcard full-directory-p)) (tramp-message-for-buffer multi-method method user host 10 "Inserting directory `ls %s %s', wildcard %s, fulldir %s" @@ -2857,9 +2777,6 @@ (defun tramp-handle-unhandled-file-name-directory (filename) "Like `unhandled-file-name-directory' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'unhandled-file-name-directory - filename)) (expand-file-name "~/"))) ;; Canonicalization of file names. @@ -2893,8 +2810,6 @@ (list name nil)) ;; Dissect NAME. (with-parsed-tramp-file-name name nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'expand-file-name name nil)) (unless (file-name-absolute-p path) (setq path (concat "~/" path))) (save-excursion @@ -2935,11 +2850,6 @@ `tramp-end-of-output', followed by another newline." (if (tramp-tramp-file-p default-directory) (with-parsed-tramp-file-name default-directory nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (let ((default-directory (tramp-make-ange-ftp-file-name - user host path))) - (tramp-invoke-ange-ftp 'shell-command - command output-buffer error-buffer))) (let (status) (when (string-match "&[ \t]*\\'" command) (error "Tramp doesn't grok asynchronous shell commands, yet")) @@ -2979,7 +2889,7 @@ (skip-chars-forward "^ ") (setq status (read (current-buffer)))) (unless (zerop (buffer-size)) - (pop-to-buffer output-buffer)) + (display-buffer output-buffer)) status)) ;; The following is only executed if something strange was ;; happening. Emit a helpful message and do it anyway. @@ -2998,8 +2908,6 @@ (defun tramp-handle-file-local-copy (filename) "Like `file-local-copy' for tramp files." (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'file-local-copy filename)) (let ((output-buf (get-buffer-create "*tramp output*")) (tramp-buf (tramp-get-buffer multi-method method user host)) (rcp-program (tramp-get-rcp-program @@ -3114,10 +3022,7 @@ (barf-if-buffer-read-only) (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'insert-file-contents - filename visit beg end replace)) - (if (not (tramp-handle-file-exists-p filename)) + (if (not (file-exists-p filename)) (progn (when visit (setq buffer-file-name filename) @@ -3125,8 +3030,8 @@ (set-buffer-modified-p nil)) (signal 'file-error (format "File `%s' not found on remote host" filename)) - (list (tramp-handle-expand-file-name filename) 0)) - (let ((local-copy (tramp-handle-file-local-copy filename)) + (list (expand-file-name filename) 0)) + (let ((local-copy (file-local-copy filename)) (coding-system-used nil) (result nil)) (when visit @@ -3136,9 +3041,7 @@ (tramp-message-for-buffer multi-method method user host 9 "Inserting local temp file `%s'..." local-copy) - (setq result - (tramp-run-real-handler 'insert-file-contents - (list local-copy nil beg end replace))) + (setq result (insert-file-contents local-copy nil beg end replace)) ;; Now `last-coding-system-used' has right value. Remember it. (when (boundp 'last-coding-system-used) (setq coding-system-used last-coding-system-used)) @@ -3174,9 +3077,6 @@ filename)) (error "File not overwritten"))) (with-parsed-tramp-file-name filename nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'write-region - start end filename append visit)) (let ((curbuf (current-buffer)) (rcp-program (tramp-get-rcp-program multi-method (tramp-find-method multi-method method user host) @@ -3380,15 +3280,15 @@ "Invoke normal file name handler for OPERATION. First arg specifies the OPERATION, second arg is a list of arguments to pass to the OPERATION." - (let* ((op (if (eq operation 'ange-ftp-hook-function) - (car args) - operation)) - (inhibit-file-name-handlers - (list 'tramp-file-name-handler - 'tramp-completion-file-name-handler - (and (eq inhibit-file-name-operation op) - inhibit-file-name-handlers))) - (inhibit-file-name-operation op)) + (let* ((inhibit-file-name-handlers + `(tramp-file-name-handler + tramp-completion-file-name-handler + cygwin-mount-name-hook-function + cygwin-mount-map-drive-hook-function + . + ,(and (eq inhibit-file-name-operation operation) + inhibit-file-name-handlers))) + (inhibit-file-name-operation operation)) (apply operation args))) ;; This function is used from `tramp-completion-file-name-handler' functions @@ -3399,26 +3299,106 @@ "Invoke `tramp-file-name-handler' for OPERATION. First arg specifies the OPERATION, second arg is a list of arguments to pass to the OPERATION." - (let* ((op (if (eq operation 'ange-ftp-hook-function) - (car args) - operation)) - (inhibit-file-name-handlers - (list 'tramp-completion-file-name-handler - (and (eq inhibit-file-name-operation op) - inhibit-file-name-handlers))) - (inhibit-file-name-operation op)) + (let* ((inhibit-file-name-handlers + `(tramp-completion-file-name-handler + cygwin-mount-name-hook-function + cygwin-mount-map-drive-hook-function + . + ,(and (eq inhibit-file-name-operation operation) + inhibit-file-name-handlers))) + (inhibit-file-name-operation operation)) (apply operation args))) +;; We handle here all file primitives. Most of them have the file +;; name as first parameter; nevertheless we check for them explicitly +;; in order to be be signalled if a new primitive appears. This +;; scenario is needed because there isn't a way to decide by +;; syntactical means whether a foreign method must be called. It would +;; ease the live if `file-name-handler-alist' would support a decision +;; function as well but regexp only. +(defun tramp-file-name-for-operation (operation &rest args) + "Return file name related to OPERATION file primitive. +ARGS are the arguments OPERATION has been called with." + (cond + ; FILE resp DIRECTORY + ((member operation + (list 'access-file 'byte-compiler-base-file-name 'delete-directory + 'delete-file 'diff-latest-backup-file 'directory-file-name + 'directory-files 'directory-files-and-attributes + 'dired-compress-file 'dired-uncache + 'file-accessible-directory-p 'file-attributes + 'file-directory-p 'file-executable-p 'file-exists-p + 'file-local-copy 'file-modes 'file-name-as-directory + 'file-name-directory 'file-name-nondirectory + 'file-name-sans-versions 'file-ownership-preserved-p + 'file-readable-p 'file-regular-p 'file-symlink-p + 'file-truename 'file-writable-p 'find-backup-file-name + 'find-file-noselect 'get-file-buffer 'insert-directory + 'insert-file-contents 'load 'make-directory + 'make-directory-internal 'set-file-modes + 'substitute-in-file-name 'unhandled-file-name-directory + 'vc-registered + ; XEmacs only + 'abbreviate-file-name 'create-file-buffer + 'dired-file-modtime 'dired-make-compressed-filename + 'dired-recursive-delete-directory 'dired-set-file-modtime + 'dired-shell-unhandle-file-name 'dired-uucode-file + 'insert-file-contents-literally 'recover-file + 'vm-imap-check-mail 'vm-pop-check-mail 'vm-spool-check-mail)) + (expand-file-name (nth 0 args))) + ; FILE DIRECTORY resp FILE1 FILE2 + ((member operation + (list 'add-name-to-file 'copy-file 'expand-file-name + 'file-name-all-completions 'file-name-completion + 'file-newer-than-file-p 'make-symbolic-link 'rename-file + ; XEmacs only + 'dired-make-relative-symlink + 'vm-imap-move-mail 'vm-pop-move-mail 'vm-spool-move-mail)) + (save-match-data + (cond + ((string-match tramp-file-name-regexp (nth 0 args)) (nth 0 args)) + ((string-match tramp-file-name-regexp (nth 1 args)) (nth 1 args)) + (t (buffer-file-name (current-buffer)))))) + ; START END FILE + ((eq operation 'write-region) + (nth 2 args)) + ; BUF + ((member operation + (list 'set-visited-file-modtime 'verify-visited-file-modtime + ; XEmacs only + 'backup-buffer)) + (buffer-file-name + (if (bufferp (nth 0 args)) (nth 0 args) (current-buffer)))) + ; COMMAND + ((member operation + (list 'dired-call-process 'shell-command + ; XEmacs only + 'dired-print-file 'dired-shell-call-process)) + default-directory) + ; unknown file primitive + (t (error "unknown file I/O primitive: %s" operation)))) + +(defun tramp-find-foreign-file-name-handler (filename) + "Return foreign file name handler if exists." + (let (elt res) + (dolist (elt tramp-foreign-file-name-handler-alist res) + (when (funcall (car elt) filename) + (setq res (cdr elt)))) + res)) + ;; Main function. ;;;###autoload (defun tramp-file-name-handler (operation &rest args) "Invoke tramp file name handler. Falls back to normal file name handler if no tramp file name handler exists." - (let ((fn (assoc operation tramp-file-name-handler-alist))) - (if fn - (catch 'tramp-forward-to-ange-ftp - (save-match-data (apply (cdr fn) args))) - (tramp-run-real-handler operation args)))) + (save-match-data + (let* ((fn (assoc operation tramp-file-name-handler-alist)) + (filename (apply 'tramp-file-name-for-operation operation args)) + (foreign (tramp-find-foreign-file-name-handler filename))) + (cond + (foreign (apply foreign operation args)) + (fn (apply (cdr fn) args)) + (t (tramp-run-real-handler operation args)))))) (put 'tramp-file-name-handler 'file-remote-p t) ;for file-remote-p @@ -3432,8 +3412,7 @@ ;; operation args (with-output-to-string (backtrace))) (let ((fn (assoc operation tramp-completion-file-name-handler-alist))) (if fn - (catch 'tramp-forward-to-ange-ftp - (save-match-data (apply (cdr fn) args))) + (save-match-data (apply (cdr fn) args)) (tramp-completion-run-real-handler operation args)))) ;; Register in file name handler alist @@ -3444,32 +3423,6 @@ (cons tramp-completion-file-name-regexp 'tramp-completion-file-name-handler)) -;; To handle EFS, the following functions need to be dealt with: -;; -;; * dired-before-readin-hook contains efs-dired-before-readin -;; * file-name-handler-alist contains efs-file-handler-function -;; and efs-root-handler-function and efs-sifn-handler-function -;; * find-file-hooks contains efs-set-buffer-mode -;; -;; But it won't happen for EFS since the XEmacs maintainers -;; don't want to use a unified filename syntax. -(defun tramp-disable-ange-ftp () - "Turn Ange-FTP off. -This is useful for unified remoting. See -`tramp-file-name-structure-unified' and -`tramp-file-name-structure-separate' for details. Requests suitable -for Ange-FTP will be forwarded to Ange-FTP. Also see the variables -`tramp-ftp-method', `tramp-default-method', and -`tramp-default-method-alist'. - -This function is not needed in Emacsen which include Tramp, but is -present for backward compatibility." - (let ((a1 (rassq 'ange-ftp-hook-function file-name-handler-alist)) - (a2 (rassq 'ange-ftp-completion-hook-function file-name-handler-alist))) - (setq file-name-handler-alist - (delete a1 (delete a2 file-name-handler-alist))))) -(tramp-disable-ange-ftp) - (defun tramp-repair-jka-compr () "If jka-compr is already loaded, move it to the front of `file-name-handler-alist'. On Emacs 21.4 or so this will not be @@ -3480,40 +3433,6 @@ (cons jka (delete jka file-name-handler-alist)))))) (tramp-repair-jka-compr) -(defun tramp-flatten-list (arg) - "Expands all lists inside ARG to a sequential list. -Return (nil) if arg is nil." - (let ((car (car arg)) - (cdr (cdr arg))) - (cond - ((eq arg nil) '(nil)) - ((listp car) - (if (null cdr) - (tramp-flatten-list car) - (append (tramp-flatten-list car) (tramp-flatten-list cdr)))) - ((null cdr) (list car)) - (t (cons car (tramp-flatten-list cdr)))))) - -(defun tramp-invoke-ange-ftp (operation &rest args) - "Invoke the Ange-FTP handler function and throw." - (or (boundp 'ange-ftp-name-format) - (and (require 'ange-ftp) - (tramp-disable-ange-ftp))) - (let ((ange-ftp-name-format - (list (nth 0 tramp-file-name-structure) - (nth 3 tramp-file-name-structure) - (nth 2 tramp-file-name-structure) - (nth 4 tramp-file-name-structure)))) - (throw 'tramp-forward-to-ange-ftp - (tramp-run-real-handler 'ange-ftp-hook-function - (cons operation args))))) - -(defun tramp-ange-ftp-file-name-p (multi-method method user host) - "Check if it's a filename that should be forwarded to Ange-FTP." - (and tramp-unified-filenames - (null multi-method) - (string= (tramp-find-method multi-method method user host) tramp-ftp-method))) - ;;; Interactions with other packages: @@ -3523,8 +3442,6 @@ (defun tramp-handle-expand-many-files (name) "Like `PC-expand-many-files' for tramp files." (with-parsed-tramp-file-name name nil - (when (tramp-ange-ftp-file-name-p multi-method method user host) - (tramp-invoke-ange-ftp 'expand-many-files name)) (save-match-data (if (or (string-match "\\*" name) (string-match "\\?" name) @@ -3604,8 +3521,7 @@ (concat tramp-prefix-regexp "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp "$") file) - (member (match-string 1 file) - (cons tramp-ftp-method (mapcar 'car tramp-methods)))) + (member (match-string 1 file) (mapcar 'car tramp-methods))) ((or (equal last-input-event 'tab) (and (not (event-modifiers last-input-event)) (integerp last-input-event) @@ -3672,17 +3588,17 @@ ;; Method dependent user / host combinations (progn (mapcar - '(lambda (x) - (setq all-user-hosts - (append all-user-hosts - (funcall (nth 0 x) (nth 1 x))))) + (lambda (x) + (setq all-user-hosts + (append all-user-hosts + (funcall (nth 0 x) (nth 1 x))))) (tramp-get-completion-function m)) (setq result (append result (mapcar - '(lambda (x) - (tramp-get-completion-user-host - method user host (nth 0 x) (nth 1 x))) + (lambda (x) + (tramp-get-completion-user-host + method user host (nth 0 x) (nth 1 x))) (delq nil all-user-hosts))))) ;; Possible methods @@ -3734,47 +3650,46 @@ They are collected by `tramp-completion-dissect-file-name1'." (let* ((result) - (x-nil "\\|\\(\\)")) - - ;; "/method" "/[method" - (defconst tramp-completion-file-name-structure1 - (list (concat tramp-prefix-regexp "\\(" tramp-method-regexp x-nil "\\)$") - 1 9 9 9)) - ;; "/user" "/[user" - (defconst tramp-completion-file-name-structure2 - (list (concat tramp-prefix-regexp "\\(" tramp-user-regexp x-nil "\\)$") - 9 1 9 9)) - ;; "/host" "/[host" - (defconst tramp-completion-file-name-structure3 - (list (concat tramp-prefix-regexp "\\(" tramp-host-regexp x-nil "\\)$") - 9 9 1 9)) - ;; "/user@host" "/[user@host" - (defconst tramp-completion-file-name-structure4 - (list (concat tramp-prefix-regexp - "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp - "\\(" tramp-host-regexp x-nil "\\)$") - 9 1 2 9)) - ;; "/method:user" "/[method/user" - (defconst tramp-completion-file-name-structure5 - (list (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp - "\\(" tramp-user-regexp x-nil "\\)$") - 1 2 9 9)) - ;; "/method:host" "/[method/host" - (defconst tramp-completion-file-name-structure6 - (list (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp - "\\(" tramp-host-regexp x-nil "\\)$") - 1 9 2 9)) - ;; "/method:user@host" "/[method/user@host" - (defconst tramp-completion-file-name-structure7 - (list (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp - "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp - "\\(" tramp-host-regexp x-nil "\\)$") - 1 2 3 9)) - - (mapcar '(lambda (regexp) + (x-nil "\\|\\(\\)") + ;; "/method" "/[method" + (tramp-completion-file-name-structure1 + (list (concat tramp-prefix-regexp "\\(" tramp-method-regexp x-nil "\\)$") + 1 nil nil nil)) + ;; "/user" "/[user" + (tramp-completion-file-name-structure2 + (list (concat tramp-prefix-regexp "\\(" tramp-user-regexp x-nil "\\)$") + nil 1 nil nil)) + ;; "/host" "/[host" + (tramp-completion-file-name-structure3 + (list (concat tramp-prefix-regexp "\\(" tramp-host-regexp x-nil "\\)$") + nil nil 1 nil)) + ;; "/user@host" "/[user@host" + (tramp-completion-file-name-structure4 + (list (concat tramp-prefix-regexp + "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp + "\\(" tramp-host-regexp x-nil "\\)$") + nil 1 2 nil)) + ;; "/method:user" "/[method/user" + (tramp-completion-file-name-structure5 + (list (concat tramp-prefix-regexp + "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp + "\\(" tramp-user-regexp x-nil "\\)$") + 1 2 nil nil)) + ;; "/method:host" "/[method/host" + (tramp-completion-file-name-structure6 + (list (concat tramp-prefix-regexp + "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp + "\\(" tramp-host-regexp x-nil "\\)$") + 1 nil 2 nil)) + ;; "/method:user@host" "/[method/user@host" + (tramp-completion-file-name-structure7 + (list (concat tramp-prefix-regexp + "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp + "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp + "\\(" tramp-host-regexp x-nil "\\)$") + 1 2 3 nil))) + + (mapcar (lambda (regexp) (add-to-list 'result (tramp-completion-dissect-file-name1 regexp name))) (list @@ -3797,7 +3712,8 @@ (let (method) (save-match-data (when (string-match (nth 0 structure) name) - (setq method (match-string (nth 1 structure) name)) + (setq method (and (nth 1 structure) + (match-string (nth 1 structure) name))) (if (and method (member method tramp-multi-methods)) ;; Not handled (yet). (make-tramp-file-name @@ -3806,9 +3722,12 @@ :user nil :host nil :path nil) - (let ((user (match-string (nth 2 structure) name)) - (host (match-string (nth 3 structure) name)) - (path (match-string (nth 4 structure) name))) + (let ((user (and (nth 2 structure) + (match-string (nth 2 structure) name))) + (host (and (nth 3 structure) + (match-string (nth 3 structure) name))) + (path (and (nth 4 structure) + (match-string (nth 4 structure) name)))) (make-tramp-file-name :multi-method nil :method method @@ -3818,21 +3737,15 @@ ;; This function returns all possible method completions, adding the ;; trailing method delimeter. -;; In case of Emacs, `tramp-ftp-method' is handled as well because it doesn't -;; belong to `tramp-methods'. (defun tramp-get-completion-methods (partial-method) "Returns all method completions for PARTIAL-METHOD." - (let ((all-methods (delete "multi" (mapcar 'car tramp-methods)))) - - (mapcar - '(lambda (method) - (and method - (string-match (concat "^" (regexp-quote partial-method)) method) - ;; we must remove leading "/". - (substring (tramp-make-tramp-file-name nil method nil nil nil) 1))) - - (add-to-list 'all-methods - (when tramp-unified-filenames tramp-ftp-method))))) + (mapcar + (lambda (method) + (and method + (string-match (concat "^" (regexp-quote partial-method)) method) + ;; we must remove leading "/". + (substring (tramp-make-tramp-file-name nil method nil nil nil) 1))) + (delete "multi" (mapcar 'car tramp-methods)))) ;; Compares partial user and host names with possible completions. (defun tramp-get-completion-user-host (method partial-user partial-host user host) @@ -4401,8 +4314,8 @@ (pop-to-buffer (tramp-get-buffer multi-method method user host)) (unless (y-or-n-p (match-string 0)) (kill-process p) - (erase-buffer) (throw 'tramp-action 'permission-denied)) + (erase-buffer) (process-send-string p (concat "y" tramp-rsh-end-of-line)))) (defun tramp-action-terminal (p multi-method method user host) @@ -4692,8 +4605,8 @@ (tramp-find-method multi-method method user host) user host) (mapcar - '(lambda (x) - (format-spec x `((?u . ,(or user "root"))))) + (lambda (x) + (format-spec x `((?u . ,(or user "root"))))) (tramp-get-su-args multi-method (tramp-find-method multi-method method user host) @@ -5808,16 +5721,22 @@ "Return an `tramp-file-name' structure. The structure consists of remote method, remote user, remote host and remote path name." - (let (method) - (save-match-data - (unless (string-match (nth 0 tramp-file-name-structure) name) - (error "Not a tramp file name: %s" name)) - (setq method (match-string (nth 1 tramp-file-name-structure) name)) + (save-match-data + (let* ((match (string-match (nth 0 tramp-file-name-structure) name)) + (method + ; single-hop + (if match (match-string (nth 1 tramp-file-name-structure) name) + ; maybe multi-hop + (string-match + (format (nth 0 tramp-multi-file-name-structure) + (nth 0 tramp-multi-file-name-hop-structure)) name) + (match-string (nth 1 tramp-multi-file-name-structure) name)))) (if (and method (member method tramp-multi-methods)) ;; If it's a multi method, the file name structure contains ;; arrays of method, user and host. (tramp-dissect-multi-file-name name) ;; Normal method. First, find out default method. + (unless match (error "Not a tramp file name: %s" name)) (let ((user (match-string (nth 2 tramp-file-name-structure) name)) (host (match-string (nth 3 tramp-file-name-structure) name)) (path (match-string (nth 4 tramp-file-name-structure) name))) @@ -5923,12 +5842,6 @@ (format "%s@%s:%s" user host path) (format "%s:%s" host path))) -(defun tramp-make-ange-ftp-file-name (user host path) - "Given user, host, and path, return an Ange-FTP filename." - (if user - (format "/%s@%s:%s" user host path) - (format "/%s:%s" host path))) - (defun tramp-method-out-of-band-p (multi-method method user host) "Return t if this is an out-of-band method, nil otherwise. It is important to check for this condition, since it is not possible @@ -6412,6 +6325,14 @@ ;;; TODO: +;; * Allow putting passwords in the filename. +;; This should be implemented via a general mechanism to add +;; parameters in filenames. There is currently a kludge for +;; putting the port number into the filename for ssh and ftp +;; files. This could be subsumed by the new mechanism as well. +;; Another approach is to read a netrc file like ~/.authinfo +;; from Gnus. +;; * Handle nonlocal exits such as C-g. ;; * Autodetect if remote `ls' groks the "--dired" switch. ;; * Add fallback for inline encodings. This should be used ;; if the remote end doesn't support mimencode or a similar program. @@ -6517,10 +6438,6 @@ ;; connect to host "blabla" already if that host is unique. No idea ;; how to suppress. Maybe not an essential problem. ;; ** Try to avoid usage of `last-input-event' in `tramp-completion-mode'. -;; ** Handle quoted file names, starting with "/:". Problem is that -;; `file-name-non-special' calls later on `file-name-all-completions' -;; without ":". Hmm. Worth a bug report? -;; ** Acknowledge port numbers. ;; ** Extend `tramp-get-completion-su' for NIS and shadow passwords. ;; ** Unify `tramp-parse-{rhosts,shosts,hosts,passwd,netrc}'. ;; Code is nearly identical.