[comp.emacs] remote editing

tran@umn-cs.UUCP (Michael Tran) (10/05/87)

I wrote this small module this summer while exploring ways to get Emacs
to do remote editing on a Cray 2.

The main functions are find-remote-file (which I mapped to C-c C-f in my .emacs)
                       save-remote-file (C-c C-s)
		       write-remote-file (C-c C-w) 
;; rcp.el
;; 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
;;

(make-variable-buffer-local 'default-remote-directory)
(make-variable-buffer-local 'buffer-remote-file-name)
(setq-default default-remote-directory "sb:")

(defun find-remote-file ()
  (interactive)
  (let (proc temp s host file time default (olddir default-directory))
    (setq s "")
    (while (not (string-match "\\`[ \t]*\\([^ \t:]+\\)[ \t]*:\\(.+\\)\\'" s))
      (setq s (read-string "Find remote file: " default-remote-directory)))
    (setq host (substring s (match-beginning 1) (match-end 1)))
    (setq file (substring s (match-beginning 2) (match-end 2)))
    
    (if (string-match "[^\t /]+$" file)
	(and (setq temp (substring file (match-beginning 0) nil))
	     (setq default (substring file 0 (match-beginning 0))))
      (and (setq temp file) (setq default "")))
    (setq temp (concat "/tmp/" temp))
    (setq proc (start-process temp nil "/bin/rcp"
	  (concat host ":" file) temp))
    (setq default-remote-directory (concat host ":" default))
    (message "Finding %s:%s ..." host file)
    (setq 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))
	(progn
	  (find-file-read-only temp)
	  (message "%d bytes in %d seconds (~ %d Kb/s)"
		   (buffer-size) time (/ (buffer-size) (* time 1024)))
	  (delete-file temp)
	  (setq buffer-file-name "")
	  (setq buffer-remote-file-name (concat host ":" file))
	  (setq default-remote-directory (concat host ":" default))
	  (setq default-directory olddir)
	  )
      (message "Find-remote-file failed (exit status %d)"
	       (process-exit-status proc)))
          ))

(defun save-remote-file()
  (interactive)
  (cond ((eq 0 (length buffer-remote-file-name)) (write-remote-file))
	( t (do-write-remote-file buffer-remote-file-name))))

(defun write-remote-file ()
  (interactive)
  (let ((s ""))
    (while (not (string-match "\\`[ \t]*\\([^ \t:]+\\)[ \t]*:\\(.+\\)\\'" s))
	  (or (not (and (string= s "")
		   (eq 0 (length (setq s buffer-remote-file-name)))))
	      (setq s default-remote-directory))
	  (setq s (read-string "Write remote file: " s)))

    (do-write-remote-file s)))

(defun do-write-remote-file (s)
  (if (buffer-modified-p)
      (let (host file temp proc time (size (buffer-size))
		 (olddir default-directory))
	(string-match "\\`[ \t]*\\([^ \t:]+\\)[ \t]*:\\(.+\\)\\'" s)
	(setq host (substring s (match-beginning 1) (match-end 1)))
	(setq file (substring s (match-beginning 2) (match-end 2)))
	(setq temp (concat "/tmp/" (buffer-name)))
	(if (string-match "[^\t /]+$" file)
	    (setq default-remote-directory
		  (concat host ":" (substring file 0 (match-beginning 0))))
	  (setq default (concat host ":")))
	(setq buffer-remote-file-name (concat host ":" file))
	(write-file temp)
	(message "Writing %s:%s ..." host file)
	(setq proc (start-process temp nil "/bin/rcp" temp (concat host ":" file)))
	(setq 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))
	    (message "%d bytes in %d seconds (~ %d Kb/s)" size time (/ size (* time 1024)))
	  
	  (and (set-buffer-modified-p t)
	       (message "Write-remote-file failed (exit status %d)"
			(process-exit-status proc))))
	(delete-file temp)
	(set-visited-file-name "")
	(setq default-directory olddir)
	)
    (message "(No changes need to be saved)")
    ))

mayer@hplabsz.HPL.HP.COM (Niels Mayer) (10/08/87)

An intersting extension you might want to consider once you have remote
editing up and running: When you want to do a 'make' after you've just
edited a remote file, use a modified version of compile.el that looks at a
buffer-local variable containing the remote host's name and run the
compilation through rsh(1) on the remote host.

Under HPUX+GNU, we can access remote files by opening a network connection
via M-X netunam, and then access the file normally with ^X^F
/net/host/path/file.  I made a simple and crufty extension to compile.el
that looks to see whether the buffer-file-name is remote and if so, I
execute the compile command in a remsh (HPUX equivalent to rsh, renamed to
prevent conflict with SYSV rsh which is a restricted sh)....

Those of you using ftp.el or rcp.el to do remote editing may want to do
something similar by storing the appropriate information in a buffer-local
variable and munging compile.el appropriately.

here's the relevant diffs between the 18.47 version of compile.el and my
version:

82,91c82,119
<   (setq compilation-process
< 	(start-process "compilation" "*compilation*"
< 		       shell-file-name
< 		       "-c" (concat "exec " command)))
<   (with-output-to-temp-buffer "*compilation*"
<     (princ "cd ")
<     (princ default-directory)
<     (terpri)
<     (princ command)
<     (terpri))
---
>   (cond
>    ((string-match "/net" default-directory)
>     (let (host
> 	  host-dir)
>       (setq host (substring default-directory 
> 			    (+ 1 (match-end 0)) 
> 			    (string-match "/" default-directory (+ 1 (match-end 0)))))
>       (setq host-dir (substring default-directory 
> 				(- (match-end 0) 1) 
> 				(length default-directory)))
>       (let ((default-directory (getenv "HOME"))) ;to prevent shell bug caused by pwd being a /net directory
> 	(setq compilation-process
> 	      (start-process "compilation" "*compilation*"
> 			     "/usr/bin/remsh" host
> 			     "cd " host-dir ";" command)))
>       (with-output-to-temp-buffer "*compilation*"
> 	(princ "remsh ")
> 	(princ host)
> 	(terpri)
> 	(princ "cd ")
> 	(princ host-dir)
> 	(terpri)
> 	(princ command)
> 	(terpri))
>       )
>     )
>    (t
>     (setq compilation-process
> 	  (start-process "compilation" "*compilation*"
> 			 shell-file-name
> 			 "-c" (concat "exec " command)))
>     (with-output-to-temp-buffer "*compilation*"
>       (princ "cd ")
>       (princ default-directory)
>       (terpri)
>       (princ command)
>       (terpri))
>     ))