;; ;; SCSH process form notation ;; ;; See http://www.scsh.net/docu/html/man-Z-H-3.html#node_chap_2 ;; ;; Some minor changes due to Chicken- and R7RS-incompatible identifiers: ;; | was changed to pipe, |+ was changed to pipe+ ;; ;; || wasn't changed, but it's really the zero-length symbol ;; (module scsh-process ((& maybe-symbol->string) (run maybe-symbol->string) (exec-epf maybe-symbol->string) exec-path) (import chicken scheme data-structures) (use posix) (define-syntax & (syntax-rules () ((_ ?epf ...) (process-fork (lambda () (handle-exceptions exn ;; TODO: Figure out how SCSH does this. It shows the error ;; on stderr in the REPL, but then still quits it. ;; If we just invoke current-handler, it'll get a second REPL (begin (print-error-message exn) (exit 1)) (exec-epf ?epf ...))))))) (define-syntax run (syntax-rules () ((_ ?epf ...) ;; We reorder the values as they make more sense this way for SCSH compat: ;; scsh returns just the exit code, and conveniently we allow MV in single ;; value continuations, which makes it compatible. (receive (pid normal-exit? exit-status) (process-wait (& ?epf ...)) (values exit-status normal-exit? pid))))) (define (maybe-symbol->string s) (if (symbol? s) (symbol->string s) s)) ;; Perhaps this should really be a procedure? (define-syntax setup-redirection (syntax-rules (< > << >> = - stdports) ((_ (< ?file-name)) (setup-redirection (< 0 ?file-name))) ((_ (> ?file-name)) (setup-redirection (> 1 ?file-name))) ((_ (<< ?object)) (setup-redirection (<< 0 ?object))) ((_ (>> ?object)) (setup-redirection (>> 1 ?object))) ((_ (< ?fd ?file-name)) (duplicate-fileno (file-open (maybe-symbol->string `?file-name) open/rdonly) `?fd)) ((_ (> ?fd ?file-name)) (duplicate-fileno (file-open (maybe-symbol->string `?file-name) (fx+ open/wronly open/creat)) `?fd)) ((_ (<< ?fd ?object)) (error "<< currently not implemented")) ((_ (>> ?fd ?object)) (error ">> currently not implemented")) ((_ (= ?fd-from ?fd/port-to)) (let* ((fd/port-to ?fd/port-to) ; Evaluate once (fd-to (if (port? fd/port-to) (port->fileno fd/port-to) fd/port-to))) (duplicate-fileno ?fd-from fd-to))) ((_ (- ?fd/port)) (let ((o `?fd/port)) (cond ((fixnum? ?fd/port) (file-close o)) ((output-port? ?fd/port) (close-output-port o)) ((input-port? ?fd/port) (close-input-port o)) (else (error "Can only close i/o-ports and file descriptor numbers" o))))) ((_ stdports) (begin (setup-redirection (= 0 (current-input-port))) (setup-redirection (= 1 (current-output-port))) (setup-redirection (= 2 (current-error-port))))) ((_ ?arg0 ...) (syntax-error "Invalid redirection pattern: " `?arg0 ...)))) ;; The most "core" syntax form (define-syntax exec-epf (syntax-rules (begin) ((_ (begin ?expr0 ...)) (begin (setup-redirection (= 0 (current-input-port))) (setup-redirection (= 1 (current-output-port))) (setup-redirection (= 2 (current-error-port))) ?expr0 ...)) ((_ (?prog ?arg0 ...) ?redir0 ...) (begin (setup-redirection ?redir0) ... (exec-path `?prog `?arg0 ...))))) ;; TODO: Perhaps expose environment, and mess around with the path so that ;; execve can be used in a sensible way? Scsh has its own PATH, so we could ;; use something similar to that, but it's more work. (define (exec-path prog . args) (process-execute (maybe-symbol->string prog) (map maybe-symbol->string args))) )