Mercurial > hg > xemacs-beta
diff lisp/efs/efs-pc.el @ 98:0d2f883870bc r20-1b1
Import from CVS: tag r20-1b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:13:56 +0200 |
parents | 8fc7fe29b841 |
children | 7e54bd776075 8619ce7e4c50 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/efs/efs-pc.el Mon Aug 13 09:13:56 2007 +0200 @@ -0,0 +1,980 @@ +;; -*-Emacs-Lisp-*- +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; File: efs-pc.el +;; Release: $efs release: 1.15 $ +;; Version: $Revision: 1.1 $ +;; RCS: +;; Description: PC support for efs +;; Author: Sandy Rutherford <sandy@tsmi19.sissa.it> +;; Created: Thu Mar 18 13:06:25 1993 +;; Modified: Sun Nov 27 18:40:46 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. + +;;; Thanks to jrs@world.std.com (Rick Sladkey) for providing support for +;;; the Frontier Technologies Super-TCP server + +;;; Many thanks to the following people for beta testing: +;;; Mike Northam <mbn@hfglobe.intel.com> +;;; bagman@austin.ibm.com (Doug Bagley) +;;; Jens Petersen <J.Petersen@qmw.ac.uk> +;;; Jeff Morgenthaler <jpmorgen@wisp4.physics.wisc.edu> + +(provide 'efs-pc) +(require 'efs) + +(defconst efs-pc-version + (concat (substring "$efs release: 1.15 $" 14 -2) + "/" + (substring "$Revision: 1.1 $" 11 -2))) + +;;;----------------------------------------------------------------- +;;; PC support for efs +;;;----------------------------------------------------------------- + +;;; Works for the DOS FTP servers: +;;; Novell LAN WorkPlace v4.01 (NetWare & EXOS) +;;; PC/TCP Version 2.05 pl2 FTP Server by FTP Software +;;; Microsoft FTP Server service (beta 2) +;;; NCSA DOS ftp server. +;;; Frontier Technologies super tcp server (runs under MS WINDOWS) +;;; Alun's Windows FTP daemon for Winsock, v1.8b +;;; +;;; Works for IBM OS/2 TCP/IP FTP Version 1.2 + +;;; Currently support for all of the above FTP servers are in this file. +;;; Should they live in separate files? + +;;; host and listing type hierarchy in this file +;;; +;;; dos: dos:novell, dos:ftp, dos:ncsa, dos:microsoft, dos:stcp, dos:winsock +;;; os2: + +;;; DOS and OS/2 have slightly different filename syntaxes. +;;; +;;; DOS only allows at most one extension (".") per filename. +;;; A directory name usually has the extension ".DIR" implicit, but +;;; it seems that other extensions can be used. +;;; +;;; OS/2 running the FAT file system uses the same 8.3 format for +;;; filenames as DOS, except that extensions are allowed in directory names. +;;; OS/2 running the HPFS (high performance file system allows an arbitrary +;;; number of extensions in a filename. +;;; Mostly these differences are unimportant here, except in the dos +;;; definition of efs-allow-child-lookup. + +;;;; ---------------------------------------------------- +;;;; Utility functions and macros +;;;; ---------------------------------------------------- + +(defun efs-fix-pc-path (path &optional reverse) + ;; Convert PATH from UNIX-ish to DOS or OS/2. + ;; If REVERSE do just that. + (efs-save-match-data + (if reverse + (let ((n 0) + len res) + (if (string-match "^[a-zA-Z0-9]:" path) + ;; there's a disk + (setq res (concat "\\" path)) + (setq res (copy-sequence path))) + (setq len (length res)) + (while (< n len) + (and (= (aref res n) ?\\ ) (aset res n ?/)) + (setq n (1+ n))) + res) + (let ((n 0) + len res) + (if (string-match "^/[a-zA-Z0-9]:" path) + (setq res (substring path 1)) + (setq res (copy-sequence path))) + (setq len (length res)) + (while (< n len) + (and (= (aref res n) ?/) (aset res n ?\\ )) + (setq n (1+ n))) + res)))) + +(defmacro efs-dired-pc-move-to-end-of-filename (&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) + ;; On failure signals an error, or returns nil. + ;; This is the DOS and OS/2 version. It is common to all of the PC ftp + ;; servers since it depends only on the file name character set. + (` + (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-zA-Z0-9.$~") + (if (= opoint (point)) + (if (, no-error) + nil + (error "No file on this line")) + (point))))) + +(defun efs-dired-pc-insert-headerline (dir) + ;; Insert a blank line for aesthetics. + (insert " \n") + (forward-char -2) + (efs-real-dired-insert-headerline dir)) + + +;;;;----------------------------------------------------------- +;;;; General DOS support +;;;;----------------------------------------------------------- + +;;; Regexps to be used for host and listing-type identification. + +(defconst efs-dos:ftp-file-line-regexp + (concat + " *\\([0-9]+\\|<dir>\\) +\\([-_+=a-zA-Z0-9$~.]+\\)" + " +\\(Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\) " + "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|" + "Oct\\|Nov\\|Dec\\) [0-3][0-9] ")) + +(defconst efs-dos:microsoft-file-line-regexp + ;; matches all the way to the first char of the filename. + (concat + "[01][0-9]-[0-3][0-9]-[0-9][0-9] +[012][0-9]:[0-5][0-9][AP]M +" + "\\(<DIR>\\|[0-9]+\\) +")) + +(defconst efs-dos:ncsa-file-line-regexp + "\\([-_+=a-zA-Z0-9$.~]+\\) +\\(<DIR>\\|[0-9]+\\)[ \n]") + +(defconst efs-dos:stcp-file-line-regexp + (concat + "\\([-_+=a-zA-Z0-9$~.]+\\) +\\(<DIR>\\|[0-9]+\\) " + "+[0-9][0-9]?-[0-3][0-9]-[12][90][0-9][0-9] +" + "[0-9][0-9]?:[0-5][0-9]")) + +(defconst efs-dos:winsock-date-and-size-regexp + (concat + " \\([0-9]+\\) " + "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|" + "Dec\\) [ 0-3][0-9] \\( [12][0-9][0-9][0-9]\\|[0-2][0-9]:[0-6][0-9]\\) +")) + +(efs-defun efs-parse-listing dos + (host user dir path &optional switches) + ;; Examine the listing, which is assumed to be either a DOS or OS/2 + ;; listing, and determine the operating system type and FTP server. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + ;; No need to check for OS/2, as it gets ID'ed by a SYST in + ;; efs-guess-host-type. + (efs-save-match-data + (cond + + ;; Check for the Microsoft server + ((re-search-forward efs-dos:microsoft-file-line-regexp nil t) + (efs-add-listing-type 'dos:microsoft host user) + (efs-parse-listing 'dos:microsoft host user dir path switches)) + + ;; Check for the Novell FTP server + ((save-excursion + (goto-char (point-max)) + (forward-line -1) + (looking-at " [0-9]+ File(s)\n")) + (efs-add-listing-type 'dos:novell host user) + (efs-parse-listing 'dos:novell host user dir path switches)) + + ;; Check for FTP software's server + ((re-search-forward efs-dos:ftp-file-line-regexp nil t) + (efs-add-listing-type 'dos:ftp host user) + (efs-parse-listing 'dos:ftp host user dir path switches)) + + ;; Check for winsock + ((re-search-forward efs-dos:winsock-date-and-size-regexp nil t) + (efs-add-listing-type 'dos:winsock host user) + (efs-parse-listing 'dos:winsock host user dir path switches)) + + ;; Check for the NCSA FTP server + ((re-search-forward efs-dos:ncsa-file-line-regexp nil t) + (efs-add-listing-type 'dos:ncsa host user) + (efs-parse-listing 'dos:ncsa host user dir path switches)) + + ;; Check for Frontier's Super-TCP server + ((re-search-forward efs-dos:stcp-file-line-regexp nil t) + (efs-add-listing-type 'dos:stcp host user) + (efs-parse-listing 'dos:stcp host user dir path switches)) + + ((string-match "^/\\([A-Za-z0-9]:/\\)?$" dir) + ;; root always exists + (let ((tbl (efs-make-hashtable))) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + tbl)) + (t + ;; an error message? + nil)))) + +;; Some DOS servers (NCSA), return a 501 message for an empty disk. +(efs-defun efs-ls-dumb-check dos (line host file path lsargs msg noparse + noerror nowait cont) + (and (string-match "^501 " line) + (string-match "^/[A-Za-z0-9]:/?$" path) + (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")))) + +(efs-defun efs-fix-path dos (path &optional reverse) + (efs-fix-pc-path path reverse)) + +(efs-defun efs-fix-dir-path dos (dir-path) + ;; Convert path from UNIX-ish to DOS for a DIRectory listing. + (cond ((string-match "^/\\(.:\\)?$" dir-path) + (error "Can't list DOS or OS/2 disks")) + ;; Neither DOS nor OS/2 allows us to end the name of a directory + ;; with an "\". + ;; Adding *.* to the end also allows us to distinguish plain files from + ;; directries. All DOS servers seem to understand this except + ;; Frontier Technologies' super-tcp server. + ((string-match "/$" dir-path) + (concat (efs-fix-pc-path dir-path) "*.*")) + (t (efs-fix-pc-path dir-path)))) + +(efs-defun efs-get-pwd dos (host user &optional xpwd) + ;; Parses PWD output for the current working directory. Hopefully this is + ;; DOS proof. + (let* ((result (efs-send-cmd host user (list 'quote + (if xpwd 'xpwd 'pwd)) + "Getting PWD")) + (line (nth 1 result)) + dir) + (if (car result) + (efs-save-match-data + (and (or (string-match "\"\\([^\"]*\\)\"" line) + ;; FTP software's output. They should know better... + (string-match "Current working directory is +\\([^ ]+\\)$" + line)) + (setq dir (substring line + (match-beginning 1) + (match-end 1)))))) + (cons dir line))) + +(efs-defun efs-allow-child-lookup dos (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. + + ;; Subdirs in DOS usually don't have an extension. + (not (string-match "\\." file))) + +;;;;----------------------------------- +;;;; Support for the Novell FTP server +;;;;----------------------------------- + +(defconst efs-dos:novell-file-line-regexp + ;; Matches from the first character of the filename to the end of the date. + ;; Does not match parent directories which the server might decide + ;; to put in front of the filename. + (concat + "\\([-_+=a-zA-Z0-9$.~]+\\) +\\(<DIR>\\|[0-9]+\\) +" + "[ 0-9][0-9]-[0-9][0-9]-[0-9][0-9] ")) + +(efs-defun efs-parse-listing dos:novell + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be a Novell DOS FTP listing. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + (let ((tbl (efs-make-hashtable)) + file size dir-p) + (efs-save-match-data + ;; Can we check somehow if the listing is really for something + ;; that doesn't exist? + (goto-char (point-min)) + (while (re-search-forward efs-dos:novell-file-line-regexp + nil t) + (setq file (buffer-substring (match-beginning 1) + (match-end 1)) + size (buffer-substring (match-beginning 2) + (match-end 2))) + (if (string-equal size "<DIR>") + (setq size nil + dir-p t) + (setq size (string-to-int size) + dir-p nil)) + (efs-put-hash-entry file (list dir-p size) tbl) + (forward-line 1)) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + tbl))) + +;;; Tree Dired Support + +(defconst efs-dired-dos:novell-re-exe + "^. [ \t]+[-_+=a-zA-Z0-9$~]+\\.exe ") + +(or (assq 'dos:novell efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'dos:novell efs-dired-dos:novell-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-dos:novell-re-dir + "^. [ \t]+[-_+=a-zA-Z0-9$~]+ +<DIR>") + +(or (assq 'dos:novell efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'dos:novell efs-dired-dos:novell-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-insert-headerline dos:novell (dir) + (efs-dired-pc-insert-headerline dir)) + +(efs-defun efs-dired-manual-move-to-filename dos:novell + (&optional raise-error bol eol) + ;; In dired, move to the first char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r")) + ;; move over marker + (if (re-search-forward efs-dos:novell-file-line-regexp eol t) + (goto-char (match-beginning 0)) ; returns (point) + (and raise-error (error "No file on this line"))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename dos:novell + (&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) + ;; On failure signals an error, or returns nil. + (efs-dired-pc-move-to-end-of-filename no-error bol eol)) + +(efs-defun efs-dired-fixup-listing dos:novell + (file path &optional switches wildcard) + ;; DOS may insert the entire directory name in front of the file name. + ;; Scrape it off. The Novell server seems to do weird things when insert + ;; the full-path, so be liberal with the hatchet. + (goto-char (point-min)) + (while (re-search-forward efs-dos:novell-file-line-regexp nil t) + (beginning-of-line) + (delete-region (point) (match-beginning 0)) + (forward-line 1)) + ;; the novell server outputs lines in seemingly random order + ;; this isn't as good as sorting switches, but at least it's not random. + (sort-fields 1 (point-min) (progn (goto-char (point-max)) + (forward-line -1) + (point)))) + +(efs-defun efs-dired-ls-trim dos:novell () + (goto-char (point-min)) + (let (case-fold-search) + (forward-line 1) + (if (looking-at " [0-9]+ File(s)\n") + (delete-region (match-beginning 0) (match-end 0))))) + + +;;;;----------------------------------------------- +;;;; PC/TCP (by FTP software) support +;;;;----------------------------------------------- + +(efs-defun efs-parse-listing dos:ftp + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be an FTP Software DOS + ;; listing. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + (let ((tbl (efs-make-hashtable)) + file size dir-p) + (efs-save-match-data + ;; Can we check somehow if an empty directory is really + ;; a nonexistent directory? + (goto-char (point-min)) + (goto-char (point-min)) + (while (looking-at efs-dos:ftp-file-line-regexp) + (setq file (buffer-substring (match-beginning 2) + (match-end 2)) + size (buffer-substring (match-beginning 1) + (match-end 1))) + (if (string-equal size "<dir>") + (setq size nil + dir-p t) + (setq size (string-to-int size) + dir-p nil)) + (efs-put-hash-entry file (list dir-p size) tbl) + (forward-line 1)) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + tbl))) + +;;; Tree Dired Support + +(defconst efs-dired-dos:ftp-re-exe + "^. [ \t]*[0-9]+ +[-_+=a-zA-Z0-9$~]+\\.exe ") + +(or (assq 'dos:ftp efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'dos:ftp efs-dired-dos:ftp-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-dos:ftp-re-dir + "^. [ \t]*<dir> ") + +(or (assq 'dos:ftp efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'dos:ftp efs-dired-dos:ftp-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-insert-headerline dos:ftp (dir) + (efs-dired-pc-insert-headerline dir)) + +;;; Because dos:ftp listings have the file names right justified, +;;; I have reversed what -move-to-filename and -move-to-end-of-filename +;;; actually do. This shouldn't confuse dired, and should make browsing +;;; a dos:ftp listing more aesthetically pleasing. + +(efs-defun efs-dired-manual-move-to-filename dos:ftp + (&optional raise-error bol eol) + ;; In dired, move to the *last* char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r")) + (if (re-search-forward efs-dos:ftp-file-line-regexp eol t) + (goto-char (match-end 2)) ; returns (point) + (and raise-error (error "No file on this line"))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename dos:ftp + (&optional no-error bol eol) + ;; Assumes point is at the *end* of filename. Really moves the + ;; point to the beginning of the filename. + ;; So, it should be called only after (dired-move-to-filename t) + ;; On failure signals an error, or returns nil. + ;; This is the DOS version. It is common to all of the DOS ftp servers + ;; since it depends only on the file name character set. + (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-backward "-_+=a-zA-Z0-9.$~" bol) + (if (= opoint (point)) + (if no-error + nil + (error "No file on this line")) + (point)))) + +;;;;----------------------------------------------- +;;;; NCSA FTP support +;;;;----------------------------------------------- + +(efs-defun efs-parse-listing dos:ncsa + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be a Novell DOS FTP listing. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + (let (tbl file size dir-p next) + (efs-save-match-data + (goto-char (point-min)) + (while (re-search-forward + efs-dos:ncsa-file-line-regexp + (setq next (save-excursion (forward-line 1) (point))) t) + (setq file (buffer-substring (match-beginning 1) + (match-end 1)) + size (buffer-substring (match-beginning 2) + (match-end 2))) + (if (string-equal size "<DIR>") + (setq size nil + dir-p t) + (setq size (string-to-int size) + dir-p nil)) + (efs-put-hash-entry file (list dir-p size) + (or tbl (setq tbl (efs-make-hashtable)))) + (goto-char next)) + ;; DOS does not put . and .. in the root directory. + (if (or tbl + ;; root always exists + (string-match "^/\\([A-Za-z0-9]:/\\)?$" dir)) + (progn + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl))) + tbl))) + +;;; Tree Dired Support + +(defconst efs-dired-dos:ncsa-re-exe + "^. [ \t]+[-_+=a-zA-Z0-9$~]+\\.exe ") + +(or (assq 'dos:ncsa efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'dos:ncsa efs-dired-dos:ncsa-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-dos:ncsa-re-dir + "^. [ \t]+[-_+=a-zA-Z0-9$~]+ +<DIR>") + +(or (assq 'dos:ncsa efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'dos:ncsa efs-dired-dos:ncsa-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-insert-headerline dos:ncsa (dir) + (efs-dired-pc-insert-headerline dir)) + +(efs-defun efs-dired-manual-move-to-filename dos:ncsa + (&optional raise-error bol eol) + ;; In dired, move to the first char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r")) + (if (re-search-forward "[-_+=a-zA-Z0-9$.~]+ +\\(<DIR>\\|[0-9]\\)" eol t) + (goto-char (match-beginning 0)) ; returns (point) + (and raise-error (error "No file on this line"))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename dos:ncsa + (&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) + ;; On failure signals an error, or returns nil. + (efs-dired-pc-move-to-end-of-filename no-error bol eol)) + +(efs-defun efs-dired-fixup-listing dos:ncsa + (file path &optional switches wildcard) + ;; DOS may insert the entire directory name in front of the file name. + ;; Scrape it off. + (let (bonl) + (goto-char (point-min)) + (while (re-search-forward + efs-dos:ncsa-file-line-regexp + (setq bonl (save-excursion (forward-line 1) (point))) t) + (goto-char (match-beginning 0)) + (delete-region (point) (progn (beginning-of-line) (point))) + (goto-char bonl))) + ;; sort the buffer + (sort-fields 1 (point-min) (point-max))) + +(efs-defun efs-dired-ls-trim dos:ncsa () + (goto-char (point-min)) + (if (re-search-forward efs-dos:ncsa-file-line-regexp nil t) + (delete-region (point-min) (match-beginning 0)))) + +;;;;----------------------------------------------- +;;;; Microsoft DOS FTP support +;;;;----------------------------------------------- + +(defconst efs-dos:microsoft-valid-listing-regexp + (concat efs-dos:microsoft-file-line-regexp "\\.")) + +(efs-defun efs-parse-listing dos:microsoft + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be a Novell DOS FTP listing. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + + ;; Use the existence of a "." file as confirmation that it's really + ;; a directory listing. + (goto-char (point-min)) + (efs-save-match-data + (if (or (string-match "^/.:/$" dir) + (re-search-forward efs-dos:microsoft-valid-listing-regexp nil t)) + (let ((tbl (efs-make-hashtable)) + size dir-p) + (goto-char (point-min)) + (while (re-search-forward efs-dos:microsoft-file-line-regexp nil t) + (setq size (buffer-substring (match-beginning 1) (match-end 1))) + (if (string-equal size "<DIR>") + (setq size nil + dir-p t) + (setq size (string-to-int size) + dir-p nil)) + (efs-put-hash-entry (buffer-substring (point) + (progn (end-of-line) + (point))) + (list dir-p size) tbl) + (forward-line 1)) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + tbl)))) + +;;; Tree Dired Support + +(defconst efs-dired-dos:microsoft-re-exe + "^[^\n]+ +[-_+=a-zA-Z0-9$~]+\\.\\(EXE\\|exe\\)$") + +(or (assq 'dos:microsoft efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'dos:microsoft efs-dired-dos:microsoft-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-dos:microsoft-re-dir + "^[^\n]+ <DIR> ") + +(or (assq 'dos:microsoft efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'dos:microsoft efs-dired-dos:microsoft-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-insert-headerline dos:microsoft (dir) + (efs-dired-pc-insert-headerline dir)) + +(efs-defun efs-dired-manual-move-to-filename dos:microsoft + (&optional raise-error bol eol) + ;; In dired, move to the first char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r")) + (if (re-search-forward efs-dos:microsoft-file-line-regexp eol t) + (goto-char (match-end 0)) ; returns (point) + (and raise-error (error "No file on this line"))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename dos:microsoft + (&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) + ;; On failure signals an error, or returns nil. + (efs-dired-pc-move-to-end-of-filename no-error bol eol)) + +;;;;----------------------------------------------- +;;;; Frontier's Super-TCP FTP Server for Windows +;;;;----------------------------------------------- + +(efs-defun efs-parse-listing dos:stcp + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be a Super-TCP FTP listing. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + + ;; Use the existence of a strict file line pattern as + ;; confirmation that it's really a directory listing. + (goto-char (point-min)) + (efs-save-match-data + (let ((regexp (concat "^" efs-dos:stcp-file-line-regexp))) + (if (let ((eol (save-excursion (end-of-line) (point)))) + (re-search-forward regexp eol t)) + (let ((tbl (efs-make-hashtable)) + size dir-p) + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (setq size (buffer-substring (match-beginning 2) (match-end 2))) + (if (string-equal size "<DIR>") + (setq size nil + dir-p t) + (setq size (string-to-int size) + dir-p nil)) + (efs-put-hash-entry (buffer-substring (match-beginning 1) + (match-end 1)) + (list dir-p size) tbl) + (forward-line 1)) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + tbl))))) + +;;; Tree Dired Support + +(defconst efs-dired-dos:stcp-re-exe + "^[-_+=a-zA-Z0-9$~]+\\.\\(EXE\\|exe\\) ") + +(or (assq 'dos:stcp efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'dos:stcp efs-dired-dos:stcp-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-dos:stcp-re-dir + "^[^\n ]+ +<DIR> ") + +(or (assq 'dos:stcp efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'dos:stcp efs-dired-dos:stcp-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-insert-headerline dos:stcp (dir) + (efs-dired-pc-insert-headerline dir)) + +(efs-defun efs-dired-manual-move-to-filename dos:stcp + (&optional raise-error bol eol) + ;; In dired, move to the first char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r") + (setq bol (point))) + (if (re-search-forward efs-dos:stcp-file-line-regexp eol t) + (goto-char (match-beginning 0)) ; returns (point) + (if raise-error + (error "No file on this line") + (goto-char bol))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename dos:stcp + (&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) + ;; On failure signals an error, or returns nil. + (efs-dired-pc-move-to-end-of-filename no-error bol eol)) + +(efs-defun efs-dired-fixup-listing dos:stcp + (file path &optional switches wildcard) + ;; The Super-TCP server outputs lines in seemingly random order. + ;; This isn't as good as sorting switches, but at least it's not random. + (sort-fields 1 (point-min) (point-max))) + +;;;;---------------------------------------------------------- +;;;; Winsock DOS FTP server (Alun's FTP server) +;;;;---------------------------------------------------------- + +(efs-defun efs-parse-listing dos:winsock + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be a DOS Winsock listing. + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + + (goto-char (point-min)) + (efs-save-match-data + (if (re-search-forward efs-dos:winsock-date-and-size-regexp nil t) + (let ((tbl (efs-make-hashtable)) + size dirp) + (while + (progn + (setq size (string-to-int (buffer-substring (match-beginning 1) + (match-end 1))) + dirp (save-excursion + (beginning-of-line) + (skip-chars-forward " ") + (char-equal (following-char) ?d))) + (efs-put-hash-entry + (buffer-substring (point) (progn (end-of-line) (point))) + (list dirp size) tbl) + (re-search-forward efs-dos:winsock-date-and-size-regexp nil t))) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + tbl)))) + +(defconst efs-dired-dos:winsock-re-exe "\\.exe$") + +(or (assq 'dos:winsock efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'dos:winsock efs-dired-dos:winsock-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-dos:winsock-re-dir "^. +d") + +(or (assq 'dos:winsock efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'dos:winsock efs-dired-dos:winsock-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-insert-headerline dos:winsock (dir) + (efs-dired-pc-insert-headerline dir)) + +(efs-defun efs-dired-manual-move-to-filename dos:winsock + (&optional raise-error bol eol) + ;; In dired, move to the first char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r") + (setq bol (point))) + (if (re-search-forward efs-dos:winsock-date-and-size-regexp eol t) + (point) + (if raise-error + (error "No file on this line") + (goto-char bol))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename dos:winsock + (&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) + ;; On failure signals an error, or returns nil. + (efs-dired-pc-move-to-end-of-filename no-error bol eol)) + +(efs-defun efs-dired-fixup-listing dos:winsock + (file path &optional switches wildcard) + ;; The Winsock server outputs lines in seemingly random order. + ;; This isn't as good as sorting switches, but at least it's not random. + (sort-fields 9 (point-min) (point-max))) + +;;;;----------------------------------------------------------- +;;;; OS/2 Support +;;;;----------------------------------------------------------- + +;;; OS/2 has two types of file systems, FAT and HPFS. In the FAT file system +;;; filenames are restricted to the traditional DOS 8 + 3 syntax. In the +;;; HPFS file system, filenames can have arbitrarily many extensions (.'s). +;;; As well, file lines for "." and ".." are listed for HPFS. +;;; For the FAT FS, "." and ".." lines are only listed for sudirs, it seems. +;;; Go figure... + +(defconst efs-os2-file-line-regexp + (concat + " +\\([0-9]+\\) +\\([^ ]+\\)? +[01][0-9]-[0-3][0-9]-[0-9][0-9] +" + "[0-2][0-9]:[0-6][0-9] +")) + +(efs-defun efs-fix-path os2 (path &optional reverse) + (efs-fix-pc-path path reverse)) + +(efs-defun efs-fix-dir-path os2 (dir-path) + ;; Convert path from UNIX-ish to DOS for a DIRectory listing. + (cond ((string-match "^/\\(.:\\)?$" dir-path) + (error "Can't list DOS or OS/2 disks")) + ;; Neither DOS nor OS/2 allows us to end the name of a directory + ;; with an "\". + ;; Can't just hack it off, because if the dir is C:, we'll get the + ;; default dir. + ;; Don't apend the filename wildcard to distinguish + ;; plain files from directories, because OS/2 and DOS may + ;; not agree on what the wildcard is. Also, can't then tell + ;; the difference between plain files and empty directories. + ((string-match "/$" dir-path) + (concat (efs-fix-pc-path dir-path) ".")) + (t (efs-fix-pc-path dir-path)))) + +(defconst efs-os2-dot-line-regexp + (concat efs-os2-file-line-regexp "\\.\n")) + +(efs-defun efs-parse-listing os2 + (host user dir path &optional switches) + ;; Parse the current buffer which is assumed to be an OS/2 listing. + ;; To make sure that it is really a directory listing and not a bogus + ;; listing of a single file, make sure that there is an entry for ".". + ;; HOST = remote host name + ;; USER = remote user name + ;; DIR = directory as a full remote path + ;; PATH = directory in full efs-path syntax + (efs-save-match-data + (if (or + (string-match "^/.:/$" dir) ; FAT proofing + (progn + (goto-char (point-min)) + (re-search-forward efs-os2-dot-line-regexp nil t))) + (let ((tbl (efs-make-hashtable))) + (goto-char (point-min)) + (efs-put-hash-entry "." '(t) tbl) + (efs-put-hash-entry ".." '(t) tbl) + (while (looking-at efs-os2-file-line-regexp) + (end-of-line) + (efs-put-hash-entry + (buffer-substring (match-end 0) (point)) + (list (and + (match-beginning 2) + (string-equal "DIR" + (buffer-substring (match-beginning 2) + (match-end 2)))) + (string-to-int (buffer-substring (match-beginning 1) + (match-end 1)))) + tbl) + (forward-line 1)) + tbl)))) + +;;; Tree Dired + +(defconst efs-dired-os2-re-exe + "^[^\n]+\\.EXEC?$") + +(or (assq 'os2 efs-dired-re-exe-alist) + (setq efs-dired-re-exe-alist + (cons (cons 'os2 efs-dired-os2-re-exe) + efs-dired-re-exe-alist))) + +(defconst efs-dired-os2-re-dir + "^ +[0-9]+ +DIR ") + +(or (assq 'os2 efs-dired-re-dir-alist) + (setq efs-dired-re-dir-alist + (cons (cons 'os2 efs-dired-os2-re-dir) + efs-dired-re-dir-alist))) + +(efs-defun efs-dired-manual-move-to-filename os2 + (&optional raise-error bol eol) + ;; In dired, move to the first char of filename on this line. + ;; Returns (point) or nil if raise-error is nil, and there is no + ;; no filename on this line. + ;; This version is for OS/2 + (or eol (setq eol (save-excursion (skip-chars-forward "^\r\n") (point)))) + (let (case-fold-search) + (if bol + (goto-char bol) + (skip-chars-backward "^\n\r") + (setq bol (point))) + (if (and + (> (- eol bol) 24) + (progn + (forward-char 2) + (looking-at efs-os2-file-line-regexp))) + (goto-char (match-end 0)) + (and raise-error (error "No file on this line"))))) + +(efs-defun efs-dired-manual-move-to-end-of-filename os2 + (&optional no-error bol eol) + (efs-dired-pc-move-to-end-of-filename no-error bol eol)) + +(efs-defun efs-dired-insert-headerline os2 (dir) + (efs-dired-pc-insert-headerline dir)) + +;; end of efs-pc.el