github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/os/tempfile.go (about)

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package os
     6  
     7  import "errors"
     8  
     9  // fastrand provided by runtime.
    10  // We generate random temporary file names so that there's a good
    11  // chance the file doesn't exist yet - keeps the number of tries in
    12  // TempFile to a minimum.
    13  func fastrand() uint32
    14  
    15  func nextRandom() string {
    16  	return uitoa(uint(fastrand()))
    17  }
    18  
    19  // CreateTemp creates a new temporary file in the directory dir,
    20  // opens the file for reading and writing, and returns the resulting file.
    21  // The filename is generated by taking pattern and adding a random string to the end.
    22  // If pattern includes a "*", the random string replaces the last "*".
    23  // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir.
    24  // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
    25  // The caller can use the file's Name method to find the pathname of the file.
    26  // It is the caller's responsibility to remove the file when it is no longer needed.
    27  func CreateTemp(dir, pattern string) (*File, error) {
    28  	if dir == "" {
    29  		dir = TempDir()
    30  	}
    31  
    32  	prefix, suffix, err := prefixAndSuffix(pattern)
    33  	if err != nil {
    34  		return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
    35  	}
    36  	prefix = joinPath(dir, prefix)
    37  
    38  	try := 0
    39  	for {
    40  		name := prefix + nextRandom() + suffix
    41  		f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
    42  		if IsExist(err) {
    43  			if try++; try < 10000 {
    44  				continue
    45  			}
    46  			return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
    47  		}
    48  		return f, err
    49  	}
    50  }
    51  
    52  var errPatternHasSeparator = errors.New("pattern contains path separator")
    53  
    54  // prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
    55  // returning prefix as the part before "*" and suffix as the part after "*".
    56  func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
    57  	for i := 0; i < len(pattern); i++ {
    58  		if IsPathSeparator(pattern[i]) {
    59  			return "", "", errPatternHasSeparator
    60  		}
    61  	}
    62  	if pos := lastIndex(pattern, '*'); pos != -1 {
    63  		prefix, suffix = pattern[:pos], pattern[pos+1:]
    64  	} else {
    65  		prefix = pattern
    66  	}
    67  	return prefix, suffix, nil
    68  }
    69  
    70  // MkdirTemp creates a new temporary directory in the directory dir
    71  // and returns the pathname of the new directory.
    72  // The new directory's name is generated by adding a random string to the end of pattern.
    73  // If pattern includes a "*", the random string replaces the last "*" instead.
    74  // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir.
    75  // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory.
    76  // It is the caller's responsibility to remove the directory when it is no longer needed.
    77  func MkdirTemp(dir, pattern string) (string, error) {
    78  	if dir == "" {
    79  		dir = TempDir()
    80  	}
    81  
    82  	prefix, suffix, err := prefixAndSuffix(pattern)
    83  	if err != nil {
    84  		return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err}
    85  	}
    86  	prefix = joinPath(dir, prefix)
    87  
    88  	try := 0
    89  	for {
    90  		name := prefix + nextRandom() + suffix
    91  		err := Mkdir(name, 0700)
    92  		if err == nil {
    93  			return name, nil
    94  		}
    95  		if IsExist(err) {
    96  			if try++; try < 10000 {
    97  				continue
    98  			}
    99  			return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
   100  		}
   101  		if IsNotExist(err) {
   102  			if _, err := Stat(dir); IsNotExist(err) {
   103  				return "", err
   104  			}
   105  		}
   106  		return "", err
   107  	}
   108  }
   109  
   110  func joinPath(dir, name string) string {
   111  	if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
   112  		return dir + name
   113  	}
   114  	return dir + string(PathSeparator) + name
   115  }
   116  
   117  // LastIndexByte from the strings package.
   118  func lastIndex(s string, sep byte) int {
   119  	for i := len(s) - 1; i >= 0; i-- {
   120  		if s[i] == sep {
   121  			return i
   122  		}
   123  	}
   124  	return -1
   125  }