run-program.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- run-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