Mercurial > emacs
comparison lisp/net/tramp-smb.el @ 81758:f03856eb136b
* files.el (file-remote-p): Introduce optional parameter CONNECTED.
* net/tramp.el:
* net/tramp-ftp.el:
* net/tramp-smb.el:
* net/tramp-uu.el:
* net/trampver.el: Migrate to Tramp 2.1.
* net/tramp-cache.el:
* net/tramp-fish.el:
* net/tramp-gw.el: New Tramp packages.
* net/tramp-util.el:
* net/tramp-vc.el: Removed.
* net/ange-ftp.el: Add ange-ftp property to 'start-file-process
(ange-ftp-file-remote-p): Handle optional parameter CONNECTED.
* net/rcompile.el (remote-compile): Handle Tramp 2.1 arguments.
* progmodes/compile.el (compilation-start): Redefine
`start-process' temporarily when `default-directory' is remote.
Remove case of synchronous compilation, this won't happen ever.
(compilation-setup): Make local variable `comint-file-name-prefix'
for remote compilation.
author | Michael Albinus <michael.albinus@gmx.de> |
---|---|
date | Sun, 08 Jul 2007 18:03:20 +0000 |
parents | 0cc9b64806d2 |
children | 26330ef1aa46 a66921565bcb |
comparison
equal
deleted
inserted
replaced
81757:d4e68ecdb000 | 81758:f03856eb136b |
---|---|
1 ;;; tramp-smb.el --- Tramp access functions for SMB servers -*- coding: iso-8859-1; -*- | 1 ;;; tramp-smb.el --- Tramp access functions for SMB servers -*- coding: iso-8859-1; -*- |
2 | 2 |
3 ;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. | 3 ;; Copyright (C) 2002, 2003, 2004, 2005, 2006, |
4 ;; 2007 Free Software Foundation, Inc. | |
4 | 5 |
5 ;; Author: Michael Albinus <michael.albinus@gmx.de> | 6 ;; Author: Michael Albinus <michael.albinus@gmx.de> |
6 ;; Keywords: comm, processes | 7 ;; Keywords: comm, processes |
7 | 8 |
8 ;; This file is part of GNU Emacs. | 9 ;; This file is part of GNU Emacs. |
9 | 10 |
10 ;; GNU Emacs is free software; you can redistribute it and/or modify | 11 ;; GNU Emacs is free software; you can redistribute it and/or modify |
11 ;; it under the terms of the GNU General Public License as published by | 12 ;; it under the terms of the GNU General Public License as published by |
12 ;; the Free Software Foundation; either version 2, or (at your option) | 13 ;; the Free Software Foundation; either version 3 of the License, or |
13 ;; any later version. | 14 ;; (at your option) any later version. |
14 | 15 |
15 ;; GNU Emacs is distributed in the hope that it will be useful, | 16 ;; GNU Emacs is distributed in the hope that it will be useful, |
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 ;; GNU General Public License for more details. | 19 ;; GNU General Public License for more details. |
19 | 20 |
20 ;; You should have received a copy of the GNU General Public License | 21 ;; You should have received a copy of the GNU General Public License |
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the | 22 ;; along with GNU Emacs; see the file COPYING. If not, see |
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 23 ;; <http://www.gnu.org/licenses/>. |
23 ;; Boston, MA 02110-1301, USA. | |
24 | 24 |
25 ;;; Commentary: | 25 ;;; Commentary: |
26 | 26 |
27 ;; Access functions for SMB servers like SAMBA or M$ Windows from Tramp. | 27 ;; Access functions for SMB servers like SAMBA or M$ Windows from Tramp. |
28 | 28 |
29 ;;; Code: | 29 ;;; Code: |
30 | 30 |
31 (require 'tramp) | 31 (require 'tramp) |
32 (require 'tramp-cache) | |
32 | 33 |
33 ;; Pacify byte-compiler | 34 ;; Pacify byte-compiler |
34 (eval-when-compile (require 'custom)) | 35 (eval-when-compile (require 'custom)) |
35 | 36 |
36 ;; Avoid byte-compiler warnings if the byte-compiler supports this. | 37 ;; Avoid byte-compiler warnings if the byte-compiler supports this. |
37 ;; Currently, XEmacs supports this. | 38 ;; Currently, XEmacs supports this. |
38 (eval-when-compile | 39 (eval-when-compile |
39 (when (fboundp 'byte-compiler-options) | 40 (when (featurep 'xemacs) |
40 (let (unused-vars) ; Pacify Emacs byte-compiler | 41 (byte-compiler-options (warnings (- unused-vars))))) |
41 (defalias 'warnings 'identity) ; Pacify Emacs byte-compiler | |
42 (byte-compiler-options (warnings (- unused-vars)))))) | |
43 | 42 |
44 ;; Define SMB method ... | 43 ;; Define SMB method ... |
45 (defcustom tramp-smb-method "smb" | 44 (defcustom tramp-smb-method "smb" |
46 "*Method to connect SAMBA and M$ SMB servers." | 45 "*Method to connect SAMBA and M$ SMB servers." |
47 :group 'tramp | 46 :group 'tramp |
51 (add-to-list 'tramp-methods (cons tramp-smb-method nil)) | 50 (add-to-list 'tramp-methods (cons tramp-smb-method nil)) |
52 | 51 |
53 ;; Add a default for `tramp-default-method-alist'. Rule: If there is | 52 ;; Add a default for `tramp-default-method-alist'. Rule: If there is |
54 ;; a domain in USER, it must be the SMB method. | 53 ;; a domain in USER, it must be the SMB method. |
55 (add-to-list 'tramp-default-method-alist | 54 (add-to-list 'tramp-default-method-alist |
56 (list "" "%" tramp-smb-method)) | 55 `(nil "%" ,tramp-smb-method)) |
56 | |
57 ;; Add a default for `tramp-default-user-alist'. Rule: For the SMB method, | |
58 ;; the anonymous user is chosen. | |
59 (add-to-list 'tramp-default-user-alist | |
60 `(,tramp-smb-method nil "")) | |
57 | 61 |
58 ;; Add completion function for SMB method. | 62 ;; Add completion function for SMB method. |
59 (tramp-set-completion-function | 63 (tramp-set-completion-function |
60 tramp-smb-method | 64 tramp-smb-method |
61 '((tramp-parse-netrc "~/.netrc"))) | 65 '((tramp-parse-netrc "~/.netrc"))) |
67 | 71 |
68 (defconst tramp-smb-prompt "^smb: .+> \\|^\\s-+Server\\s-+Comment$" | 72 (defconst tramp-smb-prompt "^smb: .+> \\|^\\s-+Server\\s-+Comment$" |
69 "Regexp used as prompt in smbclient.") | 73 "Regexp used as prompt in smbclient.") |
70 | 74 |
71 (defconst tramp-smb-errors | 75 (defconst tramp-smb-errors |
76 ;; `regexp-opt' not possible because of first string. | |
72 (mapconcat | 77 (mapconcat |
73 'identity | 78 'identity |
74 '(; Connection error | 79 '(;; Connection error / timeout |
75 "Connection to \\S-+ failed" | 80 "Connection to \\S-+ failed" |
76 ; Samba | 81 "Read from server failed, maybe it closed the connection" |
82 ;; Samba | |
77 "ERRDOS" | 83 "ERRDOS" |
78 "ERRSRV" | 84 "ERRSRV" |
79 "ERRbadfile" | 85 "ERRbadfile" |
80 "ERRbadpw" | 86 "ERRbadpw" |
81 "ERRfilexists" | 87 "ERRfilexists" |
82 "ERRnoaccess" | 88 "ERRnoaccess" |
83 "ERRnomem" | 89 "ERRnomem" |
84 "ERRnosuchshare" | 90 "ERRnosuchshare" |
85 ; Windows NT 4.0, Windows 5.0 (Windows 2000), Windows 5.1 (Windows XP) | 91 ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000), |
92 ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003) | |
86 "NT_STATUS_ACCESS_DENIED" | 93 "NT_STATUS_ACCESS_DENIED" |
87 "NT_STATUS_ACCOUNT_LOCKED_OUT" | 94 "NT_STATUS_ACCOUNT_LOCKED_OUT" |
88 "NT_STATUS_BAD_NETWORK_NAME" | 95 "NT_STATUS_BAD_NETWORK_NAME" |
89 "NT_STATUS_CANNOT_DELETE" | 96 "NT_STATUS_CANNOT_DELETE" |
97 "NT_STATUS_DIRECTORY_NOT_EMPTY" | |
98 "NT_STATUS_DUPLICATE_NAME" | |
99 "NT_STATUS_FILE_IS_A_DIRECTORY" | |
90 "NT_STATUS_LOGON_FAILURE" | 100 "NT_STATUS_LOGON_FAILURE" |
91 "NT_STATUS_NETWORK_ACCESS_DENIED" | 101 "NT_STATUS_NETWORK_ACCESS_DENIED" |
92 "NT_STATUS_NO_SUCH_FILE" | 102 "NT_STATUS_NO_SUCH_FILE" |
103 "NT_STATUS_OBJECT_NAME_COLLISION" | |
93 "NT_STATUS_OBJECT_NAME_INVALID" | 104 "NT_STATUS_OBJECT_NAME_INVALID" |
94 "NT_STATUS_OBJECT_NAME_NOT_FOUND" | 105 "NT_STATUS_OBJECT_NAME_NOT_FOUND" |
95 "NT_STATUS_SHARING_VIOLATION" | 106 "NT_STATUS_SHARING_VIOLATION" |
107 "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE" | |
96 "NT_STATUS_WRONG_PASSWORD") | 108 "NT_STATUS_WRONG_PASSWORD") |
97 "\\|") | 109 "\\|") |
98 "Regexp for possible error strings of SMB servers. | 110 "Regexp for possible error strings of SMB servers. |
99 Used instead of analyzing error codes of commands.") | 111 Used instead of analyzing error codes of commands.") |
100 | 112 |
101 (defvar tramp-smb-share nil | 113 (defconst tramp-smb-actions-with-share |
102 "Holds the share name for the current buffer. | 114 '((tramp-smb-prompt tramp-action-succeed) |
103 This variable is local to each buffer.") | 115 (tramp-password-prompt-regexp tramp-action-password) |
104 (make-variable-buffer-local 'tramp-smb-share) | 116 (tramp-wrong-passwd-regexp tramp-action-permission-denied) |
105 | 117 (tramp-smb-errors tramp-action-permission-denied) |
106 (defvar tramp-smb-share-cache nil | 118 (tramp-process-alive-regexp tramp-action-process-alive)) |
107 "Caches the share names accessible to host related to the current buffer. | 119 "List of pattern/action pairs. |
108 This variable is local to each buffer.") | 120 This list is used for login to SMB servers. |
109 (make-variable-buffer-local 'tramp-smb-share-cache) | 121 |
110 | 122 See `tramp-actions-before-shell' for more info.") |
111 (defvar tramp-smb-inodes nil | 123 |
112 "Keeps virtual inodes numbers for SMB files.") | 124 (defconst tramp-smb-actions-without-share |
125 '((tramp-password-prompt-regexp tramp-action-password) | |
126 (tramp-wrong-passwd-regexp tramp-action-permission-denied) | |
127 (tramp-smb-errors tramp-action-permission-denied) | |
128 (tramp-process-alive-regexp tramp-action-out-of-band)) | |
129 "List of pattern/action pairs. | |
130 This list is used for login to SMB servers. | |
131 | |
132 See `tramp-actions-before-shell' for more info.") | |
113 | 133 |
114 ;; New handlers should be added here. | 134 ;; New handlers should be added here. |
115 (defconst tramp-smb-file-name-handler-alist | 135 (defconst tramp-smb-file-name-handler-alist |
116 '( | 136 '( |
117 ;; `access-file' performed by default handler | 137 ;; `access-file' performed by default handler |
122 (delete-file . tramp-smb-handle-delete-file) | 142 (delete-file . tramp-smb-handle-delete-file) |
123 ;; `diff-latest-backup-file' performed by default handler | 143 ;; `diff-latest-backup-file' performed by default handler |
124 (directory-file-name . tramp-handle-directory-file-name) | 144 (directory-file-name . tramp-handle-directory-file-name) |
125 (directory-files . tramp-smb-handle-directory-files) | 145 (directory-files . tramp-smb-handle-directory-files) |
126 (directory-files-and-attributes . tramp-smb-handle-directory-files-and-attributes) | 146 (directory-files-and-attributes . tramp-smb-handle-directory-files-and-attributes) |
127 (dired-call-process . tramp-smb-not-handled) | 147 (dired-call-process . ignore) |
128 (dired-compress-file . tramp-smb-not-handled) | 148 (dired-compress-file . ignore) |
129 ;; `dired-uncache' performed by default handler | 149 ;; `dired-uncache' performed by default handler |
130 ;; `expand-file-name' not necessary because we cannot expand "~/" | 150 ;; `expand-file-name' not necessary because we cannot expand "~/" |
131 (file-accessible-directory-p . tramp-smb-handle-file-directory-p) | 151 (file-accessible-directory-p . tramp-smb-handle-file-directory-p) |
132 (file-attributes . tramp-smb-handle-file-attributes) | 152 (file-attributes . tramp-smb-handle-file-attributes) |
133 (file-directory-p . tramp-smb-handle-file-directory-p) | 153 (file-directory-p . tramp-smb-handle-file-directory-p) |
141 (file-name-completion . tramp-handle-file-name-completion) | 161 (file-name-completion . tramp-handle-file-name-completion) |
142 (file-name-directory . tramp-handle-file-name-directory) | 162 (file-name-directory . tramp-handle-file-name-directory) |
143 (file-name-nondirectory . tramp-handle-file-name-nondirectory) | 163 (file-name-nondirectory . tramp-handle-file-name-nondirectory) |
144 ;; `file-name-sans-versions' performed by default handler | 164 ;; `file-name-sans-versions' performed by default handler |
145 (file-newer-than-file-p . tramp-smb-handle-file-newer-than-file-p) | 165 (file-newer-than-file-p . tramp-smb-handle-file-newer-than-file-p) |
146 (file-ownership-preserved-p . tramp-smb-not-handled) | 166 (file-ownership-preserved-p . ignore) |
147 (file-readable-p . tramp-smb-handle-file-exists-p) | 167 (file-readable-p . tramp-smb-handle-file-exists-p) |
148 (file-regular-p . tramp-handle-file-regular-p) | 168 (file-regular-p . tramp-handle-file-regular-p) |
149 (file-symlink-p . tramp-smb-not-handled) | 169 (file-symlink-p . tramp-handle-file-symlink-p) |
150 ;; `file-truename' performed by default handler | 170 ;; `file-truename' performed by default handler |
151 (file-writable-p . tramp-smb-handle-file-writable-p) | 171 (file-writable-p . tramp-smb-handle-file-writable-p) |
152 (find-backup-file-name . tramp-handle-find-backup-file-name) | 172 (find-backup-file-name . tramp-handle-find-backup-file-name) |
153 ;; `find-file-noselect' performed by default handler | 173 ;; `find-file-noselect' performed by default handler |
154 ;; `get-file-buffer' performed by default handler | 174 ;; `get-file-buffer' performed by default handler |
155 (insert-directory . tramp-smb-handle-insert-directory) | 175 (insert-directory . tramp-smb-handle-insert-directory) |
156 (insert-file-contents . tramp-handle-insert-file-contents) | 176 (insert-file-contents . tramp-handle-insert-file-contents) |
157 (load . tramp-handle-load) | 177 (load . tramp-handle-load) |
158 (make-directory . tramp-smb-handle-make-directory) | 178 (make-directory . tramp-smb-handle-make-directory) |
159 (make-directory-internal . tramp-smb-handle-make-directory-internal) | 179 (make-directory-internal . tramp-smb-handle-make-directory-internal) |
160 (make-symbolic-link . tramp-smb-not-handled) | 180 (make-symbolic-link . ignore) |
161 (rename-file . tramp-smb-handle-rename-file) | 181 (rename-file . tramp-smb-handle-rename-file) |
162 (set-file-modes . tramp-smb-not-handled) | 182 (set-file-modes . ignore) |
163 (set-visited-file-modtime . tramp-smb-not-handled) | 183 (set-visited-file-modtime . ignore) |
164 (shell-command . tramp-smb-not-handled) | 184 (shell-command . ignore) |
165 (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name) | 185 (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name) |
166 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) | 186 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) |
167 (vc-registered . tramp-smb-not-handled) | 187 (vc-registered . ignore) |
168 (verify-visited-file-modtime . tramp-smb-not-handled) | 188 (verify-visited-file-modtime . ignore) |
169 (write-region . tramp-smb-handle-write-region) | 189 (write-region . tramp-smb-handle-write-region) |
170 ) | 190 ) |
171 "Alist of handler functions for Tramp SMB method. | 191 "Alist of handler functions for Tramp SMB method. |
172 Operations not mentioned here will be handled by the default Emacs primitives.") | 192 Operations not mentioned here will be handled by the default Emacs primitives.") |
173 | 193 |
174 (defun tramp-smb-file-name-p (filename) | 194 (defun tramp-smb-file-name-p (filename) |
175 "Check if it's a filename for SMB servers." | 195 "Check if it's a filename for SMB servers." |
176 (let ((v (tramp-dissect-file-name filename))) | 196 (let ((v (tramp-dissect-file-name filename))) |
177 (string= | 197 (string= (tramp-file-name-method v) tramp-smb-method))) |
178 (tramp-find-method | |
179 (tramp-file-name-multi-method v) | |
180 (tramp-file-name-method v) | |
181 (tramp-file-name-user v) | |
182 (tramp-file-name-host v)) | |
183 tramp-smb-method))) | |
184 | 198 |
185 (defun tramp-smb-file-name-handler (operation &rest args) | 199 (defun tramp-smb-file-name-handler (operation &rest args) |
186 "Invoke the SMB related OPERATION. | 200 "Invoke the SMB related OPERATION. |
187 First arg specifies the OPERATION, second arg is a list of arguments to | 201 First arg specifies the OPERATION, second arg is a list of arguments to |
188 pass to the OPERATION." | 202 pass to the OPERATION." |
189 (let ((fn (assoc operation tramp-smb-file-name-handler-alist))) | 203 (let ((fn (assoc operation tramp-smb-file-name-handler-alist))) |
190 (if fn | 204 (if fn |
191 (if (eq (cdr fn) 'tramp-smb-not-handled) | 205 (save-match-data (apply (cdr fn) args)) |
192 (apply (cdr fn) operation args) | |
193 (save-match-data (apply (cdr fn) args))) | |
194 (tramp-run-real-handler operation args)))) | 206 (tramp-run-real-handler operation args)))) |
195 | 207 |
196 (add-to-list 'tramp-foreign-file-name-handler-alist | 208 (add-to-list 'tramp-foreign-file-name-handler-alist |
197 (cons 'tramp-smb-file-name-p 'tramp-smb-file-name-handler)) | 209 (cons 'tramp-smb-file-name-p 'tramp-smb-file-name-handler)) |
198 | 210 |
199 | 211 |
200 ;; File name primitives | 212 ;; File name primitives |
201 | 213 |
202 (defun tramp-smb-not-handled (operation &rest args) | |
203 "Default handler for all functions which are disrecarded." | |
204 (tramp-message 10 "Won't be handled: %s %s" operation args) | |
205 nil) | |
206 | |
207 (defun tramp-smb-handle-copy-file | 214 (defun tramp-smb-handle-copy-file |
208 (filename newname &optional ok-if-already-exists keep-date) | 215 (filename newname &optional ok-if-already-exists keep-date) |
209 "Like `copy-file' for tramp files. | 216 "Like `copy-file' for Tramp files. |
210 KEEP-DATE is not handled in case NEWNAME resides on an SMB server." | 217 KEEP-DATE is not handled in case NEWNAME resides on an SMB server." |
211 (setq filename (expand-file-name filename) | 218 (setq filename (expand-file-name filename) |
212 newname (expand-file-name newname)) | 219 newname (expand-file-name newname)) |
213 | 220 |
214 (let ((tmpfile (file-local-copy filename))) | 221 (let ((tmpfile (file-local-copy filename))) |
215 | 222 |
216 (if tmpfile | 223 (if tmpfile |
217 ;; remote filename | 224 ;; Remote filename. |
218 (rename-file tmpfile newname ok-if-already-exists) | 225 (rename-file tmpfile newname ok-if-already-exists) |
219 | 226 |
220 ;; remote newname | 227 ;; Remote newname. |
221 (when (file-directory-p newname) | 228 (when (file-directory-p newname) |
222 (setq newname (expand-file-name | 229 (setq newname (expand-file-name |
223 (file-name-nondirectory filename) newname))) | 230 (file-name-nondirectory filename) newname))) |
224 (when (and (not ok-if-already-exists) | |
225 (file-exists-p newname)) | |
226 (error "copy-file: file %s already exists" newname)) | |
227 | 231 |
228 (with-parsed-tramp-file-name newname nil | 232 (with-parsed-tramp-file-name newname nil |
229 (save-excursion | 233 (when (and (not ok-if-already-exists) |
230 (let ((share (tramp-smb-get-share localname)) | 234 (file-exists-p newname)) |
231 (file (tramp-smb-get-localname localname t))) | 235 (tramp-error v 'file-already-exists newname)) |
232 (unless share | 236 |
233 (error "Target `%s' must contain a share name" filename)) | 237 ;; We must also flush the cache of the directory, because |
234 (tramp-smb-maybe-open-connection user host share) | 238 ;; file-attributes reads the values from there. |
235 (tramp-message-for-buffer | 239 (tramp-flush-file-property v (file-name-directory localname)) |
236 nil tramp-smb-method user host | 240 (tramp-flush-file-property v localname) |
237 5 "Copying file %s to file %s..." filename newname) | 241 (let ((share (tramp-smb-get-share localname)) |
238 (if (tramp-smb-send-command | 242 (file (tramp-smb-get-localname localname t))) |
239 user host (format "put %s \"%s\"" filename file)) | 243 (unless share |
240 (tramp-message-for-buffer | 244 (tramp-error |
241 nil tramp-smb-method user host | 245 v 'file-error "Target `%s' must contain a share name" newname)) |
242 5 "Copying file %s to file %s...done" filename newname) | 246 (tramp-message v 0 "Copying file %s to file %s..." filename newname) |
243 (error "Cannot copy `%s'" filename)))))))) | 247 (if (tramp-smb-send-command |
248 v (format "put %s \"%s\"" filename file)) | |
249 (tramp-message | |
250 v 0 "Copying file %s to file %s...done" filename newname) | |
251 (tramp-error v 'file-error "Cannot copy `%s'" filename))))))) | |
244 | 252 |
245 (defun tramp-smb-handle-delete-directory (directory) | 253 (defun tramp-smb-handle-delete-directory (directory) |
246 "Like `delete-directory' for tramp files." | 254 "Like `delete-directory' for Tramp files." |
247 (setq directory (directory-file-name (expand-file-name directory))) | 255 (setq directory (directory-file-name (expand-file-name directory))) |
248 (when (file-exists-p directory) | 256 (when (file-exists-p directory) |
249 (with-parsed-tramp-file-name directory nil | 257 (with-parsed-tramp-file-name directory nil |
250 (save-excursion | 258 ;; We must also flush the cache of the directory, because |
251 (let ((share (tramp-smb-get-share localname)) | 259 ;; file-attributes reads the values from there. |
252 (dir (tramp-smb-get-localname (file-name-directory localname) t)) | 260 (tramp-flush-file-property v (file-name-directory localname)) |
253 (file (file-name-nondirectory localname))) | 261 (tramp-flush-directory-property v localname) |
254 (tramp-smb-maybe-open-connection user host share) | 262 (let ((dir (tramp-smb-get-localname (file-name-directory localname) t)) |
255 (if (and | 263 (file (file-name-nondirectory localname))) |
256 (tramp-smb-send-command user host (format "cd \"%s\"" dir)) | 264 (unwind-protect |
257 (tramp-smb-send-command user host (format "rmdir \"%s\"" file))) | 265 (unless (and |
258 ;; Go Home | 266 (tramp-smb-send-command v (format "cd \"%s\"" dir)) |
259 (tramp-smb-send-command user host (format "cd \\")) | 267 (tramp-smb-send-command v (format "rmdir \"%s\"" file))) |
260 ;; Error | 268 ;; Error |
261 (tramp-smb-send-command user host (format "cd \\")) | 269 (with-current-buffer (tramp-get-connection-buffer v) |
262 (error "Cannot delete directory `%s'" directory))))))) | 270 (goto-char (point-min)) |
271 (search-forward-regexp tramp-smb-errors nil t) | |
272 (tramp-error | |
273 v 'file-error "%s `%s'" (match-string 0) directory))) | |
274 ;; Always go home | |
275 (tramp-smb-send-command v (format "cd \\"))))))) | |
263 | 276 |
264 (defun tramp-smb-handle-delete-file (filename) | 277 (defun tramp-smb-handle-delete-file (filename) |
265 "Like `delete-file' for tramp files." | 278 "Like `delete-file' for Tramp files." |
266 (setq filename (expand-file-name filename)) | 279 (setq filename (expand-file-name filename)) |
267 (when (file-exists-p filename) | 280 (when (file-exists-p filename) |
268 (with-parsed-tramp-file-name filename nil | 281 (with-parsed-tramp-file-name filename nil |
269 (save-excursion | 282 ;; We must also flush the cache of the directory, because |
270 (let ((share (tramp-smb-get-share localname)) | 283 ;; file-attributes reads the values from there. |
271 (dir (tramp-smb-get-localname (file-name-directory localname) t)) | 284 (tramp-flush-file-property v (file-name-directory localname)) |
272 (file (file-name-nondirectory localname))) | 285 (tramp-flush-file-property v localname) |
273 (tramp-smb-maybe-open-connection user host share) | 286 (let ((dir (tramp-smb-get-localname (file-name-directory localname) t)) |
274 (if (and | 287 (file (file-name-nondirectory localname))) |
275 (tramp-smb-send-command user host (format "cd \"%s\"" dir)) | 288 (unwind-protect |
276 (tramp-smb-send-command user host (format "rm \"%s\"" file))) | 289 (unless (and |
277 ;; Go Home | 290 (tramp-smb-send-command v (format "cd \"%s\"" dir)) |
278 (tramp-smb-send-command user host (format "cd \\")) | 291 (tramp-smb-send-command v (format "rm \"%s\"" file))) |
279 ;; Error | 292 ;; Error |
280 (tramp-smb-send-command user host (format "cd \\")) | 293 (with-current-buffer (tramp-get-connection-buffer v) |
281 (error "Cannot delete file `%s'" filename))))))) | 294 (goto-char (point-min)) |
295 (search-forward-regexp tramp-smb-errors nil t) | |
296 (tramp-error | |
297 v 'file-error "%s `%s'" (match-string 0) filename))) | |
298 ;; Always go home | |
299 (tramp-smb-send-command v (format "cd \\"))))))) | |
282 | 300 |
283 (defun tramp-smb-handle-directory-files | 301 (defun tramp-smb-handle-directory-files |
284 (directory &optional full match nosort) | 302 (directory &optional full match nosort) |
285 "Like `directory-files' for tramp files." | 303 "Like `directory-files' for Tramp files." |
286 (setq directory (directory-file-name (expand-file-name directory))) | 304 (let ((result (mapcar 'directory-file-name |
287 (with-parsed-tramp-file-name directory nil | 305 (file-name-all-completions "" directory)))) |
288 (save-excursion | 306 ;; Discriminate with regexp |
289 (let* ((share (tramp-smb-get-share localname)) | 307 (when match |
290 (file (tramp-smb-get-localname localname nil)) | 308 (setq result |
291 (entries (tramp-smb-get-file-entries user host share file))) | 309 (delete nil |
292 ;; Just the file names are needed | 310 (mapcar (lambda (x) (when (string-match match x) x)) |
293 (setq entries (mapcar 'car entries)) | 311 result)))) |
294 ;; Discriminate with regexp | 312 ;; Append directory |
295 (when match | 313 (when full |
296 (setq entries | 314 (setq result |
297 (delete nil | 315 (mapcar |
298 (mapcar (lambda (x) (when (string-match match x) x)) | 316 (lambda (x) (expand-file-name x directory)) |
299 entries)))) | 317 result))) |
300 ;; Make absolute localnames if necessary | 318 ;; Sort them if necessary |
301 (when full | 319 (unless nosort (setq result (sort result 'string-lessp))) |
302 (setq entries | 320 ;; That's it |
303 (mapcar (lambda (x) | 321 result)) |
304 (concat (file-name-as-directory directory) x)) | |
305 entries))) | |
306 ;; Sort them if necessary | |
307 (unless nosort (setq entries (sort entries 'string-lessp))) | |
308 ;; That's it | |
309 entries)))) | |
310 | 322 |
311 (defun tramp-smb-handle-directory-files-and-attributes | 323 (defun tramp-smb-handle-directory-files-and-attributes |
312 (directory &optional full match nosort id-format) | 324 (directory &optional full match nosort id-format) |
313 "Like `directory-files-and-attributes' for tramp files." | 325 "Like `directory-files-and-attributes' for Tramp files." |
314 (mapcar | 326 (mapcar |
315 (lambda (x) | 327 (lambda (x) |
316 ;; We cannot call `file-attributes' for backward compatibility reasons. | 328 ;; We cannot call `file-attributes' for backward compatibility reasons. |
317 ;; Its optional parameter ID-FORMAT is introduced with Emacs 22. | 329 ;; Its optional parameter ID-FORMAT is introduced with Emacs 22. |
318 (cons x (tramp-smb-handle-file-attributes | 330 (cons x (tramp-smb-handle-file-attributes |
319 (if full x (concat (file-name-as-directory directory) x)) id-format))) | 331 (if full x (expand-file-name x directory)) id-format))) |
320 (directory-files directory full match nosort))) | 332 (directory-files directory full match nosort))) |
321 | 333 |
322 (defun tramp-smb-handle-file-attributes (filename &optional id-format) | 334 (defun tramp-smb-handle-file-attributes (filename &optional id-format) |
323 "Like `file-attributes' for tramp files." | 335 "Like `file-attributes' for Tramp files." |
336 ;; Reading just the filename entry via "dir localname" is not | |
337 ;; possible, because when filename is a directory, some smbclient | |
338 ;; versions return the content of the directory, and other versions | |
339 ;; don't. Therefore, the whole content of the upper directory is | |
340 ;; retrieved, and the entry of the filename is extracted from. | |
324 (with-parsed-tramp-file-name filename nil | 341 (with-parsed-tramp-file-name filename nil |
325 (save-excursion | 342 (with-file-property v localname (format "file-attributes-%s" id-format) |
326 (let* ((share (tramp-smb-get-share localname)) | 343 (let* ((entries (tramp-smb-get-file-entries |
327 (file (tramp-smb-get-localname localname nil)) | 344 (file-name-directory filename))) |
328 (entries (tramp-smb-get-file-entries user host share file)) | |
329 (entry (and entries | 345 (entry (and entries |
330 (assoc (file-name-nondirectory file) entries))) | 346 (assoc (file-name-nondirectory filename) entries))) |
331 (uid (if (and id-format (equal id-format 'string)) "nobody" -1)) | 347 (uid (if (and id-format (equal id-format 'string)) "nobody" -1)) |
332 (gid (if (and id-format (equal id-format 'string)) "nogroup" -1)) | 348 (gid (if (and id-format (equal id-format 'string)) "nogroup" -1)) |
333 (inode (tramp-smb-get-inode share file)) | 349 (inode (tramp-get-inode filename)) |
334 (device (tramp-get-device nil tramp-smb-method user host))) | 350 (device (tramp-get-device v))) |
335 | 351 |
336 ; check result | 352 ;; Check result. |
337 (when entry | 353 (when entry |
338 (list (and (string-match "d" (nth 1 entry)) | 354 (list (and (string-match "d" (nth 1 entry)) |
339 t) ;0 file type | 355 t) ;0 file type |
340 -1 ;1 link count | 356 -1 ;1 link count |
341 uid ;2 uid | 357 uid ;2 uid |
342 gid ;3 gid | 358 gid ;3 gid |
343 '(0 0) ;4 atime | 359 '(0 0) ;4 atime |
344 (nth 3 entry) ;5 mtime | 360 (nth 3 entry) ;5 mtime |
345 '(0 0) ;6 ctime | 361 '(0 0) ;6 ctime |
346 (nth 2 entry) ;7 size | 362 (nth 2 entry) ;7 size |
347 (nth 1 entry) ;8 mode | 363 (nth 1 entry) ;8 mode |
348 nil ;9 gid weird | 364 nil ;9 gid weird |
349 inode ;10 inode number | 365 inode ;10 inode number |
350 device)))))) ;11 file system number | 366 device)))))) ;11 file system number |
351 | 367 |
352 (defun tramp-smb-handle-file-directory-p (filename) | 368 (defun tramp-smb-handle-file-directory-p (filename) |
353 "Like `file-directory-p' for tramp files." | 369 "Like `file-directory-p' for Tramp files." |
370 (and (file-exists-p filename) | |
371 (eq ?d (aref (nth 8 (file-attributes filename)) 0)))) | |
372 | |
373 (defun tramp-smb-handle-file-exists-p (filename) | |
374 "Like `file-exists-p' for Tramp files." | |
375 (not (null (file-attributes filename)))) | |
376 | |
377 (defun tramp-smb-handle-file-local-copy (filename) | |
378 "Like `file-local-copy' for Tramp files." | |
354 (with-parsed-tramp-file-name filename nil | 379 (with-parsed-tramp-file-name filename nil |
355 (save-excursion | 380 (let ((file (tramp-smb-get-localname localname t)) |
356 (let* ((share (tramp-smb-get-share localname)) | 381 (tmpfil (tramp-make-temp-file filename))) |
357 (file (tramp-smb-get-localname localname nil)) | 382 (unless (file-exists-p filename) |
358 (entries (tramp-smb-get-file-entries user host share file)) | 383 (tramp-error |
359 (entry (and entries | 384 v 'file-error |
360 (assoc (file-name-nondirectory file) entries)))) | 385 "Cannot make local copy of non-existing file `%s'" filename)) |
361 (and entry | 386 (tramp-message v 4 "Fetching %s to tmp file %s..." filename tmpfil) |
362 (string-match "d" (nth 1 entry)) | 387 (if (tramp-smb-send-command v (format "get \"%s\" %s" file tmpfil)) |
363 t))))) | 388 (tramp-message |
364 | 389 v 4 "Fetching %s to tmp file %s...done" filename tmpfil) |
365 (defun tramp-smb-handle-file-exists-p (filename) | 390 (tramp-error |
366 "Like `file-exists-p' for tramp files." | 391 v 'file-error |
367 (with-parsed-tramp-file-name filename nil | 392 "Cannot make local copy of file `%s'" filename)) |
368 (save-excursion | 393 tmpfil))) |
369 (let* ((share (tramp-smb-get-share localname)) | |
370 (file (tramp-smb-get-localname localname nil)) | |
371 (entries (tramp-smb-get-file-entries user host share file))) | |
372 (and entries | |
373 (member (file-name-nondirectory file) (mapcar 'car entries)) | |
374 t))))) | |
375 | |
376 (defun tramp-smb-handle-file-local-copy (filename) | |
377 "Like `file-local-copy' for tramp files." | |
378 (with-parsed-tramp-file-name filename nil | |
379 (save-excursion | |
380 (let ((share (tramp-smb-get-share localname)) | |
381 (file (tramp-smb-get-localname localname t)) | |
382 (tmpfil (tramp-make-temp-file filename))) | |
383 (unless (file-exists-p filename) | |
384 (error "Cannot make local copy of non-existing file `%s'" filename)) | |
385 (tramp-message-for-buffer | |
386 nil tramp-smb-method user host | |
387 5 "Fetching %s to tmp file %s..." filename tmpfil) | |
388 (tramp-smb-maybe-open-connection user host share) | |
389 (if (tramp-smb-send-command | |
390 user host (format "get \"%s\" %s" file tmpfil)) | |
391 (tramp-message-for-buffer | |
392 nil tramp-smb-method user host | |
393 5 "Fetching %s to tmp file %s...done" filename tmpfil) | |
394 (error "Cannot make local copy of file `%s'" filename)) | |
395 tmpfil)))) | |
396 | 394 |
397 ;; This function should return "foo/" for directories and "bar" for | 395 ;; This function should return "foo/" for directories and "bar" for |
398 ;; files. | 396 ;; files. |
399 (defun tramp-smb-handle-file-name-all-completions (filename directory) | 397 (defun tramp-smb-handle-file-name-all-completions (filename directory) |
400 "Like `file-name-all-completions' for tramp files." | 398 "Like `file-name-all-completions' for Tramp files." |
401 (with-parsed-tramp-file-name directory nil | 399 (all-completions |
402 (save-match-data | 400 filename |
403 (save-excursion | 401 (with-parsed-tramp-file-name directory nil |
404 (let* ((share (tramp-smb-get-share localname)) | 402 (with-file-property v localname "file-name-all-completions" |
405 (file (tramp-smb-get-localname localname nil)) | 403 (save-match-data |
406 (entries (tramp-smb-get-file-entries user host share file))) | 404 (let ((entries (tramp-smb-get-file-entries directory))) |
407 | |
408 (all-completions | |
409 filename | |
410 (mapcar | 405 (mapcar |
411 (lambda (x) | 406 (lambda (x) |
412 (list | 407 (list |
413 (if (string-match "d" (nth 1 x)) | 408 (if (string-match "d" (nth 1 x)) |
414 (file-name-as-directory (nth 0 x)) | 409 (file-name-as-directory (nth 0 x)) |
415 (nth 0 x)))) | 410 (nth 0 x)))) |
416 entries))))))) | 411 entries))))))) |
417 | 412 |
418 (defun tramp-smb-handle-file-newer-than-file-p (file1 file2) | 413 (defun tramp-smb-handle-file-newer-than-file-p (file1 file2) |
419 "Like `file-newer-than-file-p' for tramp files." | 414 "Like `file-newer-than-file-p' for Tramp files." |
420 (cond | 415 (cond |
421 ((not (file-exists-p file1)) nil) | 416 ((not (file-exists-p file1)) nil) |
422 ((not (file-exists-p file2)) t) | 417 ((not (file-exists-p file2)) t) |
423 (t (tramp-smb-time-less-p (file-attributes file2) | 418 (t (tramp-time-less-p (nth 5 (file-attributes file2)) |
424 (file-attributes file1))))) | 419 (nth 5 (file-attributes file1)))))) |
425 | 420 |
426 (defun tramp-smb-handle-file-writable-p (filename) | 421 (defun tramp-smb-handle-file-writable-p (filename) |
427 "Like `file-writable-p' for tramp files." | 422 "Like `file-writable-p' for Tramp files." |
428 (if (not (file-exists-p filename)) | 423 (if (file-exists-p filename) |
429 (let ((dir (file-name-directory filename))) | 424 (string-match "w" (or (nth 8 (file-attributes filename)) "")) |
430 (and (file-exists-p dir) | 425 (let ((dir (file-name-directory filename))) |
431 (file-writable-p dir))) | 426 (and (file-exists-p dir) |
432 (with-parsed-tramp-file-name filename nil | 427 (file-writable-p dir))))) |
433 (save-excursion | |
434 (let* ((share (tramp-smb-get-share localname)) | |
435 (file (tramp-smb-get-localname localname nil)) | |
436 (entries (tramp-smb-get-file-entries user host share file)) | |
437 (entry (and entries | |
438 (assoc (file-name-nondirectory file) entries)))) | |
439 (and share entry | |
440 (string-match "w" (nth 1 entry)) | |
441 t)))))) | |
442 | 428 |
443 (defun tramp-smb-handle-insert-directory | 429 (defun tramp-smb-handle-insert-directory |
444 (filename switches &optional wildcard full-directory-p) | 430 (filename switches &optional wildcard full-directory-p) |
445 "Like `insert-directory' for tramp files. | 431 "Like `insert-directory' for Tramp files." |
446 WILDCARD and FULL-DIRECTORY-P are not handled." | |
447 (setq filename (expand-file-name filename)) | 432 (setq filename (expand-file-name filename)) |
448 (when (file-directory-p filename) | 433 (when full-directory-p |
449 ;; This check is a little bit strange, but in `dired-add-entry' | 434 ;; Called from `dired-add-entry'. |
450 ;; this function is called with a non-directory ... | |
451 (setq filename (file-name-as-directory filename))) | 435 (setq filename (file-name-as-directory filename))) |
452 (with-parsed-tramp-file-name filename nil | 436 (with-parsed-tramp-file-name filename nil |
437 (tramp-flush-file-property v (file-name-directory localname)) | |
453 (save-match-data | 438 (save-match-data |
454 (let* ((share (tramp-smb-get-share localname)) | 439 (let ((base (file-name-nondirectory filename)) |
455 (file (tramp-smb-get-localname localname nil)) | 440 ;; We should not destroy the cache entry. |
456 (entries (tramp-smb-get-file-entries user host share file))) | 441 (entries (copy-sequence |
457 | 442 (tramp-smb-get-file-entries |
458 ;; Delete dummy "" entry, useless entries | 443 (file-name-directory filename))))) |
444 | |
445 (when wildcard | |
446 (string-match "\\." base) | |
447 (setq base (replace-match "\\\\." nil nil base)) | |
448 (string-match "\\*" base) | |
449 (setq base (replace-match ".*" nil nil base)) | |
450 (string-match "\\?" base) | |
451 (setq base (replace-match ".?" nil nil base))) | |
452 | |
453 ;; Filter entries. | |
459 (setq entries | 454 (setq entries |
460 (if (file-directory-p filename) | 455 (delq |
461 (delq (assoc "" entries) entries) | 456 nil |
462 ;; We just need the only and only entry FILENAME. | 457 (if (or wildcard (zerop (length base))) |
463 (list (assoc (file-name-nondirectory filename) entries)))) | 458 ;; Check for matching entries. |
459 (mapcar | |
460 (lambda (x) | |
461 (when (string-match | |
462 (format "^%s" base) (nth 0 x)) | |
463 x)) | |
464 entries) | |
465 ;; We just need the only and only entry FILENAME. | |
466 (list (assoc base entries))))) | |
464 | 467 |
465 ;; Sort entries | 468 ;; Sort entries |
466 (setq entries | 469 (setq entries |
467 (sort | 470 (sort |
468 entries | 471 entries |
469 (lambda (x y) | 472 (lambda (x y) |
470 (if (string-match "t" switches) | 473 (if (string-match "t" switches) |
471 ; sort by date | 474 ;; Sort by date. |
472 (tramp-smb-time-less-p (nth 3 y) (nth 3 x)) | 475 (tramp-time-less-p (nth 3 y) (nth 3 x)) |
473 ; sort by name | 476 ;; Sort by name. |
474 (string-lessp (nth 0 x) (nth 0 y)))))) | 477 (string-lessp (nth 0 x) (nth 0 y)))))) |
475 | 478 |
476 ;; Print entries | 479 ;; Print entries. |
477 (mapcar | 480 (mapcar |
478 (lambda (x) | 481 (lambda (x) |
479 (insert | 482 (when (not (zerop (length (nth 0 x)))) |
480 (format | 483 (insert |
481 "%10s %3d %-8s %-8s %8s %s %s\n" | 484 (format |
482 (nth 1 x) ; mode | 485 "%10s %3d %-8s %-8s %8s %s %s\n" |
483 1 "nobody" "nogroup" | 486 (nth 1 x) ; mode |
484 (nth 2 x) ; size | 487 1 "nobody" "nogroup" |
485 (format-time-string | 488 (nth 2 x) ; size |
486 (if (tramp-smb-time-less-p | 489 (format-time-string |
487 (tramp-smb-time-subtract (current-time) (nth 3 x)) | 490 (if (tramp-time-less-p |
488 tramp-smb-half-a-year) | 491 (tramp-time-subtract (current-time) (nth 3 x)) |
489 "%b %e %R" | 492 tramp-half-a-year) |
490 "%b %e %Y") | 493 "%b %e %R" |
491 (nth 3 x)) ; date | 494 "%b %e %Y") |
492 (nth 0 x))) ; file name | 495 (nth 3 x)) ; date |
493 (forward-line) | 496 (nth 0 x))) ; file name |
494 (beginning-of-line)) | 497 (forward-line) |
495 entries))))) | 498 (beginning-of-line))) |
499 entries))))) | |
496 | 500 |
497 (defun tramp-smb-handle-make-directory (dir &optional parents) | 501 (defun tramp-smb-handle-make-directory (dir &optional parents) |
498 "Like `make-directory' for tramp files." | 502 "Like `make-directory' for Tramp files." |
499 (setq dir (directory-file-name (expand-file-name dir))) | 503 (setq dir (directory-file-name (expand-file-name dir))) |
500 (unless (file-name-absolute-p dir) | 504 (unless (file-name-absolute-p dir) |
501 (setq dir (concat default-directory dir))) | 505 (setq dir (expand-file-name dir default-directory))) |
502 (with-parsed-tramp-file-name dir nil | 506 (with-parsed-tramp-file-name dir nil |
503 (save-match-data | 507 (save-match-data |
504 (let* ((share (tramp-smb-get-share localname)) | 508 (let* ((share (tramp-smb-get-share localname)) |
505 (ldir (file-name-directory dir))) | 509 (ldir (file-name-directory dir))) |
506 ;; Make missing directory parts | 510 ;; Make missing directory parts |
508 (make-directory ldir parents)) | 512 (make-directory ldir parents)) |
509 ;; Just do it | 513 ;; Just do it |
510 (when (file-directory-p ldir) | 514 (when (file-directory-p ldir) |
511 (make-directory-internal dir)) | 515 (make-directory-internal dir)) |
512 (unless (file-directory-p dir) | 516 (unless (file-directory-p dir) |
513 (error "Couldn't make directory %s" dir)))))) | 517 (tramp-error v 'file-error "Couldn't make directory %s" dir)))))) |
514 | 518 |
515 (defun tramp-smb-handle-make-directory-internal (directory) | 519 (defun tramp-smb-handle-make-directory-internal (directory) |
516 "Like `make-directory-internal' for tramp files." | 520 "Like `make-directory-internal' for Tramp files." |
517 (setq directory (directory-file-name (expand-file-name directory))) | 521 (setq directory (directory-file-name (expand-file-name directory))) |
518 (unless (file-name-absolute-p directory) | 522 (unless (file-name-absolute-p directory) |
519 (setq directory (concat default-directory directory))) | 523 (setq directory (expand-file-name directory default-directory))) |
520 (with-parsed-tramp-file-name directory nil | 524 (with-parsed-tramp-file-name directory nil |
521 (save-match-data | 525 (save-match-data |
522 (let* ((share (tramp-smb-get-share localname)) | 526 (let* ((file (tramp-smb-get-localname localname t))) |
523 (file (tramp-smb-get-localname localname nil))) | |
524 (when (file-directory-p (file-name-directory directory)) | 527 (when (file-directory-p (file-name-directory directory)) |
525 (tramp-smb-maybe-open-connection user host share) | 528 (tramp-smb-send-command v (format "mkdir \"%s\"" file)) |
526 (tramp-smb-send-command user host (format "mkdir \"%s\"" file))) | 529 ;; We must also flush the cache of the directory, because |
530 ;; file-attributes reads the values from there. | |
531 (tramp-flush-file-property v (file-name-directory localname))) | |
527 (unless (file-directory-p directory) | 532 (unless (file-directory-p directory) |
528 (error "Couldn't make directory %s" directory)))))) | 533 (tramp-error |
534 v 'file-error "Couldn't make directory %s" directory)))))) | |
529 | 535 |
530 (defun tramp-smb-handle-rename-file | 536 (defun tramp-smb-handle-rename-file |
531 (filename newname &optional ok-if-already-exists) | 537 (filename newname &optional ok-if-already-exists) |
532 "Like `rename-file' for tramp files." | 538 "Like `rename-file' for Tramp files." |
533 (setq filename (expand-file-name filename) | 539 (setq filename (expand-file-name filename) |
534 newname (expand-file-name newname)) | 540 newname (expand-file-name newname)) |
535 | 541 |
536 (let ((tmpfile (file-local-copy filename))) | 542 (let ((tmpfile (file-local-copy filename))) |
537 | 543 |
541 | 547 |
542 ;; remote newname | 548 ;; remote newname |
543 (when (file-directory-p newname) | 549 (when (file-directory-p newname) |
544 (setq newname (expand-file-name | 550 (setq newname (expand-file-name |
545 (file-name-nondirectory filename) newname))) | 551 (file-name-nondirectory filename) newname))) |
546 (when (and (not ok-if-already-exists) | |
547 (file-exists-p newname)) | |
548 (error "rename-file: file %s already exists" newname)) | |
549 | 552 |
550 (with-parsed-tramp-file-name newname nil | 553 (with-parsed-tramp-file-name newname nil |
551 (save-excursion | 554 (when (and (not ok-if-already-exists) |
552 (let ((share (tramp-smb-get-share localname)) | 555 (file-exists-p newname)) |
553 (file (tramp-smb-get-localname localname t))) | 556 (tramp-error v 'file-already-exists newname)) |
554 (tramp-smb-maybe-open-connection user host share) | 557 ;; We must also flush the cache of the directory, because |
555 (tramp-message-for-buffer | 558 ;; file-attributes reads the values from there. |
556 nil tramp-smb-method user host | 559 (tramp-flush-file-property v (file-name-directory localname)) |
557 5 "Copying file %s to file %s..." filename newname) | 560 (tramp-flush-file-property v localname) |
558 (if (tramp-smb-send-command | 561 (let ((file (tramp-smb-get-localname localname t))) |
559 user host (format "put %s \"%s\"" filename file)) | 562 (tramp-message v 0 "Copying file %s to file %s..." filename newname) |
560 (tramp-message-for-buffer | 563 (if (tramp-smb-send-command v (format "put %s \"%s\"" filename file)) |
561 nil tramp-smb-method user host | 564 (tramp-message |
562 5 "Copying file %s to file %s...done" filename newname) | 565 v 0 "Copying file %s to file %s...done" filename newname) |
563 (error "Cannot rename `%s'" filename))))))) | 566 (tramp-error v 'file-error "Cannot rename `%s'" filename)))))) |
564 | 567 |
565 (delete-file filename)) | 568 (delete-file filename)) |
566 | 569 |
567 (defun tramp-smb-handle-substitute-in-file-name (filename) | 570 (defun tramp-smb-handle-substitute-in-file-name (filename) |
568 "Like `handle-substitute-in-file-name' for tramp files. | 571 "Like `handle-substitute-in-file-name' for Tramp files. |
569 Catches errors for shares like \"C$/\", which are common in Microsoft Windows." | 572 Catches errors for shares like \"C$/\", which are common in Microsoft Windows." |
570 (condition-case nil | 573 (condition-case nil |
571 (tramp-run-real-handler 'substitute-in-file-name (list filename)) | 574 (tramp-run-real-handler 'substitute-in-file-name (list filename)) |
572 (error filename))) | 575 (error filename))) |
573 | 576 |
574 (defun tramp-smb-handle-write-region | 577 (defun tramp-smb-handle-write-region |
575 (start end filename &optional append visit lockname confirm) | 578 (start end filename &optional append visit lockname confirm) |
576 "Like `write-region' for tramp files." | 579 "Like `write-region' for Tramp files." |
577 (unless (eq append nil) | |
578 (error "Cannot append to file using tramp (`%s')" filename)) | |
579 (setq filename (expand-file-name filename)) | 580 (setq filename (expand-file-name filename)) |
580 ;; XEmacs takes a coding system as the seventh argument, not `confirm' | |
581 (when (and (not (featurep 'xemacs)) | |
582 confirm (file-exists-p filename)) | |
583 (unless (y-or-n-p (format "File %s exists; overwrite anyway? " | |
584 filename)) | |
585 (error "File not overwritten"))) | |
586 (with-parsed-tramp-file-name filename nil | 581 (with-parsed-tramp-file-name filename nil |
587 (save-excursion | 582 (unless (eq append nil) |
588 (let ((share (tramp-smb-get-share localname)) | 583 (tramp-error |
589 (file (tramp-smb-get-localname localname t)) | 584 v 'file-error "Cannot append to file using tramp (`%s')" filename)) |
590 (curbuf (current-buffer)) | 585 ;; XEmacs takes a coding system as the seventh argument, not `confirm' |
591 tmpfil) | 586 (when (and (not (featurep 'xemacs)) |
592 ;; Write region into a tmp file. | 587 confirm (file-exists-p filename)) |
593 (setq tmpfil (tramp-make-temp-file filename)) | 588 (unless (y-or-n-p (format "File %s exists; overwrite anyway? " |
594 ;; We say `no-message' here because we don't want the visited file | 589 filename)) |
595 ;; modtime data to be clobbered from the temp file. We call | 590 (tramp-error v 'file-error "File not overwritten"))) |
596 ;; `set-visited-file-modtime' ourselves later on. | 591 ;; We must also flush the cache of the directory, because |
597 (tramp-run-real-handler | 592 ;; file-attributes reads the values from there. |
598 'write-region | 593 (tramp-flush-file-property v (file-name-directory localname)) |
599 (if confirm ; don't pass this arg unless defined for backward compat. | 594 (tramp-flush-file-property v localname) |
600 (list start end tmpfil append 'no-message lockname confirm) | 595 (let ((file (tramp-smb-get-localname localname t)) |
601 (list start end tmpfil append 'no-message lockname))) | 596 (curbuf (current-buffer)) |
602 | 597 tmpfil) |
603 (tramp-smb-maybe-open-connection user host share) | 598 ;; Write region into a tmp file. |
604 (tramp-message-for-buffer | 599 (setq tmpfil (tramp-make-temp-file filename)) |
605 nil tramp-smb-method user host | 600 ;; We say `no-message' here because we don't want the visited file |
606 5 "Writing tmp file %s to file %s..." tmpfil filename) | 601 ;; modtime data to be clobbered from the temp file. We call |
607 (if (tramp-smb-send-command | 602 ;; `set-visited-file-modtime' ourselves later on. |
608 user host (format "put %s \"%s\"" tmpfil file)) | 603 (tramp-run-real-handler |
609 (tramp-message-for-buffer | 604 'write-region |
610 nil tramp-smb-method user host | 605 (if confirm ; don't pass this arg unless defined for backward compat. |
611 5 "Writing tmp file %s to file %s...done" tmpfil filename) | 606 (list start end tmpfil append 'no-message lockname confirm) |
612 (error "Cannot write `%s'" filename)) | 607 (list start end tmpfil append 'no-message lockname))) |
613 | 608 |
614 (delete-file tmpfil) | 609 (tramp-message v 5 "Writing tmp file %s to file %s..." tmpfil filename) |
615 (unless (equal curbuf (current-buffer)) | 610 (if (tramp-smb-send-command v (format "put %s \"%s\"" tmpfil file)) |
616 (error "Buffer has changed from `%s' to `%s'" | 611 (tramp-message |
617 curbuf (current-buffer))) | 612 v 5 "Writing tmp file %s to file %s...done" tmpfil filename) |
618 (when (eq visit t) | 613 (tramp-error v 'file-error "Cannot write `%s'" filename)) |
619 (set-visited-file-modtime)))))) | 614 |
615 (delete-file tmpfil) | |
616 (unless (equal curbuf (current-buffer)) | |
617 (tramp-error | |
618 v 'file-error | |
619 "Buffer has changed from `%s' to `%s'" curbuf (current-buffer))) | |
620 (when (eq visit t) | |
621 (set-visited-file-modtime))))) | |
620 | 622 |
621 | 623 |
622 ;; Internal file name functions | 624 ;; Internal file name functions |
623 | 625 |
624 (defun tramp-smb-get-share (localname) | 626 (defun tramp-smb-get-share (localname) |
650 | 652 |
651 res))) | 653 res))) |
652 | 654 |
653 ;; Share names of a host are cached. It is very unlikely that the | 655 ;; Share names of a host are cached. It is very unlikely that the |
654 ;; shares do change during connection. | 656 ;; shares do change during connection. |
655 (defun tramp-smb-get-file-entries (user host share localname) | 657 (defun tramp-smb-get-file-entries (directory) |
656 "Read entries which match LOCALNAME. | 658 "Read entries which match DIRECTORY. |
657 Either the shares are listed, or the `dir' command is executed. | 659 Either the shares are listed, or the `dir' command is executed. |
658 Only entries matching the localname are returned. | |
659 Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)." | 660 Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)." |
660 (save-excursion | 661 (with-parsed-tramp-file-name directory nil |
661 (save-match-data | 662 (setq localname (or localname "/")) |
662 (let ((base (or (and (> (length localname) 0) | 663 (with-file-property v localname "file-entries" |
663 (string-match "\\([^/]+\\)$" localname) | 664 (with-current-buffer (tramp-get-buffer v) |
664 (regexp-quote (match-string 1 localname))) | 665 (let* ((share (tramp-smb-get-share localname)) |
665 "")) | 666 (file (tramp-smb-get-localname localname nil)) |
666 res entry) | 667 (cache (tramp-get-connection-property v "share-cache" nil)) |
667 (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) | 668 res entry) |
668 (if (and (not share) tramp-smb-share-cache) | 669 |
669 ;; Return cached shares | 670 (if (and (not share) cache) |
670 (setq res tramp-smb-share-cache) | 671 ;; Return cached shares |
671 ;; Read entries | 672 (setq res cache) |
672 (tramp-smb-maybe-open-connection user host share) | 673 |
673 (when share | 674 ;; Read entries |
674 (tramp-smb-send-command | 675 (setq file (file-name-as-directory file)) |
675 user host | 676 (when (string-match "^\\./" file) |
676 (format "dir %s" | 677 (setq file (substring file 1))) |
677 (if (zerop (length localname)) "" (concat "\"" localname "*\""))))) | 678 (if share |
678 (goto-char (point-min)) | 679 (tramp-smb-send-command v (format "dir \"%s*\"" file)) |
679 ;; Loop the listing | 680 ;; `tramp-smb-maybe-open-connection' lists also the share names |
680 (unless (re-search-forward tramp-smb-errors nil t) | 681 (tramp-smb-maybe-open-connection v)) |
681 (while (not (eobp)) | 682 |
682 (setq entry (tramp-smb-read-file-entry share)) | 683 ;; Loop the listing |
683 (forward-line) | 684 (goto-char (point-min)) |
684 (when entry (add-to-list 'res entry)))) | 685 (unless (re-search-forward tramp-smb-errors nil t) |
685 (unless share | 686 (while (not (eobp)) |
687 (setq entry (tramp-smb-read-file-entry share)) | |
688 (forward-line) | |
689 (when entry (add-to-list 'res entry)))) | |
690 | |
686 ;; Cache share entries | 691 ;; Cache share entries |
687 (setq tramp-smb-share-cache res))) | 692 (unless share |
688 | 693 (tramp-set-connection-property v "share-cache" res))) |
689 ;; Add directory itself | 694 |
690 (add-to-list 'res '("" "drwxrwxrwx" 0 (0 0))) | 695 ;; Add directory itself |
691 | 696 (add-to-list 'res '("" "drwxrwxrwx" 0 (0 0))) |
692 ;; There's a very strange error (debugged with XEmacs 21.4.14) | 697 |
693 ;; If there's no short delay, it returns nil. No idea about | 698 ;; There's a very strange error (debugged with XEmacs 21.4.14) |
694 (when (featurep 'xemacs) (sleep-for 0.01)) | 699 ;; If there's no short delay, it returns nil. No idea about. |
695 | 700 (when (featurep 'xemacs) (sleep-for 0.01)) |
696 ;; Check for matching entries | 701 |
697 (delq nil (mapcar | 702 ;; Return entries |
698 (lambda (x) (and (string-match base (nth 0 x)) x)) | 703 (delq nil res)))))) |
699 res)))))) | |
700 | 704 |
701 ;; Return either a share name (if SHARE is nil), or a file name | 705 ;; Return either a share name (if SHARE is nil), or a file name |
702 ;; | 706 ;; |
703 ;; If shares are listed, the following format is expected | 707 ;; If shares are listed, the following format is expected |
704 ;; | 708 ;; |
719 ;; \s-\{2,2\} - space delimeter | 723 ;; \s-\{2,2\} - space delimeter |
720 ;; \w\{3,3\} - weekday | 724 ;; \w\{3,3\} - weekday |
721 ;; \s- - space delimeter | 725 ;; \s- - space delimeter |
722 ;; \w\{3,3\} - month | 726 ;; \w\{3,3\} - month |
723 ;; \s- - space delimeter | 727 ;; \s- - space delimeter |
724 ;; [ 19][0-9] - day | 728 ;; [ 12][0-9] - day |
725 ;; \s- - space delimeter | 729 ;; \s- - space delimeter |
726 ;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time | 730 ;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time |
727 ;; \s- - space delimeter | 731 ;; \s- - space delimeter |
728 ;; [0-9]\{4,4\} - year | 732 ;; [0-9]\{4,4\} - year |
729 ;; | 733 ;; |
754 ;; So we try to analyze backwards. | 758 ;; So we try to analyze backwards. |
755 (defun tramp-smb-read-file-entry (share) | 759 (defun tramp-smb-read-file-entry (share) |
756 "Parse entry in SMB output buffer. | 760 "Parse entry in SMB output buffer. |
757 If SHARE is result, entries are of type dir. Otherwise, shares are listed. | 761 If SHARE is result, entries are of type dir. Otherwise, shares are listed. |
758 Result is the list (LOCALNAME MODE SIZE MTIME)." | 762 Result is the list (LOCALNAME MODE SIZE MTIME)." |
759 (let ((line (buffer-substring (point) (tramp-point-at-eol))) | 763 ;; We are called from `tramp-smb-get-file-entries', which sets the |
764 ;; current buffer. | |
765 (let ((line (buffer-substring (point) (tramp-line-end-position))) | |
760 localname mode size month day hour min sec year mtime) | 766 localname mode size month day hour min sec year mtime) |
761 | 767 |
762 (if (not share) | 768 (if (not share) |
763 | 769 |
764 ; Read share entries | 770 ;; Read share entries. |
765 (when (string-match "^\\s-+\\(\\S-+\\)\\s-+Disk" line) | 771 (when (string-match "^\\s-+\\(\\S-\\(.*\\S-\\)?\\)\\s-+Disk" line) |
766 (setq localname (match-string 1 line) | 772 (setq localname (match-string 1 line) |
767 mode "dr-xr-xr-x" | 773 mode "dr-xr-xr-x" |
768 size 0)) | 774 size 0)) |
769 | 775 |
770 ; Real listing | 776 ;; Real listing. |
771 (block nil | 777 (block nil |
772 | 778 |
773 ;; year | 779 ;; year |
774 (if (string-match "\\([0-9]+\\)$" line) | 780 (if (string-match "\\([0-9]+\\)$" line) |
775 (setq year (string-to-number (match-string 1 line)) | 781 (setq year (string-to-number (match-string 1 line)) |
831 (when (and localname mode size) | 837 (when (and localname mode size) |
832 (setq mtime | 838 (setq mtime |
833 (if (and sec min hour day month year) | 839 (if (and sec min hour day month year) |
834 (encode-time | 840 (encode-time |
835 sec min hour day | 841 sec min hour day |
836 (cdr (assoc (downcase month) tramp-smb-parse-time-months)) | 842 (cdr (assoc (downcase month) tramp-parse-time-months)) |
837 year) | 843 year) |
838 '(0 0))) | 844 '(0 0))) |
839 (list localname mode size mtime)))) | 845 (list localname mode size mtime)))) |
840 | 846 |
841 ;; Inodes don't exist for SMB files. Therefore we must generate virtual ones. | |
842 ;; Used in `find-buffer-visiting'. | |
843 ;; The method applied might be not so efficient (Ange-FTP uses hashes). But | |
844 ;; performance isn't the major issue given that file transfer will take time. | |
845 | |
846 (defun tramp-smb-get-inode (share file) | |
847 "Returns the virtual inode number. | |
848 If it doesn't exist, generate a new one." | |
849 (let ((string (concat share "/" (directory-file-name file)))) | |
850 (unless (assoc string tramp-smb-inodes) | |
851 (add-to-list 'tramp-smb-inodes | |
852 (list string (length tramp-smb-inodes)))) | |
853 (nth 1 (assoc string tramp-smb-inodes)))) | |
854 | |
855 | 847 |
856 ;; Connection functions | 848 ;; Connection functions |
857 | 849 |
858 (defun tramp-smb-send-command (user host command) | 850 (defun tramp-smb-send-command (vec command) |
859 "Send the COMMAND to USER at HOST (logged into an SMB session). | 851 "Send the COMMAND to connection VEC. |
860 Erases temporary buffer before sending the command. Returns nil if | 852 Returns nil if there has been an error message from smbclient." |
861 there has been an error message from smbclient." | 853 (tramp-smb-maybe-open-connection vec) |
862 (save-excursion | 854 (tramp-message vec 6 "%s" command) |
863 (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) | 855 (tramp-send-string vec command) |
864 (erase-buffer) | 856 (tramp-smb-wait-for-output vec)) |
865 (tramp-send-command nil tramp-smb-method user host command nil t) | 857 |
866 (tramp-smb-wait-for-output user host))) | 858 (defun tramp-smb-maybe-open-connection (vec) |
867 | 859 "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'. |
868 (defun tramp-smb-maybe-open-connection (user host share) | |
869 "Maybe open a connection to HOST, logging in as USER, using `tramp-smb-program'. | |
870 Does not do anything if a connection is already open, but re-opens the | 860 Does not do anything if a connection is already open, but re-opens the |
871 connection if a previous connection has died for some reason." | 861 connection if a previous connection has died for some reason." |
872 (let ((process-connection-type tramp-process-connection-type) | 862 (let* ((share (tramp-smb-get-share (tramp-file-name-localname vec))) |
873 (p (get-buffer-process | 863 (buf (tramp-get-buffer vec)) |
874 (tramp-get-buffer nil tramp-smb-method user host)))) | 864 (p (get-buffer-process buf))) |
875 (save-excursion | 865 |
876 (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) | 866 ;; If too much time has passed since last command was sent, look |
877 ;; Check whether it is still the same share | 867 ;; whether has been an error message; maybe due to connection timeout. |
878 (unless (and p (processp p) (string-equal tramp-smb-share share)) | 868 (with-current-buffer buf |
879 (when (and p (processp p)) | 869 (goto-char (point-min)) |
880 (delete-process p) | 870 (when (and (> (tramp-time-diff |
881 (setq p nil))) | 871 (current-time) |
882 ;; If too much time has passed since last command was sent, look | 872 (tramp-get-connection-property |
883 ;; whether process is still alive. If it isn't, kill it. | 873 p "last-cmd-time" '(0 0 0))) |
884 (when (and tramp-last-cmd-time | 874 60) |
885 (> (tramp-time-diff (current-time) tramp-last-cmd-time) 60) | 875 p (processp p) (memq (process-status p) '(run open)) |
886 p (processp p) (memq (process-status p) '(run open))) | 876 (re-search-forward tramp-smb-errors nil t)) |
887 (unless (and p (processp p) (memq (process-status p) '(run open))) | 877 (delete-process p) |
888 (delete-process p) | 878 (setq p nil))) |
889 (setq p nil)))) | 879 |
890 (unless (and p (processp p) (memq (process-status p) '(run open))) | 880 ;; Check whether it is still the same share. |
891 (when (and p (processp p)) | 881 (unless |
892 (delete-process p)) | 882 (and p (processp p) (memq (process-status p) '(run open)) |
893 (tramp-smb-open-connection user host share)))) | 883 (string-equal |
894 | 884 share |
895 (defun tramp-smb-open-connection (user host share) | 885 (tramp-get-connection-property p "smb-share" ""))) |
896 "Open a connection using `tramp-smb-program'. | 886 |
897 This starts the command `smbclient //HOST/SHARE -U USER', then waits | 887 (save-match-data |
898 for a remote password prompt. It queries the user for the password, | 888 ;; There might be unread output from checking for share names. |
899 then sends the password to the remote host. | 889 (when buf (with-current-buffer buf (erase-buffer))) |
900 | 890 (when (and p (processp p)) (delete-process p)) |
901 Domain names in USER and port numbers in HOST are acknowledged." | 891 |
902 | 892 (unless (let ((default-directory |
903 (when (and (fboundp 'executable-find) | 893 (tramp-temporary-file-directory))) |
904 (not (funcall 'executable-find tramp-smb-program))) | 894 (executable-find tramp-smb-program)) |
905 (error "Cannot find command %s in %s" tramp-smb-program exec-path)) | 895 (error "Cannot find command %s in %s" tramp-smb-program exec-path)) |
906 | 896 |
907 (save-match-data | 897 (let* ((user (tramp-file-name-user vec)) |
908 (let* ((buffer (tramp-get-buffer nil tramp-smb-method user host)) | 898 (host (tramp-file-name-host vec)) |
909 (real-user user) | 899 (real-user user) |
910 (real-host host) | 900 (real-host host) |
911 domain port args) | 901 domain port args) |
912 | 902 |
913 ; Check for domain ("user%domain") and port ("host#port") | 903 ;; Check for domain ("user%domain") and port ("host#port"). |
914 (when (and user (string-match "\\(.+\\)%\\(.+\\)" user)) | 904 (when (and user (string-match "\\(.+\\)%\\(.+\\)" user)) |
915 (setq real-user (or (match-string 1 user) user) | 905 (setq real-user (or (match-string 1 user) user) |
916 domain (match-string 2 user))) | 906 domain (match-string 2 user))) |
917 | 907 |
918 (when (and host (string-match "\\(.+\\)#\\(.+\\)" host)) | 908 (when (and host (string-match "\\(.+\\)#\\(.+\\)" host)) |
919 (setq real-host (or (match-string 1 host) host) | 909 (setq real-host (or (match-string 1 host) host) |
920 port (match-string 2 host))) | 910 port (match-string 2 host))) |
921 | 911 |
922 (if share | 912 (if share |
923 (setq args (list (concat "//" real-host "/" share))) | 913 (setq args (list (concat "//" real-host "/" share))) |
924 (setq args (list "-L" real-host ))) | 914 (setq args (list "-L" real-host ))) |
925 | 915 |
926 (if real-user | 916 (if (not (zerop (length real-user))) |
927 (setq args (append args (list "-U" real-user))) | 917 (setq args (append args (list "-U" real-user))) |
928 (setq args (append args (list "-N")))) | 918 (setq args (append args (list "-N")))) |
929 | 919 |
930 (when domain (setq args (append args (list "-W" domain)))) | 920 (when domain (setq args (append args (list "-W" domain)))) |
931 (when port (setq args (append args (list "-p" port)))) | 921 (when port (setq args (append args (list "-p" port)))) |
932 | 922 (setq args (append args (list "-s" "/dev/null"))) |
933 ; OK, let's go | 923 |
934 (tramp-pre-connection nil tramp-smb-method user host tramp-chunksize) | 924 ;; OK, let's go. |
935 (tramp-message 7 "Opening connection for //%s@%s/%s..." | 925 (tramp-message |
936 user host (or share "")) | 926 vec 3 "Opening connection for //%s%s/%s..." |
937 | 927 (if (not (zerop (length user))) (concat user "@") "") |
938 (let* ((default-directory (tramp-temporary-file-directory)) | 928 host (or share "")) |
939 ;; If we omit the conditional here, then we would use | 929 |
940 ;; `undecided-dos' in some cases. With the conditional, | 930 (let* ((coding-system-for-read nil) |
941 ;; we use nil in these cases. Which one is right? | 931 (process-connection-type tramp-process-connection-type) |
942 (coding-system-for-read (unless (and (not (featurep 'xemacs)) | 932 (p (let ((default-directory (tramp-temporary-file-directory))) |
943 (> emacs-major-version 20)) | 933 (apply #'start-process |
944 tramp-dos-coding-system)) | 934 (tramp-buffer-name vec) (tramp-get-buffer vec) |
945 (p (apply #'start-process (buffer-name buffer) buffer | 935 tramp-smb-program args)))) |
946 tramp-smb-program args))) | 936 |
947 | 937 (tramp-message |
948 (tramp-message 9 "Started process %s" (process-command p)) | 938 vec 6 "%s" (mapconcat 'identity (process-command p) " ")) |
949 (tramp-set-process-query-on-exit-flag p nil) | 939 (set-process-sentinel p 'tramp-flush-connection-property) |
950 (set-buffer buffer) | 940 (tramp-set-process-query-on-exit-flag p nil) |
951 (setq tramp-smb-share share) | 941 (tramp-set-connection-property p "smb-share" share) |
952 | 942 |
953 ; send password | 943 ;; Set variables for computing the prompt for reading password. |
954 (when real-user | 944 (setq tramp-current-method tramp-smb-method |
955 (let ((pw-prompt "Password:")) | 945 tramp-current-user user |
956 (tramp-message 9 "Sending password") | 946 tramp-current-host host) |
957 (tramp-enter-password p pw-prompt user host))) | 947 |
958 | 948 ;; Set chunksize. Otherwise, `tramp-send-string' might |
959 (unless (tramp-smb-wait-for-output user host) | 949 ;; try it itself. |
960 (tramp-clear-passwd user host) | 950 (tramp-set-connection-property p "chunksize" tramp-chunksize) |
961 (error "Cannot open connection //%s@%s/%s" | 951 |
962 user host (or share ""))))))) | 952 ;; Play login scenario. |
953 (tramp-process-actions | |
954 p vec | |
955 (if share | |
956 tramp-smb-actions-with-share | |
957 tramp-smb-actions-without-share)) | |
958 | |
959 (tramp-message | |
960 vec 3 "Opening connection for //%s%s/%s...done" | |
961 (if (not (zerop (length user))) (concat user "@") "") | |
962 host (or share "")))))))) | |
963 | 963 |
964 ;; We don't use timeouts. If needed, the caller shall wrap around. | 964 ;; We don't use timeouts. If needed, the caller shall wrap around. |
965 (defun tramp-smb-wait-for-output (user host) | 965 (defun tramp-smb-wait-for-output (vec) |
966 "Wait for output from smbclient command. | 966 "Wait for output from smbclient command. |
967 Returns nil if an error message has appeared." | 967 Returns nil if an error message has appeared." |
968 (let ((proc (get-buffer-process (current-buffer))) | 968 (with-current-buffer (tramp-get-buffer vec) |
969 (found (progn (goto-char (point-min)) | 969 (let ((p (get-buffer-process (current-buffer))) |
970 (re-search-forward tramp-smb-prompt nil t))) | 970 (found (progn (goto-char (point-min)) |
971 (err (progn (goto-char (point-min)) | 971 (re-search-forward tramp-smb-prompt nil t))) |
972 (re-search-forward tramp-smb-errors nil t)))) | 972 (err (progn (goto-char (point-min)) |
973 | 973 (re-search-forward tramp-smb-errors nil t)))) |
974 ;; Algorithm: get waiting output. See if last line contains | 974 |
975 ;; tramp-smb-prompt sentinel or tramp-smb-errors strings. | 975 ;; Algorithm: get waiting output. See if last line contains |
976 ;; If not, wait a bit and again get waiting output. | 976 ;; tramp-smb-prompt sentinel or tramp-smb-errors strings. |
977 (while (not found) | 977 ;; If not, wait a bit and again get waiting output. |
978 | 978 (while (and (not found) (not err)) |
979 ;; Accept pending output. | 979 |
980 (tramp-accept-process-output proc) | 980 ;; Accept pending output. |
981 | 981 (tramp-accept-process-output p) |
982 ;; Search for prompt. | 982 |
983 (goto-char (point-min)) | 983 ;; Search for prompt. |
984 (setq found (re-search-forward tramp-smb-prompt nil t)) | 984 (goto-char (point-min)) |
985 | 985 (setq found (re-search-forward tramp-smb-prompt nil t)) |
986 ;; Search for errors. | 986 |
987 (goto-char (point-min)) | 987 ;; Search for errors. |
988 (setq err (re-search-forward tramp-smb-errors nil t))) | 988 (goto-char (point-min)) |
989 | 989 (setq err (re-search-forward tramp-smb-errors nil t))) |
990 ;; Add output to debug buffer if appropriate. | 990 |
991 (when tramp-debug-buffer | 991 ;; When the process is still alive, read pending output. |
992 (append-to-buffer | 992 (while (and (not found) (memq (process-status p) '(run open))) |
993 (tramp-get-debug-buffer nil tramp-smb-method user host) | 993 |
994 (point-min) (point-max))) | 994 ;; Accept pending output. |
995 | 995 (tramp-accept-process-output p) |
996 ;; Return value is whether no error message has appeared. | 996 |
997 (not err))) | 997 ;; Search for prompt. |
998 | 998 (goto-char (point-min)) |
999 | 999 (setq found (re-search-forward tramp-smb-prompt nil t))) |
1000 ;; Snarfed code from time-date.el and parse-time.el | 1000 |
1001 | 1001 ;; Return value is whether no error message has appeared. |
1002 (defconst tramp-smb-half-a-year '(241 17024) | 1002 (tramp-message vec 6 "\n%s" (buffer-string)) |
1003 "Evaluated by \"(days-to-time 183)\".") | 1003 (not err)))) |
1004 | |
1005 (defconst tramp-smb-parse-time-months '(("jan" . 1) ("feb" . 2) ("mar" . 3) | |
1006 ("apr" . 4) ("may" . 5) ("jun" . 6) | |
1007 ("jul" . 7) ("aug" . 8) ("sep" . 9) | |
1008 ("oct" . 10) ("nov" . 11) ("dec" . 12)) | |
1009 "Alist mapping month names to integers.") | |
1010 | |
1011 (defun tramp-smb-time-less-p (t1 t2) | |
1012 "Say whether time value T1 is less than time value T2." | |
1013 (unless t1 (setq t1 '(0 0))) | |
1014 (unless t2 (setq t2 '(0 0))) | |
1015 (or (< (car t1) (car t2)) | |
1016 (and (= (car t1) (car t2)) | |
1017 (< (nth 1 t1) (nth 1 t2))))) | |
1018 | |
1019 (defun tramp-smb-time-subtract (t1 t2) | |
1020 "Subtract two time values. | |
1021 Return the difference in the format of a time value." | |
1022 (unless t1 (setq t1 '(0 0))) | |
1023 (unless t2 (setq t2 '(0 0))) | |
1024 (let ((borrow (< (cadr t1) (cadr t2)))) | |
1025 (list (- (car t1) (car t2) (if borrow 1 0)) | |
1026 (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2))))) | |
1027 | 1004 |
1028 | 1005 |
1029 (provide 'tramp-smb) | 1006 (provide 'tramp-smb) |
1030 | 1007 |
1031 ;;; TODO: | 1008 ;;; TODO: |
1032 | 1009 |
1033 ;; * Provide a local smb.conf. The default one might not be readable. | |
1034 ;; * Error handling in case password is wrong. | 1010 ;; * Error handling in case password is wrong. |
1035 ;; * Read password from "~/.netrc". | 1011 ;; * Read password from "~/.netrc". |
1036 ;; * Return more comprehensive file permission string. Think whether it is | 1012 ;; * Return more comprehensive file permission string. Think whether it is |
1037 ;; possible to implement `set-file-modes'. | 1013 ;; possible to implement `set-file-modes'. |
1038 ;; * Handle WILDCARD and FULL-DIRECTORY-P in | |
1039 ;; `tramp-smb-handle-insert-directory'. | |
1040 ;; * Handle links (FILENAME.LNK). | 1014 ;; * Handle links (FILENAME.LNK). |
1041 ;; * Maybe local tmp files should have the same extension like the original | 1015 ;; * Maybe local tmp files should have the same extension like the original |
1042 ;; files. Strange behaviour with jka-compr otherwise? | 1016 ;; files. Strange behaviour with jka-compr otherwise? |
1043 ;; * Copy files in dired from SMB to another method doesn't work. | |
1044 ;; * Try to remove the inclusion of dummy "" directory. Seems to be at | 1017 ;; * Try to remove the inclusion of dummy "" directory. Seems to be at |
1045 ;; several places, especially in `tramp-smb-handle-insert-directory'. | 1018 ;; several places, especially in `tramp-smb-handle-insert-directory'. |
1046 ;; * Provide variables for debug. | |
1047 ;; * (RMS) Use unwind-protect to clean up the state so as to make the state | 1019 ;; * (RMS) Use unwind-protect to clean up the state so as to make the state |
1048 ;; regular again. | 1020 ;; regular again. |
1021 ;; * Make it multi-hop capable. | |
1049 | 1022 |
1050 ;;; arch-tag: fcc9dbec-7503-4d73-b638-3c8aa59575f5 | 1023 ;;; arch-tag: fcc9dbec-7503-4d73-b638-3c8aa59575f5 |
1051 ;;; tramp-smb.el ends here | 1024 ;;; tramp-smb.el ends here |