Loading a local file in a remote Lisp with swank/slime

Viewed 138

Say that I am connected to a remote Lisp using swank/slime. I have a data file on my local machine on disk or, perhaps, in an emacs buffer. I want to process this file using the remote Lisp.

I can obviously drop out to a shell and scp the file to the server, load the file, process it, close, delete the file so that I don't make a mess on the server.

I can also paste the file into the repl:

> (defparameter *my-file* "[paste file here]")

But this is nasty if the text has quotes in it or the file is binary.

Both options are cumbersome.

Is there a nice way to get the local emacs to tunnel a file to the remote Lisp so that the remote Lisp can open it as a stream?

I'm picturing something like:

> (swank:with-open-client-file (s #p"/path/to/local/file") ... )

Edit: This example feels like it could open some holes in my local file system. Could it be done without causing serious security problems?

2 Answers

The Emacs-side of the setup could start an HTTP server that restricts which files are published. It looks like it is possible to do it in Emacs directly (e.g. https://gist.github.com/TeMPOraL/f6f5333ae93de4ce9b5bd82cdad87d32).

The remote part could then obtain streams with drakma or dexador.

This does not rely on Slime/swank.

Alternatively, you can call eval-in-emacs to execute an Emacs Lisp form that would return a string containing the content of the file. You can write a custom function in Emacs to grant access or not to the files.

For example, an unsafe version is as follows:

(defun get-file (filename)
  (with-temp-buffer
    (insert-file-contents filename)
      (buffer-string))))

Then:

CL-USER> (swank:eval-in-emacs '(get-file "/etc/passwd"))
"......"

The above requires Slime to be configured so that eval-in-emacs is enabled.

Once this works, you can write a helper function:

(defun call-with-open-client-file (function file)
  (with-input-from-string (stream (swank:eval-in-emacs `(get-file ,file)))
    (funcall function stream)))

And a macro:

(defmacro with-open-client-file (var name &body body)
  `(call-with-open-client-file (lambda (,var) ,@body) ,name)

Have you tried compiling the buffer directly (rather than loading the file)? This would only help if the buffer is compile-able, of course.

This previous answer seems relevant (Load remote Lisp files):

For your particular case, I suggest you compile the buffer (select the whole buffer C-x h, then C-c C-c), not the file (C-c C-k).

The annoyance is that this changes the buffer's point and mark. Hopefully, you won't be compiling a whole buffer all the time.

Related