github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/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 (
     8  	"errors"
     9  	"internal/itoa"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  var minrandPreviousValue uint32
    15  var minrandMutex sync.Mutex
    16  
    17  func init() {
    18  	// Avoid getting same results on every run
    19  	now := time.Now()
    20  	seed := uint32(Getpid()) ^ uint32(now.Nanosecond()) ^ uint32(now.Unix())
    21  	// initial state must be odd
    22  	minrandPreviousValue = seed | 1
    23  }
    24  
    25  // minrand() is a simple and rather poor placeholder for fastrand()
    26  // TODO: provide fastrand in runtime, as go does.  It is hard to implement properly elsewhere.
    27  func minrand() uint32 {
    28  	// c++11's minstd_rand
    29  	// https://en.wikipedia.org/wiki/Linear_congruential_generator
    30  	// m=2^32, c=0, a=48271
    31  	minrandMutex.Lock()
    32  	minrandPreviousValue *= 48271
    33  	val := minrandPreviousValue
    34  	minrandMutex.Unlock()
    35  	return val
    36  }
    37  
    38  // We generate random temporary file names so that there's a good
    39  // chance the file doesn't exist yet - keeps the number of tries in
    40  // TempFile to a minimum.
    41  func nextRandom() string {
    42  	// Discard lower four bits of minrand.
    43  	// They're not very random, and we don't need the full range here.
    44  	return itoa.Uitoa(uint(minrand() >> 4))
    45  }
    46  
    47  // CreateTemp creates a new temporary file in the directory dir,
    48  // opens the file for reading and writing, and returns the resulting file.
    49  // The filename is generated by taking pattern and adding a random string to the end.
    50  // If pattern includes a "*", the random string replaces the last "*".
    51  // If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir.
    52  // Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
    53  // The caller can use the file's Name method to find the pathname of the file.
    54  // It is the caller's responsibility to remove the file when it is no longer needed.
    55  func CreateTemp(dir, pattern string) (*File, error) {
    56  	if dir == "" {
    57  		dir = TempDir()
    58  	}
    59  
    60  	prefix, suffix, err := prefixAndSuffix(pattern)
    61  	if err != nil {
    62  		return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
    63  	}
    64  	prefix = joinPath(dir, prefix)
    65  
    66  	try := 0
    67  	for {
    68  		name := prefix + nextRandom() + suffix
    69  		f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
    70  		if IsExist(err) {
    71  			if try++; try < 10000 {
    72  				continue
    73  			}
    74  			return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
    75  		}
    76  		return f, err
    77  	}
    78  }
    79  
    80  var errPatternHasSeparator = errors.New("pattern contains path separator")
    81  
    82  // prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
    83  // returning prefix as the part before "*" and suffix as the part after "*".
    84  func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
    85  	for i := 0; i < len(pattern); i++ {
    86  		if IsPathSeparator(pattern[i]) {
    87  			return "", "", errPatternHasSeparator
    88  		}
    89  	}
    90  	if pos := lastIndex(pattern, '*'); pos != -1 {
    91  		prefix, suffix = pattern[:pos], pattern[pos+1:]
    92  	} else {
    93  		prefix = pattern
    94  	}
    95  	return prefix, suffix, nil
    96  }
    97  
    98  // MkdirTemp creates a new temporary directory in the directory dir
    99  // and returns the pathname of the new directory.
   100  // The new directory's name is generated by adding a random string to the end of pattern.
   101  // If pattern includes a "*", the random string replaces the last "*" instead.
   102  // If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir.
   103  // Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory.
   104  // It is the caller's responsibility to remove the directory when it is no longer needed.
   105  func MkdirTemp(dir, pattern string) (string, error) {
   106  	if dir == "" {
   107  		dir = TempDir()
   108  	}
   109  
   110  	prefix, suffix, err := prefixAndSuffix(pattern)
   111  	if err != nil {
   112  		return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err}
   113  	}
   114  	prefix = joinPath(dir, prefix)
   115  
   116  	try := 0
   117  	for {
   118  		name := prefix + nextRandom() + suffix
   119  		err := Mkdir(name, 0700)
   120  		if err == nil {
   121  			return name, nil
   122  		}
   123  		if IsExist(err) {
   124  			if try++; try < 10000 {
   125  				continue
   126  			}
   127  			return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
   128  		}
   129  		if IsNotExist(err) {
   130  			if _, err := Stat(dir); IsNotExist(err) {
   131  				return "", err
   132  			}
   133  		}
   134  		return "", err
   135  	}
   136  }
   137  
   138  func joinPath(dir, name string) string {
   139  	if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
   140  		return dir + name
   141  	}
   142  	return dir + string(PathSeparator) + name
   143  }
   144  
   145  // LastIndexByte from the strings package.
   146  func lastIndex(s string, sep byte) int {
   147  	for i := len(s) - 1; i >= 0; i-- {
   148  		if s[i] == sep {
   149  			return i
   150  		}
   151  	}
   152  	return -1
   153  }