intern arbitrary constants
[bpt/guile.git] / module / ice-9 / popen.scm
CommitLineData
51786bda
JB
1;; popen emulation, for non-stdio based ports.
2
e7bd20f7
MW
3;;;; Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, 2010, 2011, 2012,
4;;;; 2013 Free Software Foundation, Inc.
9a42b923 5;;;;
73be1d9e
MV
6;;;; This library is free software; you can redistribute it and/or
7;;;; modify it under the terms of the GNU Lesser General Public
8;;;; License as published by the Free Software Foundation; either
53befeb7 9;;;; version 3 of the License, or (at your option) any later version.
9a42b923 10;;;;
73be1d9e 11;;;; This library is distributed in the hope that it will be useful,
9a42b923 12;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
13;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14;;;; Lesser General Public License for more details.
9a42b923 15;;;;
73be1d9e
MV
16;;;; You should have received a copy of the GNU Lesser General Public
17;;;; License along with this library; if not, write to the Free Software
92205699 18;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
9a42b923
MV
19;;;;
20
1a179b03 21(define-module (ice-9 popen)
e7bd20f7
MW
22 :use-module (ice-9 threads)
23 :use-module (srfi srfi-9)
0f3eb627
MV
24 :export (port/pid-table open-pipe* open-pipe close-pipe open-input-pipe
25 open-output-pipe open-input-output-pipe))
26
f6ddf827 27(eval-when (expand load eval)
a2e946f1
AW
28 (load-extension (string-append "libguile-" (effective-version))
29 "scm_init_popen"))
30
e7bd20f7
MW
31(define-record-type <pipe-info>
32 (make-pipe-info pid)
33 pipe-info?
34 (pid pipe-info-pid set-pipe-info-pid!))
35
0f3eb627
MV
36(define (make-rw-port read-port write-port)
37 (make-soft-port
38 (vector
39 (lambda (c) (write-char c write-port))
40 (lambda (s) (display s write-port))
41 (lambda () (force-output write-port))
42 (lambda () (read-char read-port))
43 (lambda () (close-port read-port) (close-port write-port)))
44 "r+"))
51786bda
JB
45
46;; a guardian to ensure the cleanup is done correctly when
47;; an open pipe is gc'd or a close-port is used.
48(define pipe-guardian (make-guardian))
49
50;; a weak hash-table to store the process ids.
e7bd20f7
MW
51;; XXX use of this table is deprecated. It is no longer used here, and
52;; is populated for backward compatibility only (since it is exported).
1a179b03 53(define port/pid-table (make-weak-key-hash-table 31))
e7bd20f7 54(define port/pid-table-mutex (make-mutex))
51786bda 55
0f3eb627
MV
56(define (open-pipe* mode command . args)
57 "Executes the program @var{command} with optional arguments
58@var{args} (all strings) in a subprocess.
59A port to the process (based on pipes) is created and returned.
91a214eb 60@var{mode} specifies whether an input, an output or an input-output
0f3eb627
MV
61port to the process is created: it should be the value of
62@code{OPEN_READ}, @code{OPEN_WRITE} or @code{OPEN_BOTH}."
03a2f598
AW
63 (call-with-values (lambda ()
64 (apply open-process mode command args))
65 (lambda (read-port write-port pid)
66 (let ((port (or (and read-port write-port
67 (make-rw-port read-port write-port))
68 read-port
69 write-port
e7bd20f7
MW
70 (%make-void-port mode)))
71 (pipe-info (make-pipe-info pid)))
72
73 ;; Guard the pipe-info instead of the port, so that we can still
74 ;; call 'waitpid' even if 'close-port' is called (which clears
75 ;; the port entry).
76 (pipe-guardian pipe-info)
77 (%set-port-property! port 'popen-pipe-info pipe-info)
78
79 ;; XXX populate port/pid-table for backward compatibility.
80 (with-mutex port/pid-table-mutex
81 (hashq-set! port/pid-table port pid))
82
03a2f598 83 port))))
51786bda 84
0f3eb627
MV
85(define (open-pipe command mode)
86 "Executes the shell command @var{command} (a string) in a subprocess.
87A port to the process (based on pipes) is created and returned.
91a214eb 88@var{mode} specifies whether an input, an output or an input-output
0f3eb627
MV
89port to the process is created: it should be the value of
90@code{OPEN_READ}, @code{OPEN_WRITE} or @code{OPEN_BOTH}."
91 (open-pipe* mode "/bin/sh" "-c" command))
92
e7bd20f7
MW
93(define (fetch-pipe-info port)
94 (%port-property port 'popen-pipe-info))
51786bda 95
17330398
MW
96(define (close-process port pid)
97 (close-port port)
98 (cdr (waitpid pid)))
51786bda 99
1a179b03 100(define (close-pipe p)
ea4bcd7b
GB
101 "Closes the pipe created by @code{open-pipe}, then waits for the process
102to terminate and returns its status value, @xref{Processes, waitpid}, for
103information on how to interpret this value."
e7bd20f7
MW
104 (let ((pipe-info (fetch-pipe-info p)))
105 (unless pipe-info
106 (error "close-pipe: port not created by (ice-9 popen)"))
107 (let ((pid (pipe-info-pid pipe-info)))
108 (unless pid
109 (error "close-pipe: pid has already been cleared"))
110 ;; clear the pid to avoid repeated calls to 'waitpid'.
111 (set-pipe-info-pid! pipe-info #f)
112 (close-process p pid))))
17330398
MW
113
114(define (reap-pipes)
115 (let loop ()
e7bd20f7
MW
116 (let ((pipe-info (pipe-guardian)))
117 (when pipe-info
118 (let ((pid (pipe-info-pid pipe-info)))
119 ;; maybe 'close-pipe' was already called.
120 (when pid
121 ;; clean up without reporting errors. also avoids blocking
122 ;; the process: if the child isn't ready to be collected,
123 ;; puts it back into the guardian's live list so it can be
124 ;; tried again the next time the cleanup runs.
125 (catch 'system-error
126 (lambda ()
127 (let ((pid/status (waitpid pid WNOHANG)))
128 (if (zero? (car pid/status))
129 (pipe-guardian pipe-info) ; not ready for collection
130 (set-pipe-info-pid! pipe-info #f))))
131 (lambda args #f))))
17330398 132 (loop)))))
51786bda 133
ac373580 134(add-hook! after-gc-hook reap-pipes)
51786bda 135
1a179b03 136(define (open-input-pipe command)
a9c632a2
GH
137 "Equivalent to @code{open-pipe} with mode @code{OPEN_READ}"
138 (open-pipe command OPEN_READ))
139
1a179b03 140(define (open-output-pipe command)
a9c632a2
GH
141 "Equivalent to @code{open-pipe} with mode @code{OPEN_WRITE}"
142 (open-pipe command OPEN_WRITE))
0f3eb627
MV
143
144(define (open-input-output-pipe command)
145 "Equivalent to @code{open-pipe} with mode @code{OPEN_BOTH}"
146 (open-pipe command OPEN_BOTH))
a2e946f1 147