summaryrefslogtreecommitdiff
path: root/scsh-process.scm
diff options
context:
space:
mode:
authorPeter Bex <peter@more-magic.net>2012-10-01 16:52:59 +0100
committerPeter Bex <peter@more-magic.net>2012-10-01 16:52:59 +0100
commit9a9895123961e426afbaf33b6662f190f58f0dd7 (patch)
treef54707c6229337fdd8c5ae668910876b34b7c403 /scsh-process.scm
parent13be7874d50b16ec950ee6e07e9849107314cf4d (diff)
downloadscsh-process-9a9895123961e426afbaf33b6662f190f58f0dd7.tar.gz
Provide a more complete set of primitives, and convert fit-pipes to fork/pipe+ chain
Diffstat (limited to 'scsh-process.scm')
-rw-r--r--scsh-process.scm179
1 files changed, 119 insertions, 60 deletions
diff --git a/scsh-process.scm b/scsh-process.scm
index 2e132f1..9d92ac1 100644
--- a/scsh-process.scm
+++ b/scsh-process.scm
@@ -11,12 +11,115 @@
;; BIG FAT WARNING: Don't mix this with threading, or Bad Things will happen
(module scsh-process
- ((& fit-pipes) (run fit-pipes) (exec-epf fit-pipes)
- exec-path)
+ (;; procedures
+ exec-path fork/pipe %fork/pipe fork/pipe+ %fork/pipe+
+ run/collecting* run/string* run/strings* run/port* run/file* run/sexp* run/sexps*
+
+ ;; macros
+ run/collecting run/string run/strings run/port run/file run/sexp run/sexps
+ (& maybe-symbol->string) (run maybe-symbol->string) (exec-epf maybe-symbol->string))
(import chicken scheme data-structures)
-(use posix)
+(use extras utils files ports posix srfi-1)
+
+;; 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)
+ ;; Args can include numbers and such, too! That's why we're using ->string
+ (process-execute (maybe-symbol->string prog) (map ->string args)))
+
+;; TODO: continue-threads argument
+(define (fork/pipe #!optional thunk)
+ (fork/pipe+ '((1 2 0)) thunk))
+
+(define (fork/pipe+ conns #!optional thunk)
+ ;; Blergh, this is silly overhead we don't really need
+ (let* ((from-fds (map (lambda (x) (drop-right x 1)) conns))
+ (to-fds (map last conns))
+ (pipe-pairs (map (lambda _ (receive (create-pipe))) to-fds))
+ (pid (process-fork)))
+ (if (zero? pid) ; Child
+ (begin
+ (for-each (lambda (p from-fds-for-this-p)
+ ;; Close receiving ends of pipes in child.
+ (file-close (car p))
+ ;; Set up linkage from output fds to created pipes.
+ (for-each (lambda (from-fd)
+ (duplicate-fileno (cadr p) from-fd))
+ from-fds-for-this-p))
+ pipe-pairs from-fds)
+ (if thunk (thunk) pid))
+ (begin ; Parent
+ (for-each (lambda (p to-fd)
+ ;; Close sending end in parent.
+ (file-close (cadr p))
+ ;; Set up linkage from created pipes to the input fds.
+ (duplicate-fileno (car p) to-fd))
+ pipe-pairs to-fds)
+ pid))))
+
+;; TODO: Differentiate between fork and %fork
+(define %fork/pipe fork/pipe)
+(define %fork/pipe+ fork/pipe+)
+
+(define (maybe-symbol->string s)
+ (if (symbol? s) (symbol->string s) s))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Baroque procedural interface ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Documented under http://www.scsh.net/docu/html/man-Z-H-3.html#node_sec_2.4.2
+(define (run/collecting* fds thunk)
+ (let* ((temp-files (map (lambda () (open-input-file (create-temporary-file)))
+ fds))
+ (conns (map (lambda (temp-fd from-fd)
+ (list from-fd (open-input-file* temp-fd)))
+ temp-files fds)))
+ (apply values (fork/pipe+ conns thunk) temp-files)))
+
+(define (run/port* thunk)
+ (fork/pipe (lambda () (with-output-to-port (open-output-file* 1) thunk)))
+ (open-input-file* 0))
+(define (run/file* thunk)
+ (error "not yet implemented"))
+(define (run/string* thunk)
+ (read-string #f (run/port* thunk)))
+(define (run/strings* thunk)
+ (read-lines (run/port* thunk)))
+(define (run/sexp* thunk)
+ (read (run/port* thunk)))
+(define (run/sexps* thunk)
+ (read-all (run/port* thunk)))
+
+;;;;;;;;;;;;
+;; Syntax ;;
+;;;;;;;;;;;;
+
+(define-syntax run/collecting
+ (syntax-rules ()
+ ((_ ?fds ?epf ...) (run/collecting* `?fds (lambda () (exec-epf ?epf ...))))))
+(define-syntax run/file
+ (syntax-rules ()
+ ((_ ?epf ...) (run/file* (lambda () (exec-epf ?epf ...))))))
+(define-syntax run/port
+ (syntax-rules ()
+ ((_ ?epf ...) (run/port* (lambda () (exec-epf ?epf ...))))))
+(define-syntax run/string
+ (syntax-rules ()
+ ((_ ?epf ...) (run/string* (lambda () (exec-epf ?epf ...))))))
+(define-syntax run/strings
+ (syntax-rules ()
+ ((_ ?epf ...) (run/strings* (lambda () (exec-epf ?epf ...))))))
+(define-syntax run/sexp
+ (syntax-rules ()
+ ((_ ?epf ...) (run/sexp* (lambda () (exec-epf ?epf ...))))))
+(define-syntax run/sexps
+ (syntax-rules ()
+ ((_ ?epf ...) (run/sexps* (lambda () (exec-epf ?epf ...))))))
(define-syntax &
(syntax-rules ()
@@ -39,9 +142,6 @@
(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)
@@ -79,66 +179,30 @@
((_ ?arg0 ...)
(syntax-error "Invalid redirection pattern: " `?arg0 ...))))
-(define (fit-pipes from-fds to-fds progs)
- (define (make-pipes) (map (lambda _ (receive (create-pipe))) to-fds))
- (when (null? progs) (error "Can't fit a pipeline between zero programs"))
- (let ((initial-pairs (make-pipes)))
- ;; Close sending ends of these pipes; they're unused.
- (for-each (lambda (p) (file-close (cadr p))) initial-pairs)
- (let lp ((input-pairs initial-pairs)
- (progs progs))
- (if (null? (cdr progs))
- (begin
- ;; Fit the final input pipes to their respective fds.
- (for-each (lambda (to-fd p)
- (duplicate-fileno (car p) to-fd)
- (file-close (car p)))
- to-fds input-pairs)
- ((car progs)))
- ;; Subprocess output goes into fds in output-pairs. Their matching
- ;; input fds are used as input for the next process in the pipeline.
- (let ((output-pairs (make-pipes)))
- (process-fork (lambda ()
- ;; Close receiving end in child and set up linkage
- ;; from the output descriptors to the created pipes.
- (for-each (lambda (p from-fds-for-this-p)
- (file-close (car p))
- (for-each (lambda (from-fd)
- (duplicate-fileno (cadr p) from-fd))
- from-fds-for-this-p))
- output-pairs from-fds)
- ;; Set up input descriptors
- (for-each (lambda (to-fd p)
- (duplicate-fileno (car p) to-fd)
- (file-close (car p)))
- to-fds input-pairs)
- ((car progs))))
- ;; Close sending ends of the output pairs in the parent.
- (for-each (lambda (op) (file-close (cadr op))) output-pairs)
- (lp output-pairs (cdr progs)))))))
-
;; The most "core" syntax form
(define-syntax exec-epf
;; The nested let-syntaxes exist to let us pre-empt the fallthrough
;; whenever we see one of the recognised special rules so we don't end up
;; with the generic one if we happen to make a small mistake
(syntax-rules (pipe pipe+ begin epf)
- ((_ (pipe ?pf0 ?pf1 ...))
- (exec-epf (pipe+ ((1 2 0)) ?pf0 ?pf1 ...)))
- ((_ (pipe+ ?args ...))
+ ((_ (pipe ?pf0 ...) ?redir0 ...)
+ (exec-epf (pipe+ ((1 2 0)) ?pf0 ...) ?redir0 ...))
+ ((_ (pipe+ ?args ...) ?redir0 ...)
(let-syntax
- ((exec-pipe+
+ ((pipe+
(syntax-rules ___ ()
- ((_ ((?from0 ?from1 ___ ?to) ___) (?prog0 ?arg0 ___) ___)
- (fit-pipes `((?from0 ?from1 ___) ___)
- `(?to ___)
- (list (lambda () (exec-path `?prog0 `?arg0 ___)) ___))))))
- (exec-pipe+ ?args ...)))
- ((_ (begin ?expr0 ?expr1 ...))
+ ((_ ((?from0 ?from1 ___ ?to) ___) ?pf0 ___ ?last-pf)
+ (let ((conns `((?from0 ?from1 ___ ?to) ___)))
+ (setup-redirection ?redir0) ...
+ (begin (fork/pipe+ conns (lambda () (exec-epf (epf ?pf0))))
+ ___
+ (exec-epf (epf ?last-pf))))))))
+ (pipe+ ?args ...)))
+ ((_ (begin ?expr0 ...))
(begin (setup-redirection (= 0 (current-input-port)))
(setup-redirection (= 1 (current-output-port)))
(setup-redirection (= 2 (current-error-port)))
- ?expr0 ?expr1 ...))
+ ?expr0 ...))
;; epf can be used if you happen to have a program called
;; "epf", "begin", "pipe", etc which you'd like to run.
((_ (epf ?args ...))
@@ -153,9 +217,4 @@
((_ (?prog ?arg0 ...) ?redir0 ...)
(exec-epf (epf (?prog ?arg0 ...) ?redir0 ...)))))
-;; 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)))
) \ No newline at end of file