diff lisp/efs/efs-cms.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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/efs/efs-cms.el	Mon Aug 13 08:50:29 2007 +0200
@@ -0,0 +1,462 @@
+;; -*-Emacs-Lisp-*-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; File:         efs-cms.el
+;; Release:      $efs release: 1.15 $
+;; Version:      $Revision: 1.1 $
+;; RCS:          
+;; Description:  CMS support for efs
+;; Author:       Sandy Rutherford <sandy@ibm550.sissa.it>
+;; Created:      Fri Oct 23 08:52:00 1992
+;; Modified:     Sun Nov 27 11:46:51 1994 by sandy on gandalf
+;; Language:     Emacs-Lisp
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; This file is part of efs. See efs.el for copyright
+;;; (it's copylefted) and warrranty (there isn't one) information.
+
+(provide 'efs-cms)
+(require 'efs)
+
+(defconst efs-cms-version
+  (concat (substring "$efs release: 1.15 $" 14 -2)
+	  "/"
+	  (substring "$Revision: 1.1 $" 11 -2)))
+
+;;;; ------------------------------------------------------------
+;;;; CMS support
+;;;; ------------------------------------------------------------
+
+;;; efs has full support, including tree dired support, for hosts running
+;;; CMS.  It should be able to automatically recognize any CMS machine.
+;;; We would be grateful if you would report any failures to automatically
+;;; recognize a CMS host as a bug.
+;;; 
+;;; This should also work with CMS machines running SFS (Shared File System).
+;;; 
+;;; Filename syntax:
+;;;
+;;; CMS filenames are entered in a UNIX-y way. In otherwords, minidisks are
+;;; treated as UNIX directories. For example to access the file READ.ME in
+;;; minidisk *.311 on cuvmb.cc.columbia.edu, you would enter
+;;;   /anonymous@cuvmb.cc.columbia.edu:/*.311/READ.ME
+;;; If *.301 is the default minidisk for this account, you could access
+;;; FOO.BAR on this minidisk as
+;;;   /anonymous@cuvmb.cc.columbia.edu:FOO.BAR
+;;; CMS filenames are of the form FILE.TYPE, where both FILE and TYPE can be
+;;; up to 8 characters. Again, beware that CMS filenames are always upper
+;;; case, and hence must be entered as such.
+;;;
+;;; Tips:
+;;; 1. CMS machines, with the exception of anonymous accounts, nearly always
+;;;    need an account password. To have efs send an account password,
+;;;    you can either include it in your .netrc file, or use
+;;;    efs-set-account.
+;;; 2. efs-set-account can be used to set account passwords for specific
+;;;    minidisks. This is usually used to optain write access to the minidisk.
+;;;    As well you can put tokens of the form
+;;;    minidisk <minidisk name> <password> in your .netrc file. There can be
+;;;    as many minidisk tokens as you like, however they should follow all
+;;;    other tokens for a given machine entry. Of course, ordinary ftp
+;;;    will not understand these entries in your .netrc file.
+;;;
+
+
+;;; Since CMS doesn't have any full pathname syntax, we have to fudge
+;;; things with cd's. We actually send too many cd's, but is dangerous
+;;; to try to remember the current minidisk, because if the connection
+;;; is closed and needs to be reopened, we will find ourselves back in
+;;; the default minidisk. This is fairly likely since CMS ftp servers
+;;; usually close the connection after 5 minutes of inactivity.
+
+;;; Have I got the filename character set right?
+
+;;; The following three functions are entry points to this file.
+;;; They have been added to the appropriate alists in efs.el
+
+(efs-defun efs-fix-path cms (path &optional reverse)
+  ;; Convert PATH from UNIX-ish to CMS. If REVERSE is given, convert
+  ;; from CMS to UNIX. Actually, CMS doesn't have a full pathname syntax,
+  ;; so we fudge things by sending cd's.
+  (efs-save-match-data
+    (if reverse
+	(if (string-match ":" path)
+	    ;; It's SFS
+	    (let* ((start (match-end 0))
+		   (return (concat "/" (substring path 0 start))))
+	      (while (string-match "\\." path start)
+		(setq return (concat return "/"
+				     (substring path start
+						(match-beginning 0)))
+		      start (match-end 0)))
+	      (concat return "/" (substring path start)))
+	  ;; Since we only convert output from a pwd in this direction,
+	  ;; we'll assume that it's a minidisk, and make it into a
+	  ;; directory file name. Note that the expand-dir-hashtable
+	  ;; stores directories without the trailing /.
+	  (if (char-equal (string-to-char path) ?/)
+	      path
+	    (concat "/" path)))
+      (if (let ((case-fold-search t))
+	    (string-match
+	     (concat
+	      "^/\\([-A-Z0-9$*._+:]+\\)/"
+	      ;; In case there is a SFS
+	      "\\(\\([-A-Z0-9$*._+]+\\)/\\([-A-Z0-9$*._+]+/\\)?\\)?"
+	      "\\([-A-Z0-9$._+]+\\)$")
+	     path))
+	  (let ((minidisk (substring path 1 (match-end 1)))
+		(sfs (and (match-beginning 2)
+			  (substring path (match-beginning 3)
+				     (match-end 3))))
+		(file (substring path (match-beginning 5) (match-end 5)))
+		account)
+	    (and sfs (match-beginning 4)
+		 (setq sfs (concat sfs "." (substring path (match-beginning 4)
+						      (1- (match-end 4))))))
+	    (unwind-protect
+		(progn
+		  (or sfs
+		      (setq account
+			    (efs-get-account host user minidisk)))
+		  (efs-raw-send-cd host user (if sfs
+						 (concat minidisk sfs ".")
+					       minidisk))
+		  (if account
+		      (efs-cms-send-minidisk-acct
+		       host user minidisk account)))
+	      (if account (fillarray account 0)))
+	    file)
+	(error "Invalid CMS filename")))))
+
+(efs-defun efs-fix-dir-path cms (dir-path)
+  ;; Convert path from UNIX-ish to VMS ready for a DIRectory listing.
+  (efs-save-match-data
+    (cond
+     ((string-equal "/" dir-path)
+      (error "Cannot get listing for CMS \"/\" directory."))
+     ((let ((case-fold-search t))
+	(string-match
+	 (concat "^/\\([-A-Z0-9$*._+:]+\\)/"
+		 "\\(\\([-A-Z0-9$*._+]+\\)/\\([-A-Z0-9$*._+]+/\\)?\\)?"
+		 "\\([-A-Z0-9$*_.+]+\\)?$") dir-path))
+      (let ((minidisk (substring dir-path (match-beginning 1) (match-end 1)))
+	    (sfs (and (match-beginning 2)
+		      (concat
+		       (substring dir-path (match-beginning 3)
+				  (match-end 3)))))
+	    (file (if (match-beginning 5)
+		      (substring dir-path (match-beginning 5) (match-end 5))
+		    "*"))
+	    account)
+	(and sfs (match-beginning 4)
+	     (setq sfs (concat sfs "." (substring dir-path
+						  (match-beginning 4)
+						  (1- (match-end 4))))))
+	(unwind-protect
+	    (progn
+	      (or sfs
+		  (setq account (efs-get-account host user minidisk)))
+	      (efs-raw-send-cd host user (if sfs
+					     (concat minidisk sfs ".")
+					     minidisk))
+	      (if account
+		  (efs-cms-send-minidisk-acct host user minidisk account)))
+	  (if account (fillarray account 0)))
+	file))
+     (t (error "Invalid CMS pathname")))))
+
+(defconst efs-cms-file-line-regexp
+  (concat
+   "\\([-A-Z0-9$_+]+\\) +"
+   "\\(\\(\\([-A-Z0-9$_+]+\\) +[VF] +[0-9]+ \\)\\|\\(DIR +- \\)\\)"))
+
+(efs-defun efs-parse-listing cms
+  (host user dir path &optional switches)
+  ;; Parse the current buffer which is assumed to be a CMS directory listing.
+  ;; HOST = remote host name
+  ;; USER = remote user name
+  ;; DIR = directory as a full remote path
+  ;; PATH = directory as a full efs-path
+  (let ((tbl (efs-make-hashtable))
+	fn dir-p)
+    (goto-char (point-min))
+    (efs-save-match-data
+      (while (re-search-forward efs-cms-file-line-regexp nil t)
+	(if (match-beginning 3)
+	    (setq fn (concat (buffer-substring
+			      (match-beginning 1) (match-end 1))
+			     "."
+			     (buffer-substring
+			      (match-beginning 4) (match-end 4)))
+		  dir-p nil)
+	  (setq fn (buffer-substring (match-beginning 1) (match-end 1))
+		dir-p t))
+	(efs-put-hash-entry fn (list dir-p) tbl)
+	(forward-line 1))
+      (efs-put-hash-entry "." '(t) tbl)
+      (efs-put-hash-entry ".." '(t) tbl))
+    tbl))
+
+(defun efs-cms-send-minidisk-acct (host user minidisk account
+					&optional noretry)
+  "For HOST and USER, send the account password ACCOUNT. If MINIDISK is given,
+the account password is for that minidisk. If PROC is given, send to that
+process, rathr than use HOST and USER to look up the process."
+  (efs-save-match-data
+    (let ((result (efs-raw-send-cmd
+		   (efs-get-process host user)
+		   (concat "quote acct " account))))
+      (cond
+       ((eq (car result) 'failed)
+	(setq account nil)
+	(unwind-protect
+	    (progn
+	      (setq
+	       account
+	       (read-passwd
+		(format
+		 "Invalid acct. password for %s on %s@%s. Try again: "
+		 minidisk user host)))
+	      (if (string-equal "" account)
+		  (setq account nil)))
+	  ;; This guarantees that an interrupt will clear the account
+	  ;; password.
+	  (efs-set-account host user minidisk account))
+	(if account ; give the user another chance
+	    (efs-cms-send-minidisk-acct host user minidisk account)))
+       ((eq (car result) 'fatal)
+	(if noretry
+	    ;; give up
+	    (efs-error host user
+		       (concat "ACCOUNT password failed: " (nth 1 result)))
+	  ;; try once more
+	  (efs-cms-send-minidisk-acct host user minidisk account t))))
+      ;; return result
+      result)))
+
+(efs-defun efs-write-recover cms
+  (line cont-lines host user cmd msg pre-cont cont nowait noretry)
+  ;; If a write fails because of insufficient privileges, give the user a
+  ;; chance to send an account password.
+  (let ((cmd0 (car cmd))
+	(cmd1 (nth 1 cmd))
+	(cmd2 (nth 2 cmd)))
+    (efs-save-match-data
+      (if (and (or (memq cmd0 '(append put rename))
+		   (and (eq cmd0 'quote) (eq cmd1 'stor)))
+	       (string-match "^/\\([-A-Z0-9$*._+]+\\)/[-A-Z0-9$*._+]+$" cmd2))
+	  (let ((minidisk (substring cmd2 (match-beginning 1) (match-end 1)))
+		account retry)
+	    (unwind-protect
+		(progn
+		  (setq account
+			(read-passwd
+			 (format "Account password for minidisk %s on %s@%s: "
+				 minidisk user host)))
+		  (if (string-equal account "")
+		      (setq account nil)))
+	      (efs-set-account host user minidisk account))
+	    (if account
+		(progn
+		  (efs-cms-send-minidisk-acct host user minidisk account)
+		  (setq retry
+			(efs-send-cmd host user cmd msg pre-cont cont
+				      nowait noretry))
+		  (and (null (or cont nowait)) retry))
+	      (if cont
+		  (progn
+		    (efs-call-cont cont 'failed line cont-lines)
+		    nil)
+		(and (null nowait) (list 'failed line cont-lines)))))
+	(if cont
+	    (progn
+	      (efs-call-cont cont 'failed line cont-lines)
+	      nil)
+	  (and (null nowait) (list 'failed line cont-lines)))))))
+
+(efs-defun efs-allow-child-lookup cms (host user dir file)
+  ;; Returns t if FILE in directory DIR could possibly be a subdir
+  ;; according to its file-name syntax, and therefore a child listing should
+  ;; be attempted.
+  
+  ;; CMS file system is flat. Only minidisks are "subdirs".
+  (or (string-equal "/" dir)
+      (efs-save-match-data
+	(string-match "^/[^/:]+:/$" dir))))
+
+;;; Sorting listings
+
+(defconst efs-cms-date-and-time-regexp
+  (concat
+   " \\(1?[0-9]\\)/\\([0-3][0-9]\\)/\\([0-9][0-9]\\) +"
+   "\\([12]?[0-9]\\):\\([0-5][0-9]\\):\\([0-5][0-9]\\) "))
+
+(efs-defun efs-t-converter cms (&optional regexp reverse)
+  (if regexp
+      nil
+    (goto-char (point-min))
+    (efs-save-match-data
+      (if (re-search-forward efs-cms-date-and-time-regexp nil t)
+	  (let (list-start list bol nbol)
+	    (beginning-of-line)
+	    (setq list-start (point))
+	    (while (progn
+		     (setq bol (point))
+		     (re-search-forward efs-cms-date-and-time-regexp
+					(setq nbol (save-excursion
+						     (forward-line 1) (point)))
+					t))
+	      (setq list
+		    (cons
+		     (cons
+		      (list (string-to-int (buffer-substring
+					    (match-beginning 3)
+					    (match-end 3))) ; year
+			    (string-to-int (buffer-substring
+					    (match-beginning 1)
+					    (match-end 1))) ; month
+			    (string-to-int (buffer-substring
+					    (match-beginning 2)
+					    (match-end 2))) ; day
+			    (string-to-int (buffer-substring
+					    (match-beginning 4)
+					    (match-end 4))) ; hour
+			    (string-to-int (buffer-substring
+					    (match-beginning 5)
+					    (match-end 5))) ; minutes
+			    (string-to-int (buffer-substring
+					    (match-beginning 6)
+					    (match-end 6)))) ; seconds
+		      (buffer-substring bol nbol))
+		     list))
+	      (goto-char nbol))
+	    (if list
+		(progn
+		  (setq list
+			(mapcar 'cdr
+				(sort list 'efs-cms-t-converter-sort-pred)))
+		  (if reverse (setq list (nreverse list)))
+		  (delete-region list-start (point))
+		  (apply 'insert list)))
+	    t)))))
+
+(defun efs-cms-t-converter-sort-pred (elt1 elt2)
+  (let* ((data1 (car elt1))
+	 (data2 (car elt2))
+	 (year1 (car data1))
+	 (year2 (car data2))
+	 (month1 (nth 1 data1))
+	 (month2 (nth 1 data2))
+	 (day1 (nth 2 data1))
+	 (day2 (nth 2 data2))
+	 (hour1 (nth 3 data1))
+	 (hour2 (nth 3 data2))
+	 (minute1 (nth 4 data1))
+	 (minute2 (nth 4 data2))
+	 (second1 (nth 5 data1))
+	 (second2 (nth 5 data2)))
+    (or (> year1 year2)
+	(and (= year1 year2)
+	     (or (> month1 month2)
+		 (and (= month1 month2)
+		      (or (> day1 day2)
+			  (and (= day1 day2)
+			       (or (> hour1 hour2)
+				   (and (= hour1 hour2)
+					(or (> minute1 minute2)
+					    (and (= minute1 minute2)
+						 (or (> (nth 5 data1)
+							(nth 5 data2)))
+						 ))))))))))))
+
+
+;;; Tree dired support:
+
+(defconst efs-dired-cms-re-exe "^. [-A-Z0-9$_+]+ +EXEC ")
+
+(or (assq 'cms efs-dired-re-exe-alist)
+    (setq efs-dired-re-exe-alist
+	  (cons (cons 'cms  efs-dired-cms-re-exe)
+		efs-dired-re-exe-alist)))
+
+(defconst efs-dired-cms-re-dir "^. [-A-Z0-9$_+]+ +DIR ")
+
+(or (assq 'cms efs-dired-re-dir-alist)
+    (setq efs-dired-re-dir-alist
+	  (cons (cons 'cms  efs-dired-cms-re-dir)
+		efs-dired-re-dir-alist)))
+
+(efs-defun efs-dired-insert-headerline cms (dir)
+  ;; CMS has no total line, so we insert a blank line for
+  ;; aesthetics.
+  (insert "\n")
+  (forward-char -1)
+  (efs-real-dired-insert-headerline dir))
+
+(efs-defun efs-dired-manual-move-to-filename cms
+  (&optional raise-error bol eol)
+  ;; In dired, move to the first char of filename on this line.
+  ;; This is the CMS version.
+  (or eol (setq eol (save-excursion (skip-chars-forward "^\n\r") (point))))
+  (let (case-fold-search)
+    (if bol
+	(goto-char bol)
+      (skip-chars-backward "^\n\r")
+      (setq bol (point)))
+    (if (re-search-forward efs-cms-file-line-regexp eol t)
+	(goto-char (match-beginning 0))
+      (goto-char bol)
+      (and raise-error (error "No file on this line")))))
+
+(efs-defun efs-dired-manual-move-to-end-of-filename cms
+  (&optional no-error bol eol)
+  ;; Assumes point is at beginning of filename.
+  ;; So, it should be called only after (dired-move-to-filename t).
+  ;; case-fold-search must be nil, at least for VMS.
+  ;; On failure, signals an error or returns nil.
+  ;; This is the CMS version.
+  (let ((opoint (point)))
+    (and selective-display
+	 (null no-error)
+	 (eq (char-after
+	      (1- (or bol (save-excursion
+			    (skip-chars-backward "^\r\n")
+			    (point)))))
+	     ?\r)
+	 ;; File is hidden or omitted.
+	 (cond
+	  ((dired-subdir-hidden-p (dired-current-directory))
+	   (error
+	    (substitute-command-keys
+	     "File line is hidden. Type \\[dired-hide-subdir] to unhide.")))
+	  ((error
+	    (substitute-command-keys
+	     "File line is omitted. Type \\[dired-omit-toggle] to un-omit."
+	     )))))
+    (skip-chars-forward "-A-Z0-9$_+")
+    (or (looking-at " +DIR ")
+	(progn
+	  (skip-chars-forward " ")
+	  (skip-chars-forward "-A-Z0-9$_+")))
+    (if (or (= opoint (point)) (/= (following-char) ?\ ))
+	(if no-error
+	    nil
+	  (error "No file on this line"))
+      (point))))
+
+(efs-defun efs-dired-make-filename-string cms (filename &optional reverse)
+  (if reverse
+      (if (string-match "\\." filename)
+	  ;; Can't count on the number of blanks between the base and the
+	  ;; extension, so ignore the extension.
+	  (substring filename 0 (match-beginning 0))
+	filename)
+    (if (string-match "^\\([^ ]+\\) +\\([^ ]+\\)$" filename)
+	(concat (substring filename 0 (match-end 1))
+		"."
+		(substring filename (match-beginning 2) (match-end 2)))
+      filename)))
+
+;;; end of efs-cms.el