From 3c3e6d59065516a1745f113543fddc644c2de16d Mon Sep 17 00:00:00 2001
From: Mark Cox <markcox80@gmail.com>
Date: Wed, 19 Jan 2011 14:25:47 +1000
Subject: [PATCH] Added with-temporary-directory.

---
 posix/unix.lisp   |   13 +++++++++++--
 src/osicat.lisp   |   24 ++++++++++++++++++++++++
 src/packages.lisp |    3 +++
 tests/osicat.lisp |   40 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/posix/unix.lisp b/posix/unix.lisp
index 69b8742..936d3de 100644
--- a/posix/unix.lisp
+++ b/posix/unix.lisp
@@ -51,8 +51,17 @@
     (with-foreign-string (ptr (filename template))
       (values (%mkstemp ptr) (foreign-string-to-lisp ptr)))))
 
-(defsyscall "mkdtemp" :string
-  (template filename-designator))
+(defsyscall ("mkdtemp" %mkdtemp) :string
+  (template :pointer))
+
+(defun mkdtemp (&optional (template ""))  
+  (let ((template  (concatenate 'string 
+                                (namestring (merge-pathnames template)) 
+                                "XXXXXX")))
+    (with-foreign-string (ptr (filename template))
+      (let ((temporary-directory (%mkdtemp ptr)))
+        (namestring (make-pathname :directory (append (pathname-directory temporary-directory)
+                                                      (list (pathname-name temporary-directory)))))))))
 
 ;;;; unistd.h
 
diff --git a/src/osicat.lisp b/src/osicat.lisp
index cd3d028..47c6769 100644
--- a/src/osicat.lisp
+++ b/src/osicat.lisp
@@ -332,6 +332,30 @@ closed automatically once BODY exits."
                                      :external-format ,external-format))
      ,@body))
 
+;;;; Temporary Directories
+(defun do-with-temporary-directory (function &key (pathspec *temporary-directory*))
+  (let ((temporary-directory (nix:mkdtemp pathspec)))
+    (unwind-protect
+         (funcall function temporary-directory)
+      (delete-directory-and-files temporary-directory))))
+
+(defmacro with-temporary-directory ((directory &key (pathspec *temporary-directory*)) 
+                                    &body body)
+  "Within the lexical scope of the body, DIRECTORY is bound to a newly
+created temporary directory. When BODY exits, the created
+directory (and all its containing files) are deleted before the value
+of the last form in BODY is returned.
+
+The value of PATHSPEC determines the location and prefix of the
+temporary directory. The PATHNAME-DIRECTORY component of PATHSPEC
+determines the location in which the temporary directory is to be
+created. A non null PATHNAME-NAME of PATHSPEC will be used as a
+prefix of the temporary directory.
+"
+  `(do-with-temporary-directory #'(lambda (,directory)
+                                    ,@body)
+     :pathspec ,pathspec))
+
 ;;;; Directory access
 
 (defmacro with-directory-iterator ((iterator pathspec) &body body)
diff --git a/src/packages.lisp b/src/packages.lisp
index bac3c33..a4301f2 100644
--- a/src/packages.lisp
+++ b/src/packages.lisp
@@ -84,6 +84,9 @@ Designated directory
    #:open-temporary-file
    #:with-temporary-file
 
+   ;; Temporary directories
+   #:with-temporary-directory
+
    ;; Password entries
    #:user-info
 
diff --git a/tests/osicat.lisp b/tests/osicat.lisp
index 4f38173..a85c2c4 100644
--- a/tests/osicat.lisp
+++ b/tests/osicat.lisp
@@ -375,3 +375,43 @@
             (mapcar #'close fds))
         (file-error () t)))
   t)
+
+(deftest temporary-directory.1
+    ;; Default case
+    (let (what-was-created)
+      (with-temporary-directory (directory)
+        (assert (and (directory-pathname-p directory)
+                     (directory-exists-p directory)))
+        (setf what-was-created directory))
+      (assert what-was-created)
+      (probe-file what-was-created))
+
+  nil)
+
+(deftest temporary-directory.2
+    ;; Pathspec containing non NULL PATHNAME-DIRECTORY
+    (let (what-was-created)
+      (with-temporary-directory (directory :pathspec (merge-pathnames "app" *temporary-directory*))
+        (assert (and (directory-pathname-p directory)
+                     (directory-exists-p directory)))
+        (setf what-was-created directory))
+      (assert what-was-created) 
+      (let ((name (car (last (pathname-directory what-was-created)))))
+        (assert (string= "app" (subseq name 0 3))))      
+      (probe-file what-was-created))
+  
+  nil)
+
+(deftest temporary-directory.3
+    ;; Pathspec containing NULL PATHNAME-DIRECTORY
+    (let (what-was-created)
+      (with-temporary-directory (directory :pathspec "app")
+        (assert (and (directory-pathname-p directory)
+                     (directory-exists-p directory)))
+        (setf what-was-created directory))
+      (assert what-was-created) 
+      (let ((name (car (last (pathname-directory what-was-created)))))
+        (assert (string= "app" (subseq name 0 3))))      
+      (probe-file what-was-created))
+  
+  nil)
\ No newline at end of file
-- 
1.6.3

