Mercurial > hg > xemacs-beta
comparison lisp/efs/efs-vms.el @ 22:8fc7fe29b841 r19-15b94
Import from CVS: tag r19-15b94
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:50:29 +0200 |
parents | |
children | 7e54bd776075 9f59509498e1 |
comparison
equal
deleted
inserted
replaced
21:b88636d63495 | 22:8fc7fe29b841 |
---|---|
1 ;; -*-Emacs-Lisp-*- | |
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3 ;; | |
4 ;; File: efs-vms.el | |
5 ;; Release: $efs release: 1.15 $ | |
6 ;; Version: $Revision: 1.1 $ | |
7 ;; RCS: | |
8 ;; Description: VMS support for efs | |
9 ;; Authors: Andy Norman, Joe Wells, Sandy Rutherford <sandy@itp.ethz.ch> | |
10 ;; Modified: Sun Nov 27 18:44:59 1994 by sandy on gandalf | |
11 ;; | |
12 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
13 | |
14 ;;; This file is part of efs. See efs.el for copyright | |
15 ;;; (it's copylefted) and warrranty (there isn't one) information. | |
16 | |
17 (provide 'efs-vms) | |
18 (require 'efs) | |
19 | |
20 (defconst efs-vms-version | |
21 (concat (substring "$efs release: 1.15 $" 14 -2) | |
22 "/" | |
23 (substring "$Revision: 1.1 $" 11 -2))) | |
24 | |
25 ;;;; ------------------------------------------------------------ | |
26 ;;;; VMS support. | |
27 ;;;; ------------------------------------------------------------ | |
28 | |
29 ;;; efs has full support for VMS hosts, including tree dired support. It | |
30 ;;; should be able to automatically recognize any VMS machine. However, if it | |
31 ;;; fails to do this, you can use the command efs-add-vms-host. As well, | |
32 ;;; you can set the variable efs-vms-host-regexp in your .emacs file. We | |
33 ;;; would be grateful if you would report any failures to automatically | |
34 ;;; recognize a VMS host as a bug. | |
35 ;;; | |
36 ;;; Filename Syntax: | |
37 ;;; | |
38 ;;; For ease of *implementation*, the user enters the VMS filename syntax in a | |
39 ;;; UNIX-y way. For example: | |
40 ;;; PUB$:[ANONYMOUS.SDSCPUB.NEXT]README.TXT;1 | |
41 ;;; would be entered as: | |
42 ;;; /PUB$$:/ANONYMOUS/SDSCPUB/NEXT/README.TXT;1 | |
43 ;;; i.e. to log in as anonymous on ymir.claremont.edu and grab the file: | |
44 ;;; [.CSV.POLICY]RULES.MEM | |
45 ;;; you would type: | |
46 ;;; C-x C-f /anonymous@ymir.claremont.edu:CSV/POLICY/RULES.MEM | |
47 ;;; | |
48 ;;; A legal VMS filename is of the form: FILE.TYPE;## | |
49 ;;; where FILE can be up to 39 characters | |
50 ;;; TYPE can be up to 39 characters | |
51 ;;; ## is a version number (an integer between 1 and 32,767) | |
52 ;;; Valid characters in FILE and TYPE are A-Z 0-9 _ - $ | |
53 ;;; $ cannot begin a filename, and - cannot be used as the first or last | |
54 ;;; character. | |
55 ;;; | |
56 ;;; Tips: | |
57 ;;; 1. To access the latest version of file under VMS, you use the filename | |
58 ;;; without the ";" and version number. You should always edit the latest | |
59 ;;; version of a file. If you want to edit an earlier version, copy it to a | |
60 ;;; new file first. This has nothing to do with efs, but is simply | |
61 ;;; good VMS operating practice. Therefore, to edit FILE.TXT;3 (say 3 is | |
62 ;;; latest version), do C-x C-f /ymir.claremont.edu:FILE.TXT. If you | |
63 ;;; inadvertently do C-x C-f /ymir.claremont.edu:FILE.TXT;3, you will find | |
64 ;;; that VMS will not allow you to save the file because it will refuse to | |
65 ;;; overwrite FILE.TXT;3, but instead will want to create FILE.TXT;4, and | |
66 ;;; attach the buffer to this file. To get out of this situation, M-x | |
67 ;;; write-file /ymir.claremont.edu:FILE.TXT will attach the buffer to | |
68 ;;; latest version of the file. For this reason, in tree dired "f" | |
69 ;;; (dired-find-file), always loads the file sans version, whereas "v", | |
70 ;;; (dired-view-file), always loads the explicit version number. The | |
71 ;;; reasoning being that it reasonable to view old versions of a file, but | |
72 ;;; not to edit them. | |
73 ;;; 2. EMACS has a feature in which it does environment variable substitution | |
74 ;;; in filenames. Therefore, to enter a $ in a filename, you must quote it | |
75 ;;; by typing $$. There is a bug in EMACS, in that it neglects to quote the | |
76 ;;; $'s in the default directory when it writes it in the minibuffer. You | |
77 ;;; must edit the minibuffer to quote the $'s manually. Hopefully, this bug | |
78 ;;; will be fixed in EMACS 19. If you use Sebastian Kremer's gmhist (V 4.26 | |
79 ;;; or newer), you will not have this problem. | |
80 | |
81 | |
82 ;; Because some VMS ftp servers convert filenames to lower case | |
83 ;; we allow a-z in the filename regexp. | |
84 | |
85 (defconst efs-vms-filename-regexp | |
86 "\\([_A-Za-z0-9$][-_A-Za-z0-9$]*\\)?\\.\\([-_A-Za-z0-9$]*\\);[0-9]+") | |
87 ;; Regular expression to match for a valid VMS file name in Dired buffer. | |
88 | |
89 (defvar efs-vms-month-alist | |
90 '(("JAN" . 1) ("FEB". 2) ("MAR" . 3) ("APR" . 4) ("MAY" . 5) ("JUN" . 6) | |
91 ("JUL" . 7) ("AUG" . 8) ("SEP" . 9) ("OCT" . 10) | |
92 ("NOV" . 11) ("DEC" . 12))) | |
93 | |
94 (defvar efs-vms-date-regexp | |
95 (concat | |
96 "\\([0-3]?[0-9]\\)-" | |
97 "\\(JAN\\|FEB\\|MAR\\|APR\\|MAY\\|JUN\\|" | |
98 "JUL\\|AUG\\|SEP\\|OCT\\|NOV\\|DEC\\)-" | |
99 "\\([0-9][0-9][0-9]?[0-9]?\\) \\(\\([0-5][0-9]\\):\\([0-5][0-9]\\)" | |
100 "\\(:[0-5][0-9]\\)?\\)? ")) | |
101 | |
102 | |
103 ;;; The following two functions are entry points to this file. | |
104 ;;; They are defined as efs-autoloads in efs.el | |
105 | |
106 (efs-defun efs-fix-path vms (path &optional reverse) | |
107 ;; Convert PATH from UNIX-ish to VMS. | |
108 ;; If REVERSE given then convert from VMS to UNIX-ish. | |
109 (efs-save-match-data | |
110 (if reverse | |
111 (if (string-match | |
112 "^\\([^:]+:\\)?\\(\\[[^]]+\\]\\)?\\([^][]*\\)$" path) | |
113 (let (drive dir file) | |
114 (if (match-beginning 1) | |
115 (setq drive (substring path | |
116 (match-beginning 1) | |
117 (match-end 1)))) | |
118 (if (match-beginning 2) | |
119 (setq dir | |
120 (substring path (match-beginning 2) (match-end 2)))) | |
121 (if (match-beginning 3) | |
122 (setq file | |
123 (substring path (match-beginning 3) (match-end 3)))) | |
124 (and dir | |
125 (setq dir (apply (function concat) | |
126 (mapcar (function | |
127 (lambda (char) | |
128 (if (= char ?.) | |
129 (vector ?/) | |
130 (vector char)))) | |
131 (substring dir 1 -1))))) | |
132 (concat (and drive | |
133 (concat "/" drive "/")) | |
134 dir (and dir "/") | |
135 file)) | |
136 (error "path %s didn't match" path)) | |
137 (let (drive dir file) | |
138 (if (string-match "^/[^:/]+:/" path) | |
139 (setq drive (substring path 1 (1- (match-end 0))) | |
140 path (substring path (1- (match-end 0))))) | |
141 (setq dir (file-name-directory path) | |
142 file (efs-internal-file-name-nondirectory path)) | |
143 (if dir | |
144 (let ((len (1- (length dir))) | |
145 (n 0)) | |
146 (if (<= len 0) | |
147 (setq dir nil) | |
148 (while (<= n len) | |
149 (and (char-equal (aref dir n) ?/) | |
150 (cond | |
151 ((zerop n) (aset dir n ?\[)) | |
152 ((= n len) (aset dir n ?\])) | |
153 (t (aset dir n ?.)))) | |
154 (setq n (1+ n)))))) | |
155 (concat drive dir file))))) | |
156 | |
157 ;; It is important that this function barf for directories for which we know | |
158 ;; that we cannot possibly get a directory listing, such as "/" and "/DEV:/". | |
159 ;; This is because it saves an unnecessary FTP error, or possibly the listing | |
160 ;; might succeed, but give erroneous info. This last case is particularly | |
161 ;; likely for OS's (like MTS) for which we need to use a wildcard in order | |
162 ;; to list a directory. | |
163 | |
164 (efs-defun efs-fix-dir-path vms (dir-path) | |
165 ;; Convert path from UNIX-ish to VMS ready for a DIRectory listing. | |
166 ;; Should there be entries for .. -> [-] and . -> [] below. Don't | |
167 ;; think so, because expand-filename should have already short-circuited | |
168 ;; them. | |
169 (cond ((string-equal dir-path "/") | |
170 (error "Cannot get listing for fictitious \"/\" directory.")) | |
171 ((string-match "^/[-A-Z0-9_$]+:/$" dir-path) | |
172 (error "Cannot get listing for device.")) | |
173 ((efs-fix-path 'vms dir-path)))) | |
174 | |
175 ;; These parsing functions are as general as possible because the syntax | |
176 ;; of ftp listings from VMS hosts is a bit erratic. What saves us is that | |
177 ;; the VMS filename syntax is so rigid. If they bomb on a listing in the | |
178 ;; standard VMS Multinet format, then this is a bug. If they bomb on a listing | |
179 ;; from vms.weird.net, then too bad. | |
180 | |
181 (defmacro efs-parse-vms-filename () | |
182 "Extract the next filename from a VMS dired-like listing." | |
183 (` (if (re-search-forward | |
184 efs-vms-filename-regexp | |
185 nil t) | |
186 (buffer-substring (match-beginning 0) (match-end 0))))) | |
187 | |
188 (defun efs-parse-vms-listing () | |
189 ;; Parse the current buffer which is assumed to be a VMS DIR | |
190 ;; listing (either a short (NLIST) or long listing). | |
191 ;; Assumes that point is at the beginning of the buffer. | |
192 (let ((tbl (efs-make-hashtable)) | |
193 file) | |
194 (goto-char (point-min)) | |
195 (efs-save-match-data | |
196 (while (setq file (efs-parse-vms-filename)) | |
197 (if (string-match "\\.\\(DIR\\|dir\\);[0-9]+" file) | |
198 ;; deal with directories | |
199 (efs-put-hash-entry | |
200 (substring file 0 (match-beginning 0)) '(t) tbl) | |
201 (efs-put-hash-entry file '(nil) tbl) | |
202 (if (string-match ";[0-9]+$" file) ; deal with extension | |
203 ;; sans extension | |
204 (efs-put-hash-entry | |
205 (substring file 0 (match-beginning 0)) '(nil) tbl))) | |
206 (forward-line 1)) | |
207 ;; Would like to look for a "Total" line, or a "Directory" line to | |
208 ;; make sure that the listing isn't complete garbage before putting | |
209 ;; in "." and "..", but we can't even count on all VAX's giving us | |
210 ;; either of these. | |
211 (efs-put-hash-entry "." '(t) tbl) | |
212 (efs-put-hash-entry ".." '(t) tbl)) | |
213 tbl)) | |
214 | |
215 (efs-defun efs-parse-listing vms | |
216 (host user dir path &optional switches) | |
217 ;; Parse the current buffer which is assumed to be a VMS FTP dir | |
218 ;; format, and return a hashtable as the result. SWITCHES are never used, | |
219 ;; but they must be specified in the argument list for compatibility | |
220 ;; with the unix version of this function. | |
221 ;; HOST = remote host name | |
222 ;; USER = user name | |
223 ;; DIR = directory in as a full remote path | |
224 ;; PATH = directory in full efs path syntax | |
225 ;; SWITCHES = ls switches (not relevant here) | |
226 (goto-char (point-min)) | |
227 (efs-save-match-data | |
228 ;; check for a DIR/FULL monstrosity | |
229 (if (search-forward "\nSize:" nil t) | |
230 (progn | |
231 (efs-add-listing-type 'vms:full host user) | |
232 ;; This will cause the buffer to be refilled with an NLIST | |
233 (let ((efs-ls-uncache t)) | |
234 (efs-ls path nil (format "Relisting %s" | |
235 (efs-relativize-filename path)) | |
236 t)) | |
237 (goto-char (point-min)) | |
238 (efs-parse-vms-listing)) | |
239 (efs-parse-vms-listing)))) | |
240 | |
241 | |
242 ;;;; Sorting of listings | |
243 | |
244 (efs-defun efs-t-converter vms (&optional regexp reverse) | |
245 (if regexp | |
246 nil | |
247 (goto-char (point-min)) | |
248 (efs-save-match-data | |
249 (if (re-search-forward efs-vms-filename-regexp nil t) | |
250 (let (list-start start end list) | |
251 (beginning-of-line) | |
252 (setq list-start (point)) | |
253 (while (and (looking-at efs-vms-filename-regexp) | |
254 (progn | |
255 (setq start (point)) | |
256 (goto-char (match-end 0)) | |
257 (forward-line (if (eolp) 2 1)) | |
258 (setq end (point)) | |
259 (goto-char (match-end 0)) | |
260 (re-search-forward efs-vms-date-regexp nil t))) | |
261 (setq list | |
262 (cons | |
263 (cons | |
264 (nconc | |
265 (list (string-to-int (buffer-substring | |
266 (match-beginning 3) | |
267 (match-end 3))) ; year | |
268 (cdr (assoc | |
269 (buffer-substring (match-beginning 2) | |
270 (match-end 2)) | |
271 efs-vms-month-alist)) ; month | |
272 (string-to-int (buffer-substring | |
273 (match-beginning 1) | |
274 (match-end 1)))) ;day | |
275 (if (match-beginning 4) | |
276 (list | |
277 (string-to-int (buffer-substring | |
278 (match-beginning 5) | |
279 (match-end 5))) ; hour | |
280 (string-to-int (buffer-substring | |
281 (match-beginning 6) | |
282 (match-end 6))) ; minute | |
283 (if (match-beginning 7) | |
284 (string-to-int (buffer-substring | |
285 (1+ (match-beginning 7)) | |
286 (match-end 7))) ; seconds | |
287 0)) | |
288 (list 0 0 0))) | |
289 (buffer-substring start end)) | |
290 list)) | |
291 (goto-char end)) | |
292 (if list | |
293 (progn | |
294 (setq list | |
295 (mapcar 'cdr | |
296 (sort list 'efs-vms-t-converter-sort-pred))) | |
297 (if reverse (setq list (nreverse list))) | |
298 (delete-region list-start (point)) | |
299 (apply 'insert list))) | |
300 t))))) | |
301 | |
302 (defun efs-vms-t-converter-sort-pred (elt1 elt2) | |
303 (let* ((data1 (car elt1)) | |
304 (data2 (car elt2)) | |
305 (year1 (car data1)) | |
306 (year2 (car data2)) | |
307 (month1 (nth 1 data1)) | |
308 (month2 (nth 1 data2)) | |
309 (day1 (nth 2 data1)) | |
310 (day2 (nth 2 data2)) | |
311 (hour1 (nth 3 data1)) | |
312 (hour2 (nth 3 data2)) | |
313 (minute1 (nth 4 data1)) | |
314 (minute2 (nth 4 data2))) | |
315 (or (> year1 year2) | |
316 (and (= year1 year2) | |
317 (or (> month1 month2) | |
318 (and (= month1 month2) | |
319 (or (> day1 day2) | |
320 (and (= day1 day2) | |
321 (or (> hour1 hour2) | |
322 (and (= hour1 hour2) | |
323 (or (> minute1 minute2) | |
324 (and (= minute1 minute2) | |
325 (or (> (nth 5 data1) | |
326 (nth 5 data2))) | |
327 )))))))))))) | |
328 | |
329 | |
330 (efs-defun efs-X-converter vms (&optional regexp reverse) | |
331 ;; Sorts by extension | |
332 (if regexp | |
333 nil | |
334 (goto-char (point-min)) | |
335 (efs-save-match-data | |
336 (if (re-search-forward efs-vms-filename-regexp nil t) | |
337 (let (list-start start list) | |
338 (beginning-of-line) | |
339 (setq list-start (point)) | |
340 (while (looking-at efs-vms-filename-regexp) | |
341 (setq start (point)) | |
342 (goto-char (match-end 0)) | |
343 (forward-line (if (eolp) 2 1)) | |
344 (setq list | |
345 (cons | |
346 (cons (buffer-substring (match-beginning 2) | |
347 (match-end 2)) | |
348 (buffer-substring start (point))) | |
349 list))) | |
350 (setq list | |
351 (mapcar 'cdr | |
352 (sort list | |
353 (if reverse | |
354 (function | |
355 (lambda (x y) | |
356 (string< (car y) (car x)))) | |
357 (function | |
358 (lambda (x y) | |
359 (string< (car x) (car y)))))))) | |
360 (delete-region list-start (point)) | |
361 (apply 'insert list) | |
362 t))))) | |
363 | |
364 ;; This version only deletes file entries which have | |
365 ;; explicit version numbers, because that is all VMS allows. | |
366 | |
367 (efs-defun efs-delete-file-entry vms (path &optional dir-p) | |
368 (let ((ignore-case (memq 'vms efs-case-insensitive-host-types))) | |
369 (if dir-p | |
370 (let ((path (file-name-as-directory path)) | |
371 files) | |
372 (efs-del-hash-entry path efs-files-hashtable ignore-case) | |
373 (setq path (directory-file-name path) | |
374 files (efs-get-hash-entry (file-name-directory path) | |
375 efs-files-hashtable | |
376 ignore-case)) | |
377 (if files | |
378 (efs-del-hash-entry (efs-get-file-part path) | |
379 files ignore-case))) | |
380 (efs-save-match-data | |
381 (let ((file (efs-get-file-part path))) | |
382 (if (string-match ";[0-9]+$" file) | |
383 ;; In VMS you can't delete a file without an explicit | |
384 ;; version number, or wild-card (e.g. FOO;*) | |
385 ;; For now, we give up on wildcards. | |
386 (let ((files (efs-get-hash-entry | |
387 (file-name-directory path) | |
388 efs-files-hashtable ignore-case))) | |
389 (if files | |
390 (let ((root (substring file 0 | |
391 (match-beginning 0))) | |
392 (completion-ignore-case ignore-case) | |
393 (len (match-beginning 0))) | |
394 (efs-del-hash-entry file files ignore-case) | |
395 ;; Now we need to check if there are any | |
396 ;; versions left. If not, then delete the | |
397 ;; root entry. | |
398 (or (all-completions | |
399 root files | |
400 (function | |
401 (lambda (sym) | |
402 (string-match ";[0-9]+$" | |
403 (symbol-name sym) len)))) | |
404 (efs-del-hash-entry root files | |
405 ignore-case))))))))) | |
406 (efs-del-from-ls-cache path t ignore-case))) | |
407 | |
408 (efs-defun efs-add-file-entry vms (path dir-p size owner | |
409 &optional modes nlinks mdtm) | |
410 ;; The vms version of this function needs to keep track | |
411 ;; of vms's file versions. | |
412 (let ((ignore-case (memq 'vms efs-case-insensitive-host-types)) | |
413 (ent (let ((dir-p (null (null dir-p)))) | |
414 (if mdtm | |
415 (list dir-p size owner nil nil mdtm) | |
416 (list dir-p size owner))))) | |
417 (if dir-p | |
418 (let* ((path (directory-file-name path)) | |
419 (files (efs-get-hash-entry (file-name-directory path) | |
420 efs-files-hashtable | |
421 ignore-case))) | |
422 (if files | |
423 (efs-put-hash-entry (efs-get-file-part path) | |
424 ent files ignore-case))) | |
425 (let ((files (efs-get-hash-entry | |
426 (file-name-directory path) | |
427 efs-files-hashtable ignore-case))) | |
428 (if files | |
429 (let ((file (efs-get-file-part path))) | |
430 (efs-save-match-data | |
431 ;; In VMS files must have an extension. If there isn't | |
432 ;; one, it will be added. | |
433 (or (string-match "^[^;]*\\." file) | |
434 (if (string-match ";" file) | |
435 (setq file (concat | |
436 (substring file 0 (match-beginning 0)) | |
437 ".;" | |
438 (substring file (match-end 0)))) | |
439 (setq file (concat file ".")))) | |
440 (if (string-match ";[0-9]+$" file) | |
441 (efs-put-hash-entry | |
442 (substring file 0 (match-beginning 0)) | |
443 ent files ignore-case) | |
444 ;; Need to figure out what version of the file | |
445 ;; is being added. | |
446 (let* ((completion-ignore-case ignore-case) | |
447 (len (length file)) | |
448 (versions (all-completions | |
449 file files | |
450 (function | |
451 (lambda (sym) | |
452 (string-match ";[0-9]+$" | |
453 (symbol-name sym) len))))) | |
454 (N (1+ len)) | |
455 (max (apply | |
456 'max | |
457 (cons 0 (mapcar | |
458 (function | |
459 (lambda (x) | |
460 (string-to-int (substring x N)))) | |
461 versions))))) | |
462 ;; No need to worry about case here. | |
463 (efs-put-hash-entry | |
464 (concat file ";" (int-to-string (1+ max))) ent files)))) | |
465 (efs-put-hash-entry file ent files ignore-case))))) | |
466 (efs-del-from-ls-cache path t ignore-case))) | |
467 | |
468 (efs-defun efs-really-file-p vms (file ent) | |
469 ;; Returns whether the hash entry FILE with entry ENT is a real file. | |
470 (or (car ent) ; file-directory-p | |
471 (efs-save-match-data | |
472 (string-match ";" file)))) | |
473 | |
474 (efs-defun efs-internal-file-name-as-directory vms (name) | |
475 (efs-save-match-data | |
476 (if (string-match "\\.\\(DIR\\|dir\\)\\(;[0-9]+\\)?$" name) | |
477 (setq name (substring name 0 (match-beginning 0)))) | |
478 (let (file-name-handler-alist) | |
479 (file-name-as-directory name)))) | |
480 | |
481 (efs-defun efs-remote-directory-file-name vms (dir) | |
482 ;; Returns the VMS filename in unix directory syntax for directory DIR. | |
483 ;; This is something like /FM/SANDY/FOOBAR.DIR;1 | |
484 (efs-save-match-data | |
485 (setq dir (directory-file-name dir)) | |
486 (concat dir | |
487 (if (string-match "[a-z]" (nth 2 (efs-ftp-path dir))) | |
488 ".dir;1" | |
489 ".DIR;1")))) | |
490 | |
491 (efs-defun efs-allow-child-lookup vms (host user dir file) | |
492 ;; Returns t if FILE in directory DIR could possibly be a subdir | |
493 ;; according to its file-name syntax, and therefore a child listing should | |
494 ;; be attempted. | |
495 | |
496 ;; Subdirs in VMS can't have an extension (other than .DIR, which we | |
497 ;; have truncated). | |
498 (not (or (string-match "\\." file) | |
499 (and (boundp 'dired-local-variables-file) | |
500 (stringp dired-local-variables-file) | |
501 (string-equal dired-local-variables-file file))))) | |
502 | |
503 ;;; Tree dired support: | |
504 | |
505 ;; For this code I have borrowed liberally from Sebastian Kremer's | |
506 ;; dired-vms.el | |
507 | |
508 | |
509 ;; These regexps must be anchored to beginning of line. | |
510 ;; Beware that the ftpd may put the device in front of the filename. | |
511 | |
512 (defconst efs-dired-vms-re-exe | |
513 "^. [^ \t.]+\\.\\(EXE\\|exe\\)[; ]") | |
514 | |
515 (or (assq 'vms efs-dired-re-exe-alist) | |
516 (setq efs-dired-re-exe-alist | |
517 (cons (cons 'vms efs-dired-vms-re-exe) | |
518 efs-dired-re-exe-alist))) | |
519 | |
520 (defconst efs-dired-vms-re-dir | |
521 "^. [^ \t.]+\\.\\(DIR\\|dir\\)[; ]") | |
522 | |
523 (or (assq 'vms efs-dired-re-dir-alist) | |
524 (setq efs-dired-re-dir-alist | |
525 (cons (cons 'vms efs-dired-vms-re-dir) | |
526 efs-dired-re-dir-alist))) | |
527 | |
528 (efs-defun efs-dired-insert-headerline vms (dir) | |
529 ;; VMS inserts a headerline. I would prefer the headerline | |
530 ;; to be in efs format. This version tries to | |
531 ;; be careful, because we can't count on a headerline | |
532 ;; over ftp, and we wouldn't want to delete anything | |
533 ;; important. | |
534 (save-excursion | |
535 (if (looking-at "^ \\(list \\)?wildcard ") | |
536 (forward-line 1)) | |
537 ;; This is really aggressive. Too aggressive? | |
538 (let ((start (point))) | |
539 (skip-chars-forward " \t\n") | |
540 (if (looking-at efs-vms-filename-regexp) | |
541 (beginning-of-line) | |
542 (forward-line 1) | |
543 (skip-chars-forward " \t\n") | |
544 (beginning-of-line)) | |
545 (delete-region start (point))) | |
546 (insert " \n")) | |
547 (efs-real-dired-insert-headerline dir)) | |
548 | |
549 (efs-defun efs-dired-fixup-listing vms (file path &optional switches wildcard) | |
550 ;; Some vms machines list the entire path. Scrape this off. | |
551 (setq path (efs-fix-path | |
552 'vms | |
553 ;; Need the file-name-directory, in case of widcards. | |
554 ;; Note that path is a `local' path rel. the remote host. | |
555 ;; Lose on wildcards in parent dirs. Fix if somebody complains. | |
556 (let (file-name-handler-alist) | |
557 (file-name-directory path)))) | |
558 ;; Some machines put a Node name down too. | |
559 (let ((regexp (concat "^\\([_A-Za-z0-9][-_A-Za-z0-9]*\\$\\)?" | |
560 (regexp-quote path)))) | |
561 (goto-char (point-min)) | |
562 (while (re-search-forward regexp nil t) | |
563 (delete-region (match-beginning 0) (match-end 0)))) | |
564 ;; Now need to deal with continuation lines. | |
565 (goto-char (point-min)) | |
566 (let (col start end) | |
567 (while (re-search-forward | |
568 ";[0-9]+[ \t]*\\(\n[ \t]+\\)[^; \t\n]+[^\n;]*\n" nil t) | |
569 (setq start (match-beginning 1) | |
570 end (match-end 1)) | |
571 ;; guess at the column dimensions | |
572 (or col | |
573 (save-excursion | |
574 (goto-char (point-min)) | |
575 (if (re-search-forward | |
576 (concat efs-vms-filename-regexp | |
577 "[ \t]+[^ \t\n\r]") nil t) | |
578 (setq col (- (goto-char (match-end 0)) | |
579 (progn (beginning-of-line) (point)) | |
580 1)) | |
581 (setq col 0)))) | |
582 ;; join cont. lines. | |
583 (delete-region start end) | |
584 (goto-char start) | |
585 (insert-char ? (max (- col (current-column)) 2)))) | |
586 ;; Some vms dir listings put a triple null line before the total line. | |
587 (goto-char (point-min)) | |
588 (skip-chars-forward "\n") | |
589 (if (search-forward "\n\n\n" nil t) | |
590 (delete-char -1))) | |
591 | |
592 (efs-defun efs-dired-manual-move-to-filename vms | |
593 (&optional raise-error bol eol) | |
594 ;; In dired, move to first char of filename on this line. | |
595 ;; Returns position (point) or nil if no filename on this line. | |
596 ;; This is the VMS version. | |
597 (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) | |
598 (let (case-fold-search) | |
599 (if bol | |
600 (goto-char bol) | |
601 (skip-chars-backward "^\n\r")) | |
602 (if (re-search-forward efs-vms-filename-regexp eol t) | |
603 (goto-char (match-beginning 0)) | |
604 (and raise-error (error "No file on this line"))))) | |
605 | |
606 (efs-defun efs-dired-manual-move-to-end-of-filename vms | |
607 (&optional no-error bol eol) | |
608 ;; Assumes point is at beginning of filename. | |
609 ;; So, it should be called only after (dired-move-to-filename t). | |
610 ;; case-fold-search must be nil, at least for VMS. | |
611 ;; On failure, signals an error or returns nil. | |
612 ;; This is the VMS version. | |
613 (let ((opoint (point))) | |
614 (and selective-display | |
615 (null no-error) | |
616 (eq (char-after | |
617 (1- (or bol (save-excursion | |
618 (skip-chars-backward "^\r\n") | |
619 (point))))) | |
620 ?\r) | |
621 ;; File is hidden or omitted. | |
622 (cond | |
623 ((dired-subdir-hidden-p (dired-current-directory)) | |
624 (error | |
625 (substitute-command-keys | |
626 "File line is hidden. Type \\[dired-hide-subdir] to unhide."))) | |
627 ((error | |
628 (substitute-command-keys | |
629 "File line is omitted. Type \\[dired-omit-toggle] to un-omit." | |
630 ))))) | |
631 (skip-chars-forward "-_A-Za-z0-9$.;") | |
632 (if (or (= opoint (point)) (not (memq (following-char) '(?\ ?\t ?\n ?\r)))) | |
633 (if no-error | |
634 nil | |
635 (error "No file on this line")) | |
636 (point)))) | |
637 | |
638 (efs-defun efs-dired-ls-trim vms () | |
639 (goto-char (point-min)) | |
640 (let ((case-fold-search nil)) | |
641 (re-search-forward efs-vms-filename-regexp)) | |
642 (beginning-of-line) | |
643 (delete-region (point-min) (point)) | |
644 (forward-line 1) | |
645 (delete-region (point) (point-max))) | |
646 | |
647 (efs-defun efs-internal-file-name-sans-versions vms | |
648 (name &optional keep-backup-version) | |
649 (efs-save-match-data | |
650 (if (string-match ";[0-9]+$" name) | |
651 (substring name 0 (match-beginning 0)) | |
652 name))) | |
653 | |
654 (efs-defun efs-dired-collect-file-versions vms () | |
655 ;; If it looks like file FN has versions, return a list of the versions. | |
656 ;; That is a list of strings which are file names. | |
657 ;; The caller may want to flag some of these files for deletion. | |
658 (let ((completion-ignore-case (memq 'vms efs-case-insensitive-host-types)) | |
659 result) | |
660 (dired-map-dired-file-lines | |
661 (function | |
662 (lambda (fn) | |
663 (if (string-match ";[0-9]+$" fn) | |
664 (let* ((base-fn (substring fn 0 (match-beginning 0))) | |
665 (base-version (file-name-nondirectory | |
666 (substring fn 0 (1+ (match-beginning 0))))) | |
667 (bv-length (length base-version)) | |
668 (possibilities (and | |
669 (null (assoc base-fn result)) | |
670 (file-name-all-completions | |
671 base-version | |
672 (file-name-directory fn))))) | |
673 (if possibilities | |
674 (setq result | |
675 (cons (cons base-fn | |
676 ;; code this explicitly | |
677 ;; using backup-extract-version has a | |
678 ;; lot of function-call overhead. | |
679 (mapcar (function | |
680 (lambda (fn) | |
681 (string-to-int | |
682 (substring fn bv-length)))) | |
683 possibilities)) result)))))))) | |
684 result)) | |
685 | |
686 (efs-defun efs-dired-flag-backup-files vms (&optional unflag-p) | |
687 (interactive "P") | |
688 (let ((dired-kept-versions 1) | |
689 (kept-old-versions 0) | |
690 marker msg) | |
691 (if unflag-p | |
692 (setq marker ?\040 msg "Unflagging old versions") | |
693 (setq marker dired-del-marker msg "Purging old versions")) | |
694 (dired-clean-directory 1 marker msg))) | |
695 | |
696 (efs-defun efs-internal-diff-latest-backup-file vms (fn) | |
697 ;; For FILE;#, returns the filename FILE;N, where N | |
698 ;; is the largest number less than #, for which this file exists. | |
699 ;; Returns nil if none found. | |
700 (efs-save-match-data | |
701 (and (string-match ";[0-9]+$" fn) | |
702 (let ((base (substring fn 0 (1+ (match-beginning 0)))) | |
703 (num (1- (string-to-int (substring fn | |
704 (1+ (match-beginning 0)))))) | |
705 found file) | |
706 (while (and (setq found (> num 0)) | |
707 (not (file-exists-p | |
708 (setq file | |
709 (concat base (int-to-string num)))))) | |
710 (setq num (1- num))) | |
711 (and found file))))) | |
712 | |
713 ;;;;-------------------------------------------------------------- | |
714 ;;;; Support for VMS DIR/FULL listings. (listing type vms:full) | |
715 ;;;;-------------------------------------------------------------- | |
716 | |
717 (efs-defun efs-parse-listing vms:full | |
718 (host user dir path &optional switches) | |
719 ;; Parse the current buffer which is assumed to be a VMS FTP dir | |
720 ;; format, and return a hashtable as the result. SWITCHES are never used, | |
721 ;; but they must be specified in the argument list for compatibility | |
722 ;; with the unix version of this function. | |
723 ;; HOST = remote host name | |
724 ;; USER = user name | |
725 ;; DIR = directory in as a full remote path | |
726 ;; PATH = directory in full efs path syntax | |
727 ;; SWITCHES = ls switches (not relevant here) | |
728 (goto-char (point-min)) | |
729 (efs-save-match-data | |
730 (efs-parse-vms-listing))) | |
731 | |
732 ;;; Tree Dired | |
733 | |
734 (or (assq 'vms:full efs-dired-re-exe-alist) | |
735 (setq efs-dired-re-exe-alist | |
736 (cons (cons 'vms:full efs-dired-vms-re-exe) | |
737 efs-dired-re-exe-alist))) | |
738 | |
739 (or (assq 'vms:full efs-dired-re-dir-alist) | |
740 (setq efs-dired-re-dir-alist | |
741 (cons (cons 'vms:full efs-dired-vms-re-dir) | |
742 efs-dired-re-dir-alist))) | |
743 | |
744 (efs-defun efs-dired-insert-headerline vms:full (dir) | |
745 ;; Insert a blank line for aesthetics. | |
746 (insert " \n") | |
747 (forward-char -2) | |
748 (efs-real-dired-insert-headerline dir)) | |
749 | |
750 (efs-defun efs-dired-manual-move-to-filename vms:full | |
751 (&optional raise-error bol eol) | |
752 (let ((efs-dired-listing-type 'vms)) | |
753 (efs-dired-manual-move-to-filename raise-error bol eol))) | |
754 | |
755 (efs-defun efs-dired-manual-move-to-end-of-filename vms:full | |
756 (&optional no-error bol eol) | |
757 (let ((efs-dired-listing-type 'vms)) | |
758 (efs-dired-manual-move-to-end-of-filename no-error bol eol))) | |
759 | |
760 ;;; end of efs-vms.el |