Merge pull request #62 from kuba--/enhancement-tempdir

Add TempDir
diff --git a/.gitignore b/.gitignore
index 3f2bc47..62cdb53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
 /coverage.txt
+/vendor
+Gopkg.lock
+Gopkg.toml
+go.sum
diff --git a/test/fs.go b/test/fs.go
index 54f4529..e5d5146 100644
--- a/test/fs.go
+++ b/test/fs.go
@@ -188,6 +188,9 @@
 }
 
 func (s *FilesystemSuite) TestReadDir(c *C) {
+	err := s.FS.MkdirAll("qux", 0644)
+	c.Assert(err, IsNil)
+
 	files := []string{"foo", "bar", "qux/baz", "qux/qux"}
 	for _, name := range files {
 		err := util.WriteFile(s.FS, name, nil, 0644)
diff --git a/util/util.go b/util/util.go
index 2876375..cf7fb57 100644
--- a/util/util.go
+++ b/util/util.go
@@ -168,6 +168,45 @@
 	return
 }
 
+// TempDir creates a new temporary directory in the directory dir
+// with a name beginning with prefix and returns the path of the
+// new directory. If dir is the empty string, TempDir uses the
+// default directory for temporary files (see os.TempDir).
+// Multiple programs calling TempDir simultaneously
+// will not choose the same directory. It is the caller's responsibility
+// to remove the directory when no longer needed.
+func TempDir(fs billy.Dir, dir, prefix string) (name string, err error) {
+	// This implementation is based on stdlib ioutil.TempDir
+
+	if dir == "" {
+		dir = os.TempDir()
+	}
+
+	nconflict := 0
+	for i := 0; i < 10000; i++ {
+		try := filepath.Join(dir, prefix+nextSuffix())
+		err = fs.MkdirAll(try, 0700)
+		if os.IsExist(err) {
+			if nconflict++; nconflict > 10 {
+				randmu.Lock()
+				rand = reseed()
+				randmu.Unlock()
+			}
+			continue
+		}
+		if os.IsNotExist(err) {
+			if _, err := os.Stat(dir); os.IsNotExist(err) {
+				return "", err
+			}
+		}
+		if err == nil {
+			name = try
+		}
+		break
+	}
+	return
+}
+
 type underlying interface {
 	Underlying() billy.Basic
 }
diff --git a/util/util_test.go b/util/util_test.go
new file mode 100644
index 0000000..92a8ca0
--- /dev/null
+++ b/util/util_test.go
@@ -0,0 +1,43 @@
+package util_test
+
+import (
+	"os"
+	"path/filepath"
+	"regexp"
+	"testing"
+
+	"gopkg.in/src-d/go-billy.v4/memfs"
+	"gopkg.in/src-d/go-billy.v4/util"
+)
+
+func TestTempFile(t *testing.T) {
+	fs := memfs.New()
+
+	dir, err := util.TempDir(fs, "", "util_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer util.RemoveAll(fs, dir)
+
+	f, err := util.TempFile(fs, dir, "foo")
+	if f == nil || err != nil {
+		t.Errorf("TempFile(%q, `foo`) = %v, %v", dir, f, err)
+	}
+}
+
+func TestTempDir(t *testing.T) {
+	fs := memfs.New()
+
+	dir := os.TempDir()
+	name, err := util.TempDir(fs, dir, "util_test")
+	if name == "" || err != nil {
+		t.Errorf("TempDir(dir, `util_test`) = %v, %v", name, err)
+	}
+	if name != "" {
+		util.RemoveAll(fs, name)
+		re := regexp.MustCompile("^" + regexp.QuoteMeta(filepath.Join(dir, "util_test")) + "[0-9]+$")
+		if !re.MatchString(name) {
+			t.Errorf("TempDir(`"+dir+"`, `util_test`) created bad name %s", name)
+		}
+	}
+}