Hi,
following up on an IRC discussion yesterday, I am proposing the addition of the OPEN-TEMPORARY function and WITH-OPEN-TEMPORARY-FILE macro below (and on http://paste.lisp.org/display/129090#14). It implements functionality that is commonly required, but that is not available in any other library in a portable fashion. osicat has been suggested, but it does not implement temporary file handling on Windows.
This posting is to determine whether there is opposition regarding inclusion in Alexandria. I'll re-post as a proper patch including symbol exports when there is agreement to include it.
-Hans
(in-package :alexandria)
(defparameter *default-temporary-directory* #P"/tmp/")
(eval-when (:load-toplevel :execute) (when (and (null (logical-pathname-translations "TEMPORARY-FILES")) (probe-file *default-temporary-directory*)) (setf (logical-pathname-translations "temporary-files") `(("*.*.*" ,*default-temporary-directory*)))))
(defparameter *max-tries* 10000)
(defun generate-random-name (prefix) (format nil "~A-~36R-~36R" prefix (random 100000000) (get-internal-real-time)))
(define-condition cannot-create-temporary-file (error) ((directory :initarg :directory) (prefix :initarg :prefix) (type :initarg :type) (max-tries :initarg :max-tries)) (:report (lambda (condition stream) (with-slots (directory prefix type max-tries) condition (format stream "cannot create temporary file in directory ~A with template ~A.~A, giving up after ~D attempt~:P" (translate-logical-pathname directory) prefix type max-tries)))))
(defun open-temporary (&rest open-arguments &key type (prefix "temp") (directory #P"TEMPORARY-FILES:") (generate-random-name 'generate-random-name) (max-tries *max-tries*) &allow-other-keys) "Create a file with a randomly generated name and return the opened stream. The generated pathname consists of the PREFIX with a pseudo-random suffix. Its type will be TYPE. Temporary files are generated in DIRECTORY which defaults to the logical pathname temporary TEMPORARY-FILES:, which must be properly set up.
The name of the temporary file can be accessed calling the PATHNAME function on STREAM. For convenience, the temporary file is opened on the physical pathname, i.e. the pathname translation is performed before opening the stream.
GENERATE-RANDOM-NAME can be passed to override the default function that transforms PREFIX into a pseudorandom file name. It needs to accept one argument, the PREFIX string passed, and return the generated file name.
In order to create a unique file name, OPEN-TEMPORARY may loop internally up to MAX-TRIES times before giving up and signalling a CANNOT-CREATE-TEMPORARY-FILE condition." (loop thereis (apply #'open (translate-logical-pathname (make-pathname :name (funcall generate-random-name prefix) :type type
:defaults directory)) :direction :output :if-exists nil (remove-from-plist open-arguments :type prefix directory generate-random-name max-tries)) repeat max-tries finally (error 'cannot-create-temporary-file :directory directory :prefix prefix :type type :max-tries max-tries)))
(defmacro with-open-temporary-file ((stream &rest args) &body body) "Create a temporary file using OPEN-TEMPORARY with ARGS and run BODY with STREAM bound to the temporary file stream. See OPEN-TEMPORARY for permitted options." `(with-open-stream (,stream (open-temporary ,@args)) ,@body))