gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/cmds/core/elvish/util/claim.go (about)

     1  package util
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  // ErrClaimFileBadPattern is thrown when the pattern argument passed to
    12  // ClaimFile does not contain exactly one asterisk.
    13  var ErrClaimFileBadPattern = errors.New("ClaimFile: pattern must contain exactly one asterisk")
    14  
    15  // ClaimFile takes a directory and a pattern string containing exactly one
    16  // asterisk (e.g. "a*.log"). It opens a file in that directory, with a filename
    17  // matching the template, with "*" replaced by a number. That number is one plus
    18  // the largest of all existing files matching the template. If no such file
    19  // exists, "*" is replaced by 1. The file is opened for read and write, with
    20  // permission 0666 (before umask).
    21  //
    22  // For example, if the directory /tmp/elvish contains a1.log, a2.log and a9.log,
    23  // calling ClaimFile("/tmp/elvish", "a*.log") will open a10.log. If the
    24  // directory has no files matching the pattern, this same call will open a1.log.
    25  //
    26  // This function is useful for automatically determining unique names for log
    27  // files. Unique filenames can also be derived by embedding the PID, but using
    28  // this function preserves the chronical order of the files.
    29  //
    30  // This function is concurrency-safe: it always opens a new, unclaimed file and
    31  // is not subject to race condition.
    32  func ClaimFile(dir, pattern string) (*os.File, error) {
    33  	if strings.Count(pattern, "*") != 1 {
    34  		return nil, ErrClaimFileBadPattern
    35  	}
    36  	asterisk := strings.IndexByte(pattern, '*')
    37  	prefix, suffix := pattern[:asterisk], pattern[asterisk+1:]
    38  	files, err := ioutil.ReadDir(dir)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	max := 0
    43  	for _, file := range files {
    44  		name := file.Name()
    45  		if len(name) > len(prefix)+len(suffix) && strings.HasPrefix(name, prefix) && strings.HasSuffix(name, suffix) {
    46  			core := name[len(prefix) : len(name)-len(suffix)]
    47  			if coreNum, err := strconv.Atoi(core); err == nil {
    48  				if max < coreNum {
    49  					max = coreNum
    50  				}
    51  			}
    52  		}
    53  	}
    54  
    55  	for i := max + 1; ; i++ {
    56  		name := prefix + strconv.Itoa(i) + suffix
    57  		f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
    58  		if err == nil {
    59  			return f, nil
    60  		}
    61  		if !os.IsExist(err) {
    62  			return nil, err
    63  		}
    64  	}
    65  }