Mercurial > hg > xemacs-beta
diff lisp/packages/remote.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/packages/remote.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,304 @@ +;; remote.el version 2.6 +;; +;; Module to do remote editing via rcp. Assume .rhosts files are +;; set up properly on both machines. +;; Modeled after ftp.el by MLY.PREP.AI.MIT.EDU +;; +;; Nick Tran +;; University of Minnesota +;; Summer 87 +;; +;;; Synched up with: Not in FSF. + +;; Almost complete rewrite. Added minor mode support, better +;; defaults, rewrote find-remote-file, wrote read-remote-file-name, +;; insert-remote-file, find-file, find-alternate-remote-file, +;; get-remote-file-or-buffer, get-remote-buffer, process-wait, +;; remote-rcp-error. Also general clean up, error handling, etc. +;; Eric Raible Wednesday Sept 5, 1988 +;; +;; Automatically set major mode, added prefix arg support for most +;; file operations to toggle sense of remote editing. +;; Eric Raible Thursday October 6, 1988 +;; +;; Manipulate buffer name more appropriately +;; Eric Raible Friday October 7, 1988 +;; +;; For write-remote-file, allow default of file part of remote name. +;; Eric Raible Tuesday October 11, 1988 + +(defvar default-remote-host "navier:" + "The host to use for remote file operations when none other is appropriate.") + +(defvar track-default-remote-host t + "Controls whether default-remote-host is changed after reading a remote file name. +When non-nil, default-remote-host will have the value of the last remote host read.") + +(make-variable-buffer-local 'buffer-remote-file-name) +(set-default 'buffer-remote-file-name "") +(make-variable-buffer-local 'remote-editing) + +(defvar rcp (cond ((file-exists-p "/bin/rcp") "/bin/rcp") + ((file-exists-p "/usr/bsd/rcp") "/usr/bsd/rcp") + (t "rcp"))) + +(if (assoc 'remote-editing minor-mode-alist) + () + (setq minor-mode-alist (cons '(remote-editing " Remote") minor-mode-alist))) + +(defun remote-editing (arg) + "Toggle remote-editing mode. +With arg, turn on remote editing mode iff arg is positive, otherwise just toggle it. + +In remote editing mode, the normal bindings for find-file, +find-file-read-only, find-alternate-file, save-buffer, write-file, +and insert-file are changed to operate on a remote system by default. + +When remote editing, a prefix arg allows local file operations. When not +remote editing, a prefix arg allows remote file operations. + +It is assumed that .rhosts files are set up properly on both machines." + (interactive "P") + (setq remote-editing + (if (null arg) (not remote-editing) + (> (prefix-numeric-value arg) 0))) + (set-buffer-modified-p (buffer-modified-p))) ;No-op, but updates mode line. + +(global-set-key "\C-xr" 'remote-editing) + +;;; +;;; Macro used as front-end to normal file operation key bindings to decide between +;;; local and remote modes. Automatically constructs doc string and includes prefix arg +;;; to temporarily toggle sense of remote-editing. +;;; +(defmacro def-local-or-remote (binding name remote local) + (let ((r (symbol-name (eval remote))) + (l (symbol-name (eval local)))) + (list 'progn + (list 'global-set-key binding (list 'quote name)) + (list 'defun name '(arg) + (concat "Call either " r " or " l ". +If remote-editing (which see), call " r ", else call " l ". + +See also the documentation for " r " and " l ".") + '(interactive "P") + (list 'call-interactively + (list 'if '(xor remote-editing arg) + remote + local)))))) + +(def-local-or-remote "\C-x\C-f" find-local-or-remote-file 'find-remote-file 'find-file) +(def-local-or-remote "\C-x\C-r" find-local-or-remote-file-read-only 'find-remote-file-read-only 'find-file-read-only) +(def-local-or-remote "\C-x\C-v" find-alternate-local-or-remote-file 'find-alternate-remote-file 'find-alternate-file) +(def-local-or-remote "\C-x\C-s" save-local-or-remote-buffer 'save-remote-buffer 'save-buffer) +(def-local-or-remote "\C-x\C-w" write-local-or-remote-file 'write-remote-file 'write-file) +(def-local-or-remote "\C-xi" insert-local-or-remote-file 'insert-remote-file 'insert-file) + +(defun find-remote-file (host file) + "Edit remote file HOST:FILE (using rcp). +This command is similiar to find-file, but uses rcp to read the file from +a remote machine. Also see remote-editing." + (interactive (read-remote-file-name "Find remote file")) + (let ((buffer-or-file (get-remote-file-or-buffer host file "retrieve")) + local-file) + (if buffer-or-file + (if (bufferp buffer-or-file) + (switch-to-buffer buffer-or-file) + (setq local-file buffer-or-file) + (let ((buf (generate-new-buffer + (concat host (file-name-nondirectory file))))) + (switch-to-buffer buf) + (if (not (file-exists-p local-file)) + (message "(New remote file)") + (insert-file-contents local-file) + (set-buffer-modified-p nil) + (delete-file local-file)) + ;; dynamic binding for normal-mode + (let ((buffer-file-name (concat host file))) + (normal-mode) + (remote-editing 1) + (setq buffer-remote-file-name buffer-file-name + buffer-offer-save t))))))) + +(defun find-remote-file-read-only () + "Edit remote file FILENAME, but mark buffer as read-only. +Also see find-remote-file and remote-editing." + (interactive) + (call-interactively 'find-remote-file) + (setq buffer-read-only t)) + +(defun find-alternate-remote-file () + "Find alternate file using rcp. +This command is similiar to find-alternate-file, but uses rcp to read the file from +a remote machine. Also see remote-editing." + (interactive) + (and (buffer-modified-p) + (not buffer-read-only) + (not (yes-or-no-p (format "Buffer %s is modified; kill anyway? " + (buffer-name)))) + (error "Aborted")) + (let ((obuf (current-buffer)) + (oname (buffer-name))) + (rename-buffer " **lose**") + (unwind-protect + (apply 'find-remote-file + (read-remote-file-name "Find remote alternate file")) + (if (eq obuf (current-buffer)) + (rename-buffer oname) + (kill-buffer obuf))))) + +(defun save-remote-buffer () + "Save a file using rcp. +This command is similiar to save-buffer, but uses rcp to write the file back +to a remote machine. Also see remote-editing." + (interactive) + (if (buffer-modified-p) + (if (zerop (length buffer-remote-file-name)) + (call-interactively 'write-remote-file) + (do-write-remote-file buffer-remote-file-name)) + (message "(No changes need to be saved)"))) + +(defun write-remote-file (host file) + "Write a file HOST:FILE using rcp. +This command is similiar to write-file, but uses rcp to write the file back +to a remote machine. Also see remote-editing." + (interactive (read-remote-file-name "Write remote file" 'no-file-ok)) + (do-write-remote-file (concat host file))) + +(defun insert-remote-file (host file) + "Insert a remote file HOST:FILE using rcp. +This command is similiar to insert-file, but uses rcp to read the file from +a remote machine. Also see remote-editing." + (interactive (read-remote-file-name "Insert remote file")) + (let ((f-or-b (get-remote-file-or-buffer host file "insert"))) + (if f-or-b + (if (bufferp f-or-b) + (insert-buffer f-or-b) + (insert-file f-or-b) + (delete-file f-or-b))))) + +;;; +;;; Internal routines +;;; + +(defun do-write-remote-file (file) + (let* ((temp (concat "/tmp/" (buffer-name))) + (output (save-excursion + (prog1 (set-buffer (get-buffer-create "*Rcp Output*")) + (erase-buffer)))) + (cursor-in-echo-area t) + time) + ;; write-file doesn't quite do it. + (save-restriction + (widen) + (write-region (point-min) (point-max) temp nil 'no-message)) + (message "Sending %s..." file) + (if (setq time (process-wait (start-process "rcp" output rcp temp file))) + (progn + (if remote-editing + (let ((new-name (concat (host-part-only file) + (file-name-nondirectory (file-part-only file))))) + (or (get-buffer new-name) (rename-buffer new-name)) + (set-buffer-modified-p nil))) + (setq buffer-remote-file-name file) + (message "%d bytes in %d seconds" (buffer-size) time) + (delete-file temp)) + (remote-rcp-error output buffer-remote-file-name "update")))) + +(defun get-remote-file-or-buffer (host file message) + "Return a remote file as either a buffer or a file. +If the file HOST:FILE already has been read in, return the buffer +that contains it; otherwise try and rcp the file to the local machine. +If successful, return the local file name." + (let ((remote (concat host file)) + (temp (concat "/tmp/" (file-name-nondirectory file))) + time) + (if (string= file (file-name-directory file)) + (progn + (message "Remote directory listing not yet implemented") + nil) + (or (get-remote-buffer remote) ;; already exists + (let* ((output (save-excursion + (prog1 (set-buffer (get-buffer-create "*Rcp Output*")) + (erase-buffer)))) + (cursor-in-echo-area t)) + (message "Retrieving %s..." remote) + (if (setq time (process-wait (start-process "rcp" output rcp remote temp))) + (progn + (message "%d bytes in %d seconds" + (nth 7 (file-attributes temp)) time) + temp) + (remote-rcp-error output remote message))))))) + +(defun get-remote-buffer (name) + (save-window-excursion + (let ((buffers (buffer-list)) found) + (while (and (not found) buffers) + (set-buffer (car buffers)) + (if (string= name buffer-remote-file-name) + (setq found (car buffers))) + (setq buffers (cdr buffers))) + found))) + +(defun read-remote-file-name (prompt &optional no-file-ok) + "Read a remote file specification, and return list (host file). +Prompting with PROMPT, read a string of the form host:file. The default +value is derived from the remote file name, or if there is none, then +from the global default (default-remote-host)." + (let* ((host (or (host-part-only buffer-remote-file-name) + default-remote-host)) + (result (concat host (file-name-directory + (or (file-part-only buffer-remote-file-name) + "")))) + (prompt (concat prompt " (host:file): ")) + file) + (setq result (read-no-blanks-input prompt result)) + (while (not (string-match (if no-file-ok + ".+:" + ".+:.+") + result)) + (setq result (read-no-blanks-input prompt result))) + (setq host (host-part-only result) + file (file-part-only result)) + (and track-default-remote-host + (setq default-remote-host host)) + (list host + (if (or (null file) (string= file (file-name-directory file))) + (concat file (or (if (not (string= buffer-remote-file-name "")) + (file-name-nondirectory + (file-part-only buffer-remote-file-name))) + (file-part-only (buffer-name)) + (buffer-name))) + file)))) + +(defun host-part-only (name) + (if (string-match ".+:" name) + (substring name 0 (match-end 0)))) + +(defun file-part-only (name) + (if (string-match ".+:\\(.+\\)" name) + (substring name (match-beginning 1) (match-end 1)))) + +(defun xor (a b) + (eq (null a) (not (null b)))) + +(defun process-wait (proc) + (let ((time 0)) + (while (eq (process-status proc) 'run) + (setq time (1+ time)) + (sleep-for 1)) + (if (and (eq (process-status proc) 'exit) + (eq (process-exit-status proc) 0)) + time + nil))) + +(defun remote-rcp-error (buffer file-name message) + (save-window-excursion + (switch-to-buffer buffer) + (delete-other-windows) + (goto-char 1) + (insert (format "Unable to %s %s\n\n" message file-name)) + (goto-char (point-max)) + (message "Hit any character to continue") + (read-char) + (bury-buffer buffer)))