;; swank-larceny.scm --- Swank server for Larceny ;; ;; License: Public Domain ;; Author: Helmut Eller ;; ;; In a shell execute: ;; larceny -r6rs -program swank-larceny.scm ;; and then `M-x slime-connect' in Emacs. (library (swank os) (export getpid make-server-socket accept local-port close-socket) (import (rnrs) (primitives foreign-procedure ffi/handle->address ffi/string->asciiz sizeof:pointer sizeof:int %set-pointer %get-int)) (define getpid (foreign-procedure "getpid" '() 'int)) (define fork (foreign-procedure "fork" '() 'int)) (define close (foreign-procedure "close" '(int) 'int)) (define dup2 (foreign-procedure "dup2" '(int int) 'int)) (define bytevector-content-offset$ sizeof:pointer) (define execvp% (foreign-procedure "execvp" '(string boxed) 'int)) (define (execvp file . args) (let* ((nargs (length args)) (argv (make-bytevector (* (+ nargs 1) sizeof:pointer)))) (do ((offset 0 (+ offset sizeof:pointer)) (as args (cdr as))) ((null? as)) (%set-pointer argv offset (+ (ffi/handle->address (ffi/string->asciiz (car as))) bytevector-content-offset$))) (%set-pointer argv (* nargs sizeof:pointer) 0) (execvp% file argv))) (define pipe% (foreign-procedure "pipe" '(boxed) 'int)) (define (pipe) (let ((array (make-bytevector (* sizeof:int 2)))) (let ((r (pipe% array))) (values r (%get-int array 0) (%get-int array sizeof:int))))) (define (fork/exec file . args) (let ((pid (fork))) (cond ((= pid 0) (apply execvp file args)) (#t pid)))) (define (start-process file . args) (let-values (((r1 down-out down-in) (pipe)) ((r2 up-out up-in) (pipe)) ((r3 err-out err-in) (pipe))) (assert (= 0 r1)) (assert (= 0 r2)) (assert (= 0 r3)) (let ((pid (fork))) (case pid ((-1) (error "Failed to fork a subprocess.")) ((0) (close up-out) (close err-out) (close down-in) (dup2 down-out 0) (dup2 up-in 1) (dup2 err-in 2) (apply execvp file args) (exit 1)) (else (close down-out) (close up-in) (close err-in) (list pid (make-fd-io-stream up-out down-in) (make-fd-io-stream err-out err-out))))))) (define (make-fd-io-stream in out) (let ((write (lambda (bv start count) (fd-write out bv start count))) (read (lambda (bv start count) (fd-read in bv start count))) (closeit (lambda () (close in) (close out)))) (make-custom-binary-input/output-port "fd-stream" read write #f #f closeit))) (define write% (foreign-procedure "write" '(int ulong int) 'int)) (define (fd-write fd bytevector start count) (write% fd (+ (ffi/handle->address bytevector) bytevector-content-offset$ start) count)) (define read% (foreign-procedure "read" '(int ulong int) 'int)) (define (fd-read fd bytevector start count) ;;(printf "fd-read: ~a ~s ~a ~a\n" fd bytevector start count) (read% fd (+ (ffi/handle->address bytevector) bytevector-content-offset$ start) count)) (define (make-server-socket port) (let* ((args `("/bin/bash" "bash" "-c" ,(string-append "netcat -s 127.0.0.1 -q 0 -l -v " (if port (string-append "-p " (number->string port)) "")))) (nc (apply start-process args)) (err (transcoded-port (list-ref nc 2) (make-transcoder (latin-1-codec)))) (line (get-line err)) (pos (last-index-of line '#\]))) (cond (pos (let* ((tail (substring line (+ pos 1) (string-length line))) (port (get-datum (open-string-input-port tail)))) (list (car nc) (cadr nc) err port))) (#t (error "netcat failed: " line))))) (define (accept socket codec) (let* ((line (get-line (caddr socket))) (pos (last-index-of line #\]))) (cond (pos (close-port (caddr socket)) (let ((stream (cadr socket))) (let ((io (transcoded-port stream (make-transcoder codec)))) (values io io)))) (else (error "accept failed: " line))))) (define (local-port socket) (list-ref socket 3)) (define (last-index-of str chr) (let loop ((i (string-length str))) (cond ((<= i 0) #f) (#t (let ((i (- i 1))) (cond ((char=? (string-ref str i) chr) i) (#t (loop i)))))))) (define (close-socket socket) ;;(close-port (cadr socket)) #f ) ) (library (swank sys) (export implementation-name eval-in-interaction-environment) (import (rnrs) (primitives system-features aeryn-evaluator)) (define (implementation-name) "larceny") ;; see $LARCENY/r6rsmode.sch: ;; Larceny's ERR5RS and R6RS modes. ;; Code names: ;; Aeryn ERR5RS ;; D'Argo R6RS-compatible ;; Spanky R6RS-conforming (not yet implemented) (define (eval-in-interaction-environment form) (aeryn-evaluator form)) ) (import (rnrs) (rnrs eval) (larceny load)) (load "swank-r6rs.scm") (eval '(start-server #f) (environment '(swank)))