lib/os: patch TempDir, MkdirTemp, CreateTemp
This commit is contained in:
@@ -105,3 +105,21 @@ type sliceHead struct {
|
|||||||
len int
|
len int
|
||||||
cap int
|
cap int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LastIndexByte(s []byte, c byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func LastIndexByteString(s string, c byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|||||||
@@ -286,7 +286,6 @@ func (f *File) wrapErr(op string, err error) error {
|
|||||||
return &PathError{Op: op, Path: f.name, Err: err}
|
return &PathError{Op: op, Path: f.name, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// TempDir returns the default directory to use for temporary files.
|
// TempDir returns the default directory to use for temporary files.
|
||||||
//
|
//
|
||||||
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
|
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
|
||||||
@@ -299,7 +298,6 @@ func (f *File) wrapErr(op string, err error) error {
|
|||||||
func TempDir() string {
|
func TempDir() string {
|
||||||
return tempDir()
|
return tempDir()
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Chmod changes the mode of the file to mode.
|
// Chmod changes the mode of the file to mode.
|
||||||
// If there is an error, it will be of type *PathError.
|
// If there is an error, it will be of type *PathError.
|
||||||
|
|||||||
@@ -272,7 +272,6 @@ func Mkdir(name string, perm FileMode) error {
|
|||||||
|
|
||||||
// TODO(xsw):
|
// TODO(xsw):
|
||||||
// func MkdirAll(path string, perm FileMode) error
|
// func MkdirAll(path string, perm FileMode) error
|
||||||
// func MkdirTemp(dir, pattern string) (string, error)
|
|
||||||
// func NewSyscallError(syscall string, err error) error
|
// func NewSyscallError(syscall string, err error) error
|
||||||
|
|
||||||
// func ReadFile(name string) ([]byte, error)
|
// func ReadFile(name string) ([]byte, error)
|
||||||
@@ -327,9 +326,6 @@ func Symlink(oldname, newname string) error {
|
|||||||
return &LinkError{"symlink", oldname, newname, syscall.Errno(ret)}
|
return &LinkError{"symlink", oldname, newname, syscall.Errno(ret)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(xsw):
|
|
||||||
// func TempDir() string
|
|
||||||
|
|
||||||
func Truncate(name string, size int64) error {
|
func Truncate(name string, size int64) error {
|
||||||
ret := os.Truncate(c.AllocaCStr(name), os.OffT(size))
|
ret := os.Truncate(c.AllocaCStr(name), os.OffT(size))
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
|
|||||||
115
internal/lib/os/tempfile.go
Normal file
115
internal/lib/os/tempfile.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/c"
|
||||||
|
"github.com/goplus/llgo/internal/lib/internal/bytealg"
|
||||||
|
"github.com/goplus/llgo/internal/lib/internal/itoa"
|
||||||
|
)
|
||||||
|
|
||||||
|
func nextRandom() string {
|
||||||
|
return itoa.Uitoa(uint(uint32(c.Rand())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: 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 := bytealg.LastIndexByteString(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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user