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