diff --git a/uiop/stream.lisp b/uiop/stream.lisp index 80051980e8d63bb7b40dd6b3db470848cfc64121..c138626a41f1359b3e5b53cae5b0428895b63b29 100644 --- a/uiop/stream.lisp +++ b/uiop/stream.lisp @@ -163,15 +163,38 @@ It must never be modified, though only good implementations will even enforce th (with-safe-io-syntax (:package package) (read-from-string string eof-error-p eof-value :start start :end end :preserve-whitespace preserve-whitespace)))) -;;; Output to a stream or string, FORMAT-style +;;; Output helpers (with-upgradability () - (defun call-with-output (output function) + (defun call-with-output-file (pathname thunk + &key + (element-type *default-stream-element-type*) + (external-format *utf-8-external-format*) + (if-exists :error) + (if-does-not-exist :create)) + "Open FILE for input with given recognizes options, call THUNK with the resulting stream. +Other keys are accepted but discarded." + (with-open-file (s pathname :direction :output + :element-type element-type + :external-format external-format + :if-exists if-exists + :if-does-not-exist if-does-not-exist) + (funcall thunk s))) + + (defmacro with-output-file ((var pathname &rest keys + &key element-type external-format if-exists if-does-not-exist) + &body body) + (declare (ignore element-type external-format if-exists if-does-not-exist)) + `(call-with-output-file ,pathname #'(lambda (,var) ,@body) ,@keys)) + + (defun call-with-output (output function &key keys) "Calls FUNCTION with an actual stream argument, behaving like FORMAT with respect to how stream designators are interpreted: -If OUTPUT is a stream, use it as the stream. +If OUTPUT is a STREAM, use it as the stream. If OUTPUT is NIL, use a STRING-OUTPUT-STREAM as the stream, and return the resulting string. If OUTPUT is T, use *STANDARD-OUTPUT* as the stream. -If OUTPUT is a string with a fill-pointer, use it as a string-output-stream. +If OUTPUT is a STRING with a fill-pointer, use it as a string-output-stream. +If OUTPUT is a PATHNAME, open the file and write to it, passing KEYS to WITH-OUTPUT-FILE +-- this latter as an extension since ASDF 3.1. Otherwise, signal an error." (etypecase output (null @@ -182,7 +205,9 @@ Otherwise, signal an error." (funcall function output)) (string (assert (fill-pointer output)) - (with-output-to-string (stream output) (funcall function stream))))) + (with-output-to-string (stream output) (funcall function stream))) + (pathname + (apply 'call-with-output-file output function keys)))) (defmacro with-output ((output-var &optional (value output-var)) &body body) "Bind OUTPUT-VAR to an output stream, coercing VALUE (default: previous binding of OUTPUT-VAR) @@ -198,25 +223,6 @@ as per FORMAT, and evaluate BODY within the scope of this binding." ;;; Input helpers (with-upgradability () - (defun call-with-input (input function) - "Calls FUNCTION with an actual stream argument, interpreting -stream designators like READ, but also coercing strings to STRING-INPUT-STREAM. -If INPUT is a STREAM, use it as the stream. -If INPUT is NIL, use a *STANDARD-INPUT* as the stream. -If INPUT is T, use *TERMINAL-IO* as the stream. -As an extension, if INPUT is a string, use it as a string-input-stream. -Otherwise, signal an error." - (etypecase input - (null (funcall function *standard-input*)) - ((eql t) (funcall function *terminal-io*)) - (stream (funcall function input)) - (string (with-input-from-string (stream input) (funcall function stream))))) - - (defmacro with-input ((input-var &optional (value input-var)) &body body) - "Bind INPUT-VAR to an input stream, coercing VALUE (default: previous binding of INPUT-VAR) -as per CALL-WITH-INPUT, and evaluate BODY within the scope of this binding." - `(call-with-input ,value #'(lambda (,input-var) ,@body))) - (defun call-with-input-file (pathname thunk &key (element-type *default-stream-element-type*) @@ -236,26 +242,29 @@ Other keys are accepted but discarded." (declare (ignore element-type external-format if-does-not-exist)) `(call-with-input-file ,pathname #'(lambda (,var) ,@body) ,@keys)) - (defun call-with-output-file (pathname thunk - &key - (element-type *default-stream-element-type*) - (external-format *utf-8-external-format*) - (if-exists :error) - (if-does-not-exist :create)) - "Open FILE for input with given recognizes options, call THUNK with the resulting stream. -Other keys are accepted but discarded." - (with-open-file (s pathname :direction :output - :element-type element-type - :external-format external-format - :if-exists if-exists - :if-does-not-exist if-does-not-exist) - (funcall thunk s))) + (defun call-with-input (input function &key keys) + "Calls FUNCTION with an actual stream argument, interpreting +stream designators like READ, but also coercing strings to STRING-INPUT-STREAM, +and PATHNAME to FILE-STREAM. +If INPUT is a STREAM, use it as the stream. +If INPUT is NIL, use a *STANDARD-INPUT* as the stream. +If INPUT is T, use *TERMINAL-IO* as the stream. +If INPUT is a STRING, use it as a string-input-stream. +If INPUT is a PATHNAME, open it, passing KEYS to WITH-INPUT-FILE +-- the latter is an extension since ASDF 3.1. +Otherwise, signal an error." + (etypecase input + (null (funcall function *standard-input*)) + ((eql t) (funcall function *terminal-io*)) + (stream (funcall function input)) + (string (with-input-from-string (stream input) (funcall function stream))) + (pathname (apply 'call-with-input-file input function keys)))) + + (defmacro with-input ((input-var &optional (value input-var)) &body body) + "Bind INPUT-VAR to an input stream, coercing VALUE (default: previous binding of INPUT-VAR) +as per CALL-WITH-INPUT, and evaluate BODY within the scope of this binding." + `(call-with-input ,value #'(lambda (,input-var) ,@body)))) - (defmacro with-output-file ((var pathname &rest keys - &key element-type external-format if-exists if-does-not-exist) - &body body) - (declare (ignore element-type external-format if-exists if-does-not-exist)) - `(call-with-output-file ,pathname #'(lambda (,var) ,@body) ,@keys))) ;;; Null device (with-upgradability ()