summaryrefslogtreecommitdiff
path: root/scsh-process.scm
blob: 74727ce20a4bc03ed47dcc1478f09231cd1efbd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
;;
;; 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)))
)