|  | // Copyright 2010 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package os | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "internal/itoa" | 
|  | ) | 
|  |  | 
|  | // fastrand provided by runtime. | 
|  | // We generate random temporary file names so that there's a good | 
|  | // chance the file doesn't exist yet - keeps the number of tries in | 
|  | // TempFile to a minimum. | 
|  | func fastrand() uint32 | 
|  |  | 
|  | func nextRandom() string { | 
|  | return itoa.Uitoa(uint(fastrand())) | 
|  | } | 
|  |  | 
|  | // CreateTemp creates a new temporary file in the directory dir, | 
|  | // opens the file for reading and writing, and returns the resulting file. | 
|  | // The filename is generated by taking pattern and adding a random string to the end. | 
|  | // If pattern includes a "*", the random string replaces the last "*". | 
|  | // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir. | 
|  | // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file. | 
|  | // The caller can use the file's Name method to find the pathname of the file. | 
|  | // It is the caller's responsibility to remove the file when it is no longer needed. | 
|  | func CreateTemp(dir, pattern string) (*File, error) { | 
|  | if dir == "" { | 
|  | dir = TempDir() | 
|  | } | 
|  |  | 
|  | prefix, suffix, err := prefixAndSuffix(pattern) | 
|  | if err != nil { | 
|  | return nil, &PathError{Op: "createtemp", Path: pattern, Err: err} | 
|  | } | 
|  | prefix = joinPath(dir, prefix) | 
|  |  | 
|  | try := 0 | 
|  | for { | 
|  | name := prefix + nextRandom() + suffix | 
|  | f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600) | 
|  | if IsExist(err) { | 
|  | if try++; try < 10000 { | 
|  | continue | 
|  | } | 
|  | return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} | 
|  | } | 
|  | return f, err | 
|  | } | 
|  | } | 
|  |  | 
|  | var errPatternHasSeparator = errors.New("pattern contains path separator") | 
|  |  | 
|  | // prefixAndSuffix splits pattern by the last wildcard "*", if applicable, | 
|  | // returning prefix as the part before "*" and suffix as the part after "*". | 
|  | func prefixAndSuffix(pattern string) (prefix, suffix string, err error) { | 
|  | for i := 0; i < len(pattern); i++ { | 
|  | if IsPathSeparator(pattern[i]) { | 
|  | return "", "", errPatternHasSeparator | 
|  | } | 
|  | } | 
|  | if pos := lastIndex(pattern, '*'); pos != -1 { | 
|  | prefix, suffix = pattern[:pos], pattern[pos+1:] | 
|  | } else { | 
|  | prefix = pattern | 
|  | } | 
|  | return prefix, suffix, nil | 
|  | } | 
|  |  | 
|  | // MkdirTemp creates a new temporary directory in the directory dir | 
|  | // and returns the pathname of the new directory. | 
|  | // The new directory's name is generated by adding a random string to the end of pattern. | 
|  | // If pattern includes a "*", the random string replaces the last "*" instead. | 
|  | // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir. | 
|  | // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory. | 
|  | // It is the caller's responsibility to remove the directory when it is no longer needed. | 
|  | func MkdirTemp(dir, pattern string) (string, error) { | 
|  | if dir == "" { | 
|  | dir = TempDir() | 
|  | } | 
|  |  | 
|  | prefix, suffix, err := prefixAndSuffix(pattern) | 
|  | if err != nil { | 
|  | return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err} | 
|  | } | 
|  | prefix = joinPath(dir, prefix) | 
|  |  | 
|  | try := 0 | 
|  | for { | 
|  | name := prefix + nextRandom() + suffix | 
|  | err := Mkdir(name, 0700) | 
|  | if err == nil { | 
|  | return name, nil | 
|  | } | 
|  | if IsExist(err) { | 
|  | if try++; try < 10000 { | 
|  | continue | 
|  | } | 
|  | return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} | 
|  | } | 
|  | if IsNotExist(err) { | 
|  | if _, err := Stat(dir); IsNotExist(err) { | 
|  | return "", err | 
|  | } | 
|  | } | 
|  | return "", err | 
|  | } | 
|  | } | 
|  |  | 
|  | func joinPath(dir, name string) string { | 
|  | if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) { | 
|  | return dir + name | 
|  | } | 
|  | return dir + string(PathSeparator) + name | 
|  | } | 
|  |  | 
|  | // LastIndexByte from the strings package. | 
|  | func lastIndex(s string, sep byte) int { | 
|  | for i := len(s) - 1; i >= 0; i-- { | 
|  | if s[i] == sep { | 
|  | return i | 
|  | } | 
|  | } | 
|  | return -1 | 
|  | } |