trun-program.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
(HTM) git clone git://bitreich.org/clic/ git://hg6vgqziawt5s4dj.onion/clic/
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) LICENSE
---
trun-program.lisp (30529B)
---
1 ;;;; -------------------------------------------------------------------------
2 ;;;; run-program initially from xcvb-driver.
3
4 (uiop/package:define-package :uiop/run-program
5 (:nicknames :asdf/run-program) ; OBSOLETE. Used by cl-sane, printv.
6 (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/version
7 :uiop/pathname :uiop/os :uiop/filesystem :uiop/stream :uiop/launch-program)
8 (:export
9 #:run-program
10 #:slurp-input-stream #:vomit-output-stream
11 #:subprocess-error
12 #:subprocess-error-code #:subprocess-error-command #:subprocess-error-process)
13 (:import-from :uiop/launch-program
14 #:%handle-if-does-not-exist #:%handle-if-exists #:%interactivep
15 #:input-stream #:output-stream #:error-output-stream))
16 (in-package :uiop/run-program)
17
18 ;;;; Slurping a stream, typically the output of another program
19 (with-upgradability ()
20 (defun call-stream-processor (fun processor stream)
21 "Given FUN (typically SLURP-INPUT-STREAM or VOMIT-OUTPUT-STREAM,
22 a PROCESSOR specification which is either an atom or a list specifying
23 a processor an keyword arguments, call the specified processor with
24 the given STREAM as input"
25 (if (consp processor)
26 (apply fun (first processor) stream (rest processor))
27 (funcall fun processor stream)))
28
29 (defgeneric slurp-input-stream (processor input-stream &key)
30 (:documentation
31 "SLURP-INPUT-STREAM is a generic function with two positional arguments
32 PROCESSOR and INPUT-STREAM and additional keyword arguments, that consumes (slurps)
33 the contents of the INPUT-STREAM and processes them according to a method
34 specified by PROCESSOR.
35
36 Built-in methods include the following:
37 * if PROCESSOR is a function, it is called with the INPUT-STREAM as its argument
38 * if PROCESSOR is a list, its first element should be a function. It will be applied to a cons of the
39 INPUT-STREAM and the rest of the list. That is (x . y) will be treated as
40 \(APPLY x <stream> y\)
41 * if PROCESSOR is an output-stream, the contents of INPUT-STREAM is copied to the output-stream,
42 per copy-stream-to-stream, with appropriate keyword arguments.
43 * if PROCESSOR is the symbol CL:STRING or the keyword :STRING, then the contents of INPUT-STREAM
44 are returned as a string, as per SLURP-STREAM-STRING.
45 * if PROCESSOR is the keyword :LINES then the INPUT-STREAM will be handled by SLURP-STREAM-LINES.
46 * if PROCESSOR is the keyword :LINE then the INPUT-STREAM will be handled by SLURP-STREAM-LINE.
47 * if PROCESSOR is the keyword :FORMS then the INPUT-STREAM will be handled by SLURP-STREAM-FORMS.
48 * if PROCESSOR is the keyword :FORM then the INPUT-STREAM will be handled by SLURP-STREAM-FORM.
49 * if PROCESSOR is T, it is treated the same as *standard-output*. If it is NIL, NIL is returned.
50
51 Programmers are encouraged to define their own methods for this generic function."))
52
53 #-genera
54 (defmethod slurp-input-stream ((function function) input-stream &key)
55 (funcall function input-stream))
56
57 (defmethod slurp-input-stream ((list cons) input-stream &key)
58 (apply (first list) input-stream (rest list)))
59
60 #-genera
61 (defmethod slurp-input-stream ((output-stream stream) input-stream
62 &key linewise prefix (element-type 'character) buffer-size)
63 (copy-stream-to-stream
64 input-stream output-stream
65 :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
66
67 (defmethod slurp-input-stream ((x (eql 'string)) stream &key stripped)
68 (slurp-stream-string stream :stripped stripped))
69
70 (defmethod slurp-input-stream ((x (eql :string)) stream &key stripped)
71 (slurp-stream-string stream :stripped stripped))
72
73 (defmethod slurp-input-stream ((x (eql :lines)) stream &key count)
74 (slurp-stream-lines stream :count count))
75
76 (defmethod slurp-input-stream ((x (eql :line)) stream &key (at 0))
77 (slurp-stream-line stream :at at))
78
79 (defmethod slurp-input-stream ((x (eql :forms)) stream &key count)
80 (slurp-stream-forms stream :count count))
81
82 (defmethod slurp-input-stream ((x (eql :form)) stream &key (at 0))
83 (slurp-stream-form stream :at at))
84
85 (defmethod slurp-input-stream ((x (eql t)) stream &rest keys &key &allow-other-keys)
86 (apply 'slurp-input-stream *standard-output* stream keys))
87
88 (defmethod slurp-input-stream ((x null) (stream t) &key)
89 nil)
90
91 (defmethod slurp-input-stream ((pathname pathname) input
92 &key
93 (element-type *default-stream-element-type*)
94 (external-format *utf-8-external-format*)
95 (if-exists :rename-and-delete)
96 (if-does-not-exist :create)
97 buffer-size
98 linewise)
99 (with-output-file (output pathname
100 :element-type element-type
101 :external-format external-format
102 :if-exists if-exists
103 :if-does-not-exist if-does-not-exist)
104 (copy-stream-to-stream
105 input output
106 :element-type element-type :buffer-size buffer-size :linewise linewise)))
107
108 (defmethod slurp-input-stream (x stream
109 &key linewise prefix (element-type 'character) buffer-size)
110 (declare (ignorable stream linewise prefix element-type buffer-size))
111 (cond
112 #+genera
113 ((functionp x) (funcall x stream))
114 #+genera
115 ((output-stream-p x)
116 (copy-stream-to-stream
117 stream x
118 :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
119 (t
120 (parameter-error "Invalid ~S destination ~S" 'slurp-input-stream x)))))
121
122 ;;;; Vomiting a stream, typically into the input of another program.
123 (with-upgradability ()
124 (defgeneric vomit-output-stream (processor output-stream &key)
125 (:documentation
126 "VOMIT-OUTPUT-STREAM is a generic function with two positional arguments
127 PROCESSOR and OUTPUT-STREAM and additional keyword arguments, that produces (vomits)
128 some content onto the OUTPUT-STREAM, according to a method specified by PROCESSOR.
129
130 Built-in methods include the following:
131 * if PROCESSOR is a function, it is called with the OUTPUT-STREAM as its argument
132 * if PROCESSOR is a list, its first element should be a function.
133 It will be applied to a cons of the OUTPUT-STREAM and the rest of the list.
134 That is (x . y) will be treated as \(APPLY x <stream> y\)
135 * if PROCESSOR is an input-stream, its contents will be copied the OUTPUT-STREAM,
136 per copy-stream-to-stream, with appropriate keyword arguments.
137 * if PROCESSOR is a string, its contents will be printed to the OUTPUT-STREAM.
138 * if PROCESSOR is T, it is treated the same as *standard-input*. If it is NIL, nothing is done.
139
140 Programmers are encouraged to define their own methods for this generic function."))
141
142 #-genera
143 (defmethod vomit-output-stream ((function function) output-stream &key)
144 (funcall function output-stream))
145
146 (defmethod vomit-output-stream ((list cons) output-stream &key)
147 (apply (first list) output-stream (rest list)))
148
149 #-genera
150 (defmethod vomit-output-stream ((input-stream stream) output-stream
151 &key linewise prefix (element-type 'character) buffer-size)
152 (copy-stream-to-stream
153 input-stream output-stream
154 :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
155
156 (defmethod vomit-output-stream ((x string) stream &key fresh-line terpri)
157 (princ x stream)
158 (when fresh-line (fresh-line stream))
159 (when terpri (terpri stream))
160 (values))
161
162 (defmethod vomit-output-stream ((x (eql t)) stream &rest keys &key &allow-other-keys)
163 (apply 'vomit-output-stream *standard-input* stream keys))
164
165 (defmethod vomit-output-stream ((x null) (stream t) &key)
166 (values))
167
168 (defmethod vomit-output-stream ((pathname pathname) input
169 &key
170 (element-type *default-stream-element-type*)
171 (external-format *utf-8-external-format*)
172 (if-exists :rename-and-delete)
173 (if-does-not-exist :create)
174 buffer-size
175 linewise)
176 (with-output-file (output pathname
177 :element-type element-type
178 :external-format external-format
179 :if-exists if-exists
180 :if-does-not-exist if-does-not-exist)
181 (copy-stream-to-stream
182 input output
183 :element-type element-type :buffer-size buffer-size :linewise linewise)))
184
185 (defmethod vomit-output-stream (x stream
186 &key linewise prefix (element-type 'character) buffer-size)
187 (declare (ignorable stream linewise prefix element-type buffer-size))
188 (cond
189 #+genera
190 ((functionp x) (funcall x stream))
191 #+genera
192 ((input-stream-p x)
193 (copy-stream-to-stream
194 x stream
195 :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
196 (t
197 (parameter-error "Invalid ~S source ~S" 'vomit-output-stream x)))))
198
199
200 ;;;; Run-program: synchronously run a program in a subprocess, handling input, output and error-output.
201 (with-upgradability ()
202 (define-condition subprocess-error (error)
203 ((code :initform nil :initarg :code :reader subprocess-error-code)
204 (command :initform nil :initarg :command :reader subprocess-error-command)
205 (process :initform nil :initarg :process :reader subprocess-error-process))
206 (:report (lambda (condition stream)
207 (format stream "Subprocess ~@[~S~% ~]~@[with command ~S~% ~]exited with error~@[ code ~D~]"
208 (subprocess-error-process condition)
209 (subprocess-error-command condition)
210 (subprocess-error-code condition)))))
211
212 (defun %check-result (exit-code &key command process ignore-error-status)
213 (unless ignore-error-status
214 (unless (eql exit-code 0)
215 (cerror "IGNORE-ERROR-STATUS"
216 'subprocess-error :command command :code exit-code :process process)))
217 exit-code)
218
219 (defun %active-io-specifier-p (specifier)
220 "Determines whether a run-program I/O specifier requires Lisp-side processing
221 via SLURP-INPUT-STREAM or VOMIT-OUTPUT-STREAM (return T),
222 or whether it's already taken care of by the implementation's underlying run-program."
223 (not (typep specifier '(or null string pathname (member :interactive :output)
224 #+(or cmucl (and sbcl os-unix) scl) (or stream (eql t))
225 #+lispworks file-stream))))
226
227 (defun %run-program (command &rest keys &key &allow-other-keys)
228 "DEPRECATED. Use LAUNCH-PROGRAM instead."
229 (apply 'launch-program command keys))
230
231 (defun %call-with-program-io (gf tval stream-easy-p fun direction spec activep returner
232 &key
233 (element-type #-clozure *default-stream-element-type* #+clozure 'character)
234 (external-format *utf-8-external-format*) &allow-other-keys)
235 ;; handle redirection for run-program and system
236 ;; SPEC is the specification for the subprocess's input or output or error-output
237 ;; TVAL is the value used if the spec is T
238 ;; GF is the generic function to call to handle arbitrary values of SPEC
239 ;; STREAM-EASY-P is T if we're going to use a RUN-PROGRAM that copies streams in the background
240 ;; (it's only meaningful on CMUCL, SBCL, SCL that actually do it)
241 ;; DIRECTION is :INPUT, :OUTPUT or :ERROR-OUTPUT for the direction of this io argument
242 ;; FUN is a function of the new reduced spec and an activity function to call with a stream
243 ;; when the subprocess is active and communicating through that stream.
244 ;; ACTIVEP is a boolean true if we will get to run code while the process is running
245 ;; ELEMENT-TYPE and EXTERNAL-FORMAT control what kind of temporary file we may open.
246 ;; RETURNER is a function called with the value of the activity.
247 ;; --- TODO (fare@tunes.org): handle if-output-exists and such when doing it the hard way.
248 (declare (ignorable stream-easy-p))
249 (let* ((actual-spec (if (eq spec t) tval spec))
250 (activity-spec (if (eq actual-spec :output)
251 (ecase direction
252 ((:input :output)
253 (parameter-error "~S does not allow ~S as a ~S spec"
254 'run-program :output direction))
255 ((:error-output)
256 nil))
257 actual-spec)))
258 (labels ((activity (stream)
259 (call-function returner (call-stream-processor gf activity-spec stream)))
260 (easy-case ()
261 (funcall fun actual-spec nil))
262 (hard-case ()
263 (if activep
264 (funcall fun :stream #'activity)
265 (with-temporary-file (:pathname tmp)
266 (ecase direction
267 (:input
268 (with-output-file (s tmp :if-exists :overwrite
269 :external-format external-format
270 :element-type element-type)
271 (activity s))
272 (funcall fun tmp nil))
273 ((:output :error-output)
274 (multiple-value-prog1 (funcall fun tmp nil)
275 (with-input-file (s tmp
276 :external-format external-format
277 :element-type element-type)
278 (activity s)))))))))
279 (typecase activity-spec
280 ((or null string pathname (eql :interactive))
281 (easy-case))
282 #+(or cmucl (and sbcl os-unix) scl) ;; streams are only easy on implementations that try very hard
283 (stream
284 (if stream-easy-p (easy-case) (hard-case)))
285 (t
286 (hard-case))))))
287
288 (defmacro place-setter (place)
289 (when place
290 (let ((value (gensym)))
291 `#'(lambda (,value) (setf ,place ,value)))))
292
293 (defmacro with-program-input (((reduced-input-var
294 &optional (input-activity-var (gensym) iavp))
295 input-form &key setf stream-easy-p active keys) &body body)
296 `(apply '%call-with-program-io 'vomit-output-stream *standard-input* ,stream-easy-p
297 #'(lambda (,reduced-input-var ,input-activity-var)
298 ,@(unless iavp `((declare (ignore ,input-activity-var))))
299 ,@body)
300 :input ,input-form ,active (place-setter ,setf) ,keys))
301
302 (defmacro with-program-output (((reduced-output-var
303 &optional (output-activity-var (gensym) oavp))
304 output-form &key setf stream-easy-p active keys) &body body)
305 `(apply '%call-with-program-io 'slurp-input-stream *standard-output* ,stream-easy-p
306 #'(lambda (,reduced-output-var ,output-activity-var)
307 ,@(unless oavp `((declare (ignore ,output-activity-var))))
308 ,@body)
309 :output ,output-form ,active (place-setter ,setf) ,keys))
310
311 (defmacro with-program-error-output (((reduced-error-output-var
312 &optional (error-output-activity-var (gensym) eoavp))
313 error-output-form &key setf stream-easy-p active keys)
314 &body body)
315 `(apply '%call-with-program-io 'slurp-input-stream *error-output* ,stream-easy-p
316 #'(lambda (,reduced-error-output-var ,error-output-activity-var)
317 ,@(unless eoavp `((declare (ignore ,error-output-activity-var))))
318 ,@body)
319 :error-output ,error-output-form ,active (place-setter ,setf) ,keys))
320
321 (defun %use-launch-program (command &rest keys
322 &key input output error-output ignore-error-status &allow-other-keys)
323 ;; helper for RUN-PROGRAM when using LAUNCH-PROGRAM
324 #+(or cormanlisp gcl (and lispworks os-windows) mcl xcl)
325 (progn
326 command keys input output error-output ignore-error-status ;; ignore
327 (not-implemented-error '%use-launch-program))
328 (when (member :stream (list input output error-output))
329 (parameter-error "~S: ~S is not allowed as synchronous I/O redirection argument"
330 'run-program :stream))
331 (let* ((active-input-p (%active-io-specifier-p input))
332 (active-output-p (%active-io-specifier-p output))
333 (active-error-output-p (%active-io-specifier-p error-output))
334 (activity
335 (cond
336 (active-output-p :output)
337 (active-input-p :input)
338 (active-error-output-p :error-output)
339 (t nil)))
340 output-result error-output-result exit-code process-info)
341 (with-program-output ((reduced-output output-activity)
342 output :keys keys :setf output-result
343 :stream-easy-p t :active (eq activity :output))
344 (with-program-error-output ((reduced-error-output error-output-activity)
345 error-output :keys keys :setf error-output-result
346 :stream-easy-p t :active (eq activity :error-output))
347 (with-program-input ((reduced-input input-activity)
348 input :keys keys
349 :stream-easy-p t :active (eq activity :input))
350 (setf process-info
351 (apply 'launch-program command
352 :input reduced-input :output reduced-output
353 :error-output (if (eq error-output :output) :output reduced-error-output)
354 keys))
355 (labels ((get-stream (stream-name &optional fallbackp)
356 (or (slot-value process-info stream-name)
357 (when fallbackp
358 (slot-value process-info 'bidir-stream))))
359 (run-activity (activity stream-name &optional fallbackp)
360 (if-let (stream (get-stream stream-name fallbackp))
361 (funcall activity stream)
362 (error 'subprocess-error
363 :code `(:missing ,stream-name)
364 :command command :process process-info))))
365 (unwind-protect
366 (ecase activity
367 ((nil))
368 (:input (run-activity input-activity 'input-stream t))
369 (:output (run-activity output-activity 'output-stream t))
370 (:error-output (run-activity error-output-activity 'error-output-stream)))
371 (close-streams process-info)
372 (setf exit-code (wait-process process-info)))))))
373 (%check-result exit-code
374 :command command :process process-info
375 :ignore-error-status ignore-error-status)
376 (values output-result error-output-result exit-code)))
377
378 (defun %normalize-system-command (command) ;; helper for %USE-SYSTEM
379 (etypecase command
380 (string command)
381 (list (escape-shell-command
382 (os-cond
383 ((os-unix-p) (cons "exec" command))
384 (t command))))))
385
386 (defun %redirected-system-command (command in out err directory) ;; helper for %USE-SYSTEM
387 (flet ((redirect (spec operator)
388 (let ((pathname
389 (typecase spec
390 (null (null-device-pathname))
391 (string (parse-native-namestring spec))
392 (pathname spec)
393 ((eql :output)
394 (unless (equal operator " 2>>")
395 (parameter-error "~S: only the ~S argument can be ~S"
396 'run-program :error-output :output))
397 (return-from redirect '(" 2>&1"))))))
398 (when pathname
399 (list operator " "
400 (escape-shell-token (native-namestring pathname)))))))
401 (let* ((redirections (append (redirect in " <") (redirect out " >>") (redirect err " 2>>")))
402 (normalized (%normalize-system-command command))
403 (directory (or directory #+(or abcl xcl) (getcwd)))
404 (chdir (when directory
405 (let ((dir-arg (escape-shell-token (native-namestring directory))))
406 (os-cond
407 ((os-unix-p) `("cd " ,dir-arg " ; "))
408 ((os-windows-p) `("cd /d " ,dir-arg " & ")))))))
409 (reduce/strcat
410 (os-cond
411 ((os-unix-p) `(,@(when redirections `("exec " ,@redirections " ; ")) ,@chdir ,normalized))
412 ((os-windows-p) `(,@redirections " (" ,@chdir ,normalized ")")))))))
413
414 (defun %system (command &rest keys &key directory
415 input (if-input-does-not-exist :error)
416 output (if-output-exists :supersede)
417 error-output (if-error-output-exists :supersede)
418 &allow-other-keys)
419 "A portable abstraction of a low-level call to libc's system()."
420 (declare (ignorable keys directory input if-input-does-not-exist output
421 if-output-exists error-output if-error-output-exists))
422 (when (member :stream (list input output error-output))
423 (parameter-error "~S: ~S is not allowed as synchronous I/O redirection argument"
424 'run-program :stream))
425 #+(or abcl allegro clozure cmucl ecl (and lispworks os-unix) mkcl sbcl scl)
426 (let (#+(or abcl ecl mkcl)
427 (version (parse-version
428 #-abcl
429 (lisp-implementation-version)
430 #+abcl
431 (second (split-string (implementation-identifier) :separator '(#\-))))))
432 (nest
433 #+abcl (unless (lexicographic< '< version '(1 4 0)))
434 #+ecl (unless (lexicographic<= '< version '(16 0 0)))
435 #+mkcl (unless (lexicographic<= '< version '(1 1 9)))
436 (return-from %system
437 (wait-process
438 (apply 'launch-program (%normalize-system-command command) keys)))))
439 #+(or abcl clasp clisp cormanlisp ecl gcl genera (and lispworks os-windows) mkcl xcl)
440 (let ((%command (%redirected-system-command command input output error-output directory)))
441 ;; see comments for these functions
442 (%handle-if-does-not-exist input if-input-does-not-exist)
443 (%handle-if-exists output if-output-exists)
444 (%handle-if-exists error-output if-error-output-exists)
445 #+abcl (ext:run-shell-command %command)
446 #+(or clasp ecl) (let ((*standard-input* *stdin*)
447 (*standard-output* *stdout*)
448 (*error-output* *stderr*))
449 (ext:system %command))
450 #+clisp
451 (let ((raw-exit-code
452 (or
453 #.`(#+os-windows ,@'(ext:run-shell-command %command)
454 #+os-unix ,@'(ext:run-program "/bin/sh" :arguments `("-c" ,%command))
455 :wait t :input :terminal :output :terminal)
456 0)))
457 (if (minusp raw-exit-code)
458 (- 128 raw-exit-code)
459 raw-exit-code))
460 #+cormanlisp (win32:system %command)
461 #+gcl (system:system %command)
462 #+genera (not-implemented-error '%system)
463 #+(and lispworks os-windows)
464 (system:call-system %command :current-directory directory :wait t)
465 #+mcl (ccl::with-cstrs ((%%command %command)) (_system %%command))
466 #+mkcl (mkcl:system %command)
467 #+xcl (system:%run-shell-command %command)))
468
469 (defun %use-system (command &rest keys
470 &key input output error-output ignore-error-status &allow-other-keys)
471 ;; helper for RUN-PROGRAM when using %system
472 (let (output-result error-output-result exit-code)
473 (with-program-output ((reduced-output)
474 output :keys keys :setf output-result)
475 (with-program-error-output ((reduced-error-output)
476 error-output :keys keys :setf error-output-result)
477 (with-program-input ((reduced-input) input :keys keys)
478 (setf exit-code (apply '%system command
479 :input reduced-input :output reduced-output
480 :error-output reduced-error-output keys)))))
481 (%check-result exit-code
482 :command command
483 :ignore-error-status ignore-error-status)
484 (values output-result error-output-result exit-code)))
485
486 (defun run-program (command &rest keys
487 &key ignore-error-status (force-shell nil force-shell-suppliedp)
488 input (if-input-does-not-exist :error)
489 output (if-output-exists :supersede)
490 error-output (if-error-output-exists :supersede)
491 (element-type #-clozure *default-stream-element-type* #+clozure 'character)
492 (external-format *utf-8-external-format*)
493 &allow-other-keys)
494 "Run program specified by COMMAND,
495 either a list of strings specifying a program and list of arguments,
496 or a string specifying a shell command (/bin/sh on Unix, CMD.EXE on Windows);
497 _synchronously_ process its output as specified and return the processing results
498 when the program and its output processing are complete.
499
500 Always call a shell (rather than directly execute the command when possible)
501 if FORCE-SHELL is specified. Similarly, never call a shell if FORCE-SHELL is
502 specified to be NIL.
503
504 Signal a continuable SUBPROCESS-ERROR if the process wasn't successful (exit-code 0),
505 unless IGNORE-ERROR-STATUS is specified.
506
507 If OUTPUT is a pathname, a string designating a pathname, or NIL (the default)
508 designating the null device, the file at that path is used as output.
509 If it's :INTERACTIVE, output is inherited from the current process;
510 beware that this may be different from your *STANDARD-OUTPUT*,
511 and under SLIME will be on your *inferior-lisp* buffer.
512 If it's T, output goes to your current *STANDARD-OUTPUT* stream.
513 Otherwise, OUTPUT should be a value that is a suitable first argument to
514 SLURP-INPUT-STREAM (qv.), or a list of such a value and keyword arguments.
515 In this case, RUN-PROGRAM will create a temporary stream for the program output;
516 the program output, in that stream, will be processed by a call to SLURP-INPUT-STREAM,
517 using OUTPUT as the first argument (or the first element of OUTPUT, and the rest as keywords).
518 The primary value resulting from that call (or NIL if no call was needed)
519 will be the first value returned by RUN-PROGRAM.
520 E.g., using :OUTPUT :STRING will have it return the entire output stream as a string.
521 And using :OUTPUT '(:STRING :STRIPPED T) will have it return the same string
522 stripped of any ending newline.
523
524 IF-OUTPUT-EXISTS, which is only meaningful if OUTPUT is a string or a
525 pathname, can take the values :ERROR, :APPEND, and :SUPERSEDE (the
526 default). The meaning of these values and their effect on the case
527 where OUTPUT does not exist, is analogous to the IF-EXISTS parameter
528 to OPEN with :DIRECTION :OUTPUT.
529
530 ERROR-OUTPUT is similar to OUTPUT, except that the resulting value is returned
531 as the second value of RUN-PROGRAM. T designates the *ERROR-OUTPUT*.
532 Also :OUTPUT means redirecting the error output to the output stream,
533 in which case NIL is returned.
534
535 IF-ERROR-OUTPUT-EXISTS is similar to IF-OUTPUT-EXIST, except that it
536 affects ERROR-OUTPUT rather than OUTPUT.
537
538 INPUT is similar to OUTPUT, except that VOMIT-OUTPUT-STREAM is used,
539 no value is returned, and T designates the *STANDARD-INPUT*.
540
541 IF-INPUT-DOES-NOT-EXIST, which is only meaningful if INPUT is a string
542 or a pathname, can take the values :CREATE and :ERROR (the
543 default). The meaning of these values is analogous to the
544 IF-DOES-NOT-EXIST parameter to OPEN with :DIRECTION :INPUT.
545
546 ELEMENT-TYPE and EXTERNAL-FORMAT are passed on
547 to your Lisp implementation, when applicable, for creation of the output stream.
548
549 One and only one of the stream slurping or vomiting may or may not happen
550 in parallel in parallel with the subprocess,
551 depending on options and implementation,
552 and with priority being given to output processing.
553 Other streams are completely produced or consumed
554 before or after the subprocess is spawned, using temporary files.
555
556 RUN-PROGRAM returns 3 values:
557 0- the result of the OUTPUT slurping if any, or NIL
558 1- the result of the ERROR-OUTPUT slurping if any, or NIL
559 2- either 0 if the subprocess exited with success status,
560 or an indication of failure via the EXIT-CODE of the process"
561 (declare (ignorable input output error-output if-input-does-not-exist if-output-exists
562 if-error-output-exists element-type external-format ignore-error-status))
563 #-(or abcl allegro clasp clisp clozure cmucl cormanlisp ecl gcl lispworks mcl mkcl sbcl scl xcl)
564 (not-implemented-error 'run-program)
565 (apply (if (or force-shell
566 ;; Per doc string, set FORCE-SHELL to T if we get command as a string.
567 ;; But don't override user's specified preference. [2015/06/29:rpg]
568 (and (stringp command)
569 (or (not force-shell-suppliedp)
570 #-(or allegro clisp clozure sbcl) (os-cond ((os-windows-p) t))))
571 #+(or clasp clisp cormanlisp gcl (and lispworks os-windows) mcl xcl) t
572 ;; A race condition in ECL <= 16.0.0 prevents using ext:run-program
573 #+ecl #.(if-let (ver (parse-version (lisp-implementation-version)))
574 (lexicographic<= '< ver '(16 0 0)))
575 #+(and lispworks os-unix) (%interactivep input output error-output))
576 '%use-system '%use-launch-program)
577 command keys)))
578