;; -*-Emacs-Lisp-*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; File: efs-mvs.el ;; Release: $efs release: 1.24 $ ;; Version: #Revision: 1.4 $ ;; RCS: ;; Description: MVS support for efs ;; Author: Sandy Rutherford ;; Created: Sat Nov 14 02:04:54 1992 ;; Modified: Sun Nov 27 18:37:54 1994 by sandy on gandalf ;; Language: Emacs-Lisp ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; This file is part of efs. See efs.el for copyright ;;; (it's copylefted) and warranty (there isn't one) information. ;;; -------------------------------------------------------- ;;; MVS support ;;; -------------------------------------------------------- (provide 'efs-mvs) (require 'efs) (defconst efs-mvs-version (concat (substring "$efs release: 1.24 $" 14 -2) "/" (substring "#Revision: 1.4 $" 11 -2))) ;; What's the MVS character set for valid partitioned data sets? ;; I'll guess [-A-Z0-9_$+] ;; The top level directory in MVS contains partitioned data sets. ;; We will view these as directories. The data sets within each ;; partitioned data set will be viewed as files. ;; ;; In MVS an entry for a "sub-dir" may have the same name as a plain ;; file. This is impossible in unix, so we retain the "dots" at the ;; end of subdir names, to distinuguish. ;; i.e. FOO.BAR --> /FOO./BAR (efs-defun efs-send-pwd mvs (host user &optional xpwd) ;; Broken quoting for PWD output on some MVS servers. (let* ((result (efs-send-cmd host user '(pwd) "Getting EXPLORER PWD")) (line (nth 1 result)) dir) (and (car result) (efs-save-match-data (and (string-match " \"'?\\([0-9A-Z]+\\)'?\"" line) (setq dir (substring line (match-beginning 1) (match-end 1)))))) (cons dir line))) (efs-defun efs-fix-path mvs (path &optional reverse) ;; Convert PATH from UNIX-ish to MVS. (efs-save-match-data (if reverse (let ((start 0) (res "/")) ;; MVS has only files, some of which are partitioned ;; into smaller files (partitioned data sets). We will ;; assume that path starts with a partitioned dataset. (while (string-match "\\." path) ;; grab the dot too, because in mvs prefixes and plain ;; files can have the same name. (setq res (concat res (substring path start (match-end 0)) "/") start (match-end 0))) (concat res (substring path start))) (let ((start 1) res) (while (string-match "/" path start) (setq res (concat res (substring path start (match-beginning 0))) start (match-end 0))) (concat res (substring path start)))))) (efs-defun efs-fix-dir-path mvs (dir-path) ;; Convert path from UNIX-ish to MVS for a DIR listing. (cond ((string-equal "/" dir-path) " ") (t (concat (efs-fix-path 'mvs dir-path) "*")))) (efs-defun efs-allow-child-lookup mvs (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. ;; MVS file system is flat. Only partitioned data sets are "subdirs". (efs-save-match-data (string-match "\\.$" file))) (efs-defun efs-parse-listing mvs (host user dir path &optional switches) ;; Guesses the type of mvs listings. (efs-save-match-data (goto-char (point-min)) (cond ((looking-at "Volume ") (efs-add-listing-type 'mvs:tcp host user) (efs-parse-listing 'mvs:tcp host user dir path switches)) ((looking-at "[-A-Z0-9_$.+]+ ") (efs-add-listing-type 'mvs:nih host user) (efs-parse-listing 'mvs:nih host user dir path switches)) (t ;; Since MVS works on a template system, return an empty hashtable. (let ((tbl (efs-make-hashtable))) (efs-put-hash-entry "." '(t) tbl) (efs-put-hash-entry ".." '(t) tbl) tbl))))) (efs-defun efs-ls-dumb-check mvs (line host file path lsargs msg noparse noerror nowait cont) ;; Because of the template structure of the MVS file system, empty ;; directories are the same as non-existent. It's better for us to treat ;; them as empty. (and (string-match "^550 " line) (let ((parse (or (null noparse) (eq noparse 'parse) (efs-parsable-switches-p lsargs t)))) (efs-add-to-ls-cache file lsargs "\n" parse) (if parse (efs-set-files file (let ((tbl (efs-make-hashtable))) (efs-put-hash-entry "." '(t) tbl) (efs-put-hash-entry ".." '(t) tbl) tbl))) (if nowait (progn (if cont (efs-call-cont cont "\n")) t) (if cont (efs-call-cont cont "\n")) "\n")))) ;;;; ---------------------------------------------------- ;;;; Support for the NIH FTP server. ;;;; ---------------------------------------------------- (efs-defun efs-parse-listing mvs:nih (host user dir path &optional switches) ;; Parse the current buffer which is assumed to be an MVS listing ;; Based on the listing format of the NIH server. Hope that this format ;; is widespread. If a directory doesn't exist, get a 426 ftp error. ;; HOST = remote host name ;; USER = user name ;; DIR = directory as a remote full path ;; PATH = directory in full efs-syntax (let ((tbl (efs-make-hashtable)) (top-p (string-equal "/" dir)) ;; assume that everything top-level is a partitioned data set ) (goto-char (point-min)) (efs-save-match-data (while (re-search-forward "^[-A-Z0-9_$.+]+" nil t) (efs-put-hash-entry (concat (buffer-substring (match-beginning 0) (match-end 0)) (and top-p ".")) (list top-p) tbl) (forward-line 1)) (efs-put-hash-entry "." '(t) tbl) (or top-p (efs-put-hash-entry ".." '(t) tbl))) tbl)) ;;; Tree dired support (defconst efs-dired-mvs-re-exe "^. [-A-Z0-9_$+]+\\.EXE " "Regular expression to use to search for MVS executables.") (or (assq 'mvs:nih efs-dired-re-exe-alist) (setq efs-dired-re-exe-alist (cons (cons 'mvs:nih efs-dired-mvs-re-exe) efs-dired-re-exe-alist))) (efs-defun efs-dired-insert-headerline mvs:nih (dir) ;; MVS 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 mvs:nih (&optional raise-error bol eol) ;; In dired, move to the first char of the filename on this line. ;; This is the MVS 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))) ;; MVS listings are pretty loose. Tough to tell when we've got a file line. (if (and (> (- eol bol) 2) (progn (forward-char 2) (skip-chars-forward " \t") (looking-at "[-A-Z0-9$_.+]+[ \n\r]"))) (point) (goto-char bol) (and raise-error (error "No file on this line"))))) (efs-defun efs-dired-manual-move-to-end-of-filename mvs:nih (&optional no-error bol eol) ;; Assumes point is at the 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 MVS 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$_.+" eol) (if (or (= opoint (point)) (not (memq (char-after (point)) '(?\n ?\r ?\ )))) (if no-error nil (error "No file on this line")) (point)))) (efs-defun efs-dired-get-filename mvs:nih (&optional localp no-error-if-not-filep) (let ((name (efs-real-dired-get-filename localp no-error-if-not-filep)) (parsed (efs-ftp-path (dired-current-directory)))) (if (and name (string-equal "/" (nth 2 parsed))) (concat name ".") name))) (efs-defun efs-dired-fixup-listing mvs:nih (file path &optional switches wildcard) ;; MVS listings have trailing spaces to 80 columns. ;; Can lead to a mess after indentation. (goto-char (point-min)) (while (re-search-forward " +$" nil t) (replace-match ""))) ;;;; ------------------------------------------------------- ;;;; Support for the TCPFTP MVS server ;;;; ------------------------------------------------------- ;;; ;;; For TCPFTP IBM MVS V2R2.1 Does it really work? (efs-defun efs-parse-listing mvs:tcp (host user dir path &optional switches) ;; Parse the current buffer which is assumed to be an MVS listing ;; Based on the listing format of the NIH server. Hope that this format ;; is widespread. If a directory doesn't exist, get a 426 ftp error. ;; HOST = remote host name ;; USER = user name ;; DIR = directory as a remote full path ;; PATH = directory in full efs-syntax (efs-save-match-data (goto-char (point-min)) (and (looking-at "Volume ") (let ((top-tbl (efs-make-hashtable)) (case-fold (memq 'mvs efs-case-insensitive-host-types)) tbl-list file dn fn tbl dir-p) (forward-line 1) (while (not (eobp)) (end-of-line) (setq file (buffer-substring (point) (progn (skip-chars-backward "^ ") (point))) dn path dir-p (string-match "\\." file)) (efs-put-hash-entry file '(nil) top-tbl) (if dir-p (progn (setq dir-p (1+ dir-p) fn (substring file 0 dir-p)) (efs-put-hash-entry fn '(t) top-tbl) (while dir-p (setq dn (efs-internal-file-name-as-directory nil (concat dn fn)) file (substring file dir-p) tbl (cdr (assoc dn tbl-list))) (or tbl (setq tbl (efs-make-hashtable) tbl-list (cons (cons dn tbl) tbl-list))) (efs-put-hash-entry file '(nil) tbl) (setq dir-p (string-match "\\." file)) (if dir-p (progn (setq dir-p (1+ dir-p) fn (substring file 0 dir-p)) (efs-put-hash-entry fn '(t) tbl)))))) (forward-line 1)) (while tbl-list (efs-put-hash-entry (car (car tbl-list)) (cdr (car tbl-list)) efs-files-hashtable case-fold) (setq tbl-list (cdr tbl-list))) top-tbl)))) ;;; Tree Dired (efs-defun efs-dired-manual-move-to-filename mvs:tcp (&optional raise-error bol eol) ;; In dired, move to the first char of the filename on this line. ;; This is the MVS 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 (and (re-search-forward " [0-9][0-9]/[0-9][0-9]/[0-9][0-9] " eol t) (progn (goto-char eol) (skip-chars-backward "-A-Z0-9$_.") (char-equal (char-before (point)) ?\ )) (/= eol (point))) (point) (goto-char bol) (and raise-error (error "No file on this line"))))) (efs-defun efs-dired-manual-move-to-end-of-filename mvs:tcp (&optional no-error bol eol) ;; Assumes point is at the 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 MVS 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$_.+" eol) (if (or (= opoint (point)) (not (memq (char-after (point)) '(?\n ?\r ?\ )))) (if no-error nil (error "No file on this line")) (point)))) ;;; end of efs-mvs.el