pax_global_header00006660000000000000000000000064136071325020014511gustar00rootroot0000000000000052 comment=bc89b17ba21ce5b8a495fa55a94e3fe32ecf4ed8 go-internal-1.5.2/000077500000000000000000000000001360713250200137355ustar00rootroot00000000000000go-internal-1.5.2/.gitattributes000066400000000000000000000005431360713250200166320ustar00rootroot00000000000000# By default, do not attempt to do any end-of-line conversion upon # checkin or checkout. * -text # For specific file-types, force conversion to LF line endings. This # can be overridden below or in a more specific .gitattributes if, for # example, we want to allow a .txt file to contain a CRLF line ending *.go text eol=lf *.txt text eol=lf go-internal-1.5.2/.travis.yml000066400000000000000000000015171360713250200160520ustar00rootroot00000000000000language: go os: - windows - linux - osx branches: only: - master go: - "1.11.x" - "1.12.x" - "1.13" env: - GO111MODULE=on go_import_path: github.com/rogpeppe/go-internal # Add this before_install until we have a definitive resolution on # https://travis-ci.community/t/files-in-checkout-have-eol-changed-from-lf-to-crlf/349/2 before_install: - cd ../.. - mv $TRAVIS_REPO_SLUG _old - git config --global core.autocrlf false - git clone --depth=50 _old $TRAVIS_REPO_SLUG - cd $TRAVIS_REPO_SLUG install: "echo no install step required" script: - go test -race ./... - go mod tidy # https://github.com/golang/go/issues/27868#issuecomment-431413621 - go list all > /dev/null - test -z "$(gofmt -d .)" || (gofmt -d . && false) - test -z "$(git status --porcelain)" || (git status; git diff && false) go-internal-1.5.2/LICENSE000066400000000000000000000027071360713250200147500ustar00rootroot00000000000000Copyright (c) 2018 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. go-internal-1.5.2/README.md000066400000000000000000000016341360713250200152200ustar00rootroot00000000000000This repository factors out an opinionated selection of internal packages and functionality from the Go standard library. Currently this consists mostly of packages and testing code from within the Go tool implementation. Included are the following: - dirhash: calculate hashes over directory trees the same way that the Go tool does. - goproxytest: a GOPROXY implementation designed for test use. - gotooltest: Use the Go tool inside test scripts (see testscript below) - imports: list of known architectures and OSs, and support for reading import import statements. - modfile: read and write `go.mod` files while preserving formatting and comments. - module: module paths and versions. - par: do work in parallel. - semver: semantic version parsing. - testenv: information on the current testing environment. - testscript: script-based testing based on txtar files - txtar: simple text-based file archives for testing. go-internal-1.5.2/cache/000077500000000000000000000000001360713250200150005ustar00rootroot00000000000000go-internal-1.5.2/cache/cache.go000066400000000000000000000353201360713250200163750ustar00rootroot00000000000000// Copyright 2017 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 cache implements a build artifact cache. package cache import ( "bytes" "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "strconv" "strings" "time" ) // An ActionID is a cache action key, the hash of a complete description of a // repeatable computation (command line, environment variables, // input file contents, executable contents). type ActionID [HashSize]byte // An OutputID is a cache output key, the hash of an output of a computation. type OutputID [HashSize]byte // A Cache is a package cache, backed by a file system directory tree. type Cache struct { dir string log *os.File now func() time.Time } // Open opens and returns the cache in the given directory. // // It is safe for multiple processes on a single machine to use the // same cache directory in a local file system simultaneously. // They will coordinate using operating system file locks and may // duplicate effort but will not corrupt the cache. // // However, it is NOT safe for multiple processes on different machines // to share a cache directory (for example, if the directory were stored // in a network file system). File locking is notoriously unreliable in // network file systems and may not suffice to protect the cache. // func Open(dir string) (*Cache, error) { info, err := os.Stat(dir) if err != nil { return nil, err } if !info.IsDir() { return nil, &os.PathError{Op: "open", Path: dir, Err: fmt.Errorf("not a directory")} } for i := 0; i < 256; i++ { name := filepath.Join(dir, fmt.Sprintf("%02x", i)) if err := os.MkdirAll(name, 0777); err != nil { return nil, err } } f, err := os.OpenFile(filepath.Join(dir, "log.txt"), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) if err != nil { return nil, err } c := &Cache{ dir: dir, log: f, now: time.Now, } return c, nil } // fileName returns the name of the file corresponding to the given id. func (c *Cache) fileName(id [HashSize]byte, key string) string { return filepath.Join(c.dir, fmt.Sprintf("%02x", id[0]), fmt.Sprintf("%x", id)+"-"+key) } var errMissing = errors.New("cache entry not found") const ( // action entry file is "v1 \n" hexSize = HashSize * 2 entrySize = 2 + 1 + hexSize + 1 + hexSize + 1 + 20 + 1 + 20 + 1 ) // verify controls whether to run the cache in verify mode. // In verify mode, the cache always returns errMissing from Get // but then double-checks in Put that the data being written // exactly matches any existing entry. This provides an easy // way to detect program behavior that would have been different // had the cache entry been returned from Get. // // verify is enabled by setting the environment variable // GODEBUG=gocacheverify=1. var verify = false // DebugTest is set when GODEBUG=gocachetest=1 is in the environment. var DebugTest = false func init() { initEnv() } func initEnv() { verify = false debugHash = false debug := strings.Split(os.Getenv("GODEBUG"), ",") for _, f := range debug { if f == "gocacheverify=1" { verify = true } if f == "gocachehash=1" { debugHash = true } if f == "gocachetest=1" { DebugTest = true } } } // Get looks up the action ID in the cache, // returning the corresponding output ID and file size, if any. // Note that finding an output ID does not guarantee that the // saved file for that output ID is still available. func (c *Cache) Get(id ActionID) (Entry, error) { if verify { return Entry{}, errMissing } return c.get(id) } type Entry struct { OutputID OutputID Size int64 Time time.Time } // get is Get but does not respect verify mode, so that Put can use it. func (c *Cache) get(id ActionID) (Entry, error) { missing := func() (Entry, error) { fmt.Fprintf(c.log, "%d miss %x\n", c.now().Unix(), id) return Entry{}, errMissing } f, err := os.Open(c.fileName(id, "a")) if err != nil { return missing() } defer f.Close() entry := make([]byte, entrySize+1) // +1 to detect whether f is too long if n, err := io.ReadFull(f, entry); n != entrySize || err != io.ErrUnexpectedEOF { return missing() } if entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\n' { return missing() } eid, entry := entry[3:3+hexSize], entry[3+hexSize:] eout, entry := entry[1:1+hexSize], entry[1+hexSize:] esize, entry := entry[1:1+20], entry[1+20:] etime, entry := entry[1:1+20], entry[1+20:] var buf [HashSize]byte if _, err := hex.Decode(buf[:], eid); err != nil || buf != id { return missing() } if _, err := hex.Decode(buf[:], eout); err != nil { return missing() } i := 0 for i < len(esize) && esize[i] == ' ' { i++ } size, err := strconv.ParseInt(string(esize[i:]), 10, 64) if err != nil || size < 0 { return missing() } i = 0 for i < len(etime) && etime[i] == ' ' { i++ } tm, err := strconv.ParseInt(string(etime[i:]), 10, 64) if err != nil || size < 0 { return missing() } fmt.Fprintf(c.log, "%d get %x\n", c.now().Unix(), id) c.used(c.fileName(id, "a")) return Entry{buf, size, time.Unix(0, tm)}, nil } // GetFile looks up the action ID in the cache and returns // the name of the corresponding data file. func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) { entry, err = c.Get(id) if err != nil { return "", Entry{}, err } file = c.OutputFile(entry.OutputID) info, err := os.Stat(file) if err != nil || info.Size() != entry.Size { return "", Entry{}, errMissing } return file, entry, nil } // GetBytes looks up the action ID in the cache and returns // the corresponding output bytes. // GetBytes should only be used for data that can be expected to fit in memory. func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) { entry, err := c.Get(id) if err != nil { return nil, entry, err } data, _ := ioutil.ReadFile(c.OutputFile(entry.OutputID)) if sha256.Sum256(data) != entry.OutputID { return nil, entry, errMissing } return data, entry, nil } // OutputFile returns the name of the cache file storing output with the given OutputID. func (c *Cache) OutputFile(out OutputID) string { file := c.fileName(out, "d") c.used(file) return file } // Time constants for cache expiration. // // We set the mtime on a cache file on each use, but at most one per mtimeInterval (1 hour), // to avoid causing many unnecessary inode updates. The mtimes therefore // roughly reflect "time of last use" but may in fact be older by at most an hour. // // We scan the cache for entries to delete at most once per trimInterval (1 day). // // When we do scan the cache, we delete entries that have not been used for // at least trimLimit (5 days). Statistics gathered from a month of usage by // Go developers found that essentially all reuse of cached entries happened // within 5 days of the previous reuse. See golang.org/issue/22990. const ( mtimeInterval = 1 * time.Hour trimInterval = 24 * time.Hour trimLimit = 5 * 24 * time.Hour ) // used makes a best-effort attempt to update mtime on file, // so that mtime reflects cache access time. // // Because the reflection only needs to be approximate, // and to reduce the amount of disk activity caused by using // cache entries, used only updates the mtime if the current // mtime is more than an hour old. This heuristic eliminates // nearly all of the mtime updates that would otherwise happen, // while still keeping the mtimes useful for cache trimming. func (c *Cache) used(file string) { info, err := os.Stat(file) if err == nil && c.now().Sub(info.ModTime()) < mtimeInterval { return } os.Chtimes(file, c.now(), c.now()) } // Trim removes old cache entries that are likely not to be reused. func (c *Cache) Trim() { now := c.now() // We maintain in dir/trim.txt the time of the last completed cache trim. // If the cache has been trimmed recently enough, do nothing. // This is the common case. data, _ := ioutil.ReadFile(filepath.Join(c.dir, "trim.txt")) t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64) if err == nil && now.Sub(time.Unix(t, 0)) < trimInterval { return } // Trim each of the 256 subdirectories. // We subtract an additional mtimeInterval // to account for the imprecision of our "last used" mtimes. cutoff := now.Add(-trimLimit - mtimeInterval) for i := 0; i < 256; i++ { subdir := filepath.Join(c.dir, fmt.Sprintf("%02x", i)) c.trimSubdir(subdir, cutoff) } ioutil.WriteFile(filepath.Join(c.dir, "trim.txt"), []byte(fmt.Sprintf("%d", now.Unix())), 0666) } // trimSubdir trims a single cache subdirectory. func (c *Cache) trimSubdir(subdir string, cutoff time.Time) { // Read all directory entries from subdir before removing // any files, in case removing files invalidates the file offset // in the directory scan. Also, ignore error from f.Readdirnames, // because we don't care about reporting the error and we still // want to process any entries found before the error. f, err := os.Open(subdir) if err != nil { return } names, _ := f.Readdirnames(-1) f.Close() for _, name := range names { // Remove only cache entries (xxxx-a and xxxx-d). if !strings.HasSuffix(name, "-a") && !strings.HasSuffix(name, "-d") { continue } entry := filepath.Join(subdir, name) info, err := os.Stat(entry) if err == nil && info.ModTime().Before(cutoff) { os.Remove(entry) } } } // putIndexEntry adds an entry to the cache recording that executing the action // with the given id produces an output with the given output id (hash) and size. func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify bool) error { // Note: We expect that for one reason or another it may happen // that repeating an action produces a different output hash // (for example, if the output contains a time stamp or temp dir name). // While not ideal, this is also not a correctness problem, so we // don't make a big deal about it. In particular, we leave the action // cache entries writable specifically so that they can be overwritten. // // Setting GODEBUG=gocacheverify=1 does make a big deal: // in verify mode we are double-checking that the cache entries // are entirely reproducible. As just noted, this may be unrealistic // in some cases but the check is also useful for shaking out real bugs. entry := []byte(fmt.Sprintf("v1 %x %x %20d %20d\n", id, out, size, time.Now().UnixNano())) if verify && allowVerify { old, err := c.get(id) if err == nil && (old.OutputID != out || old.Size != size) { // panic to show stack trace, so we can see what code is generating this cache entry. msg := fmt.Sprintf("go: internal cache error: cache verify failed: id=%x changed:<<<\n%s\n>>>\nold: %x %d\nnew: %x %d", id, reverseHash(id), out, size, old.OutputID, old.Size) panic(msg) } } file := c.fileName(id, "a") if err := ioutil.WriteFile(file, entry, 0666); err != nil { os.Remove(file) return err } os.Chtimes(file, c.now(), c.now()) // mainly for tests fmt.Fprintf(c.log, "%d put %x %x %d\n", c.now().Unix(), id, out, size) return nil } // Put stores the given output in the cache as the output for the action ID. // It may read file twice. The content of file must not change between the two passes. func (c *Cache) Put(id ActionID, file io.ReadSeeker) (OutputID, int64, error) { return c.put(id, file, true) } // PutNoVerify is like Put but disables the verify check // when GODEBUG=goverifycache=1 is set. // It is meant for data that is OK to cache but that we expect to vary slightly from run to run, // like test output containing times and the like. func (c *Cache) PutNoVerify(id ActionID, file io.ReadSeeker) (OutputID, int64, error) { return c.put(id, file, false) } func (c *Cache) put(id ActionID, file io.ReadSeeker, allowVerify bool) (OutputID, int64, error) { // Compute output ID. h := sha256.New() if _, err := file.Seek(0, 0); err != nil { return OutputID{}, 0, err } size, err := io.Copy(h, file) if err != nil { return OutputID{}, 0, err } var out OutputID h.Sum(out[:0]) // Copy to cached output file (if not already present). if err := c.copyFile(file, out, size); err != nil { return out, size, err } // Add to cache index. return out, size, c.putIndexEntry(id, out, size, allowVerify) } // PutBytes stores the given bytes in the cache as the output for the action ID. func (c *Cache) PutBytes(id ActionID, data []byte) error { _, _, err := c.Put(id, bytes.NewReader(data)) return err } // copyFile copies file into the cache, expecting it to have the given // output ID and size, if that file is not present already. func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error { name := c.fileName(out, "d") info, err := os.Stat(name) if err == nil && info.Size() == size { // Check hash. if f, err := os.Open(name); err == nil { h := sha256.New() io.Copy(h, f) f.Close() var out2 OutputID h.Sum(out2[:0]) if out == out2 { return nil } } // Hash did not match. Fall through and rewrite file. } // Copy file to cache directory. mode := os.O_RDWR | os.O_CREATE if err == nil && info.Size() > size { // shouldn't happen but fix in case mode |= os.O_TRUNC } f, err := os.OpenFile(name, mode, 0666) if err != nil { return err } defer f.Close() if size == 0 { // File now exists with correct size. // Only one possible zero-length file, so contents are OK too. // Early return here makes sure there's a "last byte" for code below. return nil } // From here on, if any of the I/O writing the file fails, // we make a best-effort attempt to truncate the file f // before returning, to avoid leaving bad bytes in the file. // Copy file to f, but also into h to double-check hash. if _, err := file.Seek(0, 0); err != nil { f.Truncate(0) return err } h := sha256.New() w := io.MultiWriter(f, h) if _, err := io.CopyN(w, file, size-1); err != nil { f.Truncate(0) return err } // Check last byte before writing it; writing it will make the size match // what other processes expect to find and might cause them to start // using the file. buf := make([]byte, 1) if _, err := file.Read(buf); err != nil { f.Truncate(0) return err } h.Write(buf) sum := h.Sum(nil) if !bytes.Equal(sum, out[:]) { f.Truncate(0) return fmt.Errorf("file content changed underfoot") } // Commit cache file entry. if _, err := f.Write(buf); err != nil { f.Truncate(0) return err } if err := f.Close(); err != nil { // Data might not have been written, // but file may look like it is the right size. // To be extra careful, remove cached file. os.Remove(name) return err } os.Chtimes(name, c.now(), c.now()) // mainly for tests return nil } go-internal-1.5.2/cache/cache_test.go000066400000000000000000000210611360713250200174310ustar00rootroot00000000000000// Copyright 2017 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 cache import ( "bytes" "encoding/binary" "fmt" "io/ioutil" "os" "path/filepath" "testing" "time" ) func init() { verify = false // even if GODEBUG is set } func TestBasic(t *testing.T) { dir, err := ioutil.TempDir("", "cachetest-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) _, err = Open(filepath.Join(dir, "notexist")) if err == nil { t.Fatal(`Open("tmp/notexist") succeeded, want failure`) } cdir := filepath.Join(dir, "c1") if err := os.Mkdir(cdir, 0777); err != nil { t.Fatal(err) } c1, err := Open(cdir) if err != nil { t.Fatalf("Open(c1) (create): %v", err) } if err := c1.putIndexEntry(dummyID(1), dummyID(12), 13, true); err != nil { t.Fatalf("addIndexEntry: %v", err) } if err := c1.putIndexEntry(dummyID(1), dummyID(2), 3, true); err != nil { // overwrite entry t.Fatalf("addIndexEntry: %v", err) } if entry, err := c1.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 { t.Fatalf("c1.Get(1) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(2), 3) } c2, err := Open(cdir) if err != nil { t.Fatalf("Open(c2) (reuse): %v", err) } if entry, err := c2.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 { t.Fatalf("c2.Get(1) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(2), 3) } if err := c2.putIndexEntry(dummyID(2), dummyID(3), 4, true); err != nil { t.Fatalf("addIndexEntry: %v", err) } if entry, err := c1.Get(dummyID(2)); err != nil || entry.OutputID != dummyID(3) || entry.Size != 4 { t.Fatalf("c1.Get(2) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(3), 4) } } func TestGrowth(t *testing.T) { dir, err := ioutil.TempDir("", "cachetest-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) c, err := Open(dir) if err != nil { t.Fatalf("Open: %v", err) } n := 10000 if testing.Short() { n = 1000 } for i := 0; i < n; i++ { if err := c.putIndexEntry(dummyID(i), dummyID(i*99), int64(i)*101, true); err != nil { t.Fatalf("addIndexEntry: %v", err) } id := ActionID(dummyID(i)) entry, err := c.Get(id) if err != nil { t.Fatalf("Get(%x): %v", id, err) } if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 { t.Errorf("Get(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101) } } for i := 0; i < n; i++ { id := ActionID(dummyID(i)) entry, err := c.Get(id) if err != nil { t.Fatalf("Get2(%x): %v", id, err) } if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 { t.Errorf("Get2(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101) } } } func TestVerifyPanic(t *testing.T) { os.Setenv("GODEBUG", "gocacheverify=1") initEnv() defer func() { os.Unsetenv("GODEBUG") verify = false }() if !verify { t.Fatal("initEnv did not set verify") } dir, err := ioutil.TempDir("", "cachetest-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) c, err := Open(dir) if err != nil { t.Fatalf("Open: %v", err) } id := ActionID(dummyID(1)) if err := c.PutBytes(id, []byte("abc")); err != nil { t.Fatal(err) } defer func() { if err := recover(); err != nil { t.Log(err) return } }() c.PutBytes(id, []byte("def")) t.Fatal("mismatched Put did not panic in verify mode") } func TestCacheLog(t *testing.T) { dir, err := ioutil.TempDir("", "cachetest-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) c, err := Open(dir) if err != nil { t.Fatalf("Open: %v", err) } c.now = func() time.Time { return time.Unix(1e9, 0) } id := ActionID(dummyID(1)) c.Get(id) c.PutBytes(id, []byte("abc")) c.Get(id) c, err = Open(dir) if err != nil { t.Fatalf("Open #2: %v", err) } c.now = func() time.Time { return time.Unix(1e9+1, 0) } c.Get(id) id2 := ActionID(dummyID(2)) c.Get(id2) c.PutBytes(id2, []byte("abc")) c.Get(id2) c.Get(id) data, err := ioutil.ReadFile(filepath.Join(dir, "log.txt")) if err != nil { t.Fatal(err) } want := `1000000000 miss 0100000000000000000000000000000000000000000000000000000000000000 1000000000 put 0100000000000000000000000000000000000000000000000000000000000000 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad 3 1000000000 get 0100000000000000000000000000000000000000000000000000000000000000 1000000001 get 0100000000000000000000000000000000000000000000000000000000000000 1000000001 miss 0200000000000000000000000000000000000000000000000000000000000000 1000000001 put 0200000000000000000000000000000000000000000000000000000000000000 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad 3 1000000001 get 0200000000000000000000000000000000000000000000000000000000000000 1000000001 get 0100000000000000000000000000000000000000000000000000000000000000 ` if string(data) != want { t.Fatalf("log:\n%s\nwant:\n%s", string(data), want) } } func dummyID(x int) [HashSize]byte { var out [HashSize]byte binary.LittleEndian.PutUint64(out[:], uint64(x)) return out } func TestCacheTrim(t *testing.T) { dir, err := ioutil.TempDir("", "cachetest-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) c, err := Open(dir) if err != nil { t.Fatalf("Open: %v", err) } const start = 1000000000 now := int64(start) c.now = func() time.Time { return time.Unix(now, 0) } checkTime := func(name string, mtime int64) { t.Helper() file := filepath.Join(c.dir, name[:2], name) info, err := os.Stat(file) if err != nil { t.Fatal(err) } if info.ModTime().Unix() != mtime { t.Fatalf("%s mtime = %d, want %d", name, info.ModTime().Unix(), mtime) } } id := ActionID(dummyID(1)) c.PutBytes(id, []byte("abc")) entry, _ := c.Get(id) c.PutBytes(ActionID(dummyID(2)), []byte("def")) mtime := now checkTime(fmt.Sprintf("%x-a", id), mtime) checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime) // Get should not change recent mtimes. now = start + 10 c.Get(id) checkTime(fmt.Sprintf("%x-a", id), mtime) checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime) // Get should change distant mtimes. now = start + 5000 mtime2 := now if _, err := c.Get(id); err != nil { t.Fatal(err) } c.OutputFile(entry.OutputID) checkTime(fmt.Sprintf("%x-a", id), mtime2) checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime2) // Trim should leave everything alone: it's all too new. c.Trim() if _, err := c.Get(id); err != nil { t.Fatal(err) } c.OutputFile(entry.OutputID) data, err := ioutil.ReadFile(filepath.Join(dir, "trim.txt")) if err != nil { t.Fatal(err) } checkTime(fmt.Sprintf("%x-a", dummyID(2)), start) // Trim less than a day later should not do any work at all. now = start + 80000 c.Trim() if _, err := c.Get(id); err != nil { t.Fatal(err) } c.OutputFile(entry.OutputID) data2, err := ioutil.ReadFile(filepath.Join(dir, "trim.txt")) if err != nil { t.Fatal(err) } if !bytes.Equal(data, data2) { t.Fatalf("second trim did work: %q -> %q", data, data2) } // Fast forward and do another trim just before the 5 day cutoff. // Note that because of usedQuantum the cutoff is actually 5 days + 1 hour. // We used c.Get(id) just now, so 5 days later it should still be kept. // On the other hand almost a full day has gone by since we wrote dummyID(2) // and we haven't looked at it since, so 5 days later it should be gone. now += 5 * 86400 checkTime(fmt.Sprintf("%x-a", dummyID(2)), start) c.Trim() if _, err := c.Get(id); err != nil { t.Fatal(err) } c.OutputFile(entry.OutputID) mtime3 := now if _, err := c.Get(dummyID(2)); err == nil { // haven't done a Get for this since original write above t.Fatalf("Trim did not remove dummyID(2)") } // The c.Get(id) refreshed id's mtime again. // Check that another 5 days later it is still not gone, // but check by using checkTime, which doesn't bring mtime forward. now += 5 * 86400 c.Trim() checkTime(fmt.Sprintf("%x-a", id), mtime3) checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3) // Half a day later Trim should still be a no-op, because there was a Trim recently. // Even though the entry for id is now old enough to be trimmed, // it gets a reprieve until the time comes for a new Trim scan. now += 86400 / 2 c.Trim() checkTime(fmt.Sprintf("%x-a", id), mtime3) checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3) // Another half a day later, Trim should actually run, and it should remove id. now += 86400/2 + 1 c.Trim() if _, err := c.Get(dummyID(1)); err == nil { t.Fatal("Trim did not remove dummyID(1)") } } go-internal-1.5.2/cache/default.go000066400000000000000000000046211360713250200167560ustar00rootroot00000000000000// Copyright 2017 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 cache import ( "fmt" "io/ioutil" "os" "path/filepath" "sync" ) // Default returns the default cache to use, or nil if no cache should be used. func Default() *Cache { defaultOnce.Do(initDefaultCache) return defaultCache } var ( defaultOnce sync.Once defaultCache *Cache ) // cacheREADME is a message stored in a README in the cache directory. // Because the cache lives outside the normal Go trees, we leave the // README as a courtesy to explain where it came from. const cacheREADME = `This directory holds cached build artifacts from the Go build system. Run "go clean -cache" if the directory is getting too large. See golang.org to learn more about Go. ` // initDefaultCache does the work of finding the default cache // the first time Default is called. func initDefaultCache() { dir, showWarnings := defaultDir() if dir == "off" { return } if err := os.MkdirAll(dir, 0777); err != nil { if showWarnings { fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) } return } if _, err := os.Stat(filepath.Join(dir, "README")); err != nil { // Best effort. ioutil.WriteFile(filepath.Join(dir, "README"), []byte(cacheREADME), 0666) } c, err := Open(dir) if err != nil { if showWarnings { fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) } return } defaultCache = c } // DefaultDir returns the effective GOCACHE setting. // It returns "off" if the cache is disabled. func DefaultDir() string { dir, _ := defaultDir() return dir } // defaultDir returns the effective GOCACHE setting. // It returns "off" if the cache is disabled. // The second return value reports whether warnings should // be shown if the cache fails to initialize. func defaultDir() (string, bool) { dir := os.Getenv("GOCACHE") if dir != "" { return dir, true } // Compute default location. dir, err := os.UserCacheDir() if err != nil { return "off", true } dir = filepath.Join(dir, "go-build") // Do this after filepath.Join, so that the path has been cleaned. showWarnings := true switch dir { case "/.cache/go-build": // probably docker run with -u flag // https://golang.org/issue/26280 showWarnings = false } return dir, showWarnings } go-internal-1.5.2/cache/default_unix_test.go000066400000000000000000000037331360713250200210630ustar00rootroot00000000000000// Copyright 2018 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. // +build !windows,!darwin,!plan9 package cache import ( "os" "strings" "testing" ) func TestDefaultDir(t *testing.T) { goCacheDir := "/tmp/test-go-cache" xdgCacheDir := "/tmp/test-xdg-cache" homeDir := "/tmp/test-home" // undo env changes when finished defer func(GOCACHE, XDG_CACHE_HOME, HOME string) { os.Setenv("GOCACHE", GOCACHE) os.Setenv("XDG_CACHE_HOME", XDG_CACHE_HOME) os.Setenv("HOME", HOME) }(os.Getenv("GOCACHE"), os.Getenv("XDG_CACHE_HOME"), os.Getenv("HOME")) os.Setenv("GOCACHE", goCacheDir) os.Setenv("XDG_CACHE_HOME", xdgCacheDir) os.Setenv("HOME", homeDir) dir, showWarnings := defaultDir() if dir != goCacheDir { t.Errorf("Cache DefaultDir %q should be $GOCACHE %q", dir, goCacheDir) } if !showWarnings { t.Error("Warnings should be shown when $GOCACHE is set") } os.Unsetenv("GOCACHE") dir, showWarnings = defaultDir() if !strings.HasPrefix(dir, xdgCacheDir+"/") { t.Errorf("Cache DefaultDir %q should be under $XDG_CACHE_HOME %q when $GOCACHE is unset", dir, xdgCacheDir) } if !showWarnings { t.Error("Warnings should be shown when $XDG_CACHE_HOME is set") } os.Unsetenv("XDG_CACHE_HOME") dir, showWarnings = defaultDir() if !strings.HasPrefix(dir, homeDir+"/.cache/") { t.Errorf("Cache DefaultDir %q should be under $HOME/.cache %q when $GOCACHE and $XDG_CACHE_HOME are unset", dir, homeDir+"/.cache") } if !showWarnings { t.Error("Warnings should be shown when $HOME is not /") } os.Unsetenv("HOME") if dir, _ := defaultDir(); dir != "off" { t.Error("Cache not disabled when $GOCACHE, $XDG_CACHE_HOME, and $HOME are unset") } os.Setenv("HOME", "/") if _, showWarnings := defaultDir(); showWarnings { // https://golang.org/issue/26280 t.Error("Cache initialization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /") } } go-internal-1.5.2/cache/hash.go000066400000000000000000000104301360713250200162500ustar00rootroot00000000000000// Copyright 2017 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 cache import ( "bytes" "crypto/sha256" "fmt" "hash" "io" "os" "runtime" "sync" ) var debugHash = false // set when GODEBUG=gocachehash=1 // HashSize is the number of bytes in a hash. const HashSize = 32 // A Hash provides access to the canonical hash function used to index the cache. // The current implementation uses salted SHA256, but clients must not assume this. type Hash struct { h hash.Hash name string // for debugging buf *bytes.Buffer // for verify } // hashSalt is a salt string added to the beginning of every hash // created by NewHash. Using the Go version makes sure that different // versions of the go command (or even different Git commits during // work on the development branch) do not address the same cache // entries, so that a bug in one version does not affect the execution // of other versions. This salt will result in additional ActionID files // in the cache, but not additional copies of the large output files, // which are still addressed by unsalted SHA256. var hashSalt = []byte(runtime.Version()) // Subkey returns an action ID corresponding to mixing a parent // action ID with a string description of the subkey. func Subkey(parent ActionID, desc string) ActionID { h := sha256.New() h.Write([]byte("subkey:")) h.Write(parent[:]) h.Write([]byte(desc)) var out ActionID h.Sum(out[:0]) if debugHash { fmt.Fprintf(os.Stderr, "HASH subkey %x %q = %x\n", parent, desc, out) } if verify { hashDebug.Lock() hashDebug.m[out] = fmt.Sprintf("subkey %x %q", parent, desc) hashDebug.Unlock() } return out } // NewHash returns a new Hash. // The caller is expected to Write data to it and then call Sum. func NewHash(name string) *Hash { h := &Hash{h: sha256.New(), name: name} if debugHash { fmt.Fprintf(os.Stderr, "HASH[%s]\n", h.name) } h.Write(hashSalt) if verify { h.buf = new(bytes.Buffer) } return h } // Write writes data to the running hash. func (h *Hash) Write(b []byte) (int, error) { if debugHash { fmt.Fprintf(os.Stderr, "HASH[%s]: %q\n", h.name, b) } if h.buf != nil { h.buf.Write(b) } return h.h.Write(b) } // Sum returns the hash of the data written previously. func (h *Hash) Sum() [HashSize]byte { var out [HashSize]byte h.h.Sum(out[:0]) if debugHash { fmt.Fprintf(os.Stderr, "HASH[%s]: %x\n", h.name, out) } if h.buf != nil { hashDebug.Lock() if hashDebug.m == nil { hashDebug.m = make(map[[HashSize]byte]string) } hashDebug.m[out] = h.buf.String() hashDebug.Unlock() } return out } // In GODEBUG=gocacheverify=1 mode, // hashDebug holds the input to every computed hash ID, // so that we can work backward from the ID involved in a // cache entry mismatch to a description of what should be there. var hashDebug struct { sync.Mutex m map[[HashSize]byte]string } // reverseHash returns the input used to compute the hash id. func reverseHash(id [HashSize]byte) string { hashDebug.Lock() s := hashDebug.m[id] hashDebug.Unlock() return s } var hashFileCache struct { sync.Mutex m map[string][HashSize]byte } // FileHash returns the hash of the named file. // It caches repeated lookups for a given file, // and the cache entry for a file can be initialized // using SetFileHash. // The hash used by FileHash is not the same as // the hash used by NewHash. func FileHash(file string) ([HashSize]byte, error) { hashFileCache.Lock() out, ok := hashFileCache.m[file] hashFileCache.Unlock() if ok { return out, nil } h := sha256.New() f, err := os.Open(file) if err != nil { if debugHash { fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err) } return [HashSize]byte{}, err } _, err = io.Copy(h, f) f.Close() if err != nil { if debugHash { fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err) } return [HashSize]byte{}, err } h.Sum(out[:0]) if debugHash { fmt.Fprintf(os.Stderr, "HASH %s: %x\n", file, out) } SetFileHash(file, out) return out, nil } // SetFileHash sets the hash returned by FileHash for file. func SetFileHash(file string, sum [HashSize]byte) { hashFileCache.Lock() if hashFileCache.m == nil { hashFileCache.m = make(map[string][HashSize]byte) } hashFileCache.m[file] = sum hashFileCache.Unlock() } go-internal-1.5.2/cache/hash_test.go000066400000000000000000000021571360713250200173160ustar00rootroot00000000000000// Copyright 2017 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 cache import ( "fmt" "io/ioutil" "os" "testing" ) func TestHash(t *testing.T) { oldSalt := hashSalt hashSalt = nil defer func() { hashSalt = oldSalt }() h := NewHash("alice") h.Write([]byte("hello world")) sum := fmt.Sprintf("%x", h.Sum()) want := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" if sum != want { t.Errorf("hash(hello world) = %v, want %v", sum, want) } } func TestHashFile(t *testing.T) { f, err := ioutil.TempFile("", "cmd-go-test-") if err != nil { t.Fatal(err) } name := f.Name() fmt.Fprintf(f, "hello world") defer os.Remove(name) if err := f.Close(); err != nil { t.Fatal(err) } var h ActionID // make sure hash result is assignable to ActionID h, err = FileHash(name) if err != nil { t.Fatal(err) } sum := fmt.Sprintf("%x", h) want := "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" if sum != want { t.Errorf("hash(hello world) = %v, want %v", sum, want) } } go-internal-1.5.2/cmd/000077500000000000000000000000001360713250200145005ustar00rootroot00000000000000go-internal-1.5.2/cmd/testscript/000077500000000000000000000000001360713250200167045ustar00rootroot00000000000000go-internal-1.5.2/cmd/testscript/README.md000066400000000000000000000052501360713250200201650ustar00rootroot00000000000000``` The testscript command runs github.com/rogpeppe/go-internal/testscript scripts in a fresh temporary work directory tree. Usage: testscript [-v] files... The testscript command is designed to make it easy to create self-contained reproductions of command sequences. Each file is opened as a script and run as described in the documentation for github.com/rogpeppe/go-internal/testscript. The special filename "-" is interpreted as the standard input. As a special case, supporting files/directories in the .gomodproxy subdirectory will be served via a github.com/rogpeppe/go-internal/goproxytest server which is available to each script via the GOPROXY environment variable. The contents of the .gomodproxy subdirectory are not available to the script except via the proxy server. See the documentation for github.com/rogpeppe/go-internal/goproxytest for details on the format of these files/directories. Examples ======== The following example, fruit.txt, shows a simple reproduction that includes .gomodproxy supporting files: go get -m fruit.com go list fruit.com/... stdout 'fruit.com/fruit' -- go.mod -- module mod -- .gomodproxy/fruit.com_v1.0.0/.mod -- module fruit.com -- .gomodproxy/fruit.com_v1.0.0/.info -- {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} -- .gomodproxy/fruit.com_v1.0.0/fruit/fruit.go -- package fruit const Name = "Apple" Running testscript -v fruit.txt we get: ... > go get -m fruit.com [stderr] go: finding fruit.com v1.0.0 > go list fruit.com/... [stdout] fruit.com/fruit [stderr] go: downloading fruit.com v1.0.0 > stdout 'fruit.com/fruit' PASS The following example, goimports.txt, shows a simple reproduction involving goimports: go install golang.org/x/tools/cmd/goimports # check goimports help information exec goimports -d main.go stdout 'import "math"' -- go.mod -- module mod require golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 -- main.go -- package mod const Pi = math.Pi Running testscript -v goimports.txt we get: ... > go install golang.org/x/tools/cmd/goimports [stderr] go: finding golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 go: downloading golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 # check goimports help information (0.015s) > exec goimports -d main.go [stdout] diff -u main.go.orig main.go --- main.go.orig 2019-01-08 16:03:35.861907738 +0000 +++ main.go 2019-01-08 16:03:35.861907738 +0000 @@ -1,3 +1,5 @@ package mod +import "math" + const Pi = math.Pi > stdout 'import "math"' PASS ``` go-internal-1.5.2/cmd/testscript/help.go000066400000000000000000000061031360713250200201630ustar00rootroot00000000000000package main import ( "fmt" "io" ) func mainUsage(f io.Writer) { fmt.Fprint(f, mainHelp) } var mainHelp = ` The testscript command runs github.com/rogpeppe/go-internal/testscript scripts in a fresh temporary work directory tree. Usage: testscript [-v] [-e VAR]... files... The testscript command is designed to make it easy to create self-contained reproductions of command sequences. Each file is opened as a script and run as described in the documentation for github.com/rogpeppe/go-internal/testscript. The special filename "-" is interpreted as the standard input. As a special case, supporting files/directories in the .gomodproxy subdirectory will be served via a github.com/rogpeppe/go-internal/goproxytest server which is available to each script via the GOPROXY environment variable. The contents of the .gomodproxy subdirectory are not available to the script except via the proxy server. See the documentation for github.com/rogpeppe/go-internal/goproxytest for details on the format of these files/directories. Environment variables can be passed through to each script with the -e flag, where VAR is the name of the variable. Variables override testscript-defined values, with the exception of WORK which cannot be overridden. The -e flag can appear multiple times to specify multiple variables. Examples ======== The following example, fruit.txt, shows a simple reproduction that includes .gomodproxy supporting files: go get -m fruit.com go list fruit.com/... stdout 'fruit.com/fruit' -- go.mod -- module mod -- .gomodproxy/fruit.com_v1.0.0/.mod -- module fruit.com -- .gomodproxy/fruit.com_v1.0.0/.info -- {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} -- .gomodproxy/fruit.com_v1.0.0/fruit/fruit.go -- package fruit const Name = "Apple" Running testscript -v fruit.txt we get: ... > go get -m fruit.com [stderr] go: finding fruit.com v1.0.0 > go list fruit.com/... [stdout] fruit.com/fruit [stderr] go: downloading fruit.com v1.0.0 > stdout 'fruit.com/fruit' PASS The following example, goimports.txt, shows a simple reproduction involving goimports: go install golang.org/x/tools/cmd/goimports # check goimports help information exec goimports -d main.go stdout 'import "math"' -- go.mod -- module mod require golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 -- main.go -- package mod const Pi = math.Pi Running testscript -v goimports.txt we get: ... > go install golang.org/x/tools/cmd/goimports [stderr] go: finding golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 go: downloading golang.org/x/tools v0.0.0-20181221235234-d00ac6d27372 # check goimports help information (0.015s) > exec goimports -d main.go [stdout] diff -u main.go.orig main.go --- main.go.orig 2019-01-08 16:03:35.861907738 +0000 +++ main.go 2019-01-08 16:03:35.861907738 +0000 @@ -1,3 +1,5 @@ package mod +import "math" + const Pi = math.Pi > stdout 'import "math"' PASS `[1:] go-internal-1.5.2/cmd/testscript/main.go000066400000000000000000000135001360713250200201560ustar00rootroot00000000000000// Copyright 2018 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 main import ( "errors" "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strconv" "strings" "github.com/rogpeppe/go-internal/goproxytest" "github.com/rogpeppe/go-internal/gotooltest" "github.com/rogpeppe/go-internal/testscript" "github.com/rogpeppe/go-internal/txtar" ) const ( // goModProxyDir is the special subdirectory in a txtar script's supporting files // within which we expect to find github.com/rogpeppe/go-internal/goproxytest // directories. goModProxyDir = ".gomodproxy" ) type envVarsFlag struct { vals []string } func (e *envVarsFlag) String() string { return fmt.Sprintf("%v", e.vals) } func (e *envVarsFlag) Set(v string) error { e.vals = append(e.vals, v) return nil } func main() { os.Exit(main1()) } func main1() int { switch err := mainerr(); err { case nil: return 0 case flag.ErrHelp: return 2 default: fmt.Fprintln(os.Stderr, err) return 1 } } func mainerr() (retErr error) { fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) fs.Usage = func() { mainUsage(os.Stderr) } var envVars envVarsFlag fWork := fs.Bool("work", false, "print temporary work directory and do not remove when done") fVerbose := fs.Bool("v", false, "run tests verbosely") fs.Var(&envVars, "e", "pass through environment variable to script (can appear multiple times)") if err := fs.Parse(os.Args[1:]); err != nil { return err } td, err := ioutil.TempDir("", "testscript") if err != nil { return fmt.Errorf("unable to create temp dir: %v", err) } if *fWork { fmt.Fprintf(os.Stderr, "temporary work directory: %v\n", td) } else { defer os.RemoveAll(td) } files := fs.Args() if len(files) == 0 { files = []string{"-"} } for i, fileName := range files { // TODO make running files concurrent by default? If we do, note we'll need to do // something smarter with the runner stdout and stderr below runDir := filepath.Join(td, strconv.Itoa(i)) if err := os.Mkdir(runDir, 0777); err != nil { return fmt.Errorf("failed to create a run directory within %v for %v: %v", td, fileName, err) } if err := run(runDir, fileName, *fVerbose, envVars.vals); err != nil { return err } } return nil } var ( failedRun = errors.New("failed run") skipRun = errors.New("skip") ) type runner struct { verbose bool } func (r runner) Skip(is ...interface{}) { panic(skipRun) } func (r runner) Fatal(is ...interface{}) { r.Log(is...) r.FailNow() } func (r runner) Parallel() { // No-op for now; we are currently only running a single script in a // testscript instance. } func (r runner) Log(is ...interface{}) { fmt.Print(is...) } func (r runner) FailNow() { panic(failedRun) } func (r runner) Run(n string, f func(t testscript.T)) { // For now we we don't top/tail the run of a subtest. We are currently only // running a single script in a testscript instance, which means that we // will only have a single subtest. f(r) } func (r runner) Verbose() bool { return r.verbose } func run(runDir, fileName string, verbose bool, envVars []string) error { var ar *txtar.Archive var err error mods := filepath.Join(runDir, goModProxyDir) if err := os.MkdirAll(mods, 0777); err != nil { return fmt.Errorf("failed to create goModProxy dir: %v", err) } if fileName == "-" { fileName = "" byts, err := ioutil.ReadAll(os.Stdin) if err != nil { return fmt.Errorf("failed to read from stdin: %v", err) } ar = txtar.Parse(byts) } else { ar, err = txtar.ParseFile(fileName) } if err != nil { return fmt.Errorf("failed to txtar parse %v: %v", fileName, err) } var script, gomodProxy txtar.Archive script.Comment = ar.Comment for _, f := range ar.Files { fp := filepath.Clean(filepath.FromSlash(f.Name)) parts := strings.Split(fp, string(os.PathSeparator)) if len(parts) > 1 && parts[0] == goModProxyDir { gomodProxy.Files = append(gomodProxy.Files, f) } else { script.Files = append(script.Files, f) } } if txtar.Write(&gomodProxy, runDir); err != nil { return fmt.Errorf("failed to write .gomodproxy files: %v", err) } if err := ioutil.WriteFile(filepath.Join(runDir, "script.txt"), txtar.Format(&script), 0666); err != nil { return fmt.Errorf("failed to write script for %v: %v", fileName, err) } p := testscript.Params{ Dir: runDir, } if _, err := exec.LookPath("go"); err == nil { if err := gotooltest.Setup(&p); err != nil { return fmt.Errorf("failed to setup go tool for %v run: %v", fileName, err) } } addSetup := func(f func(env *testscript.Env) error) { origSetup := p.Setup p.Setup = func(env *testscript.Env) error { if origSetup != nil { if err := origSetup(env); err != nil { return err } } return f(env) } } if len(gomodProxy.Files) > 0 { srv, err := goproxytest.NewServer(mods, "") if err != nil { return fmt.Errorf("cannot start proxy for %v: %v", fileName, err) } defer srv.Close() addSetup(func(env *testscript.Env) error { // Add GOPROXY after calling the original setup // so that it overrides any GOPROXY set there. env.Vars = append(env.Vars, "GOPROXY="+srv.URL, "GONOSUMDB=*", ) return nil }) } if len(envVars) > 0 { addSetup(func(env *testscript.Env) error { for _, v := range envVars { if v == "WORK" { // cannot override WORK continue } env.Vars = append(env.Vars, v+"="+os.Getenv(v)) } return nil }) } r := runner{ verbose: verbose, } func() { defer func() { switch recover() { case nil, skipRun: case failedRun: err = failedRun default: panic(fmt.Errorf("unexpected panic: %v [%T]", err, err)) } }() testscript.RunT(r, p) }() if err != nil { return fmt.Errorf("error running %v in %v\n", fileName, runDir) } return nil } go-internal-1.5.2/cmd/testscript/main_test.go000066400000000000000000000043711360713250200212230ustar00rootroot00000000000000// Copyright 2018 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 main import ( "bytes" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" "github.com/rogpeppe/go-internal/gotooltest" "github.com/rogpeppe/go-internal/internal/os/execpath" "github.com/rogpeppe/go-internal/testscript" ) func TestMain(m *testing.M) { os.Exit(testscript.RunMain(m, map[string]func() int{ "testscript": main1, })) } func TestScripts(t *testing.T) { if _, err := exec.LookPath("go"); err != nil { t.Fatalf("need go in PATH for these tests") } var stderr bytes.Buffer cmd := exec.Command("go", "env", "GOMOD") cmd.Stderr = &stderr out, err := cmd.Output() if err != nil { t.Fatalf("failed to run %v: %v\n%s", strings.Join(cmd.Args, " "), err, stderr.String()) } gomod := string(out) if gomod == "" { t.Fatalf("apparently we are not running in module mode?") } p := testscript.Params{ Dir: "testdata", Setup: func(env *testscript.Env) error { env.Vars = append(env.Vars, "GOINTERNALMODPATH="+filepath.Dir(gomod), "GONOSUMDB=*", ) return nil }, Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ "dropgofrompath": dropgofrompath, "setfilegoproxy": setfilegoproxy, }, } if err := gotooltest.Setup(&p); err != nil { t.Fatal(err) } testscript.Run(t, p) } func dropgofrompath(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! dropgofrompath") } var newPath []string for _, d := range filepath.SplitList(ts.Getenv("PATH")) { getenv := func(k string) string { if k == "PATH" { return d } return ts.Getenv(k) } if _, err := execpath.Look("go", getenv); err != nil { newPath = append(newPath, d) } } ts.Setenv("PATH", strings.Join(newPath, string(filepath.ListSeparator))) } func setfilegoproxy(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! setfilegoproxy") } path := args[0] path = filepath.ToSlash(path) // probably sufficient to just handle spaces path = strings.Replace(path, " ", "%20", -1) if runtime.GOOS == "windows" { path = "/" + path } ts.Setenv("GOPROXY", "file://"+path) } go-internal-1.5.2/cmd/testscript/testdata/000077500000000000000000000000001360713250200205155ustar00rootroot00000000000000go-internal-1.5.2/cmd/testscript/testdata/env_var_no_go.txt000066400000000000000000000043151360713250200241020ustar00rootroot00000000000000# Test passing environment variables to scripts with no go command on PATH # # This is the counterpart to env_var_with_go.txt unquote noproxy.txt unquote withproxy.txt dropgofrompath env BANANA=banana env GOPATH=$WORK/ourgopath env GOPROXY= # no GOPROXY, no pass-through, no proxy testscript -v noproxy.txt stdout ^BANANA=$ stdout ^GOPATH=$ stdout ^GOPROXY=$ ! stderr .+ # no GOPROXY, no pass-through, with proxy testscript -v withproxy.txt stdout ^BANANA=$ stdout ^GOPATH=$ stdout ^GOPROXY=http://.*/mod$ ! stderr .+ # no GOPROXY, with pass-through, no proxy testscript -v -e BANANA -e GOPATH -e GOPROXY noproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$ ! stderr .+ # no GOPROXY, with pass-through, with proxy testscript -v -e BANANA -e GOPATH -e GOPROXY withproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$ ! stderr .+ setfilegoproxy $WORK/proxy # with GOPROXY, no pass-through, no proxy testscript -v noproxy.txt stdout ^BANANA=$ stdout ^GOPATH=$ stdout ^GOPROXY=$ ! stderr .+ # with GOPROXY, no pass-through, with proxy testscript -v withproxy.txt stdout ^BANANA=$ stdout ^GOPATH=$ stdout ^GOPROXY=http://.*/mod$ ! stderr .+ # with GOPROXY, with pass-through, no proxy testscript -v -e BANANA -e GOPATH -e GOPROXY noproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$GOPROXY$ ! stderr .+ # with GOPROXY, with pass-through, with proxy testscript -v -e BANANA -e GOPATH -e GOPROXY withproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$GOPROXY$ ! stderr .+ -- noproxy.txt -- >env BANANA >env GOPATH >env GOPROXY -- withproxy.txt -- >env BANANA >env GOPATH >env GOPROXY >-- .gomodproxy/fruit.com_v1.0.0/.mod -- >module fruit.com > >-- .gomodproxy/fruit.com_v1.0.0/.info -- >{"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} > >-- .gomodproxy/fruit.com_v1.0.0/go.mod -- >module fruit.com > >-- .gomodproxy/fruit.com_v1.0.0/fruit/fruit.go -- >package fruit > >const Apple = "apple" >-- .gomodproxy/fruit.com_v1.0.0/coretest/coretest.go -- >// package coretest becomes a candidate for the missing >// core import in main above >package coretest > >const Mandarin = "mandarin" go-internal-1.5.2/cmd/testscript/testdata/env_var_with_go.txt000066400000000000000000000054011360713250200244360ustar00rootroot00000000000000# Test passing environment variables to scripts with the go command on PATH # # Below where we check the output of testscript -v, we have to match against # the string literal $WORK because testscript rewrites the actual directory # to $WORK. Hence we don't want to expand this script's $WORK in such a comparison. # # This is the counterpart to env_var_no_go.txt unquote noproxy.txt unquote withproxy.txt # Baseline testscript -v noproxy.txt stdout ^BANANA=$ stdout '^GOPATH=\$WORK[/\\]gopath'$ [!go1.13] stdout ^GOPROXY=$ [go1.13] stdout ^GOPROXY=https://proxy.golang.org,direct$ ! stderr .+ env BANANA=banana env GOPATH=$WORK/ourgopath env GOPROXY= # no GOPROXY, no pass-through, no proxy testscript -v noproxy.txt stdout ^BANANA=$ stdout '^GOPATH=\$WORK[/\\]gopath'$ [!go1.13] stdout ^GOPROXY=$ [go1.13] stdout ^GOPROXY=https://proxy.golang.org,direct$ ! stderr .+ # no GOPROXY, no pass-through, with proxy testscript -v withproxy.txt stdout ^BANANA=$ stdout '^GOPATH=\$WORK[/\\]gopath'$ stdout ^GOPROXY=http://.*/mod$ ! stderr .+ # no GOPROXY, with pass-through, no proxy testscript -v -e BANANA -e GOPATH -e GOPROXY noproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$ ! stderr .+ # no GOPROXY, with pass-through, with proxy testscript -v -e BANANA -e GOPATH -e GOPROXY withproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$ ! stderr .+ setfilegoproxy $WORK/proxy # with GOPROXY, no pass-through, no proxy testscript -v noproxy.txt stdout ^BANANA=$ stdout '^GOPATH=\$WORK[/\\]gopath'$ stdout ^GOPROXY=$GOPROXY$ ! stderr .+ # with GOPROXY, no pass-through, with proxy testscript -v withproxy.txt stdout ^BANANA=$ stdout '^GOPATH=\$WORK[/\\]gopath'$ stdout ^GOPROXY=http://.*/mod$ ! stderr .+ # with GOPROXY, with pass-through, no proxy testscript -v -e BANANA -e GOPATH -e GOPROXY noproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$GOPROXY$ ! stderr .+ # with GOPROXY, with pass-through, with proxy testscript -v -e BANANA -e GOPATH -e GOPROXY withproxy.txt stdout ^BANANA=banana$ stdout ^GOPATH=${WORK@R}[/\\]ourgopath$ stdout ^GOPROXY=$GOPROXY$ ! stderr .+ -- noproxy.txt -- >env BANANA >env GOPATH >env GOPROXY -- withproxy.txt -- >env BANANA >env GOPATH >env GOPROXY >-- .gomodproxy/fruit.com_v1.0.0/.mod -- >module fruit.com > >-- .gomodproxy/fruit.com_v1.0.0/.info -- >{"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} > >-- .gomodproxy/fruit.com_v1.0.0/go.mod -- >module fruit.com > >-- .gomodproxy/fruit.com_v1.0.0/fruit/fruit.go -- >package fruit > >const Apple = "apple" >-- .gomodproxy/fruit.com_v1.0.0/coretest/coretest.go -- >// package coretest becomes a candidate for the missing >// core import in main above >package coretest > >const Mandarin = "mandarin" go-internal-1.5.2/cmd/testscript/testdata/error.txt000066400000000000000000000003271360713250200224110ustar00rootroot00000000000000# should support skip unquote file.txt # stdin stdin file.txt ! testscript -v stderr 'error running in' # file-based ! testscript -v file.txt stderr 'error running file.txt in' -- file.txt -- >exec false go-internal-1.5.2/cmd/testscript/testdata/help.txt000066400000000000000000000001661360713250200222110ustar00rootroot00000000000000# Simply sanity check on help output ! testscript -help ! stdout .+ stderr 'The testscript command' stderr 'Examples' go-internal-1.5.2/cmd/testscript/testdata/nogo.txt000066400000000000000000000002441360713250200222200ustar00rootroot00000000000000# should support skip unquote file.txt env PATH= ! testscript -v file.txt stdout 'unknown command "go"' stderr 'error running file.txt in' -- file.txt -- >go env go-internal-1.5.2/cmd/testscript/testdata/noproxy.txt000066400000000000000000000003721360713250200227760ustar00rootroot00000000000000# With no .gomodproxy supporting files, we use the GOPROXY from # the environment. env GOPROXY=0.1.2.3 unquote file.txt testscript -v file.txt -- file.txt -- >go env >[!windows] stdout '^GOPROXY="0.1.2.3"$' >[windows] stdout '^set GOPROXY=0.1.2.3$' go-internal-1.5.2/cmd/testscript/testdata/simple.txt000066400000000000000000000016221360713250200225500ustar00rootroot00000000000000# With .gomodproxy supporting files, any GOPROXY from the # environment should be overridden by the test proxy. env GOPROXY=0.1.2.3 unquote file.txt testscript -v file.txt stdout 'example.com/mod' ! stderr .+ -- file.txt -- >go list -m >stdout 'example.com/mod' >go list fruit.com/... >stdout 'fruit.com/fruit' >stdout 'fruit.com/coretest' >-- go.mod -- >module example.com/mod > >require fruit.com v1.0.0 >-- .gomodproxy/fruit.com_v1.0.0/.mod -- >module fruit.com > >-- .gomodproxy/fruit.com_v1.0.0/.info -- >{"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} > >-- .gomodproxy/fruit.com_v1.0.0/go.mod -- >module fruit.com > >-- .gomodproxy/fruit.com_v1.0.0/fruit/fruit.go -- >package fruit > >const Apple = "apple" >-- .gomodproxy/fruit.com_v1.0.0/coretest/coretest.go -- >// package coretest becomes a candidate for the missing >// core import in main above >package coretest > >const Mandarin = "mandarin" go-internal-1.5.2/cmd/testscript/testdata/skip.txt000066400000000000000000000002001360713250200222140ustar00rootroot00000000000000# should support skip unquote file.txt testscript -v file.txt stdout 'go version' ! stderr .+ -- file.txt -- >go version >skip go-internal-1.5.2/cmd/testscript/testdata/work.txt000066400000000000000000000002341360713250200222370ustar00rootroot00000000000000# should support skip unquote file.txt testscript -v -work file.txt stderr '\Qtemporary work directory: '$WORK'\E[/\\]tmp[/\\]' -- file.txt -- >exec true go-internal-1.5.2/cmd/txtar-addmod/000077500000000000000000000000001360713250200170705ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-addmod/addmod.go000066400000000000000000000125451360713250200206560ustar00rootroot00000000000000// Copyright 2018 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. // The txtar-addmod command adds a module as a txtar archive to the a testdata module directory // as understood by the goproxytest package (see https://godoc.org/github.com/rogpeppe/go-internal/goproxytest). // // Usage: // // txtar-addmod dir path@version... // // where dir is the directory to add the module to. // // In general, it's intended to be used only for very small modules - we do not want to check // very large files into testdata/mod. // // It is acceptable to edit the archive afterward to remove or shorten files. // package main import ( "bytes" "flag" "fmt" "io/ioutil" "log" "os" "os/exec" "path/filepath" "strings" "github.com/rogpeppe/go-internal/module" "github.com/rogpeppe/go-internal/txtar" ) func usage() { fmt.Fprintf(os.Stderr, "usage: txtar-addmod dir path@version...\n") flag.PrintDefaults() fmt.Fprintf(os.Stderr, ` The txtar-addmod command adds a module as a txtar archive to the testdata module directory as understood by the goproxytest package (see https://godoc.org/github.com/rogpeppe/go-internal/goproxytest). The dir argument names to directory to add the module to. If dir is "-", the result will instead be written to the standard output in a form suitable for embedding directly into a testscript txtar file, with each file prefixed with the ".gomodproxy" directory. In general, txtar-addmod is intended to be used only for very small modules - we do not want to check very large files into testdata/mod. It is acceptable to edit the archive afterward to remove or shorten files. `) os.Exit(2) } var tmpdir string func fatalf(format string, args ...interface{}) { os.RemoveAll(tmpdir) log.Fatalf(format, args...) } const goCmd = "go" func main() { os.Exit(main1()) } var allFiles = flag.Bool("all", false, "include all source files") func main1() int { flag.Usage = usage flag.Parse() if flag.NArg() < 2 { usage() } targetDir := flag.Arg(0) modules := flag.Args()[1:] log.SetPrefix("txtar-addmod: ") log.SetFlags(0) var err error tmpdir, err = ioutil.TempDir("", "txtar-addmod-") if err != nil { log.Fatal(err) } run := func(command string, args ...string) string { cmd := exec.Command(command, args...) cmd.Dir = tmpdir var stderr bytes.Buffer cmd.Stderr = &stderr out, err := cmd.Output() if err != nil { fatalf("%s %s: %v\n%s", command, strings.Join(args, " "), err, stderr.Bytes()) } return string(out) } gopath := strings.TrimSpace(run("go", "env", "GOPATH")) if gopath == "" { fatalf("cannot find GOPATH") } exitCode := 0 for _, arg := range modules { if err := ioutil.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module m\n"), 0666); err != nil { fatalf("%v", err) } run(goCmd, "get", "-d", arg) path := arg if i := strings.Index(path, "@"); i >= 0 { path = path[:i] } out := run(goCmd, "list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", path) f := strings.Fields(out) if len(f) != 3 { log.Printf("go list -m %s: unexpected output %q", arg, out) exitCode = 1 continue } path, vers, dir := f[0], f[1], f[2] encpath, err := module.EncodePath(path) if err != nil { log.Printf("failed to encode path %q: %v", path, err) continue } path = encpath mod, err := ioutil.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".mod")) if err != nil { log.Printf("%s: %v", arg, err) exitCode = 1 continue } info, err := ioutil.ReadFile(filepath.Join(gopath, "pkg/mod/cache/download", path, "@v", vers+".info")) if err != nil { log.Printf("%s: %v", arg, err) exitCode = 1 continue } a := new(txtar.Archive) title := arg if !strings.Contains(arg, "@") { title += "@" + vers } dir = filepath.Clean(dir) modDir := strings.Replace(path, "/", "_", -1) + "_" + vers filePrefix := "" if targetDir == "-" { filePrefix = ".gomodproxy/" + modDir + "/" } else { // No comment if we're writing to stdout. a.Comment = []byte(fmt.Sprintf("module %s\n\n", title)) } a.Files = []txtar.File{ {Name: filePrefix + ".mod", Data: mod}, {Name: filePrefix + ".info", Data: info}, } err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if !info.Mode().IsRegular() { return nil } // TODO: skip dirs like "testdata" or "_foo" unless -all // is given? name := info.Name() switch { case *allFiles: case name == "go.mod": case strings.HasSuffix(name, ".go"): default: // the name is not in the whitelist, and we're // not including all files via -all return nil } data, err := ioutil.ReadFile(path) if err != nil { return err } a.Files = append(a.Files, txtar.File{ Name: filePrefix + strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data, }) return nil }) if err != nil { log.Printf("%s: %v", arg, err) exitCode = 1 continue } data := txtar.Format(a) if targetDir == "-" { if _, err := os.Stdout.Write(data); err != nil { log.Printf("cannot write output: %v", err) exitCode = 1 break } } else { if err := ioutil.WriteFile(filepath.Join(targetDir, modDir+".txt"), data, 0666); err != nil { log.Printf("%s: %v", arg, err) exitCode = 1 continue } } } os.RemoveAll(tmpdir) return exitCode } go-internal-1.5.2/cmd/txtar-addmod/script_test.go000066400000000000000000000021301360713250200217560ustar00rootroot00000000000000// Copyright 2018 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 main import ( "fmt" "os" "testing" "github.com/rogpeppe/go-internal/goproxytest" "github.com/rogpeppe/go-internal/gotooltest" "github.com/rogpeppe/go-internal/testscript" ) var proxyURL string func TestMain(m *testing.M) { os.Exit(testscript.RunMain(gobinMain{m}, map[string]func() int{ "txtar-addmod": main1, })) } type gobinMain struct { m *testing.M } func (m gobinMain) Run() int { // Start the Go proxy server running for all tests. srv, err := goproxytest.NewServer("testdata/mod", "") if err != nil { fmt.Fprintf(os.Stderr, "cannot start proxy: %v", err) return 1 } proxyURL = srv.URL return m.m.Run() } func TestScripts(t *testing.T) { p := testscript.Params{ Dir: "testdata", Setup: func(e *testscript.Env) error { e.Vars = append(e.Vars, "GOPROXY="+proxyURL, "GONOSUMDB=*", ) return nil }, } if err := gotooltest.Setup(&p); err != nil { t.Fatal(err) } testscript.Run(t, p) } go-internal-1.5.2/cmd/txtar-addmod/testdata/000077500000000000000000000000001360713250200207015ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-addmod/testdata/encode.txt000066400000000000000000000003211360713250200226730ustar00rootroot00000000000000mkdir $WORK/out txtar-addmod $WORK/out github.com/shurcooL/httpfs@v0.0.0-20171119174359-809beceb2371 ! stdout .+ ! stderr .+ exists $WORK/out/github.com_shurcoo!l_httpfs_v0.0.0-20171119174359-809beceb2371.txt go-internal-1.5.2/cmd/txtar-addmod/testdata/mod/000077500000000000000000000000001360713250200214605ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-addmod/testdata/mod/github.com_gobin-testrepos_simple-main_v1.0.0.txt000066400000000000000000000007031360713250200326610ustar00rootroot00000000000000module github.com/gobin-testrepos/simple-main@v1.0.0 -- .mod -- module github.com/gobin-testrepos/simple-main -- .info -- {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} -- go.mod -- module github.com/gobin-testrepos/simple-main -- main.go -- package main import "fmt" func main() { fmt.Println("I am a simple module-based main") } -- foobar -- This is an extra file that can't be imported with the Go package, but may still be used in a test. github.com_shurcoo!l_httpfs_v0.0.0-20171119174359-809beceb2371.txt000066400000000000000000000003161360713250200336650ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-addmod/testdata/modmodule github.com/shurcooL/httpfs@v0.0.0-20171119174359-809beceb2371 -- .mod -- module github.com/shurcooL/httpfs -- .info -- {"Version":"v0.0.0-20171119174359-809beceb2371","Time":"2017-11-19T17:43:59Z"} go-internal-1.5.2/cmd/txtar-addmod/testdata/to_stdout.txt000066400000000000000000000012031360713250200234620ustar00rootroot00000000000000unquote expect txtar-addmod - github.com/gobin-testrepos/simple-main cmp stdout expect ! stderr .+ -- expect -- >-- .gomodproxy/github.com_gobin-testrepos_simple-main_v1.0.0/.mod -- >module github.com/gobin-testrepos/simple-main >-- .gomodproxy/github.com_gobin-testrepos_simple-main_v1.0.0/.info -- >{"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} >-- .gomodproxy/github.com_gobin-testrepos_simple-main_v1.0.0/go.mod -- >module github.com/gobin-testrepos/simple-main >-- .gomodproxy/github.com_gobin-testrepos_simple-main_v1.0.0/main.go -- >package main > >import "fmt" > >func main() { > fmt.Println("I am a simple module-based main") >} go-internal-1.5.2/cmd/txtar-addmod/testdata/txtar-addmod-self.txt000066400000000000000000000006071360713250200247640ustar00rootroot00000000000000mkdir $WORK/out txtar-addmod $WORK/out github.com/gobin-testrepos/simple-main ! stdout .+ ! stderr .+ exists $WORK/out/github.com_gobin-testrepos_simple-main_v1.0.0.txt ! grep foobar $WORK/out/github.com_gobin-testrepos_simple-main_v1.0.0.txt txtar-addmod -all $WORK/out github.com/gobin-testrepos/simple-main grep '-- foobar --' $WORK/out/github.com_gobin-testrepos_simple-main_v1.0.0.txt go-internal-1.5.2/cmd/txtar-c/000077500000000000000000000000001360713250200160625ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-c/savedir.go000066400000000000000000000046331360713250200200540ustar00rootroot00000000000000// Copyright 2018 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. // The txtar-c command archives a directory tree as a txtar archive printed to standard output. // // Usage: // // txtar-c /path/to/dir >saved.txt // // See https://godoc.org/github.com/rogpeppe/go-internal/txtar for details of the format // and how to parse a txtar file. // package main import ( "bytes" stdflag "flag" "fmt" "io/ioutil" "log" "os" "path/filepath" "strings" "unicode/utf8" "github.com/rogpeppe/go-internal/txtar" ) var flag = stdflag.NewFlagSet(os.Args[0], stdflag.ContinueOnError) func usage() { fmt.Fprintf(os.Stderr, "usage: txtar-c dir >saved.txt\n") flag.PrintDefaults() } var ( quoteFlag = flag.Bool("quote", false, "quote files that contain txtar file markers instead of failing") allFlag = flag.Bool("a", false, "include dot files too") ) func main() { os.Exit(main1()) } func main1() int { flag.Usage = usage if flag.Parse(os.Args[1:]) != nil { return 2 } if flag.NArg() != 1 { usage() return 2 } log.SetPrefix("txtar-c: ") log.SetFlags(0) dir := flag.Arg(0) a := new(txtar.Archive) dir = filepath.Clean(dir) filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if path == dir { return nil } name := info.Name() if strings.HasPrefix(name, ".") && !*allFlag { if info.IsDir() { return filepath.SkipDir } return nil } if !info.Mode().IsRegular() { return nil } data, err := ioutil.ReadFile(path) if err != nil { log.Fatal(err) } if !utf8.Valid(data) { log.Printf("%s: ignoring file with invalid UTF-8 data", path) return nil } if len(data) > 0 && !bytes.HasSuffix(data, []byte("\n")) { log.Printf("%s: adding final newline", path) data = append(data, '\n') } filename := strings.TrimPrefix(path, dir+string(filepath.Separator)) if txtar.NeedsQuote(data) { if !*quoteFlag { log.Printf("%s: ignoring file with txtar marker in", path) return nil } data, err = txtar.Quote(data) if err != nil { log.Printf("%s: ignoring unquotable file: %v", path, err) return nil } a.Comment = append(a.Comment, []byte("unquote "+filename+"\n")...) } a.Files = append(a.Files, txtar.File{ Name: filepath.ToSlash(filename), Data: data, }) return nil }) data := txtar.Format(a) os.Stdout.Write(data) return 0 } go-internal-1.5.2/cmd/txtar-c/script_test.go000066400000000000000000000007121360713250200207540ustar00rootroot00000000000000// Copyright 2018 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 main import ( "os" "testing" "github.com/rogpeppe/go-internal/testscript" ) func TestMain(m *testing.M) { os.Exit(testscript.RunMain(m, map[string]func() int{ "txtar-c": main1, })) } func TestScripts(t *testing.T) { testscript.Run(t, testscript.Params{ Dir: "testdata", }) } go-internal-1.5.2/cmd/txtar-c/testdata/000077500000000000000000000000001360713250200176735ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-c/testdata/all.txt000066400000000000000000000014111360713250200212010ustar00rootroot00000000000000unquote expect-all unquote expect-no-all # Without the -a flag, it should ignore . files. txtar-c blah ! stderr .+ cmp stdout expect-no-all # With the -a flag, it should include them. txtar-c -a blah ! stderr .+ cmp stdout expect-all -- blah/.foo/foo -- foo -- blah/.other -- other -- blah/go.mod -- module example.com/blah -- blah/main.go -- package main import "fmt" func main() { fmt.Println("Hello, world!") } -- expect-all -- >-- .foo/foo -- >foo >-- .other -- >other >-- go.mod -- >module example.com/blah > >-- main.go -- >package main > >import "fmt" > >func main() { > fmt.Println("Hello, world!") >} -- expect-no-all -- >-- go.mod -- >module example.com/blah > >-- main.go -- >package main > >import "fmt" > >func main() { > fmt.Println("Hello, world!") >} go-internal-1.5.2/cmd/txtar-c/testdata/needquote.txt000066400000000000000000000003441360713250200224260ustar00rootroot00000000000000unquote blah/withsep unquote expect txtar-c blah stderr 'txtar-c: blah.withsep: ignoring file with txtar marker in' cmp stdout expect -- blah/withsep -- >-- separator -- >foo -- blah/nosep -- bar -- expect -- >-- nosep -- >bar go-internal-1.5.2/cmd/txtar-c/testdata/quote.txt000066400000000000000000000003051360713250200215670ustar00rootroot00000000000000unquote blah/withsep unquote expect txtar-c -quote blah ! stderr .+ cmp stdout expect -- blah/withsep -- >-- separator -- >foo -- expect -- >unquote withsep >-- withsep -- >>-- separator -- >>foo go-internal-1.5.2/cmd/txtar-c/testdata/txtar-savedir-self.txt000066400000000000000000000006261360713250200241640ustar00rootroot00000000000000unquote expect txtar-c blah ! stderr .+ cmp stdout expect -- blah/go.mod -- module example.com/blah -- blah/main.go -- package main import "fmt" func main() { fmt.Println("Hello, world!") } -- blah/subdir/x -- x contents -- expect -- >-- go.mod -- >module example.com/blah > >-- main.go -- >package main > >import "fmt" > >func main() { > fmt.Println("Hello, world!") >} >-- subdir/x -- >x contents go-internal-1.5.2/cmd/txtar-goproxy/000077500000000000000000000000001360713250200173475ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-goproxy/main.go000066400000000000000000000020411360713250200206170ustar00rootroot00000000000000// Copyright 2018 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. // The txtar-goproxy command runs a Go module proxy from a txtar module // directory (as manipulated by the txtar-addmod command). // // This allows interactive experimentation with the set of proxied modules. // For example: // // cd cmd/go // go test -proxy=localhost:1234 & // export GOPROXY=http://localhost:1234/mod // // and then run go commands as usual. package main import ( "flag" "fmt" "log" "os" "github.com/rogpeppe/go-internal/goproxytest" ) var proxyAddr = flag.String("addr", "", "run proxy on this network address") func usage() { fmt.Fprintf(os.Stderr, "usage: txtar-goproxy [flags] dir\n") flag.PrintDefaults() os.Exit(2) } func main() { flag.Usage = usage flag.Parse() if flag.NArg() != 1 { usage() } srv, err := goproxytest.NewServer(flag.Arg(0), *proxyAddr) if err != nil { log.Fatal(err) } fmt.Printf("export GOPROXY=%s\n", srv.URL) select {} } go-internal-1.5.2/cmd/txtar-x/000077500000000000000000000000001360713250200161075ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-x/extract.go000066400000000000000000000023701360713250200201120ustar00rootroot00000000000000// Copyright 2018 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. // The txtar-x command extracts a txtar archive to a filesystem. // // Usage: // // txtar-x [-C root-dir] saved.txt // // See https://godoc.org/github.com/rogpeppe/go-internal/txtar for details of the format // and how to parse a txtar file. // package main import ( "flag" "fmt" "io/ioutil" "log" "os" "github.com/rogpeppe/go-internal/txtar" ) var ( extractDir = flag.String("C", ".", "directory to extract files into") ) func usage() { fmt.Fprintf(os.Stderr, "usage: txtar-x [flags] [file]\n") flag.PrintDefaults() } func main() { os.Exit(main1()) } func main1() int { flag.Usage = usage flag.Parse() if flag.NArg() > 1 { usage() return 2 } log.SetPrefix("txtar-x: ") log.SetFlags(0) var a *txtar.Archive if flag.NArg() == 0 { data, err := ioutil.ReadAll(os.Stdin) if err != nil { log.Printf("cannot read stdin: %v", err) return 1 } a = txtar.Parse(data) } else { a1, err := txtar.ParseFile(flag.Arg(0)) if err != nil { log.Print(err) return 1 } a = a1 } if err := txtar.Write(a, *extractDir); err != nil { log.Print(err) return 1 } return 0 } go-internal-1.5.2/cmd/txtar-x/extract_test.go000066400000000000000000000017241360713250200211530ustar00rootroot00000000000000// Copyright 2018 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 main import ( "bytes" "io/ioutil" "os" "testing" "github.com/rogpeppe/go-internal/testscript" ) func TestMain(m *testing.M) { os.Exit(testscript.RunMain(m, map[string]func() int{ "txtar-x": main1, })) } func TestScripts(t *testing.T) { testscript.Run(t, testscript.Params{ Dir: "testdata", Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ "unquote": unquote, }, }) } func unquote(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! unquote") } for _, arg := range args { file := ts.MkAbs(arg) data, err := ioutil.ReadFile(file) ts.Check(err) data = bytes.Replace(data, []byte("\n>"), []byte("\n"), -1) data = bytes.TrimPrefix(data, []byte(">")) err = ioutil.WriteFile(file, data, 0666) ts.Check(err) } } go-internal-1.5.2/cmd/txtar-x/testdata/000077500000000000000000000000001360713250200177205ustar00rootroot00000000000000go-internal-1.5.2/cmd/txtar-x/testdata/extract-dir.txt000066400000000000000000000003271360713250200227110ustar00rootroot00000000000000unquote file.txtar txtar-x -C x/y file.txtar cmp x/y/foo expect/foo cmp x/y/a/b/bar expect/a/b/bar -- file.txtar -- >some comment > >-- foo -- >foo >-- a/b/bar -- >bar -- expect/foo -- foo -- expect/a/b/bar -- bar go-internal-1.5.2/cmd/txtar-x/testdata/extract-out-of-bounds.txt000066400000000000000000000003561360713250200246360ustar00rootroot00000000000000unquote file1.txtar file2.txtar ! txtar-x file1.txtar stderr '"\.\./foo": outside parent directory' ! txtar-x file2.txtar stderr '"/foo": outside parent directory' -- file1.txtar -- >-- ../foo -- >foo -- file2.txtar -- >-- /foo -- >foo go-internal-1.5.2/cmd/txtar-x/testdata/extract-stdin.txt000066400000000000000000000003161360713250200232520ustar00rootroot00000000000000unquote file.txtar stdin file.txtar txtar-x cmp foo expect/foo cmp a/b/bar expect/a/b/bar -- file.txtar -- >some comment > >-- foo -- >foo >-- a/b/bar -- >bar -- expect/foo -- foo -- expect/a/b/bar -- bar go-internal-1.5.2/cmd/txtar-x/testdata/extract.txt000066400000000000000000000003101360713250200221250ustar00rootroot00000000000000unquote file.txtar txtar-x file.txtar cmp foo expect/foo cmp a/b/bar expect/a/b/bar -- file.txtar -- >some comment > >-- foo -- >foo >-- a/b/bar -- >bar -- expect/foo -- foo -- expect/a/b/bar -- bar go-internal-1.5.2/dirhash/000077500000000000000000000000001360713250200153575ustar00rootroot00000000000000go-internal-1.5.2/dirhash/hash.go000066400000000000000000000045011360713250200166310ustar00rootroot00000000000000// Copyright 2018 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 dirhash defines hashes over directory trees. package dirhash import ( "archive/zip" "crypto/sha256" "encoding/base64" "errors" "fmt" "io" "os" "path/filepath" "sort" "strings" ) var DefaultHash = Hash1 type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error) func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) { h := sha256.New() files = append([]string(nil), files...) sort.Strings(files) for _, file := range files { if strings.Contains(file, "\n") { return "", errors.New("filenames with newlines are not supported") } r, err := open(file) if err != nil { return "", err } hf := sha256.New() _, err = io.Copy(hf, r) r.Close() if err != nil { return "", err } fmt.Fprintf(h, "%x %s\n", hf.Sum(nil), file) } return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil } func HashDir(dir, prefix string, hash Hash) (string, error) { files, err := DirFiles(dir, prefix) if err != nil { return "", err } osOpen := func(name string) (io.ReadCloser, error) { return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix))) } return hash(files, osOpen) } func DirFiles(dir, prefix string) ([]string, error) { var files []string dir = filepath.Clean(dir) err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } rel := file if dir != "." { rel = file[len(dir)+1:] } f := filepath.Join(prefix, rel) files = append(files, filepath.ToSlash(f)) return nil }) if err != nil { return nil, err } return files, nil } func HashZip(zipfile string, hash Hash) (string, error) { z, err := zip.OpenReader(zipfile) if err != nil { return "", err } defer z.Close() var files []string zfiles := make(map[string]*zip.File) for _, file := range z.File { files = append(files, file.Name) zfiles[file.Name] = file } zipOpen := func(name string) (io.ReadCloser, error) { f := zfiles[name] if f == nil { return nil, fmt.Errorf("file %q not found in zip", name) // should never happen } return f.Open() } return hash(files, zipOpen) } go-internal-1.5.2/dirhash/hash_test.go000066400000000000000000000064701360713250200176770ustar00rootroot00000000000000// Copyright 2018 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 dirhash import ( "archive/zip" "crypto/sha256" "encoding/base64" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "testing" ) func h(s string) string { return fmt.Sprintf("%x", sha256.Sum256([]byte(s))) } func htop(k string, s string) string { sum := sha256.Sum256([]byte(s)) return k + ":" + base64.StdEncoding.EncodeToString(sum[:]) } func TestHash1(t *testing.T) { files := []string{"xyz", "abc"} open := func(name string) (io.ReadCloser, error) { return ioutil.NopCloser(strings.NewReader("data for " + name)), nil } want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz")) out, err := Hash1(files, open) if err != nil { t.Fatal(err) } if out != want { t.Errorf("Hash1(...) = %s, want %s", out, want) } _, err = Hash1([]string{"xyz", "a\nbc"}, open) if err == nil { t.Error("Hash1: expected error on newline in filenames") } } func TestHashDir(t *testing.T) { dir, err := ioutil.TempDir("", "dirhash-test-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil { t.Fatal(err) } want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz")) out, err := HashDir(dir, "prefix", Hash1) if err != nil { t.Fatalf("HashDir: %v", err) } if out != want { t.Errorf("HashDir(...) = %s, want %s", out, want) } } func TestHashZip(t *testing.T) { f, err := ioutil.TempFile("", "dirhash-test-") if err != nil { t.Fatal(err) } defer os.Remove(f.Name()) defer f.Close() z := zip.NewWriter(f) w, err := z.Create("prefix/xyz") if err != nil { t.Fatal(err) } w.Write([]byte("data for xyz")) w, err = z.Create("prefix/abc") if err != nil { t.Fatal(err) } w.Write([]byte("data for abc")) if err := z.Close(); err != nil { t.Fatal(err) } if err := f.Close(); err != nil { t.Fatal(err) } want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz")) out, err := HashZip(f.Name(), Hash1) if err != nil { t.Fatalf("HashDir: %v", err) } if out != want { t.Errorf("HashDir(...) = %s, want %s", out, want) } } func TestDirFiles(t *testing.T) { dir, err := ioutil.TempDir("", "dirfiles-test-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil { t.Fatal(err) } if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil { t.Fatal(err) } prefix := "foo/bar@v2.3.4" out, err := DirFiles(dir, prefix) if err != nil { t.Fatalf("DirFiles: %v", err) } for _, file := range out { if !strings.HasPrefix(file, prefix) { t.Errorf("Dir file = %s, want prefix %s", file, prefix) } } } go-internal-1.5.2/fmtsort/000077500000000000000000000000001360713250200154335ustar00rootroot00000000000000go-internal-1.5.2/fmtsort/export_test.go000066400000000000000000000004411360713250200203410ustar00rootroot00000000000000// Copyright 2018 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 fmtsort import "reflect" func Compare(a, b reflect.Value) int { return compare(a, b) } const BrokenNaNs = brokenNaNs go-internal-1.5.2/fmtsort/mapelem.go000066400000000000000000000005671360713250200174120ustar00rootroot00000000000000// +build go1.12 package fmtsort import "reflect" const brokenNaNs = false func mapElems(mapValue reflect.Value) ([]reflect.Value, []reflect.Value) { key := make([]reflect.Value, mapValue.Len()) value := make([]reflect.Value, len(key)) iter := mapValue.MapRange() for i := 0; iter.Next(); i++ { key[i] = iter.Key() value[i] = iter.Value() } return key, value } go-internal-1.5.2/fmtsort/mapelem_1.11.go000066400000000000000000000010471360713250200200440ustar00rootroot00000000000000// +build !go1.12 package fmtsort import "reflect" const brokenNaNs = true func mapElems(mapValue reflect.Value) ([]reflect.Value, []reflect.Value) { key := mapValue.MapKeys() value := make([]reflect.Value, len(key)) for i, k := range key { v := mapValue.MapIndex(k) if !v.IsValid() { // Note: we can't retrieve the value, probably because // the key is NaN, so just do the best we can and // add a zero value of the correct type in that case. v = reflect.Zero(mapValue.Type().Elem()) } value[i] = v } return key, value } go-internal-1.5.2/fmtsort/sort.go000066400000000000000000000127171360713250200167610ustar00rootroot00000000000000// Copyright 2018 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 fmtsort provides a general stable ordering mechanism // for maps, on behalf of the fmt and text/template packages. // It is not guaranteed to be efficient and works only for types // that are valid map keys. package fmtsort import ( "reflect" "sort" ) // Note: Throughout this package we avoid calling reflect.Value.Interface as // it is not always legal to do so and it's easier to avoid the issue than to face it. // SortedMap represents a map's keys and values. The keys and values are // aligned in index order: Value[i] is the value in the map corresponding to Key[i]. type SortedMap struct { Key []reflect.Value Value []reflect.Value } func (o *SortedMap) Len() int { return len(o.Key) } func (o *SortedMap) Less(i, j int) bool { return compare(o.Key[i], o.Key[j]) < 0 } func (o *SortedMap) Swap(i, j int) { o.Key[i], o.Key[j] = o.Key[j], o.Key[i] o.Value[i], o.Value[j] = o.Value[j], o.Value[i] } // Sort accepts a map and returns a SortedMap that has the same keys and // values but in a stable sorted order according to the keys, modulo issues // raised by unorderable key values such as NaNs. // // The ordering rules are more general than with Go's < operator: // // - when applicable, nil compares low // - ints, floats, and strings order by < // - NaN compares less than non-NaN floats // - bool compares false before true // - complex compares real, then imag // - pointers compare by machine address // - channel values compare by machine address // - structs compare each field in turn // - arrays compare each element in turn. // Otherwise identical arrays compare by length. // - interface values compare first by reflect.Type describing the concrete type // and then by concrete value as described in the previous rules. // func Sort(mapValue reflect.Value) *SortedMap { if mapValue.Type().Kind() != reflect.Map { return nil } key, value := mapElems(mapValue) sorted := &SortedMap{ Key: key, Value: value, } sort.Stable(sorted) return sorted } // compare compares two values of the same type. It returns -1, 0, 1 // according to whether a > b (1), a == b (0), or a < b (-1). // If the types differ, it returns -1. // See the comment on Sort for the comparison rules. func compare(aVal, bVal reflect.Value) int { aType, bType := aVal.Type(), bVal.Type() if aType != bType { return -1 // No good answer possible, but don't return 0: they're not equal. } switch aVal.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: a, b := aVal.Int(), bVal.Int() switch { case a < b: return -1 case a > b: return 1 default: return 0 } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: a, b := aVal.Uint(), bVal.Uint() switch { case a < b: return -1 case a > b: return 1 default: return 0 } case reflect.String: a, b := aVal.String(), bVal.String() switch { case a < b: return -1 case a > b: return 1 default: return 0 } case reflect.Float32, reflect.Float64: return floatCompare(aVal.Float(), bVal.Float()) case reflect.Complex64, reflect.Complex128: a, b := aVal.Complex(), bVal.Complex() if c := floatCompare(real(a), real(b)); c != 0 { return c } return floatCompare(imag(a), imag(b)) case reflect.Bool: a, b := aVal.Bool(), bVal.Bool() switch { case a == b: return 0 case a: return 1 default: return -1 } case reflect.Ptr: a, b := aVal.Pointer(), bVal.Pointer() switch { case a < b: return -1 case a > b: return 1 default: return 0 } case reflect.Chan: if c, ok := nilCompare(aVal, bVal); ok { return c } ap, bp := aVal.Pointer(), bVal.Pointer() switch { case ap < bp: return -1 case ap > bp: return 1 default: return 0 } case reflect.Struct: for i := 0; i < aVal.NumField(); i++ { if c := compare(aVal.Field(i), bVal.Field(i)); c != 0 { return c } } return 0 case reflect.Array: for i := 0; i < aVal.Len(); i++ { if c := compare(aVal.Index(i), bVal.Index(i)); c != 0 { return c } } return 0 case reflect.Interface: if c, ok := nilCompare(aVal, bVal); ok { return c } c := compare(reflect.ValueOf(aVal.Elem().Type()), reflect.ValueOf(bVal.Elem().Type())) if c != 0 { return c } return compare(aVal.Elem(), bVal.Elem()) default: // Certain types cannot appear as keys (maps, funcs, slices), but be explicit. panic("bad type in compare: " + aType.String()) } } // nilCompare checks whether either value is nil. If not, the boolean is false. // If either value is nil, the boolean is true and the integer is the comparison // value. The comparison is defined to be 0 if both are nil, otherwise the one // nil value compares low. Both arguments must represent a chan, func, // interface, map, pointer, or slice. func nilCompare(aVal, bVal reflect.Value) (int, bool) { if aVal.IsNil() { if bVal.IsNil() { return 0, true } return -1, true } if bVal.IsNil() { return 1, true } return 0, false } // floatCompare compares two floating-point values. NaNs compare low. func floatCompare(a, b float64) int { switch { case isNaN(a): return -1 // No good answer if b is a NaN so don't bother checking. case isNaN(b): return 1 case a < b: return -1 case a > b: return 1 } return 0 } func isNaN(a float64) bool { return a != a } go-internal-1.5.2/fmtsort/sort_test.go000066400000000000000000000153551360713250200200210ustar00rootroot00000000000000// Copyright 2018 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 fmtsort_test import ( "fmt" "math" "reflect" "strings" "testing" "github.com/rogpeppe/go-internal/fmtsort" ) var compareTests = [][]reflect.Value{ ct(reflect.TypeOf(int(0)), -1, 0, 1), ct(reflect.TypeOf(int8(0)), -1, 0, 1), ct(reflect.TypeOf(int16(0)), -1, 0, 1), ct(reflect.TypeOf(int32(0)), -1, 0, 1), ct(reflect.TypeOf(int64(0)), -1, 0, 1), ct(reflect.TypeOf(uint(0)), 0, 1, 5), ct(reflect.TypeOf(uint8(0)), 0, 1, 5), ct(reflect.TypeOf(uint16(0)), 0, 1, 5), ct(reflect.TypeOf(uint32(0)), 0, 1, 5), ct(reflect.TypeOf(uint64(0)), 0, 1, 5), ct(reflect.TypeOf(uintptr(0)), 0, 1, 5), ct(reflect.TypeOf(string("")), "", "a", "ab"), ct(reflect.TypeOf(float32(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), ct(reflect.TypeOf(float64(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), ct(reflect.TypeOf(complex64(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), ct(reflect.TypeOf(complex128(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), ct(reflect.TypeOf(false), false, true), ct(reflect.TypeOf(&ints[0]), &ints[0], &ints[1], &ints[2]), ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]), ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}), ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}), ct(reflect.TypeOf(interface{}(interface{}(0))), iFace, 1, 2, 3), } var iFace interface{} func ct(typ reflect.Type, args ...interface{}) []reflect.Value { value := make([]reflect.Value, len(args)) for i, v := range args { x := reflect.ValueOf(v) if !x.IsValid() { // Make it a typed nil. x = reflect.Zero(typ) } else { x = x.Convert(typ) } value[i] = x } return value } func TestCompare(t *testing.T) { for _, test := range compareTests { for i, v0 := range test { for j, v1 := range test { c := fmtsort.Compare(v0, v1) var expect int switch { case i == j: expect = 0 // NaNs are tricky. if typ := v0.Type(); (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) && math.IsNaN(v0.Float()) { expect = -1 } case i < j: expect = -1 case i > j: expect = 1 } if c != expect { t.Errorf("%s: compare(%v,%v)=%d; expect %d", v0.Type(), v0, v1, c, expect) } } } } } type sortTest struct { data interface{} // Always a map. print string // Printed result using our custom printer. printBrokenNaNs string // Printed result when NaN support is broken (pre Go1.12). } var sortTests = []sortTest{ { data: map[int]string{7: "bar", -3: "foo"}, print: "-3:foo 7:bar", }, { data: map[uint8]string{7: "bar", 3: "foo"}, print: "3:foo 7:bar", }, { data: map[string]string{"7": "bar", "3": "foo"}, print: "3:foo 7:bar", }, { data: map[float64]string{7: "bar", -3: "foo", math.NaN(): "nan", math.Inf(0): "inf"}, print: "NaN:nan -3:foo 7:bar +Inf:inf", printBrokenNaNs: "NaN: -3:foo 7:bar +Inf:inf", }, { data: map[complex128]string{7 + 2i: "bar2", 7 + 1i: "bar", -3: "foo", complex(math.NaN(), 0i): "nan", complex(math.Inf(0), 0i): "inf"}, print: "(NaN+0i):nan (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf", printBrokenNaNs: "(NaN+0i): (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf", }, { data: map[bool]string{true: "true", false: "false"}, print: "false:false true:true", }, { data: chanMap(), print: "CHAN0:0 CHAN1:1 CHAN2:2", }, { data: pointerMap(), print: "PTR0:0 PTR1:1 PTR2:2", }, { data: map[toy]string{toy{7, 2}: "72", toy{7, 1}: "71", toy{3, 4}: "34"}, print: "{3 4}:34 {7 1}:71 {7 2}:72", }, { data: map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, print: "[3 4]:34 [7 1]:71 [7 2]:72", }, } func sprint(data interface{}) string { om := fmtsort.Sort(reflect.ValueOf(data)) if om == nil { return "nil" } b := new(strings.Builder) for i, key := range om.Key { if i > 0 { b.WriteRune(' ') } b.WriteString(sprintKey(key)) b.WriteRune(':') b.WriteString(fmt.Sprint(om.Value[i])) } return b.String() } // sprintKey formats a reflect.Value but gives reproducible values for some // problematic types such as pointers. Note that it only does special handling // for the troublesome types used in the test cases; it is not a general // printer. func sprintKey(key reflect.Value) string { switch str := key.Type().String(); str { case "*int": ptr := key.Interface().(*int) for i := range ints { if ptr == &ints[i] { return fmt.Sprintf("PTR%d", i) } } return "PTR???" case "chan int": c := key.Interface().(chan int) for i := range chans { if c == chans[i] { return fmt.Sprintf("CHAN%d", i) } } return "CHAN???" default: return fmt.Sprint(key) } } var ( ints [3]int chans = [3]chan int{make(chan int), make(chan int), make(chan int)} ) func pointerMap() map[*int]string { m := make(map[*int]string) for i := 2; i >= 0; i-- { m[&ints[i]] = fmt.Sprint(i) } return m } func chanMap() map[chan int]string { m := make(map[chan int]string) for i := 2; i >= 0; i-- { m[chans[i]] = fmt.Sprint(i) } return m } type toy struct { A int // Exported. b int // Unexported. } func TestOrder(t *testing.T) { for _, test := range sortTests { got := sprint(test.data) want := test.print if fmtsort.BrokenNaNs && test.printBrokenNaNs != "" { want = test.printBrokenNaNs } if got != want { t.Errorf("%s: got %q, want %q", reflect.TypeOf(test.data), got, want) } } } func TestInterface(t *testing.T) { // A map containing multiple concrete types should be sorted by type, // then value. However, the relative ordering of types is unspecified, // so test this by checking the presence of sorted subgroups. m := map[interface{}]string{ [2]int{1, 0}: "", [2]int{0, 1}: "", true: "", false: "", 3.1: "", 2.1: "", 1.1: "", math.NaN(): "", 3: "", 2: "", 1: "", "c": "", "b": "", "a": "", struct{ x, y int }{1, 0}: "", struct{ x, y int }{0, 1}: "", } got := sprint(m) typeGroups := []string{ "NaN: 1.1: 2.1: 3.1:", // float64 "false: true:", // bool "1: 2: 3:", // int "a: b: c:", // string "[0 1]: [1 0]:", // [2]int "{0 1}: {1 0}:", // struct{ x int; y int } } for _, g := range typeGroups { if !strings.Contains(got, g) { t.Errorf("sorted map should contain %q", g) } } } go-internal-1.5.2/go.mod000066400000000000000000000001221360713250200150360ustar00rootroot00000000000000module github.com/rogpeppe/go-internal go 1.11 require gopkg.in/errgo.v2 v2.1.0 go-internal-1.5.2/go.sum000066400000000000000000000013641360713250200150740ustar00rootroot00000000000000github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= go-internal-1.5.2/goproxytest/000077500000000000000000000000001360713250200163445ustar00rootroot00000000000000go-internal-1.5.2/goproxytest/allhex.go000066400000000000000000000010021360713250200201410ustar00rootroot00000000000000// Copyright 2018 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 goproxytest // This code was taken from src/cmd/go/internal/modfetch/codehost. // allHex reports whether the revision rev is entirely lower-case hexadecimal digits. func allHex(rev string) bool { for i := 0; i < len(rev); i++ { c := rev[i] if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' { continue } return false } return true } go-internal-1.5.2/goproxytest/proxy.go000066400000000000000000000200701360713250200200530ustar00rootroot00000000000000// Copyright 2018 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 goproxytest serves Go modules from a proxy server designed to run on localhost during tests, both to make tests avoid requiring specific network servers and also to make them significantly faster. Each module archive is either a file named path_vers.txt or a directory named path_vers, where slashes in path have been replaced with underscores. The archive or directory must contain two files ".info" and ".mod", to be served as the info and mod files in the proxy protocol (see https://research.swtch.com/vgo-module). The remaining files are served as the content of the module zip file. The path@vers prefix required of files in the zip file is added automatically by the proxy: the files in the archive have names without the prefix, like plain "go.mod", "x.go", and so on. See ../cmd/txtar-addmod and ../cmd/txtar-c for tools generate txtar files, although it's fine to write them by hand. */ package goproxytest import ( "archive/zip" "bytes" "encoding/json" "fmt" "io/ioutil" "log" "net" "net/http" "os" "path/filepath" "strings" "github.com/rogpeppe/go-internal/module" "github.com/rogpeppe/go-internal/par" "github.com/rogpeppe/go-internal/semver" "github.com/rogpeppe/go-internal/txtar" ) type Server struct { server *http.Server URL string dir string modList []module.Version zipCache par.Cache archiveCache par.Cache } // StartProxy starts the Go module proxy listening on the given // network address. It serves modules taken from the given directory // name. If addr is empty, it will listen on an arbitrary // localhost port. If dir is empty, "testmod" will be used. // // The returned Server should be closed after use. func NewServer(dir, addr string) (*Server, error) { var srv Server if addr == "" { addr = "localhost:0" } if dir == "" { dir = "testmod" } srv.dir = dir if err := srv.readModList(); err != nil { return nil, fmt.Errorf("cannot read modules: %v", err) } l, err := net.Listen("tcp", addr) if err != nil { return nil, fmt.Errorf("cannot listen on %q: %v", addr, err) } srv.server = &http.Server{ Handler: http.HandlerFunc(srv.handler), } addr = l.Addr().String() srv.URL = "http://" + addr + "/mod" go func() { if err := srv.server.Serve(l); err != nil && err != http.ErrServerClosed { log.Printf("go proxy: http.Serve: %v", err) } }() return &srv, nil } // Close shuts down the proxy. func (srv *Server) Close() { srv.server.Close() } func (srv *Server) readModList() error { infos, err := ioutil.ReadDir(srv.dir) if err != nil { return err } for _, info := range infos { name := info.Name() if !strings.HasSuffix(name, ".txt") && !info.IsDir() { continue } name = strings.TrimSuffix(name, ".txt") i := strings.LastIndex(name, "_v") if i < 0 { continue } encPath := strings.Replace(name[:i], "_", "/", -1) path, err := module.DecodePath(encPath) if err != nil { return fmt.Errorf("cannot decode module path in %q: %v", name, err) } encVers := name[i+1:] vers, err := module.DecodeVersion(encVers) if err != nil { return fmt.Errorf("cannot decode module version in %q: %v", name, err) } srv.modList = append(srv.modList, module.Version{Path: path, Version: vers}) } return nil } // handler serves the Go module proxy protocol. // See the proxy section of https://research.swtch.com/vgo-module. func (srv *Server) handler(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(r.URL.Path, "/mod/") { http.NotFound(w, r) return } path := strings.TrimPrefix(r.URL.Path, "/mod/") i := strings.Index(path, "/@v/") if i < 0 { http.NotFound(w, r) return } enc, file := path[:i], path[i+len("/@v/"):] path, err := module.DecodePath(enc) if err != nil { fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) http.NotFound(w, r) return } if file == "list" { n := 0 for _, m := range srv.modList { if m.Path == path && !isPseudoVersion(m.Version) { if err := module.Check(m.Path, m.Version); err == nil { fmt.Fprintf(w, "%s\n", m.Version) n++ } } } if n == 0 { http.NotFound(w, r) } return } i = strings.LastIndex(file, ".") if i < 0 { http.NotFound(w, r) return } encVers, ext := file[:i], file[i+1:] vers, err := module.DecodeVersion(encVers) if err != nil { fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) http.NotFound(w, r) return } if allHex(vers) { var best string // Convert commit hash (only) to known version. // Use latest version in semver priority, to match similar logic // in the repo-based module server (see modfetch.(*codeRepo).convert). for _, m := range srv.modList { if m.Path == path && semver.Compare(best, m.Version) < 0 { var hash string if isPseudoVersion(m.Version) { hash = m.Version[strings.LastIndex(m.Version, "-")+1:] } else { hash = srv.findHash(m) } if strings.HasPrefix(hash, vers) || strings.HasPrefix(vers, hash) { best = m.Version } } } if best != "" { vers = best } } a := srv.readArchive(path, vers) if a == nil { // As of https://go-review.googlesource.com/c/go/+/189517, cmd/go // resolves all paths. i.e. for github.com/hello/world, cmd/go attempts // to resolve github.com, github.com/hello and github.com/hello/world. // cmd/go expects a 404/410 response if there is nothing there. Hence we // cannot return with a 500. fmt.Fprintf(os.Stderr, "go proxy: no archive %s %s\n", path, vers) http.NotFound(w, r) return } switch ext { case "info", "mod": want := "." + ext for _, f := range a.Files { if f.Name == want { w.Write(f.Data) return } } case "zip": type cached struct { zip []byte err error } c := srv.zipCache.Do(a, func() interface{} { var buf bytes.Buffer z := zip.NewWriter(&buf) for _, f := range a.Files { if strings.HasPrefix(f.Name, ".") { continue } zf, err := z.Create(path + "@" + vers + "/" + f.Name) if err != nil { return cached{nil, err} } if _, err := zf.Write(f.Data); err != nil { return cached{nil, err} } } if err := z.Close(); err != nil { return cached{nil, err} } return cached{buf.Bytes(), nil} }).(cached) if c.err != nil { fmt.Fprintf(os.Stderr, "go proxy: %v\n", c.err) http.Error(w, c.err.Error(), 500) return } w.Write(c.zip) return } http.NotFound(w, r) } func (srv *Server) findHash(m module.Version) string { a := srv.readArchive(m.Path, m.Version) if a == nil { return "" } var data []byte for _, f := range a.Files { if f.Name == ".info" { data = f.Data break } } var info struct{ Short string } json.Unmarshal(data, &info) return info.Short } func (srv *Server) readArchive(path, vers string) *txtar.Archive { enc, err := module.EncodePath(path) if err != nil { fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) return nil } encVers, err := module.EncodeVersion(vers) if err != nil { fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) return nil } prefix := strings.Replace(enc, "/", "_", -1) name := filepath.Join(srv.dir, prefix+"_"+encVers+".txt") a := srv.archiveCache.Do(name, func() interface{} { a, err := txtar.ParseFile(name) if os.IsNotExist(err) { // we fallback to trying a directory name = strings.TrimSuffix(name, ".txt") a = new(txtar.Archive) err = filepath.Walk(name, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if path == name && !info.IsDir() { return fmt.Errorf("expected a directory root") } if info.IsDir() { return nil } arpath := filepath.ToSlash(strings.TrimPrefix(path, name+string(os.PathSeparator))) data, err := ioutil.ReadFile(path) if err != nil { return err } a.Files = append(a.Files, txtar.File{ Name: arpath, Data: data, }) return nil }) } if err != nil { if !os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) } a = nil } return a }).(*txtar.Archive) return a } go-internal-1.5.2/goproxytest/proxy_test.go000066400000000000000000000014441360713250200211160ustar00rootroot00000000000000// Copyright 2018 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 goproxytest_test import ( "path/filepath" "testing" "github.com/rogpeppe/go-internal/goproxytest" "github.com/rogpeppe/go-internal/gotooltest" "github.com/rogpeppe/go-internal/testscript" ) func TestScripts(t *testing.T) { srv, err := goproxytest.NewServer(filepath.Join("testdata", "mod"), "") if err != nil { t.Fatalf("cannot start proxy: %v", err) } p := testscript.Params{ Dir: "testdata", Setup: func(e *testscript.Env) error { e.Vars = append(e.Vars, "GOPROXY="+srv.URL, "GONOSUMDB=*", ) return nil }, } if err := gotooltest.Setup(&p); err != nil { t.Fatal(err) } testscript.Run(t, p) } go-internal-1.5.2/goproxytest/pseudo.go000066400000000000000000000011701360713250200201710ustar00rootroot00000000000000// Copyright 2018 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 goproxytest import ( "regexp" "strings" "github.com/rogpeppe/go-internal/semver" ) // This code was taken from cmd/go/internal/modfetch/pseudo.go var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$`) // isPseudoVersion reports whether v is a pseudo-version. func isPseudoVersion(v string) bool { return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v) } go-internal-1.5.2/goproxytest/testdata/000077500000000000000000000000001360713250200201555ustar00rootroot00000000000000go-internal-1.5.2/goproxytest/testdata/list.txt000066400000000000000000000003451360713250200216730ustar00rootroot00000000000000# prior to go 1.12 you cannot list a module without a requirement [!go1.12] go get fruit.com go list -m -versions fruit.com stdout 'v1.0.0 v1.1.0' go get -d fruit.com@v1.0.0 go get -d fruit.com@v1.1.0 -- go.mod -- module mod go-internal-1.5.2/goproxytest/testdata/mod/000077500000000000000000000000001360713250200207345ustar00rootroot00000000000000go-internal-1.5.2/goproxytest/testdata/mod/fruit.com_v1.0.0.txt000066400000000000000000000002651360713250200243100ustar00rootroot00000000000000-- .mod -- module fruit.com -- .info -- {"Version":"v1.0.0","Time":"2018-10-22T18:45:39Z"} -- go.mod -- module fruit.com -- fruit/fruit.go -- package main const Name = "Orange" go-internal-1.5.2/goproxytest/testdata/mod/fruit.com_v1.1.0/000077500000000000000000000000001360713250200235455ustar00rootroot00000000000000go-internal-1.5.2/goproxytest/testdata/mod/fruit.com_v1.1.0/.info000066400000000000000000000000631360713250200245000ustar00rootroot00000000000000{"Version":"v1.1.0","Time":"2018-10-22T18:45:39Z"} go-internal-1.5.2/goproxytest/testdata/mod/fruit.com_v1.1.0/.mod000066400000000000000000000000211360713250200243160ustar00rootroot00000000000000module fruit.com go-internal-1.5.2/goproxytest/testdata/mod/fruit.com_v1.1.0/fruit/000077500000000000000000000000001360713250200246765ustar00rootroot00000000000000go-internal-1.5.2/goproxytest/testdata/mod/fruit.com_v1.1.0/fruit/main.go000066400000000000000000000000441360713250200261470ustar00rootroot00000000000000package fruit const Name = "Apple" go-internal-1.5.2/gotooltest/000077500000000000000000000000001360713250200161405ustar00rootroot00000000000000go-internal-1.5.2/gotooltest/script_test.go000066400000000000000000000007241360713250200210350ustar00rootroot00000000000000// Copyright 2018 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 gotooltest_test import ( "testing" "github.com/rogpeppe/go-internal/gotooltest" "github.com/rogpeppe/go-internal/testscript" ) func TestSimple(t *testing.T) { p := testscript.Params{ Dir: "testdata", } if err := gotooltest.Setup(&p); err != nil { t.Fatal(err) } testscript.Run(t, p) } go-internal-1.5.2/gotooltest/setup.go000066400000000000000000000107611360713250200176340ustar00rootroot00000000000000// Copyright 2015 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 gotooltest implements functionality useful for testing // tools that use the go command. package gotooltest import ( "bytes" "encoding/json" "fmt" "go/build" "os/exec" "path/filepath" "regexp" "runtime" "strings" "sync" "github.com/rogpeppe/go-internal/testscript" ) var ( goVersionRegex = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`) goEnv struct { GOROOT string GOCACHE string GOPROXY string goversion string releaseTags []string once sync.Once err error } ) // initGoEnv initialises goEnv. It should only be called using goEnv.once.Do, // as in Setup. func initGoEnv() error { var err error run := func(args ...string) (*bytes.Buffer, *bytes.Buffer, error) { var stdout, stderr bytes.Buffer cmd := exec.Command(args[0], args[1:]...) cmd.Stdout = &stdout cmd.Stderr = &stderr return &stdout, &stderr, cmd.Run() } lout, stderr, err := run("go", "list", "-f={{context.ReleaseTags}}", "runtime") if err != nil { return fmt.Errorf("failed to determine release tags from go command: %v\n%v", err, stderr.String()) } tagStr := strings.TrimSpace(lout.String()) tagStr = strings.Trim(tagStr, "[]") goEnv.releaseTags = strings.Split(tagStr, " ") eout, stderr, err := run("go", "env", "-json", "GOROOT", "GOCACHE", "GOPROXY", ) if err != nil { return fmt.Errorf("failed to determine environment from go command: %v\n%v", err, stderr) } if err := json.Unmarshal(eout.Bytes(), &goEnv); err != nil { return fmt.Errorf("failed to unmarshal GOROOT and GOCACHE tags from go command out: %v\n%v", err, eout) } version := goEnv.releaseTags[len(goEnv.releaseTags)-1] if !goVersionRegex.MatchString(version) { return fmt.Errorf("invalid go version %q", version) } goEnv.goversion = version[2:] return nil } // Setup sets up the given test environment for tests that use the go // command. It adds support for go tags to p.Condition and adds the go // command to p.Cmds. It also wraps p.Setup to set up the environment // variables for running the go command appropriately. // // It checks go command can run, but not that it can build or run // binaries. func Setup(p *testscript.Params) error { goEnv.once.Do(func() { goEnv.err = initGoEnv() }) if goEnv.err != nil { return goEnv.err } origSetup := p.Setup p.Setup = func(e *testscript.Env) error { e.Vars = goEnviron(e.Vars) if origSetup != nil { return origSetup(e) } return nil } if p.Cmds == nil { p.Cmds = make(map[string]func(ts *testscript.TestScript, neg bool, args []string)) } p.Cmds["go"] = cmdGo origCondition := p.Condition p.Condition = func(cond string) (bool, error) { if cond == "gc" || cond == "gccgo" { // TODO this reflects the compiler that the current // binary was built with but not necessarily the compiler // that will be used. return cond == runtime.Compiler, nil } if goVersionRegex.MatchString(cond) { for _, v := range build.Default.ReleaseTags { if cond == v { return true, nil } } return false, nil } if origCondition == nil { return false, fmt.Errorf("unknown condition %q", cond) } return origCondition(cond) } return nil } func goEnviron(env0 []string) []string { env := environ(env0) workdir := env.get("WORK") return append(env, []string{ "GOPATH=" + filepath.Join(workdir, "gopath"), "CCACHE_DISABLE=1", // ccache breaks with non-existent HOME "GOARCH=" + runtime.GOARCH, "GOOS=" + runtime.GOOS, "GOROOT=" + goEnv.GOROOT, "GOCACHE=" + goEnv.GOCACHE, "GOPROXY=" + goEnv.GOPROXY, "goversion=" + goEnv.goversion, }...) } func cmdGo(ts *testscript.TestScript, neg bool, args []string) { if len(args) < 1 { ts.Fatalf("usage: go subcommand ...") } err := ts.Exec("go", args...) if err != nil { ts.Logf("[%v]\n", err) if !neg { ts.Fatalf("unexpected go command failure") } } else { if neg { ts.Fatalf("unexpected go command success") } } } type environ []string func (e0 *environ) get(name string) string { e := *e0 for i := len(e) - 1; i >= 0; i-- { v := e[i] if len(v) <= len(name) { continue } if strings.HasPrefix(v, name) && v[len(name)] == '=' { return v[len(name)+1:] } } return "" } func (e *environ) set(name, val string) { *e = append(*e, name+"="+val) } func (e *environ) unset(name string) { // TODO actually remove the name from the environment. e.set(name, "") } go-internal-1.5.2/gotooltest/testdata/000077500000000000000000000000001360713250200177515ustar00rootroot00000000000000go-internal-1.5.2/gotooltest/testdata/version.txt000066400000000000000000000002131360713250200221730ustar00rootroot00000000000000go list -f '{{context.ReleaseTags}}' runtime [go1.11] [!go1.12] stdout go1\.11 [go1.11] [!go1.12] ! stdout go1\.12 [go1.12] stdout go1\.12 go-internal-1.5.2/imports/000077500000000000000000000000001360713250200154325ustar00rootroot00000000000000go-internal-1.5.2/imports/build.go000066400000000000000000000134121360713250200170610ustar00rootroot00000000000000// Copyright 2018 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. // Copied from Go distribution src/go/build/build.go, syslist.go package imports import ( "bytes" "strings" "unicode" ) var slashslash = []byte("//") // ShouldBuild reports whether it is okay to use this file, // The rule is that in the file's leading run of // comments // and blank lines, which must be followed by a blank line // (to avoid including a Go package clause doc comment), // lines beginning with '// +build' are taken as build directives. // // The file is accepted only if each such line lists something // matching the file. For example: // // // +build windows linux // // marks the file as applicable only on Windows and Linux. // // If tags["*"] is true, then ShouldBuild will consider every // build tag except "ignore" to be both true and false for // the purpose of satisfying build tags, in order to estimate // (conservatively) whether a file could ever possibly be used // in any build. // func ShouldBuild(content []byte, tags map[string]bool) bool { // Pass 1. Identify leading run of // comments and blank lines, // which must be followed by a blank line. end := 0 p := content for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { line, p = line[:i], p[i+1:] } else { p = p[len(p):] } line = bytes.TrimSpace(line) if len(line) == 0 { // Blank line end = len(content) - len(p) continue } if !bytes.HasPrefix(line, slashslash) { // Not comment line break } } content = content[:end] // Pass 2. Process each line in the run. p = content allok := true for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { line, p = line[:i], p[i+1:] } else { p = p[len(p):] } line = bytes.TrimSpace(line) if !bytes.HasPrefix(line, slashslash) { continue } line = bytes.TrimSpace(line[len(slashslash):]) if len(line) > 0 && line[0] == '+' { // Looks like a comment +line. f := strings.Fields(string(line)) if f[0] == "+build" { ok := false for _, tok := range f[1:] { if matchTags(tok, tags) { ok = true } } if !ok { allok = false } } } } return allok } // matchTags reports whether the name is one of: // // tag (if tags[tag] is true) // !tag (if tags[tag] is false) // a comma-separated list of any of these // func matchTags(name string, tags map[string]bool) bool { if name == "" { return false } if i := strings.Index(name, ","); i >= 0 { // comma-separated list ok1 := matchTags(name[:i], tags) ok2 := matchTags(name[i+1:], tags) return ok1 && ok2 } if strings.HasPrefix(name, "!!") { // bad syntax, reject always return false } if strings.HasPrefix(name, "!") { // negation return len(name) > 1 && matchTag(name[1:], tags, false) } return matchTag(name, tags, true) } // matchTag reports whether the tag name is valid and satisfied by tags[name]==want. func matchTag(name string, tags map[string]bool, want bool) bool { // Tags must be letters, digits, underscores or dots. // Unlike in Go identifiers, all digits are fine (e.g., "386"). for _, c := range name { if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { return false } } if tags["*"] && name != "" && name != "ignore" { // Special case for gathering all possible imports: // if we put * in the tags map then all tags // except "ignore" are considered both present and not // (so we return true no matter how 'want' is set). return true } have := tags[name] if name == "linux" { have = have || tags["android"] } return have == want } // MatchFile returns false if the name contains a $GOOS or $GOARCH // suffix which does not match the current system. // The recognized name formats are: // // name_$(GOOS).* // name_$(GOARCH).* // name_$(GOOS)_$(GOARCH).* // name_$(GOOS)_test.* // name_$(GOARCH)_test.* // name_$(GOOS)_$(GOARCH)_test.* // // An exception: if GOOS=android, then files with GOOS=linux are also matched. // // If tags["*"] is true, then MatchFile will consider all possible // GOOS and GOARCH to be available and will consequently // always return true. func MatchFile(name string, tags map[string]bool) bool { if tags["*"] { return true } if dot := strings.Index(name, "."); dot != -1 { name = name[:dot] } // Before Go 1.4, a file called "linux.go" would be equivalent to having a // build tag "linux" in that file. For Go 1.4 and beyond, we require this // auto-tagging to apply only to files with a non-empty prefix, so // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating // systems, such as android, to arrive without breaking existing code with // innocuous source code in "android.go". The easiest fix: cut everything // in the name before the initial _. i := strings.Index(name, "_") if i < 0 { return true } name = name[i:] // ignore everything before first _ l := strings.Split(name, "_") if n := len(l); n > 0 && l[n-1] == "test" { l = l[:n-1] } n := len(l) if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] { return tags[l[n-2]] && tags[l[n-1]] } if n >= 1 && KnownOS[l[n-1]] { return tags[l[n-1]] } if n >= 1 && KnownArch[l[n-1]] { return tags[l[n-1]] } return true } var KnownOS = make(map[string]bool) var KnownArch = make(map[string]bool) func init() { for _, v := range strings.Fields(goosList) { KnownOS[v] = true } for _, v := range strings.Fields(goarchList) { KnownArch[v] = true } } const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos " const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm " go-internal-1.5.2/imports/read.go000066400000000000000000000132311360713250200166740ustar00rootroot00000000000000// Copyright 2012 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. // Copied from Go distribution src/go/build/read.go. package imports import ( "bufio" "errors" "io" "unicode/utf8" ) type importReader struct { b *bufio.Reader buf []byte peek byte err error eof bool nerr int } func isIdent(c byte) bool { return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf } var ( errSyntax = errors.New("syntax error") errNUL = errors.New("unexpected NUL in input") ) // syntaxError records a syntax error, but only if an I/O error has not already been recorded. func (r *importReader) syntaxError() { if r.err == nil { r.err = errSyntax } } // readByte reads the next byte from the input, saves it in buf, and returns it. // If an error occurs, readByte records the error in r.err and returns 0. func (r *importReader) readByte() byte { c, err := r.b.ReadByte() if err == nil { r.buf = append(r.buf, c) if c == 0 { err = errNUL } } if err != nil { if err == io.EOF { r.eof = true } else if r.err == nil { r.err = err } c = 0 } return c } // peekByte returns the next byte from the input reader but does not advance beyond it. // If skipSpace is set, peekByte skips leading spaces and comments. func (r *importReader) peekByte(skipSpace bool) byte { if r.err != nil { if r.nerr++; r.nerr > 10000 { panic("go/build: import reader looping") } return 0 } // Use r.peek as first input byte. // Don't just return r.peek here: it might have been left by peekByte(false) // and this might be peekByte(true). c := r.peek if c == 0 { c = r.readByte() } for r.err == nil && !r.eof { if skipSpace { // For the purposes of this reader, semicolons are never necessary to // understand the input and are treated as spaces. switch c { case ' ', '\f', '\t', '\r', '\n', ';': c = r.readByte() continue case '/': c = r.readByte() if c == '/' { for c != '\n' && r.err == nil && !r.eof { c = r.readByte() } } else if c == '*' { var c1 byte for (c != '*' || c1 != '/') && r.err == nil { if r.eof { r.syntaxError() } c, c1 = c1, r.readByte() } } else { r.syntaxError() } c = r.readByte() continue } } break } r.peek = c return r.peek } // nextByte is like peekByte but advances beyond the returned byte. func (r *importReader) nextByte(skipSpace bool) byte { c := r.peekByte(skipSpace) r.peek = 0 return c } // readKeyword reads the given keyword from the input. // If the keyword is not present, readKeyword records a syntax error. func (r *importReader) readKeyword(kw string) { r.peekByte(true) for i := 0; i < len(kw); i++ { if r.nextByte(false) != kw[i] { r.syntaxError() return } } if isIdent(r.peekByte(false)) { r.syntaxError() } } // readIdent reads an identifier from the input. // If an identifier is not present, readIdent records a syntax error. func (r *importReader) readIdent() { c := r.peekByte(true) if !isIdent(c) { r.syntaxError() return } for isIdent(r.peekByte(false)) { r.peek = 0 } } // readString reads a quoted string literal from the input. // If an identifier is not present, readString records a syntax error. func (r *importReader) readString(save *[]string) { switch r.nextByte(true) { case '`': start := len(r.buf) - 1 for r.err == nil { if r.nextByte(false) == '`' { if save != nil { *save = append(*save, string(r.buf[start:])) } break } if r.eof { r.syntaxError() } } case '"': start := len(r.buf) - 1 for r.err == nil { c := r.nextByte(false) if c == '"' { if save != nil { *save = append(*save, string(r.buf[start:])) } break } if r.eof || c == '\n' { r.syntaxError() } if c == '\\' { r.nextByte(false) } } default: r.syntaxError() } } // readImport reads an import clause - optional identifier followed by quoted string - // from the input. func (r *importReader) readImport(imports *[]string) { c := r.peekByte(true) if c == '.' { r.peek = 0 } else if isIdent(c) { r.readIdent() } r.readString(imports) } // ReadComments is like ioutil.ReadAll, except that it only reads the leading // block of comments in the file. func ReadComments(f io.Reader) ([]byte, error) { r := &importReader{b: bufio.NewReader(f)} r.peekByte(true) if r.err == nil && !r.eof { // Didn't reach EOF, so must have found a non-space byte. Remove it. r.buf = r.buf[:len(r.buf)-1] } return r.buf, r.err } // ReadImports is like ioutil.ReadAll, except that it expects a Go file as input // and stops reading the input once the imports have completed. func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) { r := &importReader{b: bufio.NewReader(f)} r.readKeyword("package") r.readIdent() for r.peekByte(true) == 'i' { r.readKeyword("import") if r.peekByte(true) == '(' { r.nextByte(false) for r.peekByte(true) != ')' && r.err == nil { r.readImport(imports) } r.nextByte(false) } else { r.readImport(imports) } } // If we stopped successfully before EOF, we read a byte that told us we were done. // Return all but that last byte, which would cause a syntax error if we let it through. if r.err == nil && !r.eof { return r.buf[:len(r.buf)-1], nil } // If we stopped for a syntax error, consume the whole file so that // we are sure we don't change the errors that go/parser returns. if r.err == errSyntax && !reportSyntaxError { r.err = nil for r.err == nil && !r.eof { r.readByte() } } return r.buf, r.err } go-internal-1.5.2/imports/read_test.go000066400000000000000000000074761360713250200177510ustar00rootroot00000000000000// Copyright 2012 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. // Copied from Go distribution src/go/build/read.go. package imports import ( "io" "strings" "testing" ) const quote = "`" type readTest struct { // Test input contains ℙ where readImports should stop. in string err string } var readImportsTests = []readTest{ { `package p`, "", }, { `package p; import "x"`, "", }, { `package p; import . "x"`, "", }, { `package p; import "x";ℙvar x = 1`, "", }, { `package p // comment import "x" import _ "x" import a "x" /* comment */ import ( "x" /* comment */ _ "x" a "x" // comment ` + quote + `x` + quote + ` _ /*comment*/ ` + quote + `x` + quote + ` a ` + quote + `x` + quote + ` ) import ( ) import () import()import()import() import();import();import() ℙvar x = 1 `, "", }, } var readCommentsTests = []readTest{ { `ℙpackage p`, "", }, { `ℙpackage p; import "x"`, "", }, { `ℙpackage p; import . "x"`, "", }, { `// foo /* bar */ /* quux */ // baz /*/ zot */ // asdf ℙHello, world`, "", }, } func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) { for i, tt := range tests { var in, testOut string j := strings.Index(tt.in, "ℙ") if j < 0 { in = tt.in testOut = tt.in } else { in = tt.in[:j] + tt.in[j+len("ℙ"):] testOut = tt.in[:j] } r := strings.NewReader(in) buf, err := read(r) if err != nil { if tt.err == "" { t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf)) continue } if !strings.Contains(err.Error(), tt.err) { t.Errorf("#%d: err=%q, expected %q", i, err, tt.err) continue } continue } if err == nil && tt.err != "" { t.Errorf("#%d: success, expected %q", i, tt.err) continue } out := string(buf) if out != testOut { t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut) } } } func TestReadImports(t *testing.T) { testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) }) } func TestReadComments(t *testing.T) { testRead(t, readCommentsTests, ReadComments) } var readFailuresTests = []readTest{ { `package`, "syntax error", }, { "package p\n\x00\nimport `math`\n", "unexpected NUL in input", }, { `package p; import`, "syntax error", }, { `package p; import "`, "syntax error", }, { "package p; import ` \n\n", "syntax error", }, { `package p; import "x`, "syntax error", }, { `package p; import _`, "syntax error", }, { `package p; import _ "`, "syntax error", }, { `package p; import _ "x`, "syntax error", }, { `package p; import .`, "syntax error", }, { `package p; import . "`, "syntax error", }, { `package p; import . "x`, "syntax error", }, { `package p; import (`, "syntax error", }, { `package p; import ("`, "syntax error", }, { `package p; import ("x`, "syntax error", }, { `package p; import ("x"`, "syntax error", }, } func TestReadFailures(t *testing.T) { // Errors should be reported (true arg to readImports). testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) }) } func TestReadFailuresIgnored(t *testing.T) { // Syntax errors should not be reported (false arg to readImports). // Instead, entire file should be the output and no error. // Convert tests not to return syntax errors. tests := make([]readTest, len(readFailuresTests)) copy(tests, readFailuresTests) for i := range tests { tt := &tests[i] if !strings.Contains(tt.err, "NUL") { tt.err = "" } } testRead(t, tests, func(r io.Reader) ([]byte, error) { return ReadImports(r, false, nil) }) } go-internal-1.5.2/imports/scan.go000066400000000000000000000043601360713250200167100ustar00rootroot00000000000000// Copyright 2018 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 imports import ( "fmt" "io/ioutil" "os" "path/filepath" "sort" "strconv" "strings" ) func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) { infos, err := ioutil.ReadDir(dir) if err != nil { return nil, nil, err } var files []string for _, info := range infos { name := info.Name() if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) { files = append(files, filepath.Join(dir, name)) } } return scanFiles(files, tags, false) } func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) { return scanFiles(files, tags, true) } func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) { imports := make(map[string]bool) testImports := make(map[string]bool) numFiles := 0 Files: for _, name := range files { r, err := os.Open(name) if err != nil { return nil, nil, err } var list []string data, err := ReadImports(r, false, &list) r.Close() if err != nil { return nil, nil, fmt.Errorf("reading %s: %v", name, err) } // import "C" is implicit requirement of cgo tag. // When listing files on the command line (explicitFiles=true) // we do not apply build tag filtering but we still do apply // cgo filtering, so no explicitFiles check here. // Why? Because we always have, and it's not worth breaking // that behavior now. for _, path := range list { if path == `"C"` && !tags["cgo"] && !tags["*"] { continue Files } } if !explicitFiles && !ShouldBuild(data, tags) { continue } numFiles++ m := imports if strings.HasSuffix(name, "_test.go") { m = testImports } for _, p := range list { q, err := strconv.Unquote(p) if err != nil { continue } m[q] = true } } if numFiles == 0 { return nil, nil, ErrNoGo } return keys(imports), keys(testImports), nil } var ErrNoGo = fmt.Errorf("no Go source files") func keys(m map[string]bool) []string { var list []string for k := range m { list = append(list, k) } sort.Strings(list) return list } go-internal-1.5.2/imports/scan_test.go000066400000000000000000000031601360713250200177440ustar00rootroot00000000000000// Copyright 2018 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 imports import ( "path/filepath" "reflect" "runtime" "testing" "github.com/rogpeppe/go-internal/testenv" ) func TestScan(t *testing.T) { testenv.MustHaveGoBuild(t) imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), nil) if err != nil { t.Fatal(err) } foundBase64 := false for _, p := range imports { if p == "encoding/base64" { foundBase64 = true } if p == "encoding/binary" { // A dependency but not an import t.Errorf("json reported as importing encoding/binary but does not") } if p == "net/http" { // A test import but not an import t.Errorf("json reported as importing encoding/binary but does not") } } if !foundBase64 { t.Errorf("json missing import encoding/base64 (%q)", imports) } foundHTTP := false for _, p := range testImports { if p == "net/http" { foundHTTP = true } if p == "unicode/utf16" { // A package import but not a test import t.Errorf("json reported as test-importing unicode/utf16 but does not") } } if !foundHTTP { t.Errorf("json missing test import net/http (%q)", testImports) } } func TestScanStar(t *testing.T) { testenv.MustHaveGoBuild(t) imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true}) if err != nil { t.Fatal(err) } want := []string{"import1", "import2", "import3", "import4"} if !reflect.DeepEqual(imports, want) { t.Errorf("ScanDir testdata/import1:\nhave %v\nwant %v", imports, want) } } go-internal-1.5.2/imports/testdata/000077500000000000000000000000001360713250200172435ustar00rootroot00000000000000go-internal-1.5.2/imports/testdata/import1/000077500000000000000000000000001360713250200206365ustar00rootroot00000000000000go-internal-1.5.2/imports/testdata/import1/x.go000066400000000000000000000000341360713250200214310ustar00rootroot00000000000000package x import "import1" go-internal-1.5.2/imports/testdata/import1/x1.go000066400000000000000000000001631360713250200215150ustar00rootroot00000000000000// +build blahblh // +build linux // +build !linux // +build windows // +build darwin package x import "import4" go-internal-1.5.2/imports/testdata/import1/x_darwin.go000066400000000000000000000000371360713250200230000ustar00rootroot00000000000000package xxxx import "import3" go-internal-1.5.2/imports/testdata/import1/x_windows.go000066400000000000000000000000341360713250200232030ustar00rootroot00000000000000package x import "import2" go-internal-1.5.2/internal/000077500000000000000000000000001360713250200155515ustar00rootroot00000000000000go-internal-1.5.2/internal/os/000077500000000000000000000000001360713250200161725ustar00rootroot00000000000000go-internal-1.5.2/internal/os/execpath/000077500000000000000000000000001360713250200177735ustar00rootroot00000000000000go-internal-1.5.2/internal/os/execpath/exec.go000066400000000000000000000002721360713250200212470ustar00rootroot00000000000000package execpath import "os/exec" type Error = exec.Error // ErrNotFound is the error resulting if a path search failed to find an executable file. var ErrNotFound = exec.ErrNotFound go-internal-1.5.2/internal/os/execpath/lp_js.go000066400000000000000000000012641360713250200214340ustar00rootroot00000000000000// Copyright 2018 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. // +build js,wasm package execpath // Look searches for an executable named file, using getenv to look up // environment variables. If getenv is nil, os.Getenv will be used. If file // contains a slash, it is tried directly and getenv will not be called. The // result may be an absolute path or a path relative to the current directory. func Look(file string, getenv func(string) string) (string, error) { // Wasm can not execute processes, so act as if there are no executables at all. return "", &Error{file, ErrNotFound} } go-internal-1.5.2/internal/os/execpath/lp_plan9.go000066400000000000000000000024431360713250200220430ustar00rootroot00000000000000// Copyright 2011 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 execpath import ( "os" "path/filepath" "strings" ) func findExecutable(file string) error { d, err := os.Stat(file) if err != nil { return err } if m := d.Mode(); !m.IsDir() && m&0111 != 0 { return nil } return os.ErrPermission } // Look searches for an executable named file, using getenv to look up // environment variables. If getenv is nil, os.Getenv will be used. If file // contains a slash, it is tried directly and getenv will not be called. The // result may be an absolute path or a path relative to the current directory. func Look(file string, getenv func(string) string) (string, error) { if getenv == nil { getenv = os.Getenv } // skip the path lookup for these prefixes skip := []string{"/", "#", "./", "../"} for _, p := range skip { if strings.HasPrefix(file, p) { err := findExecutable(file) if err == nil { return file, nil } return "", &Error{file, err} } } path := getenv("path") for _, dir := range filepath.SplitList(path) { path := filepath.Join(dir, file) if err := findExecutable(path); err == nil { return path, nil } } return "", &Error{file, ErrNotFound} } go-internal-1.5.2/internal/os/execpath/lp_unix.go000066400000000000000000000027701360713250200220060ustar00rootroot00000000000000// 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. // +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris package execpath import ( "os" "path/filepath" "strings" ) func findExecutable(file string) error { d, err := os.Stat(file) if err != nil { return err } if m := d.Mode(); !m.IsDir() && m&0111 != 0 { return nil } return os.ErrPermission } // Look searches for an executable named file, using getenv to look up // environment variables. If getenv is nil, os.Getenv will be used. If file // contains a slash, it is tried directly and getenv will not be called. The // result may be an absolute path or a path relative to the current directory. func Look(file string, getenv func(string) string) (string, error) { if getenv == nil { getenv = os.Getenv } // NOTE(rsc): I wish we could use the Plan 9 behavior here // (only bypass the path if file begins with / or ./ or ../) // but that would not match all the Unix shells. if strings.Contains(file, "/") { err := findExecutable(file) if err == nil { return file, nil } return "", &Error{file, err} } path := getenv("PATH") for _, dir := range filepath.SplitList(path) { if dir == "" { // Unix shell semantics: path element "" means "." dir = "." } path := filepath.Join(dir, file) if err := findExecutable(path); err == nil { return path, nil } } return "", &Error{file, ErrNotFound} } go-internal-1.5.2/internal/os/execpath/lp_windows.go000066400000000000000000000040471360713250200225140ustar00rootroot00000000000000// 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 execpath import ( "os" "path/filepath" "strings" ) func chkStat(file string) error { d, err := os.Stat(file) if err != nil { return err } if d.IsDir() { return os.ErrPermission } return nil } func hasExt(file string) bool { i := strings.LastIndex(file, ".") if i < 0 { return false } return strings.LastIndexAny(file, `:\/`) < i } func findExecutable(file string, exts []string) (string, error) { if len(exts) == 0 { return file, chkStat(file) } if hasExt(file) { if chkStat(file) == nil { return file, nil } } for _, e := range exts { if f := file + e; chkStat(f) == nil { return f, nil } } return "", os.ErrNotExist } // Look searches for an executable named file, using getenv to look up // environment variables. If getenv is nil, os.Getenv will be used. If file // contains a slash, it is tried directly and getenv will not be called. The // result may be an absolute path or a path relative to the current directory. // Look also uses PATHEXT environment variable to match // a suitable candidate. func Look(file string, getenv func(string) string) (string, error) { if getenv == nil { getenv = os.Getenv } var exts []string x := getenv(`PATHEXT`) if x != "" { for _, e := range strings.Split(strings.ToLower(x), `;`) { if e == "" { continue } if e[0] != '.' { e = "." + e } exts = append(exts, e) } } else { exts = []string{".com", ".exe", ".bat", ".cmd"} } if strings.ContainsAny(file, `:\/`) { if f, err := findExecutable(file, exts); err == nil { return f, nil } else { return "", &Error{file, err} } } if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { return f, nil } path := getenv("path") for _, dir := range filepath.SplitList(path) { if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { return f, nil } } return "", &Error{file, ErrNotFound} } go-internal-1.5.2/internal/syscall/000077500000000000000000000000001360713250200172235ustar00rootroot00000000000000go-internal-1.5.2/internal/syscall/windows/000077500000000000000000000000001360713250200207155ustar00rootroot00000000000000go-internal-1.5.2/internal/syscall/windows/mksyscall.go000066400000000000000000000005261360713250200232510ustar00rootroot00000000000000// Copyright 2016 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 windows //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go security_windows.go psapi_windows.go symlink_windows.go go-internal-1.5.2/internal/syscall/windows/psapi_windows.go000066400000000000000000000013141360713250200241310ustar00rootroot00000000000000// Copyright 2017 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 windows type PROCESS_MEMORY_COUNTERS struct { CB uint32 PageFaultCount uint32 PeakWorkingSetSize uintptr WorkingSetSize uintptr QuotaPeakPagedPoolUsage uintptr QuotaPagedPoolUsage uintptr QuotaPeakNonPagedPoolUsage uintptr QuotaNonPagedPoolUsage uintptr PagefileUsage uintptr PeakPagefileUsage uintptr } //sys GetProcessMemoryInfo(handle syscall.Handle, memCounters *PROCESS_MEMORY_COUNTERS, cb uint32) (err error) = psapi.GetProcessMemoryInfo go-internal-1.5.2/internal/syscall/windows/registry/000077500000000000000000000000001360713250200225655ustar00rootroot00000000000000go-internal-1.5.2/internal/syscall/windows/registry/key.go000066400000000000000000000126021360713250200237050ustar00rootroot00000000000000// Copyright 2015 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. // +build windows // Package registry provides access to the Windows registry. // // Here is a simple example, opening a registry key and reading a string value from it. // // k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) // if err != nil { // log.Fatal(err) // } // defer k.Close() // // s, _, err := k.GetStringValue("SystemRoot") // if err != nil { // log.Fatal(err) // } // fmt.Printf("Windows system root is %q\n", s) // // NOTE: This package is a copy of golang.org/x/sys/windows/registry // with KeyInfo.ModTime removed to prevent dependency cycles. // package registry import ( "io" "syscall" ) const ( // Registry key security and access rights. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx // for details. ALL_ACCESS = 0xf003f CREATE_LINK = 0x00020 CREATE_SUB_KEY = 0x00004 ENUMERATE_SUB_KEYS = 0x00008 EXECUTE = 0x20019 NOTIFY = 0x00010 QUERY_VALUE = 0x00001 READ = 0x20019 SET_VALUE = 0x00002 WOW64_32KEY = 0x00200 WOW64_64KEY = 0x00100 WRITE = 0x20006 ) // Key is a handle to an open Windows registry key. // Keys can be obtained by calling OpenKey; there are // also some predefined root keys such as CURRENT_USER. // Keys can be used directly in the Windows API. type Key syscall.Handle const ( // Windows defines some predefined root keys that are always open. // An application can use these keys as entry points to the registry. // Normally these keys are used in OpenKey to open new keys, // but they can also be used anywhere a Key is required. CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT) CURRENT_USER = Key(syscall.HKEY_CURRENT_USER) LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE) USERS = Key(syscall.HKEY_USERS) CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG) ) // Close closes open key k. func (k Key) Close() error { return syscall.RegCloseKey(syscall.Handle(k)) } // OpenKey opens a new key with path name relative to key k. // It accepts any open key, including CURRENT_USER and others, // and returns the new key and an error. // The access parameter specifies desired access rights to the // key to be opened. func OpenKey(k Key, path string, access uint32) (Key, error) { p, err := syscall.UTF16PtrFromString(path) if err != nil { return 0, err } var subkey syscall.Handle err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey) if err != nil { return 0, err } return Key(subkey), nil } // ReadSubKeyNames returns the names of subkeys of key k. // The parameter n controls the number of returned names, // analogous to the way os.File.Readdirnames works. func (k Key) ReadSubKeyNames(n int) ([]string, error) { names := make([]string, 0) // Registry key size limit is 255 bytes and described there: // https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx buf := make([]uint16, 256) //plus extra room for terminating zero byte loopItems: for i := uint32(0); ; i++ { if n > 0 { if len(names) == n { return names, nil } } l := uint32(len(buf)) for { err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil) if err == nil { break } if err == syscall.ERROR_MORE_DATA { // Double buffer size and try again. l = uint32(2 * len(buf)) buf = make([]uint16, l) continue } if err == _ERROR_NO_MORE_ITEMS { break loopItems } return names, err } names = append(names, syscall.UTF16ToString(buf[:l])) } if n > len(names) { return names, io.EOF } return names, nil } // CreateKey creates a key named path under open key k. // CreateKey returns the new key and a boolean flag that reports // whether the key already existed. // The access parameter specifies the access rights for the key // to be created. func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) { var h syscall.Handle var d uint32 err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path), 0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d) if err != nil { return 0, false, err } return Key(h), d == _REG_OPENED_EXISTING_KEY, nil } // DeleteKey deletes the subkey path of key k and its values. func DeleteKey(k Key, path string) error { return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path)) } // A KeyInfo describes the statistics of a key. It is returned by Stat. type KeyInfo struct { SubKeyCount uint32 MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte ValueCount uint32 MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte MaxValueLen uint32 // longest data component among the key's values, in bytes lastWriteTime syscall.Filetime } // Stat retrieves information about the open key k. func (k Key) Stat() (*KeyInfo, error) { var ki KeyInfo err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil, &ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount, &ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime) if err != nil { return nil, err } return &ki, nil } go-internal-1.5.2/internal/syscall/windows/registry/mksyscall.go000066400000000000000000000004271360713250200251210ustar00rootroot00000000000000// Copyright 2016 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 registry //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go go-internal-1.5.2/internal/syscall/windows/registry/syscall.go000066400000000000000000000027761360713250200246020ustar00rootroot00000000000000// Copyright 2015 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. // +build windows package registry import "syscall" const ( _REG_OPTION_NON_VOLATILE = 0 _REG_CREATED_NEW_KEY = 1 _REG_OPENED_EXISTING_KEY = 2 _ERROR_NO_MORE_ITEMS syscall.Errno = 259 ) func LoadRegLoadMUIString() error { return procRegLoadMUIStringW.Find() } //sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW //sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW //sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW //sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW //sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW //sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW go-internal-1.5.2/internal/syscall/windows/registry/value.go000066400000000000000000000266131360713250200242400ustar00rootroot00000000000000// Copyright 2015 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. // +build windows package registry import ( "errors" "io" "syscall" "unicode/utf16" "unsafe" ) const ( // Registry value types. NONE = 0 SZ = 1 EXPAND_SZ = 2 BINARY = 3 DWORD = 4 DWORD_BIG_ENDIAN = 5 LINK = 6 MULTI_SZ = 7 RESOURCE_LIST = 8 FULL_RESOURCE_DESCRIPTOR = 9 RESOURCE_REQUIREMENTS_LIST = 10 QWORD = 11 ) var ( // ErrShortBuffer is returned when the buffer was too short for the operation. ErrShortBuffer = syscall.ERROR_MORE_DATA // ErrNotExist is returned when a registry key or value does not exist. ErrNotExist = syscall.ERROR_FILE_NOT_FOUND // ErrUnexpectedType is returned by Get*Value when the value's type was unexpected. ErrUnexpectedType = errors.New("unexpected key value type") ) // GetValue retrieves the type and data for the specified value associated // with an open key k. It fills up buffer buf and returns the retrieved // byte count n. If buf is too small to fit the stored value it returns // ErrShortBuffer error along with the required buffer size n. // If no buffer is provided, it returns true and actual buffer size n. // If no buffer is provided, GetValue returns the value's type only. // If the value does not exist, the error returned is ErrNotExist. // // GetValue is a low level function. If value's type is known, use the appropriate // Get*Value function instead. func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) { pname, err := syscall.UTF16PtrFromString(name) if err != nil { return 0, 0, err } var pbuf *byte if len(buf) > 0 { pbuf = (*byte)(unsafe.Pointer(&buf[0])) } l := uint32(len(buf)) err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l) if err != nil { return int(l), valtype, err } return int(l), valtype, nil } func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) { p, err := syscall.UTF16PtrFromString(name) if err != nil { return nil, 0, err } var t uint32 n := uint32(len(buf)) for { err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n) if err == nil { return buf[:n], t, nil } if err != syscall.ERROR_MORE_DATA { return nil, 0, err } if n <= uint32(len(buf)) { return nil, 0, err } buf = make([]byte, n) } } // GetStringValue retrieves the string value for the specified // value name associated with an open key k. It also returns the value's type. // If value does not exist, GetStringValue returns ErrNotExist. // If value is not SZ or EXPAND_SZ, it will return the correct value // type and ErrUnexpectedType. func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) { data, typ, err2 := k.getValue(name, make([]byte, 64)) if err2 != nil { return "", typ, err2 } switch typ { case SZ, EXPAND_SZ: default: return "", typ, ErrUnexpectedType } if len(data) == 0 { return "", typ, nil } u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:] return syscall.UTF16ToString(u), typ, nil } // GetMUIStringValue retrieves the localized string value for // the specified value name associated with an open key k. // If the value name doesn't exist or the localized string value // can't be resolved, GetMUIStringValue returns ErrNotExist. // GetMUIStringValue panics if the system doesn't support // regLoadMUIString; use LoadRegLoadMUIString to check if // regLoadMUIString is supported before calling this function. func (k Key) GetMUIStringValue(name string) (string, error) { pname, err := syscall.UTF16PtrFromString(name) if err != nil { return "", err } buf := make([]uint16, 1024) var buflen uint32 var pdir *uint16 err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path // Try to resolve the string value using the system directory as // a DLL search path; this assumes the string value is of the form // @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320. // This approach works with tzres.dll but may have to be revised // in the future to allow callers to provide custom search paths. var s string s, err = ExpandString("%SystemRoot%\\system32\\") if err != nil { return "", err } pdir, err = syscall.UTF16PtrFromString(s) if err != nil { return "", err } err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) } for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed if buflen <= uint32(len(buf)) { break // Buffer not growing, assume race; break } buf = make([]uint16, buflen) err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) } if err != nil { return "", err } return syscall.UTF16ToString(buf), nil } // ExpandString expands environment-variable strings and replaces // them with the values defined for the current user. // Use ExpandString to expand EXPAND_SZ strings. func ExpandString(value string) (string, error) { if value == "" { return "", nil } p, err := syscall.UTF16PtrFromString(value) if err != nil { return "", err } r := make([]uint16, 100) for { n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r))) if err != nil { return "", err } if n <= uint32(len(r)) { u := (*[1 << 29]uint16)(unsafe.Pointer(&r[0]))[:] return syscall.UTF16ToString(u), nil } r = make([]uint16, n) } } // GetStringsValue retrieves the []string value for the specified // value name associated with an open key k. It also returns the value's type. // If value does not exist, GetStringsValue returns ErrNotExist. // If value is not MULTI_SZ, it will return the correct value // type and ErrUnexpectedType. func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) { data, typ, err2 := k.getValue(name, make([]byte, 64)) if err2 != nil { return nil, typ, err2 } if typ != MULTI_SZ { return nil, typ, ErrUnexpectedType } if len(data) == 0 { return nil, typ, nil } p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2] if len(p) == 0 { return nil, typ, nil } if p[len(p)-1] == 0 { p = p[:len(p)-1] // remove terminating null } val = make([]string, 0, 5) from := 0 for i, c := range p { if c == 0 { val = append(val, string(utf16.Decode(p[from:i]))) from = i + 1 } } return val, typ, nil } // GetIntegerValue retrieves the integer value for the specified // value name associated with an open key k. It also returns the value's type. // If value does not exist, GetIntegerValue returns ErrNotExist. // If value is not DWORD or QWORD, it will return the correct value // type and ErrUnexpectedType. func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) { data, typ, err2 := k.getValue(name, make([]byte, 8)) if err2 != nil { return 0, typ, err2 } switch typ { case DWORD: if len(data) != 4 { return 0, typ, errors.New("DWORD value is not 4 bytes long") } return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil case QWORD: if len(data) != 8 { return 0, typ, errors.New("QWORD value is not 8 bytes long") } return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil default: return 0, typ, ErrUnexpectedType } } // GetBinaryValue retrieves the binary value for the specified // value name associated with an open key k. It also returns the value's type. // If value does not exist, GetBinaryValue returns ErrNotExist. // If value is not BINARY, it will return the correct value // type and ErrUnexpectedType. func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) { data, typ, err2 := k.getValue(name, make([]byte, 64)) if err2 != nil { return nil, typ, err2 } if typ != BINARY { return nil, typ, ErrUnexpectedType } return data, typ, nil } func (k Key) setValue(name string, valtype uint32, data []byte) error { p, err := syscall.UTF16PtrFromString(name) if err != nil { return err } if len(data) == 0 { return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0) } return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data))) } // SetDWordValue sets the data and type of a name value // under key k to value and DWORD. func (k Key) SetDWordValue(name string, value uint32) error { return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:]) } // SetQWordValue sets the data and type of a name value // under key k to value and QWORD. func (k Key) SetQWordValue(name string, value uint64) error { return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:]) } func (k Key) setStringValue(name string, valtype uint32, value string) error { v, err := syscall.UTF16FromString(value) if err != nil { return err } buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2] return k.setValue(name, valtype, buf) } // SetStringValue sets the data and type of a name value // under key k to value and SZ. The value must not contain a zero byte. func (k Key) SetStringValue(name, value string) error { return k.setStringValue(name, SZ, value) } // SetExpandStringValue sets the data and type of a name value // under key k to value and EXPAND_SZ. The value must not contain a zero byte. func (k Key) SetExpandStringValue(name, value string) error { return k.setStringValue(name, EXPAND_SZ, value) } // SetStringsValue sets the data and type of a name value // under key k to value and MULTI_SZ. The value strings // must not contain a zero byte. func (k Key) SetStringsValue(name string, value []string) error { ss := "" for _, s := range value { for i := 0; i < len(s); i++ { if s[i] == 0 { return errors.New("string cannot have 0 inside") } } ss += s + "\x00" } v := utf16.Encode([]rune(ss + "\x00")) buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2] return k.setValue(name, MULTI_SZ, buf) } // SetBinaryValue sets the data and type of a name value // under key k to value and BINARY. func (k Key) SetBinaryValue(name string, value []byte) error { return k.setValue(name, BINARY, value) } // DeleteValue removes a named value from the key k. func (k Key) DeleteValue(name string) error { return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name)) } // ReadValueNames returns the value names of key k. // The parameter n controls the number of returned names, // analogous to the way os.File.Readdirnames works. func (k Key) ReadValueNames(n int) ([]string, error) { ki, err := k.Stat() if err != nil { return nil, err } names := make([]string, 0, ki.ValueCount) buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character loopItems: for i := uint32(0); ; i++ { if n > 0 { if len(names) == n { return names, nil } } l := uint32(len(buf)) for { err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil) if err == nil { break } if err == syscall.ERROR_MORE_DATA { // Double buffer size and try again. l = uint32(2 * len(buf)) buf = make([]uint16, l) continue } if err == _ERROR_NO_MORE_ITEMS { break loopItems } return names, err } names = append(names, syscall.UTF16ToString(buf[:l])) } if n > len(names) { return names, io.EOF } return names, nil } go-internal-1.5.2/internal/syscall/windows/registry/zsyscall_windows.go000066400000000000000000000100621360713250200265310ustar00rootroot00000000000000// Code generated by 'go generate'; DO NOT EDIT. package registry import ( "syscall" "unsafe" "github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll" ) var _ unsafe.Pointer // Do the interface allocations only once for common // Errno values. const ( errnoERROR_IO_PENDING = 997 ) var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return nil case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW") procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW") procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW") procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW") procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW") ) func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) { r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition))) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) { r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) { r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize)) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) { r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) { r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) { r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) { r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) n = uint32(r0) if n == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } go-internal-1.5.2/internal/syscall/windows/reparse_windows.go000066400000000000000000000046121360713250200244620ustar00rootroot00000000000000// Copyright 2016 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 windows const ( FSCTL_SET_REPARSE_POINT = 0x000900A4 IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 SYMLINK_FLAG_RELATIVE = 1 ) // These structures are described // in https://msdn.microsoft.com/en-us/library/cc232007.aspx // and https://msdn.microsoft.com/en-us/library/cc232006.aspx. // REPARSE_DATA_BUFFER_HEADER is a common part of REPARSE_DATA_BUFFER structure. type REPARSE_DATA_BUFFER_HEADER struct { ReparseTag uint32 // The size, in bytes, of the reparse data that follows // the common portion of the REPARSE_DATA_BUFFER element. // This value is the length of the data starting at the // SubstituteNameOffset field. ReparseDataLength uint16 Reserved uint16 } type SymbolicLinkReparseBuffer struct { // The integer that contains the offset, in bytes, // of the substitute name string in the PathBuffer array, // computed as an offset from byte 0 of PathBuffer. Note that // this offset must be divided by 2 to get the array index. SubstituteNameOffset uint16 // The integer that contains the length, in bytes, of the // substitute name string. If this string is null-terminated, // SubstituteNameLength does not include the Unicode null character. SubstituteNameLength uint16 // PrintNameOffset is similar to SubstituteNameOffset. PrintNameOffset uint16 // PrintNameLength is similar to SubstituteNameLength. PrintNameLength uint16 // Flags specifies whether the substitute name is a full path name or // a path name relative to the directory containing the symbolic link. Flags uint32 PathBuffer [1]uint16 } type MountPointReparseBuffer struct { // The integer that contains the offset, in bytes, // of the substitute name string in the PathBuffer array, // computed as an offset from byte 0 of PathBuffer. Note that // this offset must be divided by 2 to get the array index. SubstituteNameOffset uint16 // The integer that contains the length, in bytes, of the // substitute name string. If this string is null-terminated, // SubstituteNameLength does not include the Unicode null character. SubstituteNameLength uint16 // PrintNameOffset is similar to SubstituteNameOffset. PrintNameOffset uint16 // PrintNameLength is similar to SubstituteNameLength. PrintNameLength uint16 PathBuffer [1]uint16 } go-internal-1.5.2/internal/syscall/windows/security_windows.go000066400000000000000000000073231360713250200246720ustar00rootroot00000000000000// Copyright 2016 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 windows import ( "syscall" "unsafe" ) const ( SecurityAnonymous = 0 SecurityIdentification = 1 SecurityImpersonation = 2 SecurityDelegation = 3 ) //sys ImpersonateSelf(impersonationlevel uint32) (err error) = advapi32.ImpersonateSelf //sys RevertToSelf() (err error) = advapi32.RevertToSelf const ( TOKEN_ADJUST_PRIVILEGES = 0x0020 SE_PRIVILEGE_ENABLED = 0x00000002 ) type LUID struct { LowPart uint32 HighPart int32 } type LUID_AND_ATTRIBUTES struct { Luid LUID Attributes uint32 } type TOKEN_PRIVILEGES struct { PrivilegeCount uint32 Privileges [1]LUID_AND_ATTRIBUTES } //sys OpenThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) = advapi32.OpenThreadToken //sys LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) = advapi32.LookupPrivilegeValueW //sys adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) [true] = advapi32.AdjustTokenPrivileges func AdjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) error { ret, err := adjustTokenPrivileges(token, disableAllPrivileges, newstate, buflen, prevstate, returnlen) if ret == 0 { // AdjustTokenPrivileges call failed return err } // AdjustTokenPrivileges call succeeded if err == syscall.EINVAL { // GetLastError returned ERROR_SUCCESS return nil } return err } //sys DuplicateTokenEx(hExistingToken syscall.Token, dwDesiredAccess uint32, lpTokenAttributes *syscall.SecurityAttributes, impersonationLevel uint32, tokenType TokenType, phNewToken *syscall.Token) (err error) = advapi32.DuplicateTokenEx //sys SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32, tokenInformation uintptr, tokenInformationLength uint32) (err error) = advapi32.SetTokenInformation type SID_AND_ATTRIBUTES struct { Sid *syscall.SID Attributes uint32 } type TOKEN_MANDATORY_LABEL struct { Label SID_AND_ATTRIBUTES } func (tml *TOKEN_MANDATORY_LABEL) Size() uint32 { return uint32(unsafe.Sizeof(TOKEN_MANDATORY_LABEL{})) + syscall.GetLengthSid(tml.Label.Sid) } const SE_GROUP_INTEGRITY = 0x00000020 type TokenType uint32 const ( TokenPrimary TokenType = 1 TokenImpersonation TokenType = 2 ) //sys GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) = userenv.GetProfilesDirectoryW const ( LG_INCLUDE_INDIRECT = 0x1 MAX_PREFERRED_LENGTH = 0xFFFFFFFF ) type LocalGroupUserInfo0 struct { Name *uint16 } type UserInfo4 struct { Name *uint16 Password *uint16 PasswordAge uint32 Priv uint32 HomeDir *uint16 Comment *uint16 Flags uint32 ScriptPath *uint16 AuthFlags uint32 FullName *uint16 UsrComment *uint16 Parms *uint16 Workstations *uint16 LastLogon uint32 LastLogoff uint32 AcctExpires uint32 MaxStorage uint32 UnitsPerWeek uint32 LogonHours *byte BadPwCount uint32 NumLogons uint32 LogonServer *uint16 CountryCode uint32 CodePage uint32 UserSid *syscall.SID PrimaryGroupID uint32 Profile *uint16 HomeDirDrive *uint16 PasswordExpired uint32 } //sys NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) = netapi32.NetUserGetLocalGroups go-internal-1.5.2/internal/syscall/windows/symlink_windows.go000066400000000000000000000031151360713250200245040ustar00rootroot00000000000000// Copyright 2018 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 windows import "syscall" const ( ERROR_INVALID_PARAMETER syscall.Errno = 87 // symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972) SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 // FileInformationClass values FileBasicInfo = 0 // FILE_BASIC_INFO FileStandardInfo = 1 // FILE_STANDARD_INFO FileNameInfo = 2 // FILE_NAME_INFO FileStreamInfo = 7 // FILE_STREAM_INFO FileCompressionInfo = 8 // FILE_COMPRESSION_INFO FileAttributeTagInfo = 9 // FILE_ATTRIBUTE_TAG_INFO FileIdBothDirectoryInfo = 0xa // FILE_ID_BOTH_DIR_INFO FileIdBothDirectoryRestartInfo = 0xb // FILE_ID_BOTH_DIR_INFO FileRemoteProtocolInfo = 0xd // FILE_REMOTE_PROTOCOL_INFO FileFullDirectoryInfo = 0xe // FILE_FULL_DIR_INFO FileFullDirectoryRestartInfo = 0xf // FILE_FULL_DIR_INFO FileStorageInfo = 0x10 // FILE_STORAGE_INFO FileAlignmentInfo = 0x11 // FILE_ALIGNMENT_INFO FileIdInfo = 0x12 // FILE_ID_INFO FileIdExtdDirectoryInfo = 0x13 // FILE_ID_EXTD_DIR_INFO FileIdExtdDirectoryRestartInfo = 0x14 // FILE_ID_EXTD_DIR_INFO ) type FILE_ATTRIBUTE_TAG_INFO struct { FileAttributes uint32 ReparseTag uint32 } //sys GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byte, bufsize uint32) (err error) go-internal-1.5.2/internal/syscall/windows/syscall_windows.go000066400000000000000000000216561360713250200245020ustar00rootroot00000000000000// Copyright 2014 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 windows import ( "sync" "syscall" "unsafe" ) const ( ERROR_SHARING_VIOLATION syscall.Errno = 32 ERROR_LOCK_VIOLATION syscall.Errno = 33 ERROR_NOT_SUPPORTED syscall.Errno = 50 ERROR_CALL_NOT_IMPLEMENTED syscall.Errno = 120 ERROR_INVALID_NAME syscall.Errno = 123 ERROR_LOCK_FAILED syscall.Errno = 167 ERROR_NO_UNICODE_TRANSLATION syscall.Errno = 1113 ) const GAA_FLAG_INCLUDE_PREFIX = 0x00000010 const ( IF_TYPE_OTHER = 1 IF_TYPE_ETHERNET_CSMACD = 6 IF_TYPE_ISO88025_TOKENRING = 9 IF_TYPE_PPP = 23 IF_TYPE_SOFTWARE_LOOPBACK = 24 IF_TYPE_ATM = 37 IF_TYPE_IEEE80211 = 71 IF_TYPE_TUNNEL = 131 IF_TYPE_IEEE1394 = 144 ) type SocketAddress struct { Sockaddr *syscall.RawSockaddrAny SockaddrLength int32 } type IpAdapterUnicastAddress struct { Length uint32 Flags uint32 Next *IpAdapterUnicastAddress Address SocketAddress PrefixOrigin int32 SuffixOrigin int32 DadState int32 ValidLifetime uint32 PreferredLifetime uint32 LeaseLifetime uint32 OnLinkPrefixLength uint8 } type IpAdapterAnycastAddress struct { Length uint32 Flags uint32 Next *IpAdapterAnycastAddress Address SocketAddress } type IpAdapterMulticastAddress struct { Length uint32 Flags uint32 Next *IpAdapterMulticastAddress Address SocketAddress } type IpAdapterDnsServerAdapter struct { Length uint32 Reserved uint32 Next *IpAdapterDnsServerAdapter Address SocketAddress } type IpAdapterPrefix struct { Length uint32 Flags uint32 Next *IpAdapterPrefix Address SocketAddress PrefixLength uint32 } type IpAdapterAddresses struct { Length uint32 IfIndex uint32 Next *IpAdapterAddresses AdapterName *byte FirstUnicastAddress *IpAdapterUnicastAddress FirstAnycastAddress *IpAdapterAnycastAddress FirstMulticastAddress *IpAdapterMulticastAddress FirstDnsServerAddress *IpAdapterDnsServerAdapter DnsSuffix *uint16 Description *uint16 FriendlyName *uint16 PhysicalAddress [syscall.MAX_ADAPTER_ADDRESS_LENGTH]byte PhysicalAddressLength uint32 Flags uint32 Mtu uint32 IfType uint32 OperStatus uint32 Ipv6IfIndex uint32 ZoneIndices [16]uint32 FirstPrefix *IpAdapterPrefix /* more fields might be present here. */ } const ( IfOperStatusUp = 1 IfOperStatusDown = 2 IfOperStatusTesting = 3 IfOperStatusUnknown = 4 IfOperStatusDormant = 5 IfOperStatusNotPresent = 6 IfOperStatusLowerLayerDown = 7 ) //sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses //sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW //sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW const ( WSA_FLAG_OVERLAPPED = 0x01 WSA_FLAG_NO_HANDLE_INHERIT = 0x80 WSAEMSGSIZE syscall.Errno = 10040 MSG_PEEK = 0x2 MSG_TRUNC = 0x0100 MSG_CTRUNC = 0x0200 socket_error = uintptr(^uint32(0)) ) var WSAID_WSASENDMSG = syscall.GUID{ Data1: 0xa441e712, Data2: 0x754f, Data3: 0x43ca, Data4: [8]byte{0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d}, } var WSAID_WSARECVMSG = syscall.GUID{ Data1: 0xf689d7c8, Data2: 0x6f1f, Data3: 0x436b, Data4: [8]byte{0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22}, } var sendRecvMsgFunc struct { once sync.Once sendAddr uintptr recvAddr uintptr err error } type WSAMsg struct { Name *syscall.RawSockaddrAny Namelen int32 Buffers *syscall.WSABuf BufferCount uint32 Control syscall.WSABuf Flags uint32 } //sys WSASocket(af int32, typ int32, protocol int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = ws2_32.WSASocketW func loadWSASendRecvMsg() error { sendRecvMsgFunc.once.Do(func() { var s syscall.Handle s, sendRecvMsgFunc.err = syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if sendRecvMsgFunc.err != nil { return } defer syscall.CloseHandle(s) var n uint32 sendRecvMsgFunc.err = syscall.WSAIoctl(s, syscall.SIO_GET_EXTENSION_FUNCTION_POINTER, (*byte)(unsafe.Pointer(&WSAID_WSARECVMSG)), uint32(unsafe.Sizeof(WSAID_WSARECVMSG)), (*byte)(unsafe.Pointer(&sendRecvMsgFunc.recvAddr)), uint32(unsafe.Sizeof(sendRecvMsgFunc.recvAddr)), &n, nil, 0) if sendRecvMsgFunc.err != nil { return } sendRecvMsgFunc.err = syscall.WSAIoctl(s, syscall.SIO_GET_EXTENSION_FUNCTION_POINTER, (*byte)(unsafe.Pointer(&WSAID_WSASENDMSG)), uint32(unsafe.Sizeof(WSAID_WSASENDMSG)), (*byte)(unsafe.Pointer(&sendRecvMsgFunc.sendAddr)), uint32(unsafe.Sizeof(sendRecvMsgFunc.sendAddr)), &n, nil, 0) }) return sendRecvMsgFunc.err } func WSASendMsg(fd syscall.Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *syscall.Overlapped, croutine *byte) error { err := loadWSASendRecvMsg() if err != nil { return err } r1, _, e1 := syscall.Syscall6(sendRecvMsgFunc.sendAddr, 6, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if r1 == socket_error { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return err } func WSARecvMsg(fd syscall.Handle, msg *WSAMsg, bytesReceived *uint32, overlapped *syscall.Overlapped, croutine *byte) error { err := loadWSASendRecvMsg() if err != nil { return err } r1, _, e1 := syscall.Syscall6(sendRecvMsgFunc.recvAddr, 5, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(unsafe.Pointer(bytesReceived)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0) if r1 == socket_error { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return err } const ( ComputerNameNetBIOS = 0 ComputerNameDnsHostname = 1 ComputerNameDnsDomain = 2 ComputerNameDnsFullyQualified = 3 ComputerNamePhysicalNetBIOS = 4 ComputerNamePhysicalDnsHostname = 5 ComputerNamePhysicalDnsDomain = 6 ComputerNamePhysicalDnsFullyQualified = 7 ComputerNameMax = 8 MOVEFILE_REPLACE_EXISTING = 0x1 MOVEFILE_COPY_ALLOWED = 0x2 MOVEFILE_DELAY_UNTIL_REBOOT = 0x4 MOVEFILE_WRITE_THROUGH = 0x8 MOVEFILE_CREATE_HARDLINK = 0x10 MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20 ) func Rename(oldpath, newpath string) error { from, err := syscall.UTF16PtrFromString(oldpath) if err != nil { return err } to, err := syscall.UTF16PtrFromString(newpath) if err != nil { return err } return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING) } //sys LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) = kernel32.LockFileEx //sys UnlockFileEx(file syscall.Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) = kernel32.UnlockFileEx const ( LOCKFILE_FAIL_IMMEDIATELY = 0x00000001 LOCKFILE_EXCLUSIVE_LOCK = 0x00000002 ) const MB_ERR_INVALID_CHARS = 8 //sys GetACP() (acp uint32) = kernel32.GetACP //sys GetConsoleCP() (ccp uint32) = kernel32.GetConsoleCP //sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar //sys GetCurrentThread() (pseudoHandle syscall.Handle, err error) = kernel32.GetCurrentThread const STYPE_DISKTREE = 0x00 type SHARE_INFO_2 struct { Netname *uint16 Type uint32 Remark *uint16 Permissions uint32 MaxUses uint32 CurrentUses uint32 Path *uint16 Passwd *uint16 } //sys NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) = netapi32.NetShareAdd //sys NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) = netapi32.NetShareDel const ( FILE_NAME_NORMALIZED = 0x0 FILE_NAME_OPENED = 0x8 VOLUME_NAME_DOS = 0x0 VOLUME_NAME_GUID = 0x1 VOLUME_NAME_NONE = 0x4 VOLUME_NAME_NT = 0x2 ) //sys GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) = kernel32.GetFinalPathNameByHandleW func LoadGetFinalPathNameByHandle() error { return procGetFinalPathNameByHandleW.Find() } go-internal-1.5.2/internal/syscall/windows/sysdll/000077500000000000000000000000001360713250200222275ustar00rootroot00000000000000go-internal-1.5.2/internal/syscall/windows/sysdll/sysdll.go000066400000000000000000000021271360713250200240720ustar00rootroot00000000000000// Copyright 2016 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 sysdll is an internal leaf package that records and reports // which Windows DLL names are used by Go itself. These DLLs are then // only loaded from the System32 directory. See Issue 14959. package sysdll // IsSystemDLL reports whether the named dll key (a base name, like // "foo.dll") is a system DLL which should only be loaded from the // Windows SYSTEM32 directory. // // Filenames are case sensitive, but that doesn't matter because // the case registered with Add is also the same case used with // LoadDLL later. // // It has no associated mutex and should only be mutated serially // (currently: during init), and not concurrent with DLL loading. var IsSystemDLL = map[string]bool{} // Add notes that dll is a system32 DLL which should only be loaded // from the Windows SYSTEM32 directory. It returns its argument back, // for ease of use in generated code. func Add(dll string) string { IsSystemDLL[dll] = true return dll } go-internal-1.5.2/internal/syscall/windows/zsyscall_windows.go000066400000000000000000000277371360713250200247020ustar00rootroot00000000000000// Code generated by 'go generate'; DO NOT EDIT. package windows import ( "syscall" "unsafe" "github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll" ) var _ unsafe.Pointer // Do the interface allocations only once for common // Errno values. const ( errnoERROR_IO_PENDING = 997 ) var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return nil case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") procMoveFileExW = modkernel32.NewProc("MoveFileExW") procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") procWSASocketW = modws2_32.NewProc("WSASocketW") procLockFileEx = modkernel32.NewProc("LockFileEx") procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") procGetACP = modkernel32.NewProc("GetACP") procGetConsoleCP = modkernel32.NewProc("GetConsoleCP") procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") procNetShareAdd = modnetapi32.NewProc("NetShareAdd") procNetShareDel = modnetapi32.NewProc("NetShareDel") procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") procRevertToSelf = modadvapi32.NewProc("RevertToSelf") procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") procGetProfilesDirectoryW = moduserenv.NewProc("GetProfilesDirectoryW") procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") ) func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0) if r0 != 0 { errcode = syscall.Errno(r0) } return } func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) { r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags)) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) { r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(fn)), uintptr(len)) n = uint32(r0) if n == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func WSASocket(af int32, typ int32, protocol int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (handle syscall.Handle, err error) { r0, _, e1 := syscall.Syscall6(procWSASocketW.Addr(), 6, uintptr(af), uintptr(typ), uintptr(protocol), uintptr(unsafe.Pointer(protinfo)), uintptr(group), uintptr(flags)) handle = syscall.Handle(r0) if handle == syscall.InvalidHandle { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func UnlockFileEx(file syscall.Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall6(procUnlockFileEx.Addr(), 5, uintptr(file), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped)), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func GetACP() (acp uint32) { r0, _, _ := syscall.Syscall(procGetACP.Addr(), 0, 0, 0, 0) acp = uint32(r0) return } func GetConsoleCP() (ccp uint32) { r0, _, _ := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0) ccp = uint32(r0) return } func MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) { r0, _, e1 := syscall.Syscall6(procMultiByteToWideChar.Addr(), 6, uintptr(codePage), uintptr(dwFlags), uintptr(unsafe.Pointer(str)), uintptr(nstr), uintptr(unsafe.Pointer(wchar)), uintptr(nwchar)) nwrite = int32(r0) if nwrite == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func GetCurrentThread() (pseudoHandle syscall.Handle, err error) { r0, _, e1 := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) pseudoHandle = syscall.Handle(r0) if pseudoHandle == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) { r0, _, _ := syscall.Syscall6(procNetShareAdd.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(parmErr)), 0, 0) if r0 != 0 { neterr = syscall.Errno(r0) } return } func NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) { r0, _, _ := syscall.Syscall(procNetShareDel.Addr(), 3, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(netName)), uintptr(reserved)) if r0 != 0 { neterr = syscall.Errno(r0) } return } func GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) { r0, _, e1 := syscall.Syscall6(procGetFinalPathNameByHandleW.Addr(), 4, uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags), 0, 0) n = uint32(r0) if n == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func ImpersonateSelf(impersonationlevel uint32) (err error) { r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func RevertToSelf() (err error) { r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func OpenThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) { var _p0 uint32 if openasself { _p0 = 1 } else { _p0 = 0 } r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(h), uintptr(access), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) { r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemname)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) { var _p0 uint32 if disableAllPrivileges { _p0 = 1 } else { _p0 = 0 } r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) ret = uint32(r0) if true { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func DuplicateTokenEx(hExistingToken syscall.Token, dwDesiredAccess uint32, lpTokenAttributes *syscall.SecurityAttributes, impersonationLevel uint32, tokenType TokenType, phNewToken *syscall.Token) (err error) { r1, _, e1 := syscall.Syscall6(procDuplicateTokenEx.Addr(), 6, uintptr(hExistingToken), uintptr(dwDesiredAccess), uintptr(unsafe.Pointer(lpTokenAttributes)), uintptr(impersonationLevel), uintptr(tokenType), uintptr(unsafe.Pointer(phNewToken))) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32, tokenInformation uintptr, tokenInformationLength uint32) (err error) { r1, _, e1 := syscall.Syscall6(procSetTokenInformation.Addr(), 4, uintptr(tokenHandle), uintptr(tokenInformationClass), uintptr(tokenInformation), uintptr(tokenInformationLength), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetProfilesDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) { r0, _, _ := syscall.Syscall9(procNetUserGetLocalGroups.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(flags), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), 0) if r0 != 0 { neterr = syscall.Errno(r0) } return } func GetProcessMemoryInfo(handle syscall.Handle, memCounters *PROCESS_MEMORY_COUNTERS, cb uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(memCounters)), uintptr(cb)) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func GetFileInformationByHandleEx(handle syscall.Handle, class uint32, info *byte, bufsize uint32) (err error) { r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(handle), uintptr(class), uintptr(unsafe.Pointer(info)), uintptr(bufsize), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } go-internal-1.5.2/internal/textutil/000077500000000000000000000000001360713250200174335ustar00rootroot00000000000000go-internal-1.5.2/internal/textutil/diff.go000066400000000000000000000042631360713250200206770ustar00rootroot00000000000000// Copyright 2018 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 textutil import ( "fmt" "strings" ) // Diff returns a formatted diff of the two texts, // showing the entire text and the minimum line-level // additions and removals to turn text1 into text2. // (That is, lines only in text1 appear with a leading -, // and lines only in text2 appear with a leading +.) func Diff(text1, text2 string) string { if text1 != "" && !strings.HasSuffix(text1, "\n") { text1 += "(missing final newline)" } lines1 := strings.Split(text1, "\n") lines1 = lines1[:len(lines1)-1] // remove empty string after final line if text2 != "" && !strings.HasSuffix(text2, "\n") { text2 += "(missing final newline)" } lines2 := strings.Split(text2, "\n") lines2 = lines2[:len(lines2)-1] // remove empty string after final line // Naive dynamic programming algorithm for edit distance. // https://en.wikipedia.org/wiki/Wagner–Fischer_algorithm // dist[i][j] = edit distance between lines1[:len(lines1)-i] and lines2[:len(lines2)-j] // (The reversed indices make following the minimum cost path // visit lines in the same order as in the text.) dist := make([][]int, len(lines1)+1) for i := range dist { dist[i] = make([]int, len(lines2)+1) if i == 0 { for j := range dist[0] { dist[0][j] = j } continue } for j := range dist[i] { if j == 0 { dist[i][0] = i continue } cost := dist[i][j-1] + 1 if cost > dist[i-1][j]+1 { cost = dist[i-1][j] + 1 } if lines1[len(lines1)-i] == lines2[len(lines2)-j] { if cost > dist[i-1][j-1] { cost = dist[i-1][j-1] } } dist[i][j] = cost } } var buf strings.Builder i, j := len(lines1), len(lines2) for i > 0 || j > 0 { cost := dist[i][j] if i > 0 && j > 0 && cost == dist[i-1][j-1] && lines1[len(lines1)-i] == lines2[len(lines2)-j] { fmt.Fprintf(&buf, " %s\n", lines1[len(lines1)-i]) i-- j-- } else if i > 0 && cost == dist[i-1][j]+1 { fmt.Fprintf(&buf, "-%s\n", lines1[len(lines1)-i]) i-- } else { fmt.Fprintf(&buf, "+%s\n", lines2[len(lines2)-j]) j-- } } return buf.String() } go-internal-1.5.2/internal/textutil/diff_test.go000066400000000000000000000022021360713250200217250ustar00rootroot00000000000000// Copyright 2018 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 textutil_test import ( "strings" "testing" "github.com/rogpeppe/go-internal/internal/textutil" ) var diffTests = []struct { text1 string text2 string diff string }{ {"a b c", "a b d e f", "a b -c +d +e +f"}, {"", "a b c", "+a +b +c"}, {"a b c", "", "-a -b -c"}, {"a b c", "d e f", "-a -b -c +d +e +f"}, {"a b c d e f", "a b d e f", "a b -c d e f"}, {"a b c e f", "a b c d e f", "a b c +d e f"}, } func TestDiff(t *testing.T) { for _, tt := range diffTests { // Turn spaces into \n. text1 := strings.Replace(tt.text1, " ", "\n", -1) if text1 != "" { text1 += "\n" } text2 := strings.Replace(tt.text2, " ", "\n", -1) if text2 != "" { text2 += "\n" } out := textutil.Diff(text1, text2) // Cut final \n, cut spaces, turn remaining \n into spaces. out = strings.Replace(strings.Replace(strings.TrimSuffix(out, "\n"), " ", "", -1), "\n", " ", -1) if out != tt.diff { t.Errorf("diff(%q, %q) = %q, want %q", text1, text2, out, tt.diff) } } } go-internal-1.5.2/internal/textutil/doc.go000066400000000000000000000005551360713250200205340ustar00rootroot00000000000000// Copyright 2018 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 textutil contains text processing utilities. // // This package came to life as a result of refactoring code common to // internal packages we have factored out of the Go repo. package textutil go-internal-1.5.2/lockedfile/000077500000000000000000000000001360713250200160365ustar00rootroot00000000000000go-internal-1.5.2/lockedfile/internal/000077500000000000000000000000001360713250200176525ustar00rootroot00000000000000go-internal-1.5.2/lockedfile/internal/filelock/000077500000000000000000000000001360713250200214425ustar00rootroot00000000000000go-internal-1.5.2/lockedfile/internal/filelock/filelock.go000066400000000000000000000057041360713250200235670ustar00rootroot00000000000000// Copyright 2018 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 filelock provides a platform-independent API for advisory file // locking. Calls to functions in this package on platforms that do not support // advisory locks will return errors for which IsNotSupported returns true. package filelock import ( "errors" "os" ) // A File provides the minimal set of methods required to lock an open file. // File implementations must be usable as map keys. // The usual implementation is *os.File. type File interface { // Name returns the name of the file. Name() string // Fd returns a valid file descriptor. // (If the File is an *os.File, it must not be closed.) Fd() uintptr // Stat returns the FileInfo structure describing file. Stat() (os.FileInfo, error) } // Lock places an advisory write lock on the file, blocking until it can be // locked. // // If Lock returns nil, no other process will be able to place a read or write // lock on the file until this process exits, closes f, or calls Unlock on it. // // If f's descriptor is already read- or write-locked, the behavior of Lock is // unspecified. // // Closing the file may or may not release the lock promptly. Callers should // ensure that Unlock is always called when Lock succeeds. func Lock(f File) error { return lock(f, writeLock) } // RLock places an advisory read lock on the file, blocking until it can be locked. // // If RLock returns nil, no other process will be able to place a write lock on // the file until this process exits, closes f, or calls Unlock on it. // // If f is already read- or write-locked, the behavior of RLock is unspecified. // // Closing the file may or may not release the lock promptly. Callers should // ensure that Unlock is always called if RLock succeeds. func RLock(f File) error { return lock(f, readLock) } // Unlock removes an advisory lock placed on f by this process. // // The caller must not attempt to unlock a file that is not locked. func Unlock(f File) error { return unlock(f) } // String returns the name of the function corresponding to lt // (Lock, RLock, or Unlock). func (lt lockType) String() string { switch lt { case readLock: return "RLock" case writeLock: return "Lock" default: return "Unlock" } } // IsNotSupported returns a boolean indicating whether the error is known to // report that a function is not supported (possibly for a specific input). // It is satisfied by ErrNotSupported as well as some syscall errors. func IsNotSupported(err error) bool { return isNotSupported(underlyingError(err)) } var ErrNotSupported = errors.New("operation not supported") // underlyingError returns the underlying error for known os error types. func underlyingError(err error) error { switch err := err.(type) { case *os.PathError: return err.Err case *os.LinkError: return err.Err case *os.SyscallError: return err.Err } return err } go-internal-1.5.2/lockedfile/internal/filelock/filelock_fcntl.go000066400000000000000000000071321360713250200247520ustar00rootroot00000000000000// Copyright 2018 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. // +build aix solaris // This code implements the filelock API using POSIX 'fcntl' locks, which attach // to an (inode, process) pair rather than a file descriptor. To avoid unlocking // files prematurely when the same file is opened through different descriptors, // we allow only one read-lock at a time. // // Most platforms provide some alternative API, such as an 'flock' system call // or an F_OFD_SETLK command for 'fcntl', that allows for better concurrency and // does not require per-inode bookkeeping in the application. // // TODO(bcmills): If we add a build tag for Illumos (see golang.org/issue/20603) // then Illumos should use F_OFD_SETLK, and the resulting code would be as // simple as filelock_unix.go. We will still need the code in this file for AIX // or as long as Oracle Solaris provides only F_SETLK. package filelock import ( "errors" "io" "os" "sync" "syscall" ) type lockType int16 const ( readLock lockType = syscall.F_RDLCK writeLock lockType = syscall.F_WRLCK ) type inode = uint64 // type of syscall.Stat_t.Ino type inodeLock struct { owner File queue []<-chan File } type token struct{} var ( mu sync.Mutex inodes = map[File]inode{} locks = map[inode]inodeLock{} ) func lock(f File, lt lockType) (err error) { // POSIX locks apply per inode and process, and the lock for an inode is // released when *any* descriptor for that inode is closed. So we need to // synchronize access to each inode internally, and must serialize lock and // unlock calls that refer to the same inode through different descriptors. fi, err := f.Stat() if err != nil { return err } ino := fi.Sys().(*syscall.Stat_t).Ino mu.Lock() if i, dup := inodes[f]; dup && i != ino { mu.Unlock() return &os.PathError{ Op: lt.String(), Path: f.Name(), Err: errors.New("inode for file changed since last Lock or RLock"), } } inodes[f] = ino var wait chan File l := locks[ino] if l.owner == f { // This file already owns the lock, but the call may change its lock type. } else if l.owner == nil { // No owner: it's ours now. l.owner = f } else { // Already owned: add a channel to wait on. wait = make(chan File) l.queue = append(l.queue, wait) } locks[ino] = l mu.Unlock() if wait != nil { wait <- f } err = setlkw(f.Fd(), lt) if err != nil { unlock(f) return &os.PathError{ Op: lt.String(), Path: f.Name(), Err: err, } } return nil } func unlock(f File) error { var owner File mu.Lock() ino, ok := inodes[f] if ok { owner = locks[ino].owner } mu.Unlock() if owner != f { panic("unlock called on a file that is not locked") } err := setlkw(f.Fd(), syscall.F_UNLCK) mu.Lock() l := locks[ino] if len(l.queue) == 0 { // No waiters: remove the map entry. delete(locks, ino) } else { // The first waiter is sending us their file now. // Receive it and update the queue. l.owner = <-l.queue[0] l.queue = l.queue[1:] locks[ino] = l } delete(inodes, f) mu.Unlock() return err } // setlkw calls FcntlFlock with F_SETLKW for the entire file indicated by fd. func setlkw(fd uintptr, lt lockType) error { for { err := syscall.FcntlFlock(fd, syscall.F_SETLKW, &syscall.Flock_t{ Type: int16(lt), Whence: io.SeekStart, Start: 0, Len: 0, // All bytes. }) if err != syscall.EINTR { return err } } } func isNotSupported(err error) bool { return err == syscall.ENOSYS || err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP || err == ErrNotSupported } go-internal-1.5.2/lockedfile/internal/filelock/filelock_other.go000066400000000000000000000012341360713250200247620ustar00rootroot00000000000000// Copyright 2018 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. // +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris,!windows package filelock import "os" type lockType int8 const ( readLock = iota + 1 writeLock ) func lock(f File, lt lockType) error { return &os.PathError{ Op: lt.String(), Path: f.Name(), Err: ErrNotSupported, } } func unlock(f File) error { return &os.PathError{ Op: "Unlock", Path: f.Name(), Err: ErrNotSupported, } } func isNotSupported(err error) bool { return err == ErrNotSupported } go-internal-1.5.2/lockedfile/internal/filelock/filelock_plan9.go000066400000000000000000000011251360713250200246630ustar00rootroot00000000000000// Copyright 2018 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. // +build plan9 package filelock import ( "os" ) type lockType int8 const ( readLock = iota + 1 writeLock ) func lock(f File, lt lockType) error { return &os.PathError{ Op: lt.String(), Path: f.Name(), Err: ErrNotSupported, } } func unlock(f File) error { return &os.PathError{ Op: "Unlock", Path: f.Name(), Err: ErrNotSupported, } } func isNotSupported(err error) bool { return err == ErrNotSupported } go-internal-1.5.2/lockedfile/internal/filelock/filelock_test.go000066400000000000000000000101151360713250200246160ustar00rootroot00000000000000// Copyright 2018 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. // +build !js,!nacl,!plan9 package filelock_test import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "testing" "time" "github.com/rogpeppe/go-internal/testenv" "github.com/rogpeppe/go-internal/lockedfile/internal/filelock" ) func lock(t *testing.T, f *os.File) { t.Helper() err := filelock.Lock(f) t.Logf("Lock(fd %d) = %v", f.Fd(), err) if err != nil { t.Fail() } } func rLock(t *testing.T, f *os.File) { t.Helper() err := filelock.RLock(f) t.Logf("RLock(fd %d) = %v", f.Fd(), err) if err != nil { t.Fail() } } func unlock(t *testing.T, f *os.File) { t.Helper() err := filelock.Unlock(f) t.Logf("Unlock(fd %d) = %v", f.Fd(), err) if err != nil { t.Fail() } } func mustTempFile(t *testing.T) (f *os.File, remove func()) { t.Helper() base := filepath.Base(t.Name()) f, err := ioutil.TempFile("", base) if err != nil { t.Fatalf(`ioutil.TempFile("", %q) = %v`, base, err) } t.Logf("fd %d = %s", f.Fd(), f.Name()) return f, func() { f.Close() os.Remove(f.Name()) } } func mustOpen(t *testing.T, name string) *os.File { t.Helper() f, err := os.OpenFile(name, os.O_RDWR, 0) if err != nil { t.Fatalf("os.Open(%q) = %v", name, err) } t.Logf("fd %d = os.Open(%q)", f.Fd(), name) return f } const ( quiescent = 10 * time.Millisecond probablyStillBlocked = 10 * time.Second ) func mustBlock(t *testing.T, op string, f *os.File) (wait func(*testing.T)) { t.Helper() desc := fmt.Sprintf("%s(fd %d)", op, f.Fd()) done := make(chan struct{}) go func() { t.Helper() switch op { case "Lock": lock(t, f) case "RLock": rLock(t, f) default: panic("invalid op: " + op) } close(done) }() select { case <-done: t.Fatalf("%s unexpectedly did not block", desc) return nil case <-time.After(quiescent): t.Logf("%s is blocked (as expected)", desc) return func(t *testing.T) { t.Helper() select { case <-time.After(probablyStillBlocked): t.Fatalf("%s is unexpectedly still blocked", desc) case <-done: } } } } func TestLockExcludesLock(t *testing.T) { t.Parallel() f, remove := mustTempFile(t) defer remove() other := mustOpen(t, f.Name()) defer other.Close() lock(t, f) lockOther := mustBlock(t, "Lock", other) unlock(t, f) lockOther(t) unlock(t, other) } func TestLockExcludesRLock(t *testing.T) { t.Parallel() f, remove := mustTempFile(t) defer remove() other := mustOpen(t, f.Name()) defer other.Close() lock(t, f) rLockOther := mustBlock(t, "RLock", other) unlock(t, f) rLockOther(t) unlock(t, other) } func TestRLockExcludesOnlyLock(t *testing.T) { t.Parallel() f, remove := mustTempFile(t) defer remove() rLock(t, f) f2 := mustOpen(t, f.Name()) defer f2.Close() if runtime.GOOS == "solaris" || runtime.GOOS == "aix" { // When using POSIX locks (as on Solaris), we can't safely read-lock the // same inode through two different descriptors at the same time: when the // first descriptor is closed, the second descriptor would still be open but // silently unlocked. So a second RLock must block instead of proceeding. lockF2 := mustBlock(t, "RLock", f2) unlock(t, f) lockF2(t) } else { rLock(t, f2) } other := mustOpen(t, f.Name()) defer other.Close() lockOther := mustBlock(t, "Lock", other) unlock(t, f2) if runtime.GOOS != "solaris" && runtime.GOOS != "aix" { unlock(t, f) } lockOther(t) unlock(t, other) } func TestLockNotDroppedByExecCommand(t *testing.T) { testenv.MustHaveExec(t) f, remove := mustTempFile(t) defer remove() lock(t, f) other := mustOpen(t, f.Name()) defer other.Close() // Some kinds of file locks are dropped when a duplicated or forked file // descriptor is unlocked. Double-check that the approach used by os/exec does // not accidentally drop locks. cmd := exec.Command(os.Args[0], "-test.run=^$") if err := cmd.Run(); err != nil { t.Fatalf("exec failed: %v", err) } lockOther := mustBlock(t, "Lock", other) unlock(t, f) lockOther(t) unlock(t, other) } go-internal-1.5.2/lockedfile/internal/filelock/filelock_unix.go000066400000000000000000000015151360713250200246260ustar00rootroot00000000000000// Copyright 2018 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. // +build darwin dragonfly freebsd linux netbsd openbsd package filelock import ( "os" "syscall" ) type lockType int16 const ( readLock lockType = syscall.LOCK_SH writeLock lockType = syscall.LOCK_EX ) func lock(f File, lt lockType) (err error) { for { err = syscall.Flock(int(f.Fd()), int(lt)) if err != syscall.EINTR { break } } if err != nil { return &os.PathError{ Op: lt.String(), Path: f.Name(), Err: err, } } return nil } func unlock(f File) error { return lock(f, syscall.LOCK_UN) } func isNotSupported(err error) bool { return err == syscall.ENOSYS || err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP || err == ErrNotSupported } go-internal-1.5.2/lockedfile/internal/filelock/filelock_windows.go000066400000000000000000000027661360713250200253460ustar00rootroot00000000000000// Copyright 2018 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. // +build windows package filelock import ( "os" "syscall" "github.com/rogpeppe/go-internal/internal/syscall/windows" ) type lockType uint32 const ( readLock lockType = 0 writeLock lockType = windows.LOCKFILE_EXCLUSIVE_LOCK ) const ( reserved = 0 allBytes = ^uint32(0) ) func lock(f File, lt lockType) error { // Per https://golang.org/issue/19098, “Programs currently expect the Fd // method to return a handle that uses ordinary synchronous I/O.” // However, LockFileEx still requires an OVERLAPPED structure, // which contains the file offset of the beginning of the lock range. // We want to lock the entire file, so we leave the offset as zero. ol := new(syscall.Overlapped) err := windows.LockFileEx(syscall.Handle(f.Fd()), uint32(lt), reserved, allBytes, allBytes, ol) if err != nil { return &os.PathError{ Op: lt.String(), Path: f.Name(), Err: err, } } return nil } func unlock(f File) error { ol := new(syscall.Overlapped) err := windows.UnlockFileEx(syscall.Handle(f.Fd()), reserved, allBytes, allBytes, ol) if err != nil { return &os.PathError{ Op: "Unlock", Path: f.Name(), Err: err, } } return nil } func isNotSupported(err error) bool { switch err { case windows.ERROR_NOT_SUPPORTED, windows.ERROR_CALL_NOT_IMPLEMENTED, ErrNotSupported: return true default: return false } } go-internal-1.5.2/lockedfile/lockedfile.go000066400000000000000000000065211360713250200204720ustar00rootroot00000000000000// Copyright 2018 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 lockedfile creates and manipulates files whose contents should only // change atomically. package lockedfile import ( "fmt" "io" "io/ioutil" "os" "runtime" ) // A File is a locked *os.File. // // Closing the file releases the lock. // // If the program exits while a file is locked, the operating system releases // the lock but may not do so promptly: callers must ensure that all locked // files are closed before exiting. type File struct { osFile closed bool } // osFile embeds a *os.File while keeping the pointer itself unexported. // (When we close a File, it must be the same file descriptor that we opened!) type osFile struct { *os.File } // OpenFile is like os.OpenFile, but returns a locked file. // If flag includes os.O_WRONLY or os.O_RDWR, the file is write-locked; // otherwise, it is read-locked. func OpenFile(name string, flag int, perm os.FileMode) (*File, error) { var ( f = new(File) err error ) f.osFile.File, err = openFile(name, flag, perm) if err != nil { return nil, err } // Although the operating system will drop locks for open files when the go // command exits, we want to hold locks for as little time as possible, and we // especially don't want to leave a file locked after we're done with it. Our // Close method is what releases the locks, so use a finalizer to report // missing Close calls on a best-effort basis. runtime.SetFinalizer(f, func(f *File) { panic(fmt.Sprintf("lockedfile.File %s became unreachable without a call to Close", f.Name())) }) return f, nil } // Open is like os.Open, but returns a read-locked file. func Open(name string) (*File, error) { return OpenFile(name, os.O_RDONLY, 0) } // Create is like os.Create, but returns a write-locked file. func Create(name string) (*File, error) { return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) } // Edit creates the named file with mode 0666 (before umask), // but does not truncate existing contents. // // If Edit succeeds, methods on the returned File can be used for I/O. // The associated file descriptor has mode O_RDWR and the file is write-locked. func Edit(name string) (*File, error) { return OpenFile(name, os.O_RDWR|os.O_CREATE, 0666) } // Close unlocks and closes the underlying file. // // Close may be called multiple times; all calls after the first will return a // non-nil error. func (f *File) Close() error { if f.closed { return &os.PathError{ Op: "close", Path: f.Name(), Err: os.ErrClosed, } } f.closed = true err := closeFile(f.osFile.File) runtime.SetFinalizer(f, nil) return err } // Read opens the named file with a read-lock and returns its contents. func Read(name string) ([]byte, error) { f, err := Open(name) if err != nil { return nil, err } defer f.Close() return ioutil.ReadAll(f) } // Write opens the named file (creating it with the given permissions if needed), // then write-locks it and overwrites it with the given content. func Write(name string, content io.Reader, perm os.FileMode) (err error) { f, err := OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { return err } _, err = io.Copy(f, content) if closeErr := f.Close(); err == nil { err = closeErr } return err } go-internal-1.5.2/lockedfile/lockedfile_filelock.go000066400000000000000000000033551360713250200223440ustar00rootroot00000000000000// Copyright 2018 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. // +build !plan9 package lockedfile import ( "os" "github.com/rogpeppe/go-internal/lockedfile/internal/filelock" ) func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { // On BSD systems, we could add the O_SHLOCK or O_EXLOCK flag to the OpenFile // call instead of locking separately, but we have to support separate locking // calls for Linux and Windows anyway, so it's simpler to use that approach // consistently. f, err := os.OpenFile(name, flag&^os.O_TRUNC, perm) if err != nil { return nil, err } switch flag & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) { case os.O_WRONLY, os.O_RDWR: err = filelock.Lock(f) default: err = filelock.RLock(f) } if err != nil { f.Close() return nil, err } if flag&os.O_TRUNC == os.O_TRUNC { if err := f.Truncate(0); err != nil { // The documentation for os.O_TRUNC says “if possible, truncate file when // opened”, but doesn't define “possible” (golang.org/issue/28699). // We'll treat regular files (and symlinks to regular files) as “possible” // and ignore errors for the rest. if fi, statErr := f.Stat(); statErr != nil || fi.Mode().IsRegular() { filelock.Unlock(f) f.Close() return nil, err } } } return f, nil } func closeFile(f *os.File) error { // Since locking syscalls operate on file descriptors, we must unlock the file // while the descriptor is still valid — that is, before the file is closed — // and avoid unlocking files that are already closed. err := filelock.Unlock(f) if closeErr := f.Close(); err == nil { err = closeErr } return err } go-internal-1.5.2/lockedfile/lockedfile_plan9.go000066400000000000000000000047721360713250200216030ustar00rootroot00000000000000// Copyright 2018 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. // +build plan9 package lockedfile import ( "math/rand" "os" "strings" "time" ) // Opening an exclusive-use file returns an error. // The expected error strings are: // // - "open/create -- file is locked" (cwfs, kfs) // - "exclusive lock" (fossil) // - "exclusive use file already open" (ramfs) var lockedErrStrings = [...]string{ "file is locked", "exclusive lock", "exclusive use file already open", } // Even though plan9 doesn't support the Lock/RLock/Unlock functions to // manipulate already-open files, IsLocked is still meaningful: os.OpenFile // itself may return errors that indicate that a file with the ModeExclusive bit // set is already open. func isLocked(err error) bool { s := err.Error() for _, frag := range lockedErrStrings { if strings.Contains(s, frag) { return true } } return false } func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { // Plan 9 uses a mode bit instead of explicit lock/unlock syscalls. // // Per http://man.cat-v.org/plan_9/5/stat: “Exclusive use files may be open // for I/O by only one fid at a time across all clients of the server. If a // second open is attempted, it draws an error.” // // So we can try to open a locked file, but if it fails we're on our own to // figure out when it becomes available. We'll use exponential backoff with // some jitter and an arbitrary limit of 500ms. // If the file was unpacked or created by some other program, it might not // have the ModeExclusive bit set. Set it before we call OpenFile, so that we // can be confident that a successful OpenFile implies exclusive use. if fi, err := os.Stat(name); err == nil { if fi.Mode()&os.ModeExclusive == 0 { if err := os.Chmod(name, fi.Mode()|os.ModeExclusive); err != nil { return nil, err } } } else if !os.IsNotExist(err) { return nil, err } nextSleep := 1 * time.Millisecond const maxSleep = 500 * time.Millisecond for { f, err := os.OpenFile(name, flag, perm|os.ModeExclusive) if err == nil { return f, nil } if !isLocked(err) { return nil, err } time.Sleep(nextSleep) nextSleep += nextSleep if nextSleep > maxSleep { nextSleep = maxSleep } // Apply 10% jitter to avoid synchronizing collisions. nextSleep += time.Duration((0.1*rand.Float64() - 0.05) * float64(nextSleep)) } } func closeFile(f *os.File) error { return f.Close() } go-internal-1.5.2/lockedfile/lockedfile_test.go000066400000000000000000000062721360713250200215340ustar00rootroot00000000000000// Copyright 2018 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. // js and nacl do not support inter-process file locking. // +build !js,!nacl package lockedfile_test import ( "io/ioutil" "os" "path/filepath" "testing" "time" "github.com/rogpeppe/go-internal/lockedfile" ) func mustTempDir(t *testing.T) (dir string, remove func()) { t.Helper() dir, err := ioutil.TempDir("", filepath.Base(t.Name())) if err != nil { t.Fatal(err) } return dir, func() { os.RemoveAll(dir) } } const ( quiescent = 10 * time.Millisecond probablyStillBlocked = 10 * time.Second ) func mustBlock(t *testing.T, desc string, f func()) (wait func(*testing.T)) { t.Helper() done := make(chan struct{}) go func() { f() close(done) }() select { case <-done: t.Fatalf("%s unexpectedly did not block", desc) return nil case <-time.After(quiescent): return func(t *testing.T) { t.Helper() select { case <-time.After(probablyStillBlocked): t.Fatalf("%s is unexpectedly still blocked after %v", desc, probablyStillBlocked) case <-done: } } } } func TestMutexExcludes(t *testing.T) { t.Parallel() dir, remove := mustTempDir(t) defer remove() path := filepath.Join(dir, "lock") mu := lockedfile.MutexAt(path) t.Logf("mu := MutexAt(_)") unlock, err := mu.Lock() if err != nil { t.Fatalf("mu.Lock: %v", err) } t.Logf("unlock, _ := mu.Lock()") mu2 := lockedfile.MutexAt(mu.Path) t.Logf("mu2 := MutexAt(mu.Path)") wait := mustBlock(t, "mu2.Lock()", func() { unlock2, err := mu2.Lock() if err != nil { t.Errorf("mu2.Lock: %v", err) return } t.Logf("unlock2, _ := mu2.Lock()") t.Logf("unlock2()") unlock2() }) t.Logf("unlock()") unlock() wait(t) } func TestReadWaitsForLock(t *testing.T) { t.Parallel() dir, remove := mustTempDir(t) defer remove() path := filepath.Join(dir, "timestamp.txt") f, err := lockedfile.Create(path) if err != nil { t.Fatalf("Create: %v", err) } defer f.Close() const ( part1 = "part 1\n" part2 = "part 2\n" ) _, err = f.WriteString(part1) if err != nil { t.Fatalf("WriteString: %v", err) } t.Logf("WriteString(%q) = ", part1) wait := mustBlock(t, "Read", func() { b, err := lockedfile.Read(path) if err != nil { t.Errorf("Read: %v", err) return } const want = part1 + part2 got := string(b) if got == want { t.Logf("Read(_) = %q", got) } else { t.Errorf("Read(_) = %q, _; want %q", got, want) } }) _, err = f.WriteString(part2) if err != nil { t.Errorf("WriteString: %v", err) } else { t.Logf("WriteString(%q) = ", part2) } f.Close() wait(t) } func TestCanLockExistingFile(t *testing.T) { t.Parallel() dir, remove := mustTempDir(t) defer remove() path := filepath.Join(dir, "existing.txt") if err := ioutil.WriteFile(path, []byte("ok"), 0777); err != nil { t.Fatalf("ioutil.WriteFile: %v", err) } f, err := lockedfile.Edit(path) if err != nil { t.Fatalf("first Edit: %v", err) } wait := mustBlock(t, "Edit", func() { other, err := lockedfile.Edit(path) if err != nil { t.Errorf("second Edit: %v", err) } other.Close() }) f.Close() wait(t) } go-internal-1.5.2/lockedfile/mutex.go000066400000000000000000000042251360713250200175320ustar00rootroot00000000000000// Copyright 2018 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 lockedfile import ( "fmt" "os" ) // A Mutex provides mutual exclusion within and across processes by locking a // well-known file. Such a file generally guards some other part of the // filesystem: for example, a Mutex file in a directory might guard access to // the entire tree rooted in that directory. // // Mutex does not implement sync.Locker: unlike a sync.Mutex, a lockedfile.Mutex // can fail to lock (e.g. if there is a permission error in the filesystem). // // Like a sync.Mutex, a Mutex may be included as a field of a larger struct but // must not be copied after first use. The Path field must be set before first // use and must not be change thereafter. type Mutex struct { Path string // The path to the well-known lock file. Must be non-empty. } // MutexAt returns a new Mutex with Path set to the given non-empty path. func MutexAt(path string) *Mutex { if path == "" { panic("lockedfile.MutexAt: path must be non-empty") } return &Mutex{Path: path} } func (mu *Mutex) String() string { return fmt.Sprintf("lockedfile.Mutex(%s)", mu.Path) } // Lock attempts to lock the Mutex. // // If successful, Lock returns a non-nil unlock function: it is provided as a // return-value instead of a separate method to remind the caller to check the // accompanying error. (See https://golang.org/issue/20803.) func (mu *Mutex) Lock() (unlock func(), err error) { if mu.Path == "" { panic("lockedfile.Mutex: missing Path during Lock") } // We could use either O_RDWR or O_WRONLY here. If we choose O_RDWR and the // file at mu.Path is write-only, the call to OpenFile will fail with a // permission error. That's actually what we want: if we add an RLock method // in the future, it should call OpenFile with O_RDONLY and will require the // files must be readable, so we should not let the caller make any // assumptions about Mutex working with write-only files. f, err := OpenFile(mu.Path, os.O_RDWR|os.O_CREATE, 0666) if err != nil { return nil, err } return func() { f.Close() }, nil } go-internal-1.5.2/modfile/000077500000000000000000000000001360713250200153545ustar00rootroot00000000000000go-internal-1.5.2/modfile/gopkgin.go000066400000000000000000000023571360713250200173500ustar00rootroot00000000000000// Copyright 2018 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. // TODO: Figure out what gopkg.in should do. package modfile import "strings" // ParseGopkgIn splits gopkg.in import paths into their constituent parts func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) { if !strings.HasPrefix(path, "gopkg.in/") { return } f := strings.Split(path, "/") if len(f) >= 2 { if elem, v, ok := dotV(f[1]); ok { root = strings.Join(f[:2], "/") repo = "github.com/go-" + elem + "/" + elem major = v subdir = strings.Join(f[2:], "/") return root, repo, major, subdir, true } } if len(f) >= 3 { if elem, v, ok := dotV(f[2]); ok { root = strings.Join(f[:3], "/") repo = "github.com/" + f[1] + "/" + elem major = v subdir = strings.Join(f[3:], "/") return root, repo, major, subdir, true } } return } func dotV(name string) (elem, v string, ok bool) { i := len(name) - 1 for i >= 0 && '0' <= name[i] && name[i] <= '9' { i-- } if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 { return "", "", false } return name[:i-1], name[i:], true } go-internal-1.5.2/modfile/print.go000066400000000000000000000064471360713250200170520ustar00rootroot00000000000000// Copyright 2018 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 modfile implements parsing and formatting for // go.mod files. package modfile import ( "bytes" "fmt" "strings" ) func Format(f *FileSyntax) []byte { pr := &printer{} pr.file(f) return pr.Bytes() } // A printer collects the state during printing of a file or expression. type printer struct { bytes.Buffer // output buffer comment []Comment // pending end-of-line comments margin int // left margin (indent), a number of tabs } // printf prints to the buffer. func (p *printer) printf(format string, args ...interface{}) { fmt.Fprintf(p, format, args...) } // indent returns the position on the current line, in bytes, 0-indexed. func (p *printer) indent() int { b := p.Bytes() n := 0 for n < len(b) && b[len(b)-1-n] != '\n' { n++ } return n } // newline ends the current line, flushing end-of-line comments. func (p *printer) newline() { if len(p.comment) > 0 { p.printf(" ") for i, com := range p.comment { if i > 0 { p.trim() p.printf("\n") for i := 0; i < p.margin; i++ { p.printf("\t") } } p.printf("%s", strings.TrimSpace(com.Token)) } p.comment = p.comment[:0] } p.trim() p.printf("\n") for i := 0; i < p.margin; i++ { p.printf("\t") } } // trim removes trailing spaces and tabs from the current line. func (p *printer) trim() { // Remove trailing spaces and tabs from line we're about to end. b := p.Bytes() n := len(b) for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') { n-- } p.Truncate(n) } // file formats the given file into the print buffer. func (p *printer) file(f *FileSyntax) { for _, com := range f.Before { p.printf("%s", strings.TrimSpace(com.Token)) p.newline() } for i, stmt := range f.Stmt { switch x := stmt.(type) { case *CommentBlock: // comments already handled p.expr(x) default: p.expr(x) p.newline() } for _, com := range stmt.Comment().After { p.printf("%s", strings.TrimSpace(com.Token)) p.newline() } if i+1 < len(f.Stmt) { p.newline() } } } func (p *printer) expr(x Expr) { // Emit line-comments preceding this expression. if before := x.Comment().Before; len(before) > 0 { // Want to print a line comment. // Line comments must be at the current margin. p.trim() if p.indent() > 0 { // There's other text on the line. Start a new line. p.printf("\n") } // Re-indent to margin. for i := 0; i < p.margin; i++ { p.printf("\t") } for _, com := range before { p.printf("%s", strings.TrimSpace(com.Token)) p.newline() } } switch x := x.(type) { default: panic(fmt.Errorf("printer: unexpected type %T", x)) case *CommentBlock: // done case *LParen: p.printf("(") case *RParen: p.printf(")") case *Line: sep := "" for _, tok := range x.Token { p.printf("%s%s", sep, tok) sep = " " } case *LineBlock: for _, tok := range x.Token { p.printf("%s ", tok) } p.expr(&x.LParen) p.margin++ for _, l := range x.Line { p.newline() p.expr(l) } p.margin-- p.newline() p.expr(&x.RParen) } // Queue end-of-line comments for printing when we // reach the end of the line. p.comment = append(p.comment, x.Comment().Suffix...) } go-internal-1.5.2/modfile/read.go000066400000000000000000000515011360713250200166200ustar00rootroot00000000000000// Copyright 2018 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. // Module file parser. // This is a simplified copy of Google's buildifier parser. package modfile import ( "bytes" "fmt" "os" "strconv" "strings" "unicode" "unicode/utf8" ) // A Position describes the position between two bytes of input. type Position struct { Line int // line in input (starting at 1) LineRune int // rune in line (starting at 1) Byte int // byte in input (starting at 0) } // add returns the position at the end of s, assuming it starts at p. func (p Position) add(s string) Position { p.Byte += len(s) if n := strings.Count(s, "\n"); n > 0 { p.Line += n s = s[strings.LastIndex(s, "\n")+1:] p.LineRune = 1 } p.LineRune += utf8.RuneCountInString(s) return p } // An Expr represents an input element. type Expr interface { // Span returns the start and end position of the expression, // excluding leading or trailing comments. Span() (start, end Position) // Comment returns the comments attached to the expression. // This method would normally be named 'Comments' but that // would interfere with embedding a type of the same name. Comment() *Comments } // A Comment represents a single // comment. type Comment struct { Start Position Token string // without trailing newline Suffix bool // an end of line (not whole line) comment } // Comments collects the comments associated with an expression. type Comments struct { Before []Comment // whole-line comments before this expression Suffix []Comment // end-of-line comments after this expression // For top-level expressions only, After lists whole-line // comments following the expression. After []Comment } // Comment returns the receiver. This isn't useful by itself, but // a Comments struct is embedded into all the expression // implementation types, and this gives each of those a Comment // method to satisfy the Expr interface. func (c *Comments) Comment() *Comments { return c } // A FileSyntax represents an entire go.mod file. type FileSyntax struct { Name string // file path Comments Stmt []Expr } func (x *FileSyntax) Span() (start, end Position) { if len(x.Stmt) == 0 { return } start, _ = x.Stmt[0].Span() _, end = x.Stmt[len(x.Stmt)-1].Span() return start, end } func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line { if hint == nil { // If no hint given, add to the last statement of the given type. Loop: for i := len(x.Stmt) - 1; i >= 0; i-- { stmt := x.Stmt[i] switch stmt := stmt.(type) { case *Line: if stmt.Token != nil && stmt.Token[0] == tokens[0] { hint = stmt break Loop } case *LineBlock: if stmt.Token[0] == tokens[0] { hint = stmt break Loop } } } } if hint != nil { for i, stmt := range x.Stmt { switch stmt := stmt.(type) { case *Line: if stmt == hint { // Convert line to line block. stmt.InBlock = true block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}} stmt.Token = stmt.Token[1:] x.Stmt[i] = block new := &Line{Token: tokens[1:], InBlock: true} block.Line = append(block.Line, new) return new } case *LineBlock: if stmt == hint { new := &Line{Token: tokens[1:], InBlock: true} stmt.Line = append(stmt.Line, new) return new } for j, line := range stmt.Line { if line == hint { // Add new line after hint. stmt.Line = append(stmt.Line, nil) copy(stmt.Line[j+2:], stmt.Line[j+1:]) new := &Line{Token: tokens[1:], InBlock: true} stmt.Line[j+1] = new return new } } } } } new := &Line{Token: tokens} x.Stmt = append(x.Stmt, new) return new } func (x *FileSyntax) updateLine(line *Line, tokens ...string) { if line.InBlock { tokens = tokens[1:] } line.Token = tokens } func (x *FileSyntax) removeLine(line *Line) { line.Token = nil } // Cleanup cleans up the file syntax x after any edit operations. // To avoid quadratic behavior, removeLine marks the line as dead // by setting line.Token = nil but does not remove it from the slice // in which it appears. After edits have all been indicated, // calling Cleanup cleans out the dead lines. func (x *FileSyntax) Cleanup() { w := 0 for _, stmt := range x.Stmt { switch stmt := stmt.(type) { case *Line: if stmt.Token == nil { continue } case *LineBlock: ww := 0 for _, line := range stmt.Line { if line.Token != nil { stmt.Line[ww] = line ww++ } } if ww == 0 { continue } if ww == 1 { // Collapse block into single line. line := &Line{ Comments: Comments{ Before: commentsAdd(stmt.Before, stmt.Line[0].Before), Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix), After: commentsAdd(stmt.Line[0].After, stmt.After), }, Token: stringsAdd(stmt.Token, stmt.Line[0].Token), } x.Stmt[w] = line w++ continue } stmt.Line = stmt.Line[:ww] } x.Stmt[w] = stmt w++ } x.Stmt = x.Stmt[:w] } func commentsAdd(x, y []Comment) []Comment { return append(x[:len(x):len(x)], y...) } func stringsAdd(x, y []string) []string { return append(x[:len(x):len(x)], y...) } // A CommentBlock represents a top-level block of comments separate // from any rule. type CommentBlock struct { Comments Start Position } func (x *CommentBlock) Span() (start, end Position) { return x.Start, x.Start } // A Line is a single line of tokens. type Line struct { Comments Start Position Token []string InBlock bool End Position } func (x *Line) Span() (start, end Position) { return x.Start, x.End } // A LineBlock is a factored block of lines, like // // require ( // "x" // "y" // ) // type LineBlock struct { Comments Start Position LParen LParen Token []string Line []*Line RParen RParen } func (x *LineBlock) Span() (start, end Position) { return x.Start, x.RParen.Pos.add(")") } // An LParen represents the beginning of a parenthesized line block. // It is a place to store suffix comments. type LParen struct { Comments Pos Position } func (x *LParen) Span() (start, end Position) { return x.Pos, x.Pos.add(")") } // An RParen represents the end of a parenthesized line block. // It is a place to store whole-line (before) comments. type RParen struct { Comments Pos Position } func (x *RParen) Span() (start, end Position) { return x.Pos, x.Pos.add(")") } // An input represents a single input file being parsed. type input struct { // Lexing state. filename string // name of input file, for errors complete []byte // entire input remaining []byte // remaining input token []byte // token being scanned lastToken string // most recently returned token, for error messages pos Position // current input position comments []Comment // accumulated comments endRule int // position of end of current rule // Parser state. file *FileSyntax // returned top-level syntax tree parseError error // error encountered during parsing // Comment assignment state. pre []Expr // all expressions, in preorder traversal post []Expr // all expressions, in postorder traversal } func newInput(filename string, data []byte) *input { return &input{ filename: filename, complete: data, remaining: data, pos: Position{Line: 1, LineRune: 1, Byte: 0}, } } // parse parses the input file. func parse(file string, data []byte) (f *FileSyntax, err error) { in := newInput(file, data) // The parser panics for both routine errors like syntax errors // and for programmer bugs like array index errors. // Turn both into error returns. Catching bug panics is // especially important when processing many files. defer func() { if e := recover(); e != nil { if e == in.parseError { err = in.parseError } else { err = fmt.Errorf("%s:%d:%d: internal error: %v", in.filename, in.pos.Line, in.pos.LineRune, e) } } }() // Invoke the parser. in.parseFile() if in.parseError != nil { return nil, in.parseError } in.file.Name = in.filename // Assign comments to nearby syntax. in.assignComments() return in.file, nil } // Error is called to report an error. // The reason s is often "syntax error". // Error does not return: it panics. func (in *input) Error(s string) { if s == "syntax error" && in.lastToken != "" { s += " near " + in.lastToken } in.parseError = fmt.Errorf("%s:%d:%d: %v", in.filename, in.pos.Line, in.pos.LineRune, s) panic(in.parseError) } // eof reports whether the input has reached end of file. func (in *input) eof() bool { return len(in.remaining) == 0 } // peekRune returns the next rune in the input without consuming it. func (in *input) peekRune() int { if len(in.remaining) == 0 { return 0 } r, _ := utf8.DecodeRune(in.remaining) return int(r) } // peekPrefix reports whether the remaining input begins with the given prefix. func (in *input) peekPrefix(prefix string) bool { // This is like bytes.HasPrefix(in.remaining, []byte(prefix)) // but without the allocation of the []byte copy of prefix. for i := 0; i < len(prefix); i++ { if i >= len(in.remaining) || in.remaining[i] != prefix[i] { return false } } return true } // readRune consumes and returns the next rune in the input. func (in *input) readRune() int { if len(in.remaining) == 0 { in.Error("internal lexer error: readRune at EOF") } r, size := utf8.DecodeRune(in.remaining) in.remaining = in.remaining[size:] if r == '\n' { in.pos.Line++ in.pos.LineRune = 1 } else { in.pos.LineRune++ } in.pos.Byte += size return int(r) } type symType struct { pos Position endPos Position text string } // startToken marks the beginning of the next input token. // It must be followed by a call to endToken, once the token has // been consumed using readRune. func (in *input) startToken(sym *symType) { in.token = in.remaining sym.text = "" sym.pos = in.pos } // endToken marks the end of an input token. // It records the actual token string in sym.text if the caller // has not done that already. func (in *input) endToken(sym *symType) { if sym.text == "" { tok := string(in.token[:len(in.token)-len(in.remaining)]) sym.text = tok in.lastToken = sym.text } sym.endPos = in.pos } // lex is called from the parser to obtain the next input token. // It returns the token value (either a rune like '+' or a symbolic token _FOR) // and sets val to the data associated with the token. // For all our input tokens, the associated data is // val.Pos (the position where the token begins) // and val.Token (the input string corresponding to the token). func (in *input) lex(sym *symType) int { // Skip past spaces, stopping at non-space or EOF. countNL := 0 // number of newlines we've skipped past for !in.eof() { // Skip over spaces. Count newlines so we can give the parser // information about where top-level blank lines are, // for top-level comment assignment. c := in.peekRune() if c == ' ' || c == '\t' || c == '\r' { in.readRune() continue } // Comment runs to end of line. if in.peekPrefix("//") { in.startToken(sym) // Is this comment the only thing on its line? // Find the last \n before this // and see if it's all // spaces from there to here. i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n")) suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0 in.readRune() in.readRune() // Consume comment. for len(in.remaining) > 0 && in.readRune() != '\n' { } in.endToken(sym) sym.text = strings.TrimRight(sym.text, "\n") in.lastToken = "comment" // If we are at top level (not in a statement), hand the comment to // the parser as a _COMMENT token. The grammar is written // to handle top-level comments itself. if !suffix { // Not in a statement. Tell parser about top-level comment. return _COMMENT } // Otherwise, save comment for later attachment to syntax tree. if countNL > 1 { in.comments = append(in.comments, Comment{sym.pos, "", false}) } in.comments = append(in.comments, Comment{sym.pos, sym.text, suffix}) countNL = 1 return _EOL } if in.peekPrefix("/*") { in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)")) } // Found non-space non-comment. break } // Found the beginning of the next token. in.startToken(sym) defer in.endToken(sym) // End of file. if in.eof() { in.lastToken = "EOF" return _EOF } // Punctuation tokens. switch c := in.peekRune(); c { case '\n': in.readRune() return c case '(': in.readRune() return c case ')': in.readRune() return c case '"', '`': // quoted string quote := c in.readRune() for { if in.eof() { in.pos = sym.pos in.Error("unexpected EOF in string") } if in.peekRune() == '\n' { in.Error("unexpected newline in string") } c := in.readRune() if c == quote { break } if c == '\\' && quote != '`' { if in.eof() { in.pos = sym.pos in.Error("unexpected EOF in string") } in.readRune() } } in.endToken(sym) return _STRING } // Checked all punctuation. Must be identifier token. if c := in.peekRune(); !isIdent(c) { in.Error(fmt.Sprintf("unexpected input character %#q", c)) } // Scan over identifier. for isIdent(in.peekRune()) { if in.peekPrefix("//") { break } if in.peekPrefix("/*") { in.Error(fmt.Sprintf("mod files must use // comments (not /* */ comments)")) } in.readRune() } return _IDENT } // isIdent reports whether c is an identifier rune. // We treat nearly all runes as identifier runes. func isIdent(c int) bool { return c != 0 && !unicode.IsSpace(rune(c)) } // Comment assignment. // We build two lists of all subexpressions, preorder and postorder. // The preorder list is ordered by start location, with outer expressions first. // The postorder list is ordered by end location, with outer expressions last. // We use the preorder list to assign each whole-line comment to the syntax // immediately following it, and we use the postorder list to assign each // end-of-line comment to the syntax immediately preceding it. // order walks the expression adding it and its subexpressions to the // preorder and postorder lists. func (in *input) order(x Expr) { if x != nil { in.pre = append(in.pre, x) } switch x := x.(type) { default: panic(fmt.Errorf("order: unexpected type %T", x)) case nil: // nothing case *LParen, *RParen: // nothing case *CommentBlock: // nothing case *Line: // nothing case *FileSyntax: for _, stmt := range x.Stmt { in.order(stmt) } case *LineBlock: in.order(&x.LParen) for _, l := range x.Line { in.order(l) } in.order(&x.RParen) } if x != nil { in.post = append(in.post, x) } } // assignComments attaches comments to nearby syntax. func (in *input) assignComments() { const debug = false // Generate preorder and postorder lists. in.order(in.file) // Split into whole-line comments and suffix comments. var line, suffix []Comment for _, com := range in.comments { if com.Suffix { suffix = append(suffix, com) } else { line = append(line, com) } } if debug { for _, c := range line { fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) } } // Assign line comments to syntax immediately following. for _, x := range in.pre { start, _ := x.Span() if debug { fmt.Printf("pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte) } xcom := x.Comment() for len(line) > 0 && start.Byte >= line[0].Start.Byte { if debug { fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte) } xcom.Before = append(xcom.Before, line[0]) line = line[1:] } } // Remaining line comments go at end of file. in.file.After = append(in.file.After, line...) if debug { for _, c := range suffix { fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte) } } // Assign suffix comments to syntax immediately before. for i := len(in.post) - 1; i >= 0; i-- { x := in.post[i] start, end := x.Span() if debug { fmt.Printf("post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte) } // Do not assign suffix comments to end of line block or whole file. // Instead assign them to the last element inside. switch x.(type) { case *FileSyntax: continue } // Do not assign suffix comments to something that starts // on an earlier line, so that in // // x ( y // z ) // comment // // we assign the comment to z and not to x ( ... ). if start.Line != end.Line { continue } xcom := x.Comment() for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte { if debug { fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte) } xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1]) suffix = suffix[:len(suffix)-1] } } // We assigned suffix comments in reverse. // If multiple suffix comments were appended to the same // expression node, they are now in reverse. Fix that. for _, x := range in.post { reverseComments(x.Comment().Suffix) } // Remaining suffix comments go at beginning of file. in.file.Before = append(in.file.Before, suffix...) } // reverseComments reverses the []Comment list. func reverseComments(list []Comment) { for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { list[i], list[j] = list[j], list[i] } } func (in *input) parseFile() { in.file = new(FileSyntax) var sym symType var cb *CommentBlock for { tok := in.lex(&sym) switch tok { case '\n': if cb != nil { in.file.Stmt = append(in.file.Stmt, cb) cb = nil } case _COMMENT: if cb == nil { cb = &CommentBlock{Start: sym.pos} } com := cb.Comment() com.Before = append(com.Before, Comment{Start: sym.pos, Token: sym.text}) case _EOF: if cb != nil { in.file.Stmt = append(in.file.Stmt, cb) } return default: in.parseStmt(&sym) if cb != nil { in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before cb = nil } } } } func (in *input) parseStmt(sym *symType) { start := sym.pos end := sym.endPos token := []string{sym.text} for { tok := in.lex(sym) switch tok { case '\n', _EOF, _EOL: in.file.Stmt = append(in.file.Stmt, &Line{ Start: start, Token: token, End: end, }) return case '(': in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, token, sym)) return default: token = append(token, sym.text) end = sym.endPos } } } func (in *input) parseLineBlock(start Position, token []string, sym *symType) *LineBlock { x := &LineBlock{ Start: start, Token: token, LParen: LParen{Pos: sym.pos}, } var comments []Comment for { tok := in.lex(sym) switch tok { case _EOL: // ignore case '\n': if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" { comments = append(comments, Comment{}) } case _COMMENT: comments = append(comments, Comment{Start: sym.pos, Token: sym.text}) case _EOF: in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune)) case ')': x.RParen.Before = comments x.RParen.Pos = sym.pos tok = in.lex(sym) if tok != '\n' && tok != _EOF && tok != _EOL { in.Error("syntax error (expected newline after closing paren)") } return x default: l := in.parseLine(sym) x.Line = append(x.Line, l) l.Comment().Before = comments comments = nil } } } func (in *input) parseLine(sym *symType) *Line { start := sym.pos end := sym.endPos token := []string{sym.text} for { tok := in.lex(sym) switch tok { case '\n', _EOF, _EOL: return &Line{ Start: start, Token: token, End: end, InBlock: true, } default: token = append(token, sym.text) end = sym.endPos } } } const ( _EOF = -(1 + iota) _EOL _IDENT _STRING _COMMENT ) var ( slashSlash = []byte("//") moduleStr = []byte("module") ) // ModulePath returns the module path from the gomod file text. // If it cannot find a module path, it returns an empty string. // It is tolerant of unrelated problems in the go.mod file. func ModulePath(mod []byte) string { for len(mod) > 0 { line := mod mod = nil if i := bytes.IndexByte(line, '\n'); i >= 0 { line, mod = line[:i], line[i+1:] } if i := bytes.Index(line, slashSlash); i >= 0 { line = line[:i] } line = bytes.TrimSpace(line) if !bytes.HasPrefix(line, moduleStr) { continue } line = line[len(moduleStr):] n := len(line) line = bytes.TrimSpace(line) if len(line) == n || len(line) == 0 { continue } if line[0] == '"' || line[0] == '`' { p, err := strconv.Unquote(string(line)) if err != nil { return "" // malformed quoted string or multiline module path } return p } return string(line) } return "" // missing module path } go-internal-1.5.2/modfile/read_test.go000066400000000000000000000217131360713250200176610ustar00rootroot00000000000000// Copyright 2018 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 modfile import ( "bytes" "fmt" "io/ioutil" "os" "path/filepath" "reflect" "strings" "testing" "github.com/rogpeppe/go-internal/internal/textutil" ) // exists reports whether the named file exists. func exists(name string) bool { _, err := os.Stat(name) return err == nil } // Test that reading and then writing the golden files // does not change their output. func TestPrintGolden(t *testing.T) { outs, err := filepath.Glob("testdata/*.golden") if err != nil { t.Fatal(err) } for _, out := range outs { testPrint(t, out, out) } } // testPrint is a helper for testing the printer. // It reads the file named in, reformats it, and compares // the result to the file named out. func testPrint(t *testing.T, in, out string) { data, err := ioutil.ReadFile(in) if err != nil { t.Error(err) return } golden, err := ioutil.ReadFile(out) if err != nil { t.Error(err) return } base := "testdata/" + filepath.Base(in) f, err := parse(in, data) if err != nil { t.Error(err) return } ndata := Format(f) if !bytes.Equal(ndata, golden) { t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base) tdiff(t, string(golden), string(ndata)) return } } func TestParseLax(t *testing.T) { badFile := []byte(`module m surprise attack x y ( z ) exclude v1.2.3 replace <-!!! `) _, err := ParseLax("file", badFile, nil) if err != nil { t.Fatalf("ParseLax did not ignore irrelevant errors: %v", err) } } // Test that when files in the testdata directory are parsed // and printed and parsed again, we get the same parse tree // both times. func TestPrintParse(t *testing.T) { outs, err := filepath.Glob("testdata/*") if err != nil { t.Fatal(err) } for _, out := range outs { data, err := ioutil.ReadFile(out) if err != nil { t.Error(err) continue } base := "testdata/" + filepath.Base(out) f, err := parse(base, data) if err != nil { t.Errorf("parsing original: %v", err) continue } ndata := Format(f) f2, err := parse(base, ndata) if err != nil { t.Errorf("parsing reformatted: %v", err) continue } eq := eqchecker{file: base} if err := eq.check(f, f2); err != nil { t.Errorf("not equal (parse/Format/parse): %v", err) } pf1, err := Parse(base, data, nil) if err != nil { switch base { case "testdata/replace2.in", "testdata/gopkg.in.golden": t.Errorf("should parse %v: %v", base, err) } } if err == nil { pf2, err := Parse(base, ndata, nil) if err != nil { t.Errorf("Parsing reformatted: %v", err) continue } eq := eqchecker{file: base} if err := eq.check(pf1, pf2); err != nil { t.Errorf("not equal (parse/Format/Parse): %v", err) } ndata2, err := pf1.Format() if err != nil { t.Errorf("reformat: %v", err) } pf3, err := Parse(base, ndata2, nil) if err != nil { t.Errorf("Parsing reformatted2: %v", err) continue } eq = eqchecker{file: base} if err := eq.check(pf1, pf3); err != nil { t.Errorf("not equal (Parse/Format/Parse): %v", err) } ndata = ndata2 } if strings.HasSuffix(out, ".in") { golden, err := ioutil.ReadFile(strings.TrimSuffix(out, ".in") + ".golden") if err != nil { t.Error(err) continue } if !bytes.Equal(ndata, golden) { t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base) tdiff(t, string(golden), string(ndata)) return } } } } // An eqchecker holds state for checking the equality of two parse trees. type eqchecker struct { file string pos Position } // errorf returns an error described by the printf-style format and arguments, // inserting the current file position before the error text. func (eq *eqchecker) errorf(format string, args ...interface{}) error { return fmt.Errorf("%s:%d: %s", eq.file, eq.pos.Line, fmt.Sprintf(format, args...)) } // check checks that v and w represent the same parse tree. // If not, it returns an error describing the first difference. func (eq *eqchecker) check(v, w interface{}) error { return eq.checkValue(reflect.ValueOf(v), reflect.ValueOf(w)) } var ( posType = reflect.TypeOf(Position{}) commentsType = reflect.TypeOf(Comments{}) ) // checkValue checks that v and w represent the same parse tree. // If not, it returns an error describing the first difference. func (eq *eqchecker) checkValue(v, w reflect.Value) error { // inner returns the innermost expression for v. // if v is a non-nil interface value, it returns the concrete // value in the interface. inner := func(v reflect.Value) reflect.Value { for { if v.Kind() == reflect.Interface && !v.IsNil() { v = v.Elem() continue } break } return v } v = inner(v) w = inner(w) if v.Kind() == reflect.Invalid && w.Kind() == reflect.Invalid { return nil } if v.Kind() == reflect.Invalid { return eq.errorf("nil interface became %s", w.Type()) } if w.Kind() == reflect.Invalid { return eq.errorf("%s became nil interface", v.Type()) } if v.Type() != w.Type() { return eq.errorf("%s became %s", v.Type(), w.Type()) } if p, ok := v.Interface().(Expr); ok { eq.pos, _ = p.Span() } switch v.Kind() { default: return eq.errorf("unexpected type %s", v.Type()) case reflect.Bool, reflect.Int, reflect.String: vi := v.Interface() wi := w.Interface() if vi != wi { return eq.errorf("%v became %v", vi, wi) } case reflect.Slice: vl := v.Len() wl := w.Len() for i := 0; i < vl || i < wl; i++ { if i >= vl { return eq.errorf("unexpected %s", w.Index(i).Type()) } if i >= wl { return eq.errorf("missing %s", v.Index(i).Type()) } if err := eq.checkValue(v.Index(i), w.Index(i)); err != nil { return err } } case reflect.Struct: // Fields in struct must match. t := v.Type() n := t.NumField() for i := 0; i < n; i++ { tf := t.Field(i) switch { default: if err := eq.checkValue(v.Field(i), w.Field(i)); err != nil { return err } case tf.Type == posType: // ignore positions case tf.Type == commentsType: // ignore comment assignment } } case reflect.Ptr, reflect.Interface: if v.IsNil() != w.IsNil() { if v.IsNil() { return eq.errorf("unexpected %s", w.Elem().Type()) } return eq.errorf("missing %s", v.Elem().Type()) } if err := eq.checkValue(v.Elem(), w.Elem()); err != nil { return err } } return nil } // tdiff logs the diff output to t.Error. func tdiff(t *testing.T, a, b string) { data := textutil.Diff(a, b) t.Error(string(data)) } var modulePathTests = []struct { input []byte expected string }{ {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, {input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"}, {input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"}, {input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"}, {input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"}, {input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"}, {input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"}, {input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""}, {input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"}, {input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"}, {input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"}, {input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"}, {input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"}, {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, {input: []byte("module \nmodule a/b/c "), expected: "a/b/c"}, {input: []byte("module \" \""), expected: " "}, {input: []byte("module "), expected: ""}, {input: []byte("module \" a/b/c \""), expected: " a/b/c "}, {input: []byte("module \"github.com/rsc/vgotest1\" // with a comment"), expected: "github.com/rsc/vgotest1"}, } func TestModulePath(t *testing.T) { for _, test := range modulePathTests { t.Run(string(test.input), func(t *testing.T) { result := ModulePath(test.input) if result != test.expected { t.Fatalf("ModulePath(%q): %s, want %s", string(test.input), result, test.expected) } }) } } go-internal-1.5.2/modfile/replace_test.go000066400000000000000000000035171360713250200203630ustar00rootroot00000000000000// Copyright 2019 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 modfile import ( "bytes" "fmt" "testing" ) var addDropReplaceTests = []struct { in string oldpath string oldvers string newpath string newvers string dropOld bool out string }{ { ` module m require x.y/z v1.2.3 `, "x.y/z", "v1.2.3", "my.x.y/z", "v1.2.3", false, ` module m require x.y/z v1.2.3 replace x.y/z v1.2.3 => my.x.y/z v1.2.3 `, }, { ` module m require x.y/z v1.2.3 replace x.y/z => my.x.y/z v0.0.0-20190214113530-db6c41c15648 `, "x.y/z", "", "my.x.y/z", "v1.2.3", true, ` module m require x.y/z v1.2.3 replace x.y/z => my.x.y/z v1.2.3 `, }, { ` module m require x.y/z v1.2.3 replace x.y/z => my.x.y/z v0.0.0-20190214113530-db6c41c15648 `, "x.y/z", "", "", "", // empty newpath and newvers - drop only, no add true, ` module m require x.y/z v1.2.3 `, }, } func TestAddDropReplace(t *testing.T) { for i, tt := range addDropReplaceTests { t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { f, err := Parse("in", []byte(tt.in), nil) if err != nil { t.Fatal(err) } g, err := Parse("out", []byte(tt.out), nil) if err != nil { t.Fatal(err) } golden, err := g.Format() if err != nil { t.Fatal(err) } if tt.dropOld { if err := f.DropReplace(tt.oldpath, tt.oldvers); err != nil { t.Fatal(err) } } if tt.newpath != "" || tt.newvers != "" { if err := f.AddReplace(tt.oldpath, tt.oldvers, tt.newpath, tt.newvers); err != nil { t.Fatal(err) } } f.Cleanup() out, err := f.Format() if err != nil { t.Fatal(err) } if !bytes.Equal(out, golden) { t.Errorf("have:\n%s\nwant:\n%s", out, golden) } }) } } go-internal-1.5.2/modfile/rule.go000066400000000000000000000446201360713250200166600ustar00rootroot00000000000000// Copyright 2018 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 modfile import ( "bytes" "errors" "fmt" "path/filepath" "regexp" "sort" "strconv" "strings" "unicode" "github.com/rogpeppe/go-internal/module" "github.com/rogpeppe/go-internal/semver" ) // A File is the parsed, interpreted form of a go.mod file. type File struct { Module *Module Go *Go Require []*Require Exclude []*Exclude Replace []*Replace Syntax *FileSyntax } // A Module is the module statement. type Module struct { Mod module.Version Syntax *Line } // A Go is the go statement. type Go struct { Version string // "1.23" Syntax *Line } // A Require is a single require statement. type Require struct { Mod module.Version Indirect bool // has "// indirect" comment Syntax *Line } // An Exclude is a single exclude statement. type Exclude struct { Mod module.Version Syntax *Line } // A Replace is a single replace statement. type Replace struct { Old module.Version New module.Version Syntax *Line } func (f *File) AddModuleStmt(path string) error { if f.Syntax == nil { f.Syntax = new(FileSyntax) } if f.Module == nil { f.Module = &Module{ Mod: module.Version{Path: path}, Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)), } } else { f.Module.Mod.Path = path f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path)) } return nil } func (f *File) AddComment(text string) { if f.Syntax == nil { f.Syntax = new(FileSyntax) } f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{ Comments: Comments{ Before: []Comment{ { Token: text, }, }, }, }) } type VersionFixer func(path, version string) (string, error) // Parse parses the data, reported in errors as being from file, // into a File struct. It applies fix, if non-nil, to canonicalize all module versions found. func Parse(file string, data []byte, fix VersionFixer) (*File, error) { return parseToFile(file, data, fix, true) } // ParseLax is like Parse but ignores unknown statements. // It is used when parsing go.mod files other than the main module, // under the theory that most statement types we add in the future will // only apply in the main module, like exclude and replace, // and so we get better gradual deployments if old go commands // simply ignore those statements when found in go.mod files // in dependencies. func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) { return parseToFile(file, data, fix, false) } func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File, error) { fs, err := parse(file, data) if err != nil { return nil, err } f := &File{ Syntax: fs, } var errs bytes.Buffer for _, x := range fs.Stmt { switch x := x.(type) { case *Line: f.add(&errs, x, x.Token[0], x.Token[1:], fix, strict) case *LineBlock: if len(x.Token) > 1 { if strict { fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " ")) } continue } switch x.Token[0] { default: if strict { fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " ")) } continue case "module", "require", "exclude", "replace": for _, l := range x.Line { f.add(&errs, l, x.Token[0], l.Token, fix, strict) } } } } if errs.Len() > 0 { return nil, errors.New(strings.TrimRight(errs.String(), "\n")) } return f, nil } var goVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`) func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) { // If strict is false, this module is a dependency. // We ignore all unknown directives as well as main-module-only // directives like replace and exclude. It will work better for // forward compatibility if we can depend on modules that have unknown // statements (presumed relevant only when acting as the main module) // and simply ignore those statements. if !strict { switch verb { case "module", "require", "go": // want these even for dependency go.mods default: return } } switch verb { default: fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb) case "go": if f.Go != nil { fmt.Fprintf(errs, "%s:%d: repeated go statement\n", f.Syntax.Name, line.Start.Line) return } if len(args) != 1 || !goVersionRE.MatchString(args[0]) { fmt.Fprintf(errs, "%s:%d: usage: go 1.23\n", f.Syntax.Name, line.Start.Line) return } f.Go = &Go{Syntax: line} f.Go.Version = args[0] case "module": if f.Module != nil { fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line) return } f.Module = &Module{Syntax: line} if len(args) != 1 { fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line) return } s, err := parseString(&args[0]) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) return } f.Module.Mod = module.Version{Path: s} case "require", "exclude": if len(args) != 2 { fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3\n", f.Syntax.Name, line.Start.Line, verb) return } s, err := parseString(&args[0]) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) return } old := args[1] v, err := parseVersion(s, &args[1], fix) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid module version %q: %v\n", f.Syntax.Name, line.Start.Line, old, err) return } pathMajor, err := modulePathMajor(s) if err != nil { fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err) return } if !module.MatchPathMajor(v, pathMajor) { if pathMajor == "" { pathMajor = "v0 or v1" } fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v) return } if verb == "require" { f.Require = append(f.Require, &Require{ Mod: module.Version{Path: s, Version: v}, Syntax: line, Indirect: isIndirect(line), }) } else { f.Exclude = append(f.Exclude, &Exclude{ Mod: module.Version{Path: s, Version: v}, Syntax: line, }) } case "replace": arrow := 2 if len(args) >= 2 && args[1] == "=>" { arrow = 1 } if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" { fmt.Fprintf(errs, "%s:%d: usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory\n", f.Syntax.Name, line.Start.Line, verb, verb) return } s, err := parseString(&args[0]) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) return } pathMajor, err := modulePathMajor(s) if err != nil { fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err) return } var v string if arrow == 2 { old := args[1] v, err = parseVersion(s, &args[1], fix) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err) return } if !module.MatchPathMajor(v, pathMajor) { if pathMajor == "" { pathMajor = "v0 or v1" } fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, pathMajor, semver.Major(v), v) return } } ns, err := parseString(&args[arrow+1]) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err) return } nv := "" if len(args) == arrow+2 { if !IsDirectoryPath(ns) { fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)\n", f.Syntax.Name, line.Start.Line) return } if filepath.Separator == '/' && strings.Contains(ns, `\`) { fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)\n", f.Syntax.Name, line.Start.Line) return } } if len(args) == arrow+3 { old := args[arrow+1] nv, err = parseVersion(ns, &args[arrow+2], fix) if err != nil { fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err) return } if IsDirectoryPath(ns) { fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version\n", f.Syntax.Name, line.Start.Line, ns) return } } f.Replace = append(f.Replace, &Replace{ Old: module.Version{Path: s, Version: v}, New: module.Version{Path: ns, Version: nv}, Syntax: line, }) } } // isIndirect reports whether line has a "// indirect" comment, // meaning it is in go.mod only for its effect on indirect dependencies, // so that it can be dropped entirely once the effective version of the // indirect dependency reaches the given minimum version. func isIndirect(line *Line) bool { if len(line.Suffix) == 0 { return false } f := strings.Fields(line.Suffix[0].Token) return (len(f) == 2 && f[1] == "indirect" || len(f) > 2 && f[1] == "indirect;") && f[0] == "//" } // setIndirect sets line to have (or not have) a "// indirect" comment. func setIndirect(line *Line, indirect bool) { if isIndirect(line) == indirect { return } if indirect { // Adding comment. if len(line.Suffix) == 0 { // New comment. line.Suffix = []Comment{{Token: "// indirect", Suffix: true}} return } // Insert at beginning of existing comment. com := &line.Suffix[0] space := " " if len(com.Token) > 2 && com.Token[2] == ' ' || com.Token[2] == '\t' { space = "" } com.Token = "// indirect;" + space + com.Token[2:] return } // Removing comment. f := strings.Fields(line.Suffix[0].Token) if len(f) == 2 { // Remove whole comment. line.Suffix = nil return } // Remove comment prefix. com := &line.Suffix[0] i := strings.Index(com.Token, "indirect;") com.Token = "//" + com.Token[i+len("indirect;"):] } // IsDirectoryPath reports whether the given path should be interpreted // as a directory path. Just like on the go command line, relative paths // and rooted paths are directory paths; the rest are module paths. func IsDirectoryPath(ns string) bool { // Because go.mod files can move from one system to another, // we check all known path syntaxes, both Unix and Windows. return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") || strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) || len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':' } // MustQuote reports whether s must be quoted in order to appear as // a single token in a go.mod line. func MustQuote(s string) bool { for _, r := range s { if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' { return true } } return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*") } // AutoQuote returns s or, if quoting is required for s to appear in a go.mod, // the quotation of s. func AutoQuote(s string) string { if MustQuote(s) { return strconv.Quote(s) } return s } func parseString(s *string) (string, error) { t := *s if strings.HasPrefix(t, `"`) { var err error if t, err = strconv.Unquote(t); err != nil { return "", err } } else if strings.ContainsAny(t, "\"'`") { // Other quotes are reserved both for possible future expansion // and to avoid confusion. For example if someone types 'x' // we want that to be a syntax error and not a literal x in literal quotation marks. return "", fmt.Errorf("unquoted string cannot contain quote") } *s = AutoQuote(t) return t, nil } func parseVersion(path string, s *string, fix VersionFixer) (string, error) { t, err := parseString(s) if err != nil { return "", err } if fix != nil { var err error t, err = fix(path, t) if err != nil { return "", err } } if v := module.CanonicalVersion(t); v != "" { *s = v return *s, nil } return "", fmt.Errorf("version must be of the form v1.2.3") } func modulePathMajor(path string) (string, error) { _, major, ok := module.SplitPathVersion(path) if !ok { return "", fmt.Errorf("invalid module path") } return major, nil } func (f *File) Format() ([]byte, error) { return Format(f.Syntax), nil } // Cleanup cleans up the file f after any edit operations. // To avoid quadratic behavior, modifications like DropRequire // clear the entry but do not remove it from the slice. // Cleanup cleans out all the cleared entries. func (f *File) Cleanup() { w := 0 for _, r := range f.Require { if r.Mod.Path != "" { f.Require[w] = r w++ } } f.Require = f.Require[:w] w = 0 for _, x := range f.Exclude { if x.Mod.Path != "" { f.Exclude[w] = x w++ } } f.Exclude = f.Exclude[:w] w = 0 for _, r := range f.Replace { if r.Old.Path != "" { f.Replace[w] = r w++ } } f.Replace = f.Replace[:w] f.Syntax.Cleanup() } func (f *File) AddRequire(path, vers string) error { need := true for _, r := range f.Require { if r.Mod.Path == path { if need { r.Mod.Version = vers f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers) need = false } else { f.Syntax.removeLine(r.Syntax) *r = Require{} } } } if need { f.AddNewRequire(path, vers, false) } return nil } func (f *File) AddNewRequire(path, vers string, indirect bool) { line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers) setIndirect(line, indirect) f.Require = append(f.Require, &Require{module.Version{Path: path, Version: vers}, indirect, line}) } func (f *File) SetRequire(req []*Require) { need := make(map[string]string) indirect := make(map[string]bool) for _, r := range req { need[r.Mod.Path] = r.Mod.Version indirect[r.Mod.Path] = r.Indirect } for _, r := range f.Require { if v, ok := need[r.Mod.Path]; ok { r.Mod.Version = v r.Indirect = indirect[r.Mod.Path] } } var newStmts []Expr for _, stmt := range f.Syntax.Stmt { switch stmt := stmt.(type) { case *LineBlock: if len(stmt.Token) > 0 && stmt.Token[0] == "require" { var newLines []*Line for _, line := range stmt.Line { if p, err := parseString(&line.Token[0]); err == nil && need[p] != "" { line.Token[1] = need[p] delete(need, p) setIndirect(line, indirect[p]) newLines = append(newLines, line) } } if len(newLines) == 0 { continue // drop stmt } stmt.Line = newLines } case *Line: if len(stmt.Token) > 0 && stmt.Token[0] == "require" { if p, err := parseString(&stmt.Token[1]); err == nil && need[p] != "" { stmt.Token[2] = need[p] delete(need, p) setIndirect(stmt, indirect[p]) } else { continue // drop stmt } } } newStmts = append(newStmts, stmt) } f.Syntax.Stmt = newStmts for path, vers := range need { f.AddNewRequire(path, vers, indirect[path]) } f.SortBlocks() } func (f *File) DropRequire(path string) error { for _, r := range f.Require { if r.Mod.Path == path { f.Syntax.removeLine(r.Syntax) *r = Require{} } } return nil } func (f *File) AddExclude(path, vers string) error { var hint *Line for _, x := range f.Exclude { if x.Mod.Path == path && x.Mod.Version == vers { return nil } if x.Mod.Path == path { hint = x.Syntax } } f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)}) return nil } func (f *File) DropExclude(path, vers string) error { for _, x := range f.Exclude { if x.Mod.Path == path && x.Mod.Version == vers { f.Syntax.removeLine(x.Syntax) *x = Exclude{} } } return nil } func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error { need := true old := module.Version{Path: oldPath, Version: oldVers} new := module.Version{Path: newPath, Version: newVers} tokens := []string{"replace", AutoQuote(oldPath)} if oldVers != "" { tokens = append(tokens, oldVers) } tokens = append(tokens, "=>", AutoQuote(newPath)) if newVers != "" { tokens = append(tokens, newVers) } var hint *Line for _, r := range f.Replace { if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) { if need { // Found replacement for old; update to use new. r.New = new f.Syntax.updateLine(r.Syntax, tokens...) need = false continue } // Already added; delete other replacements for same. f.Syntax.removeLine(r.Syntax) *r = Replace{} } if r.Old.Path == oldPath { hint = r.Syntax } } if need { f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)}) } return nil } func (f *File) DropReplace(oldPath, oldVers string) error { for _, r := range f.Replace { if r.Old.Path == oldPath && r.Old.Version == oldVers { f.Syntax.removeLine(r.Syntax) *r = Replace{} } } return nil } func (f *File) SortBlocks() { f.removeDups() // otherwise sorting is unsafe for _, stmt := range f.Syntax.Stmt { block, ok := stmt.(*LineBlock) if !ok { continue } sort.Slice(block.Line, func(i, j int) bool { li := block.Line[i] lj := block.Line[j] for k := 0; k < len(li.Token) && k < len(lj.Token); k++ { if li.Token[k] != lj.Token[k] { return li.Token[k] < lj.Token[k] } } return len(li.Token) < len(lj.Token) }) } } func (f *File) removeDups() { have := make(map[module.Version]bool) kill := make(map[*Line]bool) for _, x := range f.Exclude { if have[x.Mod] { kill[x.Syntax] = true continue } have[x.Mod] = true } var excl []*Exclude for _, x := range f.Exclude { if !kill[x.Syntax] { excl = append(excl, x) } } f.Exclude = excl have = make(map[module.Version]bool) // Later replacements take priority over earlier ones. for i := len(f.Replace) - 1; i >= 0; i-- { x := f.Replace[i] if have[x.Old] { kill[x.Syntax] = true continue } have[x.Old] = true } var repl []*Replace for _, x := range f.Replace { if !kill[x.Syntax] { repl = append(repl, x) } } f.Replace = repl var stmts []Expr for _, stmt := range f.Syntax.Stmt { switch stmt := stmt.(type) { case *Line: if kill[stmt] { continue } case *LineBlock: var lines []*Line for _, line := range stmt.Line { if !kill[line] { lines = append(lines, line) } } stmt.Line = lines if len(lines) == 0 { continue } } stmts = append(stmts, stmt) } f.Syntax.Stmt = stmts } go-internal-1.5.2/modfile/rule_test.go000066400000000000000000000025301360713250200177110ustar00rootroot00000000000000// Copyright 2018 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 modfile import ( "bytes" "fmt" "testing" ) var addRequireTests = []struct { in string path string vers string out string }{ { ` module m require x.y/z v1.2.3 `, "x.y/z", "v1.5.6", ` module m require x.y/z v1.5.6 `, }, { ` module m require x.y/z v1.2.3 `, "x.y/w", "v1.5.6", ` module m require ( x.y/z v1.2.3 x.y/w v1.5.6 ) `, }, { ` module m require x.y/z v1.2.3 require x.y/q/v2 v2.3.4 `, "x.y/w", "v1.5.6", ` module m require x.y/z v1.2.3 require ( x.y/q/v2 v2.3.4 x.y/w v1.5.6 ) `, }, } func TestAddRequire(t *testing.T) { for i, tt := range addRequireTests { t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { f, err := Parse("in", []byte(tt.in), nil) if err != nil { t.Fatal(err) } g, err := Parse("out", []byte(tt.out), nil) if err != nil { t.Fatal(err) } golden, err := g.Format() if err != nil { t.Fatal(err) } if err := f.AddRequire(tt.path, tt.vers); err != nil { t.Fatal(err) } out, err := f.Format() if err != nil { t.Fatal(err) } if !bytes.Equal(out, golden) { t.Errorf("have:\n%s\nwant:\n%s", out, golden) } }) } } go-internal-1.5.2/modfile/testdata/000077500000000000000000000000001360713250200171655ustar00rootroot00000000000000go-internal-1.5.2/modfile/testdata/block.golden000066400000000000000000000003261360713250200214520ustar00rootroot00000000000000// comment x "y" z // block block ( // block-eol // x-before-line "x" ( y // x-eol "x1" "x2" // line "x3" "x4" "x5" // y-line "y" // y-eol "z" // z-eol ) // block-eol2 block2 ( x y z ) // eof go-internal-1.5.2/modfile/testdata/block.in000066400000000000000000000003311360713250200206040ustar00rootroot00000000000000// comment x "y" z // block block ( // block-eol // x-before-line "x" ( y // x-eol "x1" "x2" // line "x3" "x4" "x5" // y-line "y" // y-eol "z" // z-eol ) // block-eol2 block2 (x y z ) // eof go-internal-1.5.2/modfile/testdata/comment.golden000066400000000000000000000001571360713250200220240ustar00rootroot00000000000000// comment module "x" // eol // mid comment // comment 2 // comment 2 line 2 module "y" // eoy // comment 3 go-internal-1.5.2/modfile/testdata/comment.in000066400000000000000000000001551360713250200211600ustar00rootroot00000000000000// comment module "x" // eol // mid comment // comment 2 // comment 2 line 2 module "y" // eoy // comment 3 go-internal-1.5.2/modfile/testdata/empty.golden000066400000000000000000000000001360713250200215030ustar00rootroot00000000000000go-internal-1.5.2/modfile/testdata/empty.in000066400000000000000000000000001360713250200206410ustar00rootroot00000000000000go-internal-1.5.2/modfile/testdata/gopkg.in.golden000066400000000000000000000001431360713250200220710ustar00rootroot00000000000000module x require ( gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 gopkg.in/yaml.v2 v2.2.1 ) go-internal-1.5.2/modfile/testdata/module.golden000066400000000000000000000000131360713250200216360ustar00rootroot00000000000000module abc go-internal-1.5.2/modfile/testdata/module.in000066400000000000000000000000151360713250200207760ustar00rootroot00000000000000module "abc" go-internal-1.5.2/modfile/testdata/replace.golden000066400000000000000000000001211360713250200217640ustar00rootroot00000000000000module abc replace xyz v1.2.3 => /tmp/z replace xyz v1.3.4 => my/xyz v1.3.4-me go-internal-1.5.2/modfile/testdata/replace.in000066400000000000000000000001331360713250200211250ustar00rootroot00000000000000module "abc" replace "xyz" v1.2.3 => "/tmp/z" replace "xyz" v1.3.4 => "my/xyz" v1.3.4-me go-internal-1.5.2/modfile/testdata/replace2.golden000066400000000000000000000002451360713250200220550ustar00rootroot00000000000000module abc replace ( xyz v1.2.3 => /tmp/z xyz v1.3.4 => my/xyz v1.3.4-me xyz v1.4.5 => "/tmp/my dir" xyz v1.5.6 => my/xyz v1.5.6 xyz => my/other/xyz v1.5.4 ) go-internal-1.5.2/modfile/testdata/replace2.in000066400000000000000000000002631360713250200212130ustar00rootroot00000000000000module "abc" replace ( "xyz" v1.2.3 => "/tmp/z" "xyz" v1.3.4 => "my/xyz" "v1.3.4-me" xyz "v1.4.5" => "/tmp/my dir" xyz v1.5.6 => my/xyz v1.5.6 xyz => my/other/xyz v1.5.4 ) go-internal-1.5.2/modfile/testdata/rule1.golden000066400000000000000000000000571360713250200214110ustar00rootroot00000000000000module "x" module "y" require "x" require x go-internal-1.5.2/module/000077500000000000000000000000001360713250200152225ustar00rootroot00000000000000go-internal-1.5.2/module/module.go000066400000000000000000000420261360713250200170420ustar00rootroot00000000000000// Copyright 2018 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 module defines the module.Version type // along with support code. package module // IMPORTANT NOTE // // This file essentially defines the set of valid import paths for the go command. // There are many subtle considerations, including Unicode ambiguity, // security, network, and file system representations. // // This file also defines the set of valid module path and version combinations, // another topic with many subtle considerations. // // Changes to the semantics in this file require approval from rsc. import ( "fmt" "sort" "strings" "unicode" "unicode/utf8" "github.com/rogpeppe/go-internal/semver" ) // A Version is defined by a module path and version pair. type Version struct { Path string // Version is usually a semantic version in canonical form. // There are two exceptions to this general rule. // First, the top-level target of a build has no specific version // and uses Version = "". // Second, during MVS calculations the version "none" is used // to represent the decision to take no version of a given module. Version string `json:",omitempty"` } // Check checks that a given module path, version pair is valid. // In addition to the path being a valid module path // and the version being a valid semantic version, // the two must correspond. // For example, the path "yaml/v2" only corresponds to // semantic versions beginning with "v2.". func Check(path, version string) error { if err := CheckPath(path); err != nil { return err } if !semver.IsValid(version) { return fmt.Errorf("malformed semantic version %v", version) } _, pathMajor, _ := SplitPathVersion(path) if !MatchPathMajor(version, pathMajor) { if pathMajor == "" { pathMajor = "v0 or v1" } if pathMajor[0] == '.' { // .v1 pathMajor = pathMajor[1:] } return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathMajor) } return nil } // firstPathOK reports whether r can appear in the first element of a module path. // The first element of the path must be an LDH domain name, at least for now. // To avoid case ambiguity, the domain name must be entirely lower case. func firstPathOK(r rune) bool { return r == '-' || r == '.' || '0' <= r && r <= '9' || 'a' <= r && r <= 'z' } // pathOK reports whether r can appear in an import path element. // Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. // This matches what "go get" has historically recognized in import paths. // TODO(rsc): We would like to allow Unicode letters, but that requires additional // care in the safe encoding (see note below). func pathOK(r rune) bool { if r < utf8.RuneSelf { return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' } return false } // fileNameOK reports whether r can appear in a file name. // For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. // If we expand the set of allowed characters here, we have to // work harder at detecting potential case-folding and normalization collisions. // See note about "safe encoding" below. func fileNameOK(r rune) bool { if r < utf8.RuneSelf { // Entire set of ASCII punctuation, from which we remove characters: // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ // We disallow some shell special characters: " ' * < > ? ` | // (Note that some of those are disallowed by the Windows file system as well.) // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). // We allow spaces (U+0020) in file names. const allowed = "!#$%&()+,-.=@[]^_{}~ " if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { return true } for i := 0; i < len(allowed); i++ { if rune(allowed[i]) == r { return true } } return false } // It may be OK to add more ASCII punctuation here, but only carefully. // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. return unicode.IsLetter(r) } // CheckPath checks that a module path is valid. func CheckPath(path string) error { if err := checkPath(path, false); err != nil { return fmt.Errorf("malformed module path %q: %v", path, err) } i := strings.Index(path, "/") if i < 0 { i = len(path) } if i == 0 { return fmt.Errorf("malformed module path %q: leading slash", path) } if !strings.Contains(path[:i], ".") { return fmt.Errorf("malformed module path %q: missing dot in first path element", path) } if path[0] == '-' { return fmt.Errorf("malformed module path %q: leading dash in first path element", path) } for _, r := range path[:i] { if !firstPathOK(r) { return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r) } } if _, _, ok := SplitPathVersion(path); !ok { return fmt.Errorf("malformed module path %q: invalid version", path) } return nil } // CheckImportPath checks that an import path is valid. func CheckImportPath(path string) error { if err := checkPath(path, false); err != nil { return fmt.Errorf("malformed import path %q: %v", path, err) } return nil } // checkPath checks that a general path is valid. // It returns an error describing why but not mentioning path. // Because these checks apply to both module paths and import paths, // the caller is expected to add the "malformed ___ path %q: " prefix. // fileName indicates whether the final element of the path is a file name // (as opposed to a directory name). func checkPath(path string, fileName bool) error { if !utf8.ValidString(path) { return fmt.Errorf("invalid UTF-8") } if path == "" { return fmt.Errorf("empty string") } if strings.Contains(path, "..") { return fmt.Errorf("double dot") } if strings.Contains(path, "//") { return fmt.Errorf("double slash") } if path[len(path)-1] == '/' { return fmt.Errorf("trailing slash") } elemStart := 0 for i, r := range path { if r == '/' { if err := checkElem(path[elemStart:i], fileName); err != nil { return err } elemStart = i + 1 } } if err := checkElem(path[elemStart:], fileName); err != nil { return err } return nil } // checkElem checks whether an individual path element is valid. // fileName indicates whether the element is a file name (not a directory name). func checkElem(elem string, fileName bool) error { if elem == "" { return fmt.Errorf("empty path element") } if strings.Count(elem, ".") == len(elem) { return fmt.Errorf("invalid path element %q", elem) } if elem[0] == '.' && !fileName { return fmt.Errorf("leading dot in path element") } if elem[len(elem)-1] == '.' { return fmt.Errorf("trailing dot in path element") } charOK := pathOK if fileName { charOK = fileNameOK } for _, r := range elem { if !charOK(r) { return fmt.Errorf("invalid char %q", r) } } // Windows disallows a bunch of path elements, sadly. // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file short := elem if i := strings.Index(short, "."); i >= 0 { short = short[:i] } for _, bad := range badWindowsNames { if strings.EqualFold(bad, short) { return fmt.Errorf("disallowed path element %q", elem) } } return nil } // CheckFilePath checks whether a slash-separated file path is valid. func CheckFilePath(path string) error { if err := checkPath(path, true); err != nil { return fmt.Errorf("malformed file path %q: %v", path, err) } return nil } // badWindowsNames are the reserved file path elements on Windows. // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file var badWindowsNames = []string{ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", } // SplitPathVersion returns prefix and major version such that prefix+pathMajor == path // and version is either empty or "/vN" for N >= 2. // As a special case, gopkg.in paths are recognized directly; // they require ".vN" instead of "/vN", and for all N, not just N >= 2. func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { if strings.HasPrefix(path, "gopkg.in/") { return splitGopkgIn(path) } i := len(path) dot := false for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { if path[i-1] == '.' { dot = true } i-- } if i <= 1 || path[i-1] != 'v' || path[i-2] != '/' { return path, "", true } prefix, pathMajor = path[:i-2], path[i-2:] if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { return path, "", false } return prefix, pathMajor, true } // splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { if !strings.HasPrefix(path, "gopkg.in/") { return path, "", false } i := len(path) if strings.HasSuffix(path, "-unstable") { i -= len("-unstable") } for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { i-- } if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { // All gopkg.in paths must end in vN for some N. return path, "", false } prefix, pathMajor = path[:i-2], path[i-2:] if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { return path, "", false } return prefix, pathMajor, true } // MatchPathMajor reports whether the semantic version v // matches the path major version pathMajor. func MatchPathMajor(v, pathMajor string) bool { if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { pathMajor = strings.TrimSuffix(pathMajor, "-unstable") } if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. return true } m := semver.Major(v) if pathMajor == "" { return m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" } return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:] } // CanonicalVersion returns the canonical form of the version string v. // It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". func CanonicalVersion(v string) string { cv := semver.Canonical(v) if semver.Build(v) == "+incompatible" { cv += "+incompatible" } return cv } // Sort sorts the list by Path, breaking ties by comparing Versions. func Sort(list []Version) { sort.Slice(list, func(i, j int) bool { mi := list[i] mj := list[j] if mi.Path != mj.Path { return mi.Path < mj.Path } // To help go.sum formatting, allow version/file. // Compare semver prefix by semver rules, // file by string order. vi := mi.Version vj := mj.Version var fi, fj string if k := strings.Index(vi, "/"); k >= 0 { vi, fi = vi[:k], vi[k:] } if k := strings.Index(vj, "/"); k >= 0 { vj, fj = vj[:k], vj[k:] } if vi != vj { return semver.Compare(vi, vj) < 0 } return fi < fj }) } // Safe encodings // // Module paths appear as substrings of file system paths // (in the download cache) and of web server URLs in the proxy protocol. // In general we cannot rely on file systems to be case-sensitive, // nor can we rely on web servers, since they read from file systems. // That is, we cannot rely on the file system to keep rsc.io/QUOTE // and rsc.io/quote separate. Windows and macOS don't. // Instead, we must never require two different casings of a file path. // Because we want the download cache to match the proxy protocol, // and because we want the proxy protocol to be possible to serve // from a tree of static files (which might be stored on a case-insensitive // file system), the proxy protocol must never require two different casings // of a URL path either. // // One possibility would be to make the safe encoding be the lowercase // hexadecimal encoding of the actual path bytes. This would avoid ever // needing different casings of a file path, but it would be fairly illegible // to most programmers when those paths appeared in the file system // (including in file paths in compiler errors and stack traces) // in web server logs, and so on. Instead, we want a safe encoding that // leaves most paths unaltered. // // The safe encoding is this: // replace every uppercase letter with an exclamation mark // followed by the letter's lowercase equivalent. // // For example, // github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. // github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy // github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. // // Import paths that avoid upper-case letters are left unchanged. // Note that because import paths are ASCII-only and avoid various // problematic punctuation (like : < and >), the safe encoding is also ASCII-only // and avoids the same problematic punctuation. // // Import paths have never allowed exclamation marks, so there is no // need to define how to encode a literal !. // // Although paths are disallowed from using Unicode (see pathOK above), // the eventual plan is to allow Unicode letters as well, to assume that // file systems and URLs are Unicode-safe (storing UTF-8), and apply // the !-for-uppercase convention. Note however that not all runes that // are different but case-fold equivalent are an upper/lower pair. // For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) // are considered to case-fold to each other. When we do add Unicode // letters, we must not assume that upper/lower are the only case-equivalent pairs. // Perhaps the Kelvin symbol would be disallowed entirely, for example. // Or perhaps it would encode as "!!k", or perhaps as "(212A)". // // Also, it would be nice to allow Unicode marks as well as letters, // but marks include combining marks, and then we must deal not // only with case folding but also normalization: both U+00E9 ('é') // and U+0065 U+0301 ('e' followed by combining acute accent) // look the same on the page and are treated by some file systems // as the same path. If we do allow Unicode marks in paths, there // must be some kind of normalization to allow only one canonical // encoding of any character used in an import path. // EncodePath returns the safe encoding of the given module path. // It fails if the module path is invalid. func EncodePath(path string) (encoding string, err error) { if err := CheckPath(path); err != nil { return "", err } return encodeString(path) } // EncodeVersion returns the safe encoding of the given module version. // Versions are allowed to be in non-semver form but must be valid file names // and not contain exclamation marks. func EncodeVersion(v string) (encoding string, err error) { if err := checkElem(v, true); err != nil || strings.Contains(v, "!") { return "", fmt.Errorf("disallowed version string %q", v) } return encodeString(v) } func encodeString(s string) (encoding string, err error) { haveUpper := false for _, r := range s { if r == '!' || r >= utf8.RuneSelf { // This should be disallowed by CheckPath, but diagnose anyway. // The correctness of the encoding loop below depends on it. return "", fmt.Errorf("internal error: inconsistency in EncodePath") } if 'A' <= r && r <= 'Z' { haveUpper = true } } if !haveUpper { return s, nil } var buf []byte for _, r := range s { if 'A' <= r && r <= 'Z' { buf = append(buf, '!', byte(r+'a'-'A')) } else { buf = append(buf, byte(r)) } } return string(buf), nil } // DecodePath returns the module path of the given safe encoding. // It fails if the encoding is invalid or encodes an invalid path. func DecodePath(encoding string) (path string, err error) { path, ok := decodeString(encoding) if !ok { return "", fmt.Errorf("invalid module path encoding %q", encoding) } if err := CheckPath(path); err != nil { return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err) } return path, nil } // DecodeVersion returns the version string for the given safe encoding. // It fails if the encoding is invalid or encodes an invalid version. // Versions are allowed to be in non-semver form but must be valid file names // and not contain exclamation marks. func DecodeVersion(encoding string) (v string, err error) { v, ok := decodeString(encoding) if !ok { return "", fmt.Errorf("invalid version encoding %q", encoding) } if err := checkElem(v, true); err != nil { return "", fmt.Errorf("disallowed version string %q", v) } return v, nil } func decodeString(encoding string) (string, bool) { var buf []byte bang := false for _, r := range encoding { if r >= utf8.RuneSelf { return "", false } if bang { bang = false if r < 'a' || 'z' < r { return "", false } buf = append(buf, byte(r+'A'-'a')) continue } if r == '!' { bang = true continue } if 'A' <= r && r <= 'Z' { return "", false } buf = append(buf, byte(r)) } if bang { return "", false } return string(buf), true } go-internal-1.5.2/module/module_test.go000066400000000000000000000225571360713250200201100ustar00rootroot00000000000000// Copyright 2018 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 module import "testing" var checkTests = []struct { path string version string ok bool }{ {"rsc.io/quote", "0.1.0", false}, {"rsc io/quote", "v1.0.0", false}, {"github.com/go-yaml/yaml", "v0.8.0", true}, {"github.com/go-yaml/yaml", "v1.0.0", true}, {"github.com/go-yaml/yaml", "v2.0.0", false}, {"github.com/go-yaml/yaml", "v2.1.5", false}, {"github.com/go-yaml/yaml", "v3.0.0", false}, {"github.com/go-yaml/yaml/v2", "v1.0.0", false}, {"github.com/go-yaml/yaml/v2", "v2.0.0", true}, {"github.com/go-yaml/yaml/v2", "v2.1.5", true}, {"github.com/go-yaml/yaml/v2", "v3.0.0", false}, {"gopkg.in/yaml.v0", "v0.8.0", true}, {"gopkg.in/yaml.v0", "v1.0.0", false}, {"gopkg.in/yaml.v0", "v2.0.0", false}, {"gopkg.in/yaml.v0", "v2.1.5", false}, {"gopkg.in/yaml.v0", "v3.0.0", false}, {"gopkg.in/yaml.v1", "v0.8.0", false}, {"gopkg.in/yaml.v1", "v1.0.0", true}, {"gopkg.in/yaml.v1", "v2.0.0", false}, {"gopkg.in/yaml.v1", "v2.1.5", false}, {"gopkg.in/yaml.v1", "v3.0.0", false}, // For gopkg.in, .v1 means v1 only (not v0). // But early versions of vgo still generated v0 pseudo-versions for it. // Even though now we'd generate those as v1 pseudo-versions, // we accept the old pseudo-versions to avoid breaking existing go.mod files. // For example gopkg.in/yaml.v2@v2.2.1's go.mod requires check.v1 at a v0 pseudo-version. {"gopkg.in/check.v1", "v0.0.0", false}, {"gopkg.in/check.v1", "v0.0.0-20160102150405-abcdef123456", true}, {"gopkg.in/yaml.v2", "v1.0.0", false}, {"gopkg.in/yaml.v2", "v2.0.0", true}, {"gopkg.in/yaml.v2", "v2.1.5", true}, {"gopkg.in/yaml.v2", "v3.0.0", false}, {"rsc.io/quote", "v17.0.0", false}, {"rsc.io/quote", "v17.0.0+incompatible", true}, } func TestCheck(t *testing.T) { for _, tt := range checkTests { err := Check(tt.path, tt.version) if tt.ok && err != nil { t.Errorf("Check(%q, %q) = %v, wanted nil error", tt.path, tt.version, err) } else if !tt.ok && err == nil { t.Errorf("Check(%q, %q) succeeded, wanted error", tt.path, tt.version) } } } var checkPathTests = []struct { path string ok bool importOK bool fileOK bool }{ {"x.y/z", true, true, true}, {"x.y", true, true, true}, {"", false, false, false}, {"x.y/\xFFz", false, false, false}, {"/x.y/z", false, false, false}, {"x./z", false, false, false}, {".x/z", false, false, true}, {"-x/z", false, true, true}, {"x..y/z", false, false, false}, {"x.y/z/../../w", false, false, false}, {"x.y//z", false, false, false}, {"x.y/z//w", false, false, false}, {"x.y/z/", false, false, false}, {"x.y/z/v0", false, true, true}, {"x.y/z/v1", false, true, true}, {"x.y/z/v2", true, true, true}, {"x.y/z/v2.0", false, true, true}, {"X.y/z", false, true, true}, {"!x.y/z", false, false, true}, {"_x.y/z", false, true, true}, {"x.y!/z", false, false, true}, {"x.y\"/z", false, false, false}, {"x.y#/z", false, false, true}, {"x.y$/z", false, false, true}, {"x.y%/z", false, false, true}, {"x.y&/z", false, false, true}, {"x.y'/z", false, false, false}, {"x.y(/z", false, false, true}, {"x.y)/z", false, false, true}, {"x.y*/z", false, false, false}, {"x.y+/z", false, true, true}, {"x.y,/z", false, false, true}, {"x.y-/z", true, true, true}, {"x.y./zt", false, false, false}, {"x.y:/z", false, false, false}, {"x.y;/z", false, false, false}, {"x.y/z", false, false, false}, {"x.y?/z", false, false, false}, {"x.y@/z", false, false, true}, {"x.y[/z", false, false, true}, {"x.y\\/z", false, false, false}, {"x.y]/z", false, false, true}, {"x.y^/z", false, false, true}, {"x.y_/z", false, true, true}, {"x.y`/z", false, false, false}, {"x.y{/z", false, false, true}, {"x.y}/z", false, false, true}, {"x.y~/z", false, true, true}, {"x.y/z!", false, false, true}, {"x.y/z\"", false, false, false}, {"x.y/z#", false, false, true}, {"x.y/z$", false, false, true}, {"x.y/z%", false, false, true}, {"x.y/z&", false, false, true}, {"x.y/z'", false, false, false}, {"x.y/z(", false, false, true}, {"x.y/z)", false, false, true}, {"x.y/z*", false, false, false}, {"x.y/z+", true, true, true}, {"x.y/z,", false, false, true}, {"x.y/z-", true, true, true}, {"x.y/z.t", true, true, true}, {"x.y/z/t", true, true, true}, {"x.y/z:", false, false, false}, {"x.y/z;", false, false, false}, {"x.y/z<", false, false, false}, {"x.y/z=", false, false, true}, {"x.y/z>", false, false, false}, {"x.y/z?", false, false, false}, {"x.y/z@", false, false, true}, {"x.y/z[", false, false, true}, {"x.y/z\\", false, false, false}, {"x.y/z]", false, false, true}, {"x.y/z^", false, false, true}, {"x.y/z_", true, true, true}, {"x.y/z`", false, false, false}, {"x.y/z{", false, false, true}, {"x.y/z}", false, false, true}, {"x.y/z~", true, true, true}, {"x.y/x.foo", true, true, true}, {"x.y/aux.foo", false, false, false}, {"x.y/prn", false, false, false}, {"x.y/prn2", true, true, true}, {"x.y/com", true, true, true}, {"x.y/com1", false, false, false}, {"x.y/com1.txt", false, false, false}, {"x.y/calm1", true, true, true}, {"github.com/!123/logrus", false, false, true}, // TODO: CL 41822 allowed Unicode letters in old "go get" // without due consideration of the implications, and only on github.com (!). // For now, we disallow non-ASCII characters in module mode, // in both module paths and general import paths, // until we can get the implications right. // When we do, we'll enable them everywhere, not just for GitHub. {"github.com/user/unicode/испытание", false, false, true}, {"../x", false, false, false}, {"./y", false, false, false}, {"x:y", false, false, false}, {`\temp\foo`, false, false, false}, {".gitignore", false, false, true}, {".github/ISSUE_TEMPLATE", false, false, true}, {"x☺y", false, false, false}, } func TestCheckPath(t *testing.T) { for _, tt := range checkPathTests { err := CheckPath(tt.path) if tt.ok && err != nil { t.Errorf("CheckPath(%q) = %v, wanted nil error", tt.path, err) } else if !tt.ok && err == nil { t.Errorf("CheckPath(%q) succeeded, wanted error", tt.path) } err = CheckImportPath(tt.path) if tt.importOK && err != nil { t.Errorf("CheckImportPath(%q) = %v, wanted nil error", tt.path, err) } else if !tt.importOK && err == nil { t.Errorf("CheckImportPath(%q) succeeded, wanted error", tt.path) } err = CheckFilePath(tt.path) if tt.fileOK && err != nil { t.Errorf("CheckFilePath(%q) = %v, wanted nil error", tt.path, err) } else if !tt.fileOK && err == nil { t.Errorf("CheckFilePath(%q) succeeded, wanted error", tt.path) } } } var splitPathVersionTests = []struct { pathPrefix string version string }{ {"x.y/z", ""}, {"x.y/z", "/v2"}, {"x.y/z", "/v3"}, {"gopkg.in/yaml", ".v0"}, {"gopkg.in/yaml", ".v1"}, {"gopkg.in/yaml", ".v2"}, {"gopkg.in/yaml", ".v3"}, } func TestSplitPathVersion(t *testing.T) { for _, tt := range splitPathVersionTests { pathPrefix, version, ok := SplitPathVersion(tt.pathPrefix + tt.version) if pathPrefix != tt.pathPrefix || version != tt.version || !ok { t.Errorf("SplitPathVersion(%q) = %q, %q, %v, want %q, %q, true", tt.pathPrefix+tt.version, pathPrefix, version, ok, tt.pathPrefix, tt.version) } } for _, tt := range checkPathTests { pathPrefix, version, ok := SplitPathVersion(tt.path) if pathPrefix+version != tt.path { t.Errorf("SplitPathVersion(%q) = %q, %q, %v, doesn't add to input", tt.path, pathPrefix, version, ok) } } } var encodeTests = []struct { path string enc string // empty means same as path }{ {path: "ascii.com/abcdefghijklmnopqrstuvwxyz.-+/~_0123456789"}, {path: "github.com/GoogleCloudPlatform/omega", enc: "github.com/!google!cloud!platform/omega"}, } func TestEncodePath(t *testing.T) { // Check invalid paths. for _, tt := range checkPathTests { if !tt.ok { _, err := EncodePath(tt.path) if err == nil { t.Errorf("EncodePath(%q): succeeded, want error (invalid path)", tt.path) } } } // Check encodings. for _, tt := range encodeTests { enc, err := EncodePath(tt.path) if err != nil { t.Errorf("EncodePath(%q): unexpected error: %v", tt.path, err) continue } want := tt.enc if want == "" { want = tt.path } if enc != want { t.Errorf("EncodePath(%q) = %q, want %q", tt.path, enc, want) } } } var badDecode = []string{ "github.com/GoogleCloudPlatform/omega", "github.com/!google!cloud!platform!/omega", "github.com/!0google!cloud!platform/omega", "github.com/!_google!cloud!platform/omega", "github.com/!!google!cloud!platform/omega", "", } func TestDecodePath(t *testing.T) { // Check invalid decodings. for _, bad := range badDecode { _, err := DecodePath(bad) if err == nil { t.Errorf("DecodePath(%q): succeeded, want error (invalid decoding)", bad) } } // Check invalid paths (or maybe decodings). for _, tt := range checkPathTests { if !tt.ok { path, err := DecodePath(tt.path) if err == nil { t.Errorf("DecodePath(%q) = %q, want error (invalid path)", tt.path, path) } } } // Check encodings. for _, tt := range encodeTests { enc := tt.enc if enc == "" { enc = tt.path } path, err := DecodePath(enc) if err != nil { t.Errorf("DecodePath(%q): unexpected error: %v", enc, err) continue } if path != tt.path { t.Errorf("DecodePath(%q) = %q, want %q", enc, path, tt.path) } } } go-internal-1.5.2/par/000077500000000000000000000000001360713250200145175ustar00rootroot00000000000000go-internal-1.5.2/par/work.go000066400000000000000000000072431360713250200160360ustar00rootroot00000000000000// Copyright 2018 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 par implements parallel execution helpers. package par import ( "math/rand" "sync" "sync/atomic" ) // Work manages a set of work items to be executed in parallel, at most once each. // The items in the set must all be valid map keys. type Work struct { f func(interface{}) // function to run for each item running int // total number of runners mu sync.Mutex added map[interface{}]bool // items added to set todo []interface{} // items yet to be run wait sync.Cond // wait when todo is empty waiting int // number of runners waiting for todo } func (w *Work) init() { if w.added == nil { w.added = make(map[interface{}]bool) } } // Add adds item to the work set, if it hasn't already been added. func (w *Work) Add(item interface{}) { w.mu.Lock() w.init() if !w.added[item] { w.added[item] = true w.todo = append(w.todo, item) if w.waiting > 0 { w.wait.Signal() } } w.mu.Unlock() } // Do runs f in parallel on items from the work set, // with at most n invocations of f running at a time. // It returns when everything added to the work set has been processed. // At least one item should have been added to the work set // before calling Do (or else Do returns immediately), // but it is allowed for f(item) to add new items to the set. // Do should only be used once on a given Work. func (w *Work) Do(n int, f func(item interface{})) { if n < 1 { panic("par.Work.Do: n < 1") } if w.running >= 1 { panic("par.Work.Do: already called Do") } w.running = n w.f = f w.wait.L = &w.mu for i := 0; i < n-1; i++ { go w.runner() } w.runner() } // runner executes work in w until both nothing is left to do // and all the runners are waiting for work. // (Then all the runners return.) func (w *Work) runner() { for { // Wait for something to do. w.mu.Lock() for len(w.todo) == 0 { w.waiting++ if w.waiting == w.running { // All done. w.wait.Broadcast() w.mu.Unlock() return } w.wait.Wait() w.waiting-- } // Pick something to do at random, // to eliminate pathological contention // in case items added at about the same time // are most likely to contend. i := rand.Intn(len(w.todo)) item := w.todo[i] w.todo[i] = w.todo[len(w.todo)-1] w.todo = w.todo[:len(w.todo)-1] w.mu.Unlock() w.f(item) } } // Cache runs an action once per key and caches the result. type Cache struct { m sync.Map } type cacheEntry struct { done uint32 mu sync.Mutex result interface{} } // Do calls the function f if and only if Do is being called for the first time with this key. // No call to Do with a given key returns until the one call to f returns. // Do returns the value returned by the one call to f. func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { entryIface, ok := c.m.Load(key) if !ok { entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry)) } e := entryIface.(*cacheEntry) if atomic.LoadUint32(&e.done) == 0 { e.mu.Lock() if atomic.LoadUint32(&e.done) == 0 { e.result = f() atomic.StoreUint32(&e.done, 1) } e.mu.Unlock() } return e.result } // Get returns the cached result associated with key. // It returns nil if there is no such result. // If the result for key is being computed, Get does not wait for the computation to finish. func (c *Cache) Get(key interface{}) interface{} { entryIface, ok := c.m.Load(key) if !ok { return nil } e := entryIface.(*cacheEntry) if atomic.LoadUint32(&e.done) == 0 { return nil } return e.result } go-internal-1.5.2/par/work_test.go000066400000000000000000000030261360713250200170700ustar00rootroot00000000000000// Copyright 2018 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 par import ( "sync/atomic" "testing" "time" ) func TestWork(t *testing.T) { var w Work const N = 10000 n := int32(0) w.Add(N) w.Do(100, func(x interface{}) { atomic.AddInt32(&n, 1) i := x.(int) if i >= 2 { w.Add(i - 1) w.Add(i - 2) } w.Add(i >> 1) w.Add((i >> 1) ^ 1) }) if n != N+1 { t.Fatalf("ran %d items, expected %d", n, N+1) } } func TestWorkParallel(t *testing.T) { for tries := 0; tries < 10; tries++ { var w Work const N = 100 for i := 0; i < N; i++ { w.Add(i) } start := time.Now() var n int32 w.Do(N, func(x interface{}) { time.Sleep(1 * time.Millisecond) atomic.AddInt32(&n, +1) }) if n != N { t.Fatalf("par.Work.Do did not do all the work") } if time.Since(start) < N/2*time.Millisecond { return } } t.Fatalf("par.Work.Do does not seem to be parallel") } func TestCache(t *testing.T) { var cache Cache n := 1 v := cache.Do(1, func() interface{} { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) did not run f") } v = cache.Do(1, func() interface{} { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) ran f again!") } v = cache.Do(2, func() interface{} { n++; return n }) if v != 3 { t.Fatalf("cache.Do(2) did not run f") } v = cache.Do(1, func() interface{} { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) did not returned saved value from original cache.Do(1)") } } go-internal-1.5.2/renameio/000077500000000000000000000000001360713250200155345ustar00rootroot00000000000000go-internal-1.5.2/renameio/renameio.go000066400000000000000000000035421360713250200176660ustar00rootroot00000000000000// Copyright 2018 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 renameio writes files atomically by renaming temporary files. package renameio import ( "bytes" "io" "io/ioutil" "os" "path/filepath" ) const patternSuffix = "*.tmp" // Pattern returns a glob pattern that matches the unrenamed temporary files // created when writing to filename. func Pattern(filename string) string { return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix) } // WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary // file in the same directory as filename, then renames it atomically to the // final name. // // That ensures that the final location, if it exists, is always a complete file. func WriteFile(filename string, data []byte) (err error) { return WriteToFile(filename, bytes.NewReader(data)) } // WriteToFile is a variant of WriteFile that accepts the data as an io.Reader // instead of a slice. func WriteToFile(filename string, data io.Reader) (err error) { f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+patternSuffix) if err != nil { return err } defer func() { // Only call os.Remove on f.Name() if we failed to rename it: otherwise, // some other process may have created a new file with the same name after // that. if err != nil { f.Close() os.Remove(f.Name()) } }() if _, err := io.Copy(f, data); err != nil { return err } // Sync the file before renaming it: otherwise, after a crash the reader may // observe a 0-length file instead of the actual contents. // See https://golang.org/issue/22397#issuecomment-380831736. if err := f.Sync(); err != nil { return err } if err := f.Close(); err != nil { return err } return os.Rename(f.Name(), filename) } go-internal-1.5.2/semver/000077500000000000000000000000001360713250200152365ustar00rootroot00000000000000go-internal-1.5.2/semver/semver.go000066400000000000000000000210121360713250200170620ustar00rootroot00000000000000// Copyright 2018 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 semver implements comparison of semantic version strings. // In this package, semantic version strings must begin with a leading "v", // as in "v1.0.0". // // The general form of a semantic version string accepted by this package is // // vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] // // where square brackets indicate optional parts of the syntax; // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers // using only alphanumeric characters and hyphens; and // all-numeric PRERELEASE identifiers must not have leading zeros. // // This package follows Semantic Versioning 2.0.0 (see semver.org) // with two exceptions. First, it requires the "v" prefix. Second, it recognizes // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. package semver // parsed returns the parsed form of a semantic version string. type parsed struct { major string minor string patch string short string prerelease string build string err string } // IsValid reports whether v is a valid semantic version string. func IsValid(v string) bool { _, ok := parse(v) return ok } // Canonical returns the canonical formatting of the semantic version v. // It fills in any missing .MINOR or .PATCH and discards build metadata. // Two semantic versions compare equal only if their canonical formattings // are identical strings. // The canonical invalid semantic version is the empty string. func Canonical(v string) string { p, ok := parse(v) if !ok { return "" } if p.build != "" { return v[:len(v)-len(p.build)] } if p.short != "" { return v + p.short } return v } // Major returns the major version prefix of the semantic version v. // For example, Major("v2.1.0") == "v2". // If v is an invalid semantic version string, Major returns the empty string. func Major(v string) string { pv, ok := parse(v) if !ok { return "" } return v[:1+len(pv.major)] } // MajorMinor returns the major.minor version prefix of the semantic version v. // For example, MajorMinor("v2.1.0") == "v2.1". // If v is an invalid semantic version string, MajorMinor returns the empty string. func MajorMinor(v string) string { pv, ok := parse(v) if !ok { return "" } i := 1 + len(pv.major) if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { return v[:j] } return v[:i] + "." + pv.minor } // Prerelease returns the prerelease suffix of the semantic version v. // For example, Prerelease("v2.1.0-pre+meta") == "-pre". // If v is an invalid semantic version string, Prerelease returns the empty string. func Prerelease(v string) string { pv, ok := parse(v) if !ok { return "" } return pv.prerelease } // Build returns the build suffix of the semantic version v. // For example, Build("v2.1.0+meta") == "+meta". // If v is an invalid semantic version string, Build returns the empty string. func Build(v string) string { pv, ok := parse(v) if !ok { return "" } return pv.build } // Compare returns an integer comparing two versions according to // according to semantic version precedence. // The result will be 0 if v == w, -1 if v < w, or +1 if v > w. // // An invalid semantic version string is considered less than a valid one. // All invalid semantic version strings compare equal to each other. func Compare(v, w string) int { pv, ok1 := parse(v) pw, ok2 := parse(w) if !ok1 && !ok2 { return 0 } if !ok1 { return -1 } if !ok2 { return +1 } if c := compareInt(pv.major, pw.major); c != 0 { return c } if c := compareInt(pv.minor, pw.minor); c != 0 { return c } if c := compareInt(pv.patch, pw.patch); c != 0 { return c } return comparePrerelease(pv.prerelease, pw.prerelease) } // Max canonicalizes its arguments and then returns the version string // that compares greater. func Max(v, w string) string { v = Canonical(v) w = Canonical(w) if Compare(v, w) > 0 { return v } return w } func parse(v string) (p parsed, ok bool) { if v == "" || v[0] != 'v' { p.err = "missing v prefix" return } p.major, v, ok = parseInt(v[1:]) if !ok { p.err = "bad major version" return } if v == "" { p.minor = "0" p.patch = "0" p.short = ".0.0" return } if v[0] != '.' { p.err = "bad minor prefix" ok = false return } p.minor, v, ok = parseInt(v[1:]) if !ok { p.err = "bad minor version" return } if v == "" { p.patch = "0" p.short = ".0" return } if v[0] != '.' { p.err = "bad patch prefix" ok = false return } p.patch, v, ok = parseInt(v[1:]) if !ok { p.err = "bad patch version" return } if len(v) > 0 && v[0] == '-' { p.prerelease, v, ok = parsePrerelease(v) if !ok { p.err = "bad prerelease" return } } if len(v) > 0 && v[0] == '+' { p.build, v, ok = parseBuild(v) if !ok { p.err = "bad build" return } } if v != "" { p.err = "junk on end" ok = false return } ok = true return } func parseInt(v string) (t, rest string, ok bool) { if v == "" { return } if v[0] < '0' || '9' < v[0] { return } i := 1 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } if v[0] == '0' && i != 1 { return } return v[:i], v[i:], true } func parsePrerelease(v string) (t, rest string, ok bool) { // "A pre-release version MAY be denoted by appending a hyphen and // a series of dot separated identifiers immediately following the patch version. // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." if v == "" || v[0] != '-' { return } i := 1 start := 1 for i < len(v) && v[i] != '+' { if !isIdentChar(v[i]) && v[i] != '.' { return } if v[i] == '.' { if start == i || isBadNum(v[start:i]) { return } start = i + 1 } i++ } if start == i || isBadNum(v[start:i]) { return } return v[:i], v[i:], true } func parseBuild(v string) (t, rest string, ok bool) { if v == "" || v[0] != '+' { return } i := 1 start := 1 for i < len(v) { if !isIdentChar(v[i]) { return } if v[i] == '.' { if start == i { return } start = i + 1 } i++ } if start == i { return } return v[:i], v[i:], true } func isIdentChar(c byte) bool { return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' } func isBadNum(v string) bool { i := 0 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } return i == len(v) && i > 1 && v[0] == '0' } func isNum(v string) bool { i := 0 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } return i == len(v) } func compareInt(x, y string) int { if x == y { return 0 } if len(x) < len(y) { return -1 } if len(x) > len(y) { return +1 } if x < y { return -1 } else { return +1 } } func comparePrerelease(x, y string) int { // "When major, minor, and patch are equal, a pre-release version has // lower precedence than a normal version. // Example: 1.0.0-alpha < 1.0.0. // Precedence for two pre-release versions with the same major, minor, // and patch version MUST be determined by comparing each dot separated // identifier from left to right until a difference is found as follows: // identifiers consisting of only digits are compared numerically and // identifiers with letters or hyphens are compared lexically in ASCII // sort order. Numeric identifiers always have lower precedence than // non-numeric identifiers. A larger set of pre-release fields has a // higher precedence than a smaller set, if all of the preceding // identifiers are equal. // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." if x == y { return 0 } if x == "" { return +1 } if y == "" { return -1 } for x != "" && y != "" { x = x[1:] // skip - or . y = y[1:] // skip - or . var dx, dy string dx, x = nextIdent(x) dy, y = nextIdent(y) if dx != dy { ix := isNum(dx) iy := isNum(dy) if ix != iy { if ix { return -1 } else { return +1 } } if ix { if len(dx) < len(dy) { return -1 } if len(dx) > len(dy) { return +1 } } if dx < dy { return -1 } else { return +1 } } } if x == "" { return -1 } else { return +1 } } func nextIdent(x string) (dx, rest string) { i := 0 for i < len(x) && x[i] != '.' { i++ } return x[:i], x[i:] } go-internal-1.5.2/semver/semver_test.go000066400000000000000000000072121360713250200201270ustar00rootroot00000000000000// Copyright 2018 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 semver import ( "strings" "testing" ) var tests = []struct { in string out string }{ {"bad", ""}, {"v1-alpha.beta.gamma", ""}, {"v1-pre", ""}, {"v1+meta", ""}, {"v1-pre+meta", ""}, {"v1.2-pre", ""}, {"v1.2+meta", ""}, {"v1.2-pre+meta", ""}, {"v1.0.0-alpha", "v1.0.0-alpha"}, {"v1.0.0-alpha.1", "v1.0.0-alpha.1"}, {"v1.0.0-alpha.beta", "v1.0.0-alpha.beta"}, {"v1.0.0-beta", "v1.0.0-beta"}, {"v1.0.0-beta.2", "v1.0.0-beta.2"}, {"v1.0.0-beta.11", "v1.0.0-beta.11"}, {"v1.0.0-rc.1", "v1.0.0-rc.1"}, {"v1", "v1.0.0"}, {"v1.0", "v1.0.0"}, {"v1.0.0", "v1.0.0"}, {"v1.2", "v1.2.0"}, {"v1.2.0", "v1.2.0"}, {"v1.2.3-456", "v1.2.3-456"}, {"v1.2.3-456.789", "v1.2.3-456.789"}, {"v1.2.3-456-789", "v1.2.3-456-789"}, {"v1.2.3-456a", "v1.2.3-456a"}, {"v1.2.3-pre", "v1.2.3-pre"}, {"v1.2.3-pre+meta", "v1.2.3-pre"}, {"v1.2.3-pre.1", "v1.2.3-pre.1"}, {"v1.2.3-zzz", "v1.2.3-zzz"}, {"v1.2.3", "v1.2.3"}, {"v1.2.3+meta", "v1.2.3"}, {"v1.2.3+meta-pre", "v1.2.3"}, } func TestIsValid(t *testing.T) { for _, tt := range tests { ok := IsValid(tt.in) if ok != (tt.out != "") { t.Errorf("IsValid(%q) = %v, want %v", tt.in, ok, !ok) } } } func TestCanonical(t *testing.T) { for _, tt := range tests { out := Canonical(tt.in) if out != tt.out { t.Errorf("Canonical(%q) = %q, want %q", tt.in, out, tt.out) } } } func TestMajor(t *testing.T) { for _, tt := range tests { out := Major(tt.in) want := "" if i := strings.Index(tt.out, "."); i >= 0 { want = tt.out[:i] } if out != want { t.Errorf("Major(%q) = %q, want %q", tt.in, out, want) } } } func TestMajorMinor(t *testing.T) { for _, tt := range tests { out := MajorMinor(tt.in) var want string if tt.out != "" { want = tt.in if i := strings.Index(want, "+"); i >= 0 { want = want[:i] } if i := strings.Index(want, "-"); i >= 0 { want = want[:i] } switch strings.Count(want, ".") { case 0: want += ".0" case 1: // ok case 2: want = want[:strings.LastIndex(want, ".")] } } if out != want { t.Errorf("MajorMinor(%q) = %q, want %q", tt.in, out, want) } } } func TestPrerelease(t *testing.T) { for _, tt := range tests { pre := Prerelease(tt.in) var want string if tt.out != "" { if i := strings.Index(tt.out, "-"); i >= 0 { want = tt.out[i:] } } if pre != want { t.Errorf("Prerelease(%q) = %q, want %q", tt.in, pre, want) } } } func TestBuild(t *testing.T) { for _, tt := range tests { build := Build(tt.in) var want string if tt.out != "" { if i := strings.Index(tt.in, "+"); i >= 0 { want = tt.in[i:] } } if build != want { t.Errorf("Build(%q) = %q, want %q", tt.in, build, want) } } } func TestCompare(t *testing.T) { for i, ti := range tests { for j, tj := range tests { cmp := Compare(ti.in, tj.in) var want int if ti.out == tj.out { want = 0 } else if i < j { want = -1 } else { want = +1 } if cmp != want { t.Errorf("Compare(%q, %q) = %d, want %d", ti.in, tj.in, cmp, want) } } } } func TestMax(t *testing.T) { for i, ti := range tests { for j, tj := range tests { max := Max(ti.in, tj.in) want := Canonical(ti.in) if i < j { want = Canonical(tj.in) } if max != want { t.Errorf("Max(%q, %q) = %q, want %q", ti.in, tj.in, max, want) } } } } var ( v1 = "v1.0.0+metadata-dash" v2 = "v1.0.0+metadata-dash1" ) func BenchmarkCompare(b *testing.B) { for i := 0; i < b.N; i++ { if Compare(v1, v2) != 0 { b.Fatalf("bad compare") } } } go-internal-1.5.2/testenv/000077500000000000000000000000001360713250200154255ustar00rootroot00000000000000go-internal-1.5.2/testenv/testenv.go000066400000000000000000000161461360713250200174540ustar00rootroot00000000000000// Copyright 2015 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 testenv provides information about what functionality // is available in different testing environments run by the Go team. // // It is an internal package because these details are specific // to the Go team's test setup (on build.golang.org) and not // fundamental to tests in general. package testenv import ( "errors" "flag" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "testing" ) // Builder reports the name of the builder running this test // (for example, "linux-amd64" or "windows-386-gce"). // If the test is not running on the build infrastructure, // Builder returns the empty string. func Builder() string { return os.Getenv("GO_BUILDER_NAME") } // HasGoBuild reports whether the current system can build programs with ``go build'' // and then run them with os.StartProcess or exec.Command. func HasGoBuild() bool { if os.Getenv("GO_GCFLAGS") != "" { // It's too much work to require every caller of the go command // to pass along "-gcflags="+os.Getenv("GO_GCFLAGS"). // For now, if $GO_GCFLAGS is set, report that we simply can't // run go build. return false } switch runtime.GOOS { case "android", "nacl", "js": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { return false } } return true } // MustHaveGoBuild checks that the current system can build programs with ``go build'' // and then run them with os.StartProcess or exec.Command. // If not, MustHaveGoBuild calls t.Skip with an explanation. func MustHaveGoBuild(t testing.TB) { if os.Getenv("GO_GCFLAGS") != "" { t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS") } if !HasGoBuild() { t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) } } // HasGoRun reports whether the current system can run programs with ``go run.'' func HasGoRun() bool { // For now, having go run and having go build are the same. return HasGoBuild() } // MustHaveGoRun checks that the current system can run programs with ``go run.'' // If not, MustHaveGoRun calls t.Skip with an explanation. func MustHaveGoRun(t testing.TB) { if !HasGoRun() { t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH) } } // GoToolPath reports the path to the Go tool. // It is a convenience wrapper around GoTool. // If the tool is unavailable GoToolPath calls t.Skip. // If the tool should be available and isn't, GoToolPath calls t.Fatal. func GoToolPath(t testing.TB) string { MustHaveGoBuild(t) path, err := GoTool() if err != nil { t.Fatal(err) } return path } // GoTool reports the path to the Go tool. func GoTool() (string, error) { if !HasGoBuild() { return "", errors.New("platform cannot run go tool") } var exeSuffix string if runtime.GOOS == "windows" { exeSuffix = ".exe" } path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) if _, err := os.Stat(path); err == nil { return path, nil } goBin, err := exec.LookPath("go" + exeSuffix) if err != nil { return "", errors.New("cannot find go tool: " + err.Error()) } return goBin, nil } // HasExec reports whether the current system can start new processes // using os.StartProcess or (more commonly) exec.Command. func HasExec() bool { switch runtime.GOOS { case "nacl", "js": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { return false } } return true } // HasSrc reports whether the entire source tree is available under GOROOT. func HasSrc() bool { switch runtime.GOOS { case "nacl": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { return false } } return true } // MustHaveExec checks that the current system can start new processes // using os.StartProcess or (more commonly) exec.Command. // If not, MustHaveExec calls t.Skip with an explanation. func MustHaveExec(t testing.TB) { if !HasExec() { t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH) } } // HasExternalNetwork reports whether the current system can use // external (non-localhost) networks. func HasExternalNetwork() bool { return !testing.Short() && runtime.GOOS != "nacl" && runtime.GOOS != "js" } // MustHaveExternalNetwork checks that the current system can use // external (non-localhost) networks. // If not, MustHaveExternalNetwork calls t.Skip with an explanation. func MustHaveExternalNetwork(t testing.TB) { if runtime.GOOS == "nacl" || runtime.GOOS == "js" { t.Skipf("skipping test: no external network on %s", runtime.GOOS) } if testing.Short() { t.Skipf("skipping test: no external network in -short mode") } } var haveCGO bool // HasCGO reports whether the current system can use cgo. func HasCGO() bool { return haveCGO } // MustHaveCGO calls t.Skip if cgo is not available. func MustHaveCGO(t testing.TB) { if !haveCGO { t.Skipf("skipping test: no cgo") } } // HasSymlink reports whether the current system can use os.Symlink. func HasSymlink() bool { ok, _ := hasSymlink() return ok } // MustHaveSymlink reports whether the current system can use os.Symlink. // If not, MustHaveSymlink calls t.Skip with an explanation. func MustHaveSymlink(t testing.TB) { ok, reason := hasSymlink() if !ok { t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) } } // HasLink reports whether the current system can use os.Link. func HasLink() bool { // From Android release M (Marshmallow), hard linking files is blocked // and an attempt to call link() on a file will return EACCES. // - https://code.google.com/p/android-developer-preview/issues/detail?id=3150 return runtime.GOOS != "plan9" && runtime.GOOS != "android" } // MustHaveLink reports whether the current system can use os.Link. // If not, MustHaveLink calls t.Skip with an explanation. func MustHaveLink(t testing.TB) { if !HasLink() { t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH) } } var flaky = flag.Bool("flaky", false, "run known-flaky tests too") func SkipFlaky(t testing.TB, issue int) { t.Helper() if !*flaky { t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) } } func SkipFlakyNet(t testing.TB) { t.Helper() if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { t.Skip("skipping test on builder known to have frequent network failures") } } // CleanCmdEnv will fill cmd.Env with the environment, excluding certain // variables that could modify the behavior of the Go tools such as // GODEBUG and GOTRACEBACK. func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd { if cmd.Env != nil { panic("environment already set") } for _, env := range os.Environ() { // Exclude GODEBUG from the environment to prevent its output // from breaking tests that are trying to parse other command output. if strings.HasPrefix(env, "GODEBUG=") { continue } // Exclude GOTRACEBACK for the same reason. if strings.HasPrefix(env, "GOTRACEBACK=") { continue } cmd.Env = append(cmd.Env, env) } return cmd } go-internal-1.5.2/testenv/testenv_cgo.go000066400000000000000000000003401360713250200202710ustar00rootroot00000000000000// Copyright 2017 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. // +build cgo package testenv func init() { haveCGO = true } go-internal-1.5.2/testenv/testenv_notwin.go000066400000000000000000000005541360713250200210460ustar00rootroot00000000000000// Copyright 2016 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. // +build !windows package testenv import ( "runtime" ) func hasSymlink() (ok bool, reason string) { switch runtime.GOOS { case "android", "nacl", "plan9": return false, "" } return true, "" } go-internal-1.5.2/testenv/testenv_windows.go000066400000000000000000000020461360713250200212200ustar00rootroot00000000000000// Copyright 2016 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 testenv import ( "io/ioutil" "os" "path/filepath" "sync" "syscall" ) var symlinkOnce sync.Once var winSymlinkErr error func initWinHasSymlink() { tmpdir, err := ioutil.TempDir("", "symtest") if err != nil { panic("failed to create temp directory: " + err.Error()) } defer os.RemoveAll(tmpdir) err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) if err != nil { err = err.(*os.LinkError).Err switch err { case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: winSymlinkErr = err } } } func hasSymlink() (ok bool, reason string) { symlinkOnce.Do(initWinHasSymlink) switch winSymlinkErr { case nil: return true, "" case syscall.EWINDOWS: return false, ": symlinks are not supported on your version of Windows" case syscall.ERROR_PRIVILEGE_NOT_HELD: return false, ": you don't have enough privileges to create symlinks" } return false, "" } go-internal-1.5.2/testscript/000077500000000000000000000000001360713250200161415ustar00rootroot00000000000000go-internal-1.5.2/testscript/cmd.go000066400000000000000000000310561360713250200172400ustar00rootroot00000000000000// Copyright 2018 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 testscript import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" "github.com/rogpeppe/go-internal/internal/textutil" "github.com/rogpeppe/go-internal/txtar" ) // scriptCmds are the script command implementations. // Keep list and the implementations below sorted by name. // // NOTE: If you make changes here, update doc.go. // var scriptCmds = map[string]func(*TestScript, bool, []string){ "cd": (*TestScript).cmdCd, "chmod": (*TestScript).cmdChmod, "cmp": (*TestScript).cmdCmp, "cmpenv": (*TestScript).cmdCmpenv, "cp": (*TestScript).cmdCp, "env": (*TestScript).cmdEnv, "exec": (*TestScript).cmdExec, "exists": (*TestScript).cmdExists, "grep": (*TestScript).cmdGrep, "mkdir": (*TestScript).cmdMkdir, "rm": (*TestScript).cmdRm, "unquote": (*TestScript).cmdUnquote, "skip": (*TestScript).cmdSkip, "stdin": (*TestScript).cmdStdin, "stderr": (*TestScript).cmdStderr, "stdout": (*TestScript).cmdStdout, "stop": (*TestScript).cmdStop, "symlink": (*TestScript).cmdSymlink, "wait": (*TestScript).cmdWait, } // cd changes to a different directory. func (ts *TestScript) cmdCd(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! cd") } if len(args) != 1 { ts.Fatalf("usage: cd dir") } dir := args[0] if !filepath.IsAbs(dir) { dir = filepath.Join(ts.cd, dir) } info, err := os.Stat(dir) if os.IsNotExist(err) { ts.Fatalf("directory %s does not exist", dir) } ts.Check(err) if !info.IsDir() { ts.Fatalf("%s is not a directory", dir) } ts.cd = dir ts.Logf("%s\n", ts.cd) } func (ts *TestScript) cmdChmod(neg bool, args []string) { if len(args) != 2 { ts.Fatalf("usage: chmod mode file") } mode, err := strconv.ParseInt(args[0], 8, 32) if err != nil { ts.Fatalf("bad file mode %q: %v", args[0], err) } if mode > 0777 { ts.Fatalf("unsupported file mode %.3o", mode) } err = os.Chmod(ts.MkAbs(args[1]), os.FileMode(mode)) if neg { if err == nil { ts.Fatalf("unexpected chmod success") } return } if err != nil { ts.Fatalf("unexpected chmod failure: %v", err) } } // cmp compares two files. func (ts *TestScript) cmdCmp(neg bool, args []string) { if neg { // It would be strange to say "this file can have any content except this precise byte sequence". ts.Fatalf("unsupported: ! cmp") } if len(args) != 2 { ts.Fatalf("usage: cmp file1 file2") } ts.doCmdCmp(args, false) } // cmpenv compares two files with environment variable substitution. func (ts *TestScript) cmdCmpenv(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! cmpenv") } if len(args) != 2 { ts.Fatalf("usage: cmpenv file1 file2") } ts.doCmdCmp(args, true) } func (ts *TestScript) doCmdCmp(args []string, env bool) { name1, name2 := args[0], args[1] text1 := ts.ReadFile(name1) absName2 := ts.MkAbs(name2) data, err := ioutil.ReadFile(absName2) ts.Check(err) text2 := string(data) if env { text2 = ts.expand(text2) } if text1 == text2 { return } if ts.params.UpdateScripts && !env && (args[0] == "stdout" || args[0] == "stderr") { if scriptFile, ok := ts.scriptFiles[absName2]; ok { ts.scriptUpdates[scriptFile] = text1 return } // The file being compared against isn't in the txtar archive, so don't // update the script. } ts.Logf("[diff -%s +%s]\n%s\n", name1, name2, textutil.Diff(text1, text2)) ts.Fatalf("%s and %s differ", name1, name2) } // cp copies files, maybe eventually directories. func (ts *TestScript) cmdCp(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! cp") } if len(args) < 2 { ts.Fatalf("usage: cp src... dst") } dst := ts.MkAbs(args[len(args)-1]) info, err := os.Stat(dst) dstDir := err == nil && info.IsDir() if len(args) > 2 && !dstDir { ts.Fatalf("cp: destination %s is not a directory", dst) } for _, arg := range args[:len(args)-1] { var ( src string data []byte mode os.FileMode ) switch arg { case "stdout": src = arg data = []byte(ts.stdout) mode = 0666 case "stderr": src = arg data = []byte(ts.stderr) mode = 0666 default: src = ts.MkAbs(arg) info, err := os.Stat(src) ts.Check(err) mode = info.Mode() & 0777 data, err = ioutil.ReadFile(src) ts.Check(err) } targ := dst if dstDir { targ = filepath.Join(dst, filepath.Base(src)) } ts.Check(ioutil.WriteFile(targ, data, mode)) } } // env displays or adds to the environment. func (ts *TestScript) cmdEnv(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! env") } if len(args) == 0 { printed := make(map[string]bool) // env list can have duplicates; only print effective value (from envMap) once for _, kv := range ts.env { k := envvarname(kv[:strings.Index(kv, "=")]) if !printed[k] { printed[k] = true ts.Logf("%s=%s\n", k, ts.envMap[k]) } } return } for _, env := range args { i := strings.Index(env, "=") if i < 0 { // Display value instead of setting it. ts.Logf("%s=%s\n", env, ts.Getenv(env)) continue } ts.Setenv(env[:i], env[i+1:]) } } // exec runs the given command. func (ts *TestScript) cmdExec(neg bool, args []string) { if len(args) < 1 || (len(args) == 1 && args[0] == "&") { ts.Fatalf("usage: exec program [args...] [&]") } var err error if len(args) > 0 && args[len(args)-1] == "&" { var cmd *exec.Cmd cmd, err = ts.execBackground(args[0], args[1:len(args)-1]...) if err == nil { wait := make(chan struct{}) go func() { ctxWait(ts.ctxt, cmd) close(wait) }() ts.background = append(ts.background, backgroundCmd{cmd, wait, neg}) } ts.stdout, ts.stderr = "", "" } else { ts.stdout, ts.stderr, err = ts.exec(args[0], args[1:]...) if ts.stdout != "" { fmt.Fprintf(&ts.log, "[stdout]\n%s", ts.stdout) } if ts.stderr != "" { fmt.Fprintf(&ts.log, "[stderr]\n%s", ts.stderr) } if err == nil && neg { ts.Fatalf("unexpected command success") } } if err != nil { fmt.Fprintf(&ts.log, "[%v]\n", err) if ts.ctxt.Err() != nil { ts.Fatalf("test timed out while running command") } else if !neg { ts.Fatalf("unexpected command failure") } } } // exists checks that the list of files exists. func (ts *TestScript) cmdExists(neg bool, args []string) { var readonly bool if len(args) > 0 && args[0] == "-readonly" { readonly = true args = args[1:] } if len(args) == 0 { ts.Fatalf("usage: exists [-readonly] file...") } for _, file := range args { file = ts.MkAbs(file) info, err := os.Stat(file) if err == nil && neg { what := "file" if info.IsDir() { what = "directory" } ts.Fatalf("%s %s unexpectedly exists", what, file) } if err != nil && !neg { ts.Fatalf("%s does not exist", file) } if err == nil && !neg && readonly && info.Mode()&0222 != 0 { ts.Fatalf("%s exists but is writable", file) } } } // mkdir creates directories. func (ts *TestScript) cmdMkdir(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mkdir") } if len(args) < 1 { ts.Fatalf("usage: mkdir dir...") } for _, arg := range args { ts.Check(os.MkdirAll(ts.MkAbs(arg), 0777)) } } // unquote unquotes files. func (ts *TestScript) cmdUnquote(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! unquote") } for _, arg := range args { file := ts.MkAbs(arg) data, err := ioutil.ReadFile(file) ts.Check(err) data, err = txtar.Unquote(data) ts.Check(err) err = ioutil.WriteFile(file, data, 0666) ts.Check(err) } } // rm removes files or directories. func (ts *TestScript) cmdRm(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! rm") } if len(args) < 1 { ts.Fatalf("usage: rm file...") } for _, arg := range args { file := ts.MkAbs(arg) removeAll(file) // does chmod and then attempts rm ts.Check(os.RemoveAll(file)) // report error } } // skip marks the test skipped. func (ts *TestScript) cmdSkip(neg bool, args []string) { if len(args) > 1 { ts.Fatalf("usage: skip [msg]") } if neg { ts.Fatalf("unsupported: ! skip") } // Before we mark the test as skipped, shut down any background processes and // make sure they have returned the correct status. for _, bg := range ts.background { interruptProcess(bg.cmd.Process) } ts.cmdWait(false, nil) if len(args) == 1 { ts.t.Skip(args[0]) } ts.t.Skip() } func (ts *TestScript) cmdStdin(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! stdin") } if len(args) != 1 { ts.Fatalf("usage: stdin filename") } data, err := ioutil.ReadFile(ts.MkAbs(args[0])) ts.Check(err) ts.stdin = string(data) } // stdout checks that the last go command standard output matches a regexp. func (ts *TestScript) cmdStdout(neg bool, args []string) { scriptMatch(ts, neg, args, ts.stdout, "stdout") } // stderr checks that the last go command standard output matches a regexp. func (ts *TestScript) cmdStderr(neg bool, args []string) { scriptMatch(ts, neg, args, ts.stderr, "stderr") } // grep checks that file content matches a regexp. // Like stdout/stderr and unlike Unix grep, it accepts Go regexp syntax. func (ts *TestScript) cmdGrep(neg bool, args []string) { scriptMatch(ts, neg, args, "", "grep") } // stop stops execution of the test (marking it passed). func (ts *TestScript) cmdStop(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! stop") } if len(args) > 1 { ts.Fatalf("usage: stop [msg]") } if len(args) == 1 { ts.Logf("stop: %s\n", args[0]) } else { ts.Logf("stop\n") } ts.stopped = true } // symlink creates a symbolic link. func (ts *TestScript) cmdSymlink(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! symlink") } if len(args) != 3 || args[1] != "->" { ts.Fatalf("usage: symlink file -> target") } // Note that the link target args[2] is not interpreted with MkAbs: // it will be interpreted relative to the directory file is in. ts.Check(os.Symlink(args[2], ts.MkAbs(args[0]))) } // Tait waits for background commands to exit, setting stderr and stdout to their result. func (ts *TestScript) cmdWait(neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! wait") } if len(args) > 0 { ts.Fatalf("usage: wait") } var stdouts, stderrs []string for _, bg := range ts.background { <-bg.wait args := append([]string{filepath.Base(bg.cmd.Args[0])}, bg.cmd.Args[1:]...) fmt.Fprintf(&ts.log, "[background] %s: %v\n", strings.Join(args, " "), bg.cmd.ProcessState) cmdStdout := bg.cmd.Stdout.(*strings.Builder).String() if cmdStdout != "" { fmt.Fprintf(&ts.log, "[stdout]\n%s", cmdStdout) stdouts = append(stdouts, cmdStdout) } cmdStderr := bg.cmd.Stderr.(*strings.Builder).String() if cmdStderr != "" { fmt.Fprintf(&ts.log, "[stderr]\n%s", cmdStderr) stderrs = append(stderrs, cmdStderr) } if bg.cmd.ProcessState.Success() { if bg.neg { ts.Fatalf("unexpected command success") } } else { if ts.ctxt.Err() != nil { ts.Fatalf("test timed out while running command") } else if !bg.neg { ts.Fatalf("unexpected command failure") } } } ts.stdout = strings.Join(stdouts, "") ts.stderr = strings.Join(stderrs, "") ts.background = nil } // scriptMatch implements both stdout and stderr. func scriptMatch(ts *TestScript, neg bool, args []string, text, name string) { n := 0 if len(args) >= 1 && strings.HasPrefix(args[0], "-count=") { if neg { ts.Fatalf("cannot use -count= with negated match") } var err error n, err = strconv.Atoi(args[0][len("-count="):]) if err != nil { ts.Fatalf("bad -count=: %v", err) } if n < 1 { ts.Fatalf("bad -count=: must be at least 1") } args = args[1:] } extraUsage := "" want := 1 if name == "grep" { extraUsage = " file" want = 2 } if len(args) != want { ts.Fatalf("usage: %s [-count=N] 'pattern'%s", name, extraUsage) } pattern := args[0] re, err := regexp.Compile(`(?m)` + pattern) ts.Check(err) isGrep := name == "grep" if isGrep { name = args[1] // for error messages data, err := ioutil.ReadFile(ts.MkAbs(args[1])) ts.Check(err) text = string(data) } if neg { if re.MatchString(text) { if isGrep { ts.Logf("[%s]\n%s\n", name, text) } ts.Fatalf("unexpected match for %#q found in %s: %s", pattern, name, re.FindString(text)) } } else { if !re.MatchString(text) { if isGrep { ts.Logf("[%s]\n%s\n", name, text) } ts.Fatalf("no match for %#q found in %s", pattern, name) } if n > 0 { count := len(re.FindAllString(text, -1)) if count != n { if isGrep { ts.Logf("[%s]\n%s\n", name, text) } ts.Fatalf("have %d matches for %#q, want %d", count, pattern, n) } } } } go-internal-1.5.2/testscript/cover.go000066400000000000000000000145011360713250200176070ustar00rootroot00000000000000// Copyright 2018 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 testscript import ( "bufio" "fmt" "io" "log" "os" "regexp" "strconv" "strings" "sync/atomic" "testing" "gopkg.in/errgo.v2/fmt/errors" ) // mergeCoverProfile merges the coverage information in f into // cover. It assumes that the coverage information in f is // always produced from the same binary for every call. func mergeCoverProfile(cover *testing.Cover, r io.Reader) error { scanner, err := newProfileScanner(r) if err != nil { return errors.Wrap(err) } if scanner.Mode() != testing.CoverMode() { return errors.Newf("unexpected coverage mode in subcommand") } if cover.Mode == "" { cover.Mode = scanner.Mode() } isCount := cover.Mode == "count" if cover.Counters == nil { cover.Counters = make(map[string][]uint32) cover.Blocks = make(map[string][]testing.CoverBlock) } // Note that we rely on the fact that the coverage is written // out file-by-file, with all blocks for a file in sequence. var ( filename string blockId uint32 counters []uint32 blocks []testing.CoverBlock ) flush := func() { if len(counters) > 0 { cover.Counters[filename] = counters cover.Blocks[filename] = blocks } } for scanner.Scan() { block := scanner.Block() if scanner.Filename() != filename { flush() filename = scanner.Filename() counters = cover.Counters[filename] blocks = cover.Blocks[filename] blockId = 0 } else { blockId++ } if int(blockId) >= len(counters) { counters = append(counters, block.Count) blocks = append(blocks, block.CoverBlock) continue } // TODO check that block.CoverBlock == blocks[blockId] ? if isCount { counters[blockId] += block.Count } else { counters[blockId] |= block.Count } } flush() if scanner.Err() != nil { return errors.Notef(err, nil, "error scanning profile") } return nil } var ( coverChan chan *os.File coverDone chan testing.Cover ) func goCoverProfileMerge() { if coverChan != nil { panic("RunMain called twice!") } coverChan = make(chan *os.File) coverDone = make(chan testing.Cover) go mergeCoverProfiles() } func mergeCoverProfiles() { var cover testing.Cover for f := range coverChan { if err := mergeCoverProfile(&cover, f); err != nil { log.Printf("cannot merge coverage profile from %v: %v", f.Name(), err) } f.Close() os.Remove(f.Name()) } coverDone <- cover } func finalizeCoverProfile() error { cprof := coverProfile() if cprof == "" { return nil } f, err := os.Open(cprof) if err != nil { return errors.Notef(err, nil, "cannot open existing cover profile") } coverChan <- f close(coverChan) cover := <-coverDone f, err = os.Create(cprof) if err != nil { return errors.Notef(err, nil, "cannot create cover profile") } defer f.Close() w := bufio.NewWriter(f) if err := writeCoverProfile1(w, cover); err != nil { return errors.Wrap(err) } if err := w.Flush(); err != nil { return errors.Wrap(err) } if err := f.Close(); err != nil { return errors.Wrap(err) } return nil } func writeCoverProfile1(w io.Writer, cover testing.Cover) error { fmt.Fprintf(w, "mode: %s\n", cover.Mode) var active, total int64 var count uint32 for name, counts := range cover.Counters { blocks := cover.Blocks[name] for i := range counts { stmts := int64(blocks[i].Stmts) total += stmts count = atomic.LoadUint32(&counts[i]) // For -mode=atomic. if count > 0 { active += stmts } _, err := fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", name, blocks[i].Line0, blocks[i].Col0, blocks[i].Line1, blocks[i].Col1, stmts, count, ) if err != nil { return errors.Wrap(err) } } } if total == 0 { total = 1 } fmt.Printf("total coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages) return nil } type profileScanner struct { mode string err error scanner *bufio.Scanner filename string block coverBlock } type coverBlock struct { testing.CoverBlock Count uint32 } var profileLineRe = regexp.MustCompile(`^(.+):([0-9]+)\.([0-9]+),([0-9]+)\.([0-9]+) ([0-9]+) ([0-9]+)$`) func toInt(s string) int { i, err := strconv.Atoi(s) if err != nil { panic(err) } return i } func newProfileScanner(r io.Reader) (*profileScanner, error) { s := &profileScanner{ scanner: bufio.NewScanner(r), } // First line is "mode: foo", where foo is "set", "count", or "atomic". // Rest of file is in the format // encoding/base64/base64.go:34.44,37.40 3 1 // where the fields are: name.go:line.column,line.column numberOfStatements count if !s.scanner.Scan() { return nil, errors.Newf("no lines found in profile: %v", s.Err()) } line := s.scanner.Text() mode := strings.TrimPrefix(line, "mode: ") if len(mode) == len(line) { return nil, fmt.Errorf("bad mode line %q", line) } s.mode = mode return s, nil } // Mode returns the profile's coverage mode (one of "atomic", "count: // or "set"). func (s *profileScanner) Mode() string { return s.mode } // Err returns any error encountered when scanning a profile. func (s *profileScanner) Err() error { if s.err == io.EOF { return nil } return s.err } // Block returns the most recently scanned profile block, or the zero // block if Scan has not been called or has returned false. func (s *profileScanner) Block() coverBlock { if s.err == nil { return s.block } return coverBlock{} } // Filename returns the filename of the most recently scanned profile // block, or the empty string if Scan has not been called or has // returned false. func (s *profileScanner) Filename() string { if s.err == nil { return s.filename } return "" } // Scan scans the next line in a coverage profile and reports whether // a line was found. func (s *profileScanner) Scan() bool { if s.err != nil { return false } if !s.scanner.Scan() { s.err = io.EOF return false } m := profileLineRe.FindStringSubmatch(s.scanner.Text()) if m == nil { s.err = errors.Newf("line %q doesn't match expected format %v", m, profileLineRe) return false } s.filename = m[1] s.block = coverBlock{ CoverBlock: testing.CoverBlock{ Line0: uint32(toInt(m[2])), Col0: uint16(toInt(m[3])), Line1: uint32(toInt(m[4])), Col1: uint16(toInt(m[5])), Stmts: uint16(toInt(m[6])), }, Count: uint32(toInt(m[7])), } return true } go-internal-1.5.2/testscript/doc.go000066400000000000000000000265551360713250200172520ustar00rootroot00000000000000// Copyright 2018 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 testscript provides support for defining filesystem-based tests by creating scripts in a directory. To invoke the tests, call testscript.Run. For example: func TestFoo(t *testing.T) { testscript.Run(t, testscript.Params{ Dir: "testdata", }) } A testscript directory holds test scripts *.txt run during 'go test'. Each script defines a subtest; the exact set of allowable commands in a script are defined by the parameters passed to the Run function. To run a specific script foo.txt go test cmd/go -run=TestName/^foo$ where TestName is the name of the test that Run is called from. To define an executable command (or several) that can be run as part of the script, call RunMain with the functions that implement the command's functionality. The command functions will be called in a separate process, so are free to mutate global variables without polluting the top level test binary. func TestMain(m *testing.M) { os.Exit(testscript.RunMain(m, map[string] func() int{ "testscript": testscriptMain, })) } In general script files should have short names: a few words, not whole sentences. The first word should be the general category of behavior being tested, often the name of a subcommand to be tested or a concept (vendor, pattern). Each script is a text archive (go doc github.com/rogpeppe/go-internal/txtar). The script begins with an actual command script to run followed by the content of zero or more supporting files to create in the script's temporary file system before it starts executing. As an example: # hello world exec cat hello.text stdout 'hello world\n' ! stderr . -- hello.text -- hello world Each script runs in a fresh temporary work directory tree, available to scripts as $WORK. Scripts also have access to these other environment variables: HOME=/no-home PATH= TMPDIR=$WORK/tmp devnull= goversion= The environment variable $exe (lowercase) is an empty string on most systems, ".exe" on Windows. The script's supporting files are unpacked relative to $WORK and then the script begins execution in that directory as well. Thus the example above runs in $WORK with $WORK/hello.txt containing the listed contents. The lines at the top of the script are a sequence of commands to be executed by a small script engine in the testscript package (not the system shell). The script stops and the overall test fails if any particular command fails. Each line is parsed into a sequence of space-separated command words, with environment variable expansion and # marking an end-of-line comment. Adding single quotes around text keeps spaces in that text from being treated as word separators and also disables environment variable expansion. Inside a single-quoted block of text, a repeated single quote indicates a literal single quote, as in: 'Don''t communicate by sharing memory.' A line beginning with # is a comment and conventionally explains what is being done or tested at the start of a new phase in the script. A special form of environment variable syntax can be used to quote regexp metacharacters inside environment variables. The "@R" suffix is special, and indicates that the variable should be quoted. ${VAR@R} The command prefix ! indicates that the command on the rest of the line (typically go or a matching predicate) must fail, not succeed. Only certain commands support this prefix. They are indicated below by [!] in the synopsis. The command prefix [cond] indicates that the command on the rest of the line should only run when the condition is satisfied. The predefined conditions are: - [short] for testing.Short() - [net] for whether the external network can be used - [link] for whether the OS has hard link support - [symlink] for whether the OS has symbolic link support - [exec:prog] for whether prog is available for execution (found by exec.LookPath) A condition can be negated: [!short] means to run the rest of the line when testing.Short() is false. Additional conditions can be added by passing a function to Params.Condition. The predefined commands are: - cd dir Change to the given directory for future commands. - chmod mode file Change the permissions of file or directory to the given octal mode (000 to 777). - cmp file1 file2 Check that the named files have the same content. By convention, file1 is the actual data and file2 the expected data. File1 can be "stdout" or "stderr" to use the standard output or standard error from the most recent exec or wait command. (If the files have differing content, the failure prints a diff.) - cmpenv file1 file2 Like cmp, but environment variables in file2 are substituted before the comparison. For example, $GOOS is replaced by the target GOOS. - cp src... dst Copy the listed files to the target file or existing directory. src can include "stdout" or "stderr" to use the standard output or standard error from the most recent exec or go command. - env [key=value...] With no arguments, print the environment (useful for debugging). Otherwise add the listed key=value pairs to the environment. - [!] exec program [args...] [&] Run the given executable program with the arguments. It must (or must not) succeed. Note that 'exec' does not terminate the script (unlike in Unix shells). If the last token is '&', the program executes in the background. The standard output and standard error of the previous command is cleared, but the output of the background process is buffered — and checking of its exit status is delayed — until the next call to 'wait', 'skip', or 'stop' or the end of the test. At the end of the test, any remaining background processes are terminated using os.Interrupt (if supported) or os.Kill. Standard input can be provided using the stdin command; this will be cleared after exec has been called. - [!] exists [-readonly] file... Each of the listed files or directories must (or must not) exist. If -readonly is given, the files or directories must be unwritable. - [!] grep [-count=N] pattern file The file's content must (or must not) match the regular expression pattern. For positive matches, -count=N specifies an exact number of matches to require. - mkdir path... Create the listed directories, if they do not already exists. - unquote file... Rewrite each file by replacing any leading ">" characters from each line. This enables a file to contain substrings that look like txtar file markers. See also https://godoc.org/github.com/rogpeppe/go-internal/txtar#Unquote - rm file... Remove the listed files or directories. - skip [message] Mark the test skipped, including the message if given. - stdin file Set the standard input for the next exec command to the contents of the given file. - [!] stderr [-count=N] pattern Apply the grep command (see above) to the standard error from the most recent exec or wait command. - [!] stdout [-count=N] pattern Apply the grep command (see above) to the standard output from the most recent exec or wait command. - stop [message] Stop the test early (marking it as passing), including the message if given. - symlink file -> target Create file as a symlink to target. The -> (like in ls -l output) is required. - wait Wait for all 'exec' and 'go' commands started in the background (with the '&' token) to exit, and display success or failure status for them. After a call to wait, the 'stderr' and 'stdout' commands will apply to the concatenation of the corresponding streams of the background commands, in the order in which those commands were started. When TestScript runs a script and the script fails, by default TestScript shows the execution of the most recent phase of the script (since the last # comment) and only shows the # comments for earlier phases. For example, here is a multi-phase script with a bug in it (TODO: make this example less go-command specific): # GOPATH with p1 in d2, p2 in d2 env GOPATH=$WORK/d1${:}$WORK/d2 # build & install p1 env go install -i p1 ! stale p1 ! stale p2 # modify p2 - p1 should appear stale cp $WORK/p2x.go $WORK/d2/src/p2/p2.go stale p1 p2 # build & install p1 again go install -i p11 ! stale p1 ! stale p2 -- $WORK/d1/src/p1/p1.go -- package p1 import "p2" func F() { p2.F() } -- $WORK/d2/src/p2/p2.go -- package p2 func F() {} -- $WORK/p2x.go -- package p2 func F() {} func G() {} The bug is that the final phase installs p11 instead of p1. The test failure looks like: $ go test -run=Script --- FAIL: TestScript (3.75s) --- FAIL: TestScript/install_rebuild_gopath (0.16s) script_test.go:223: # GOPATH with p1 in d2, p2 in d2 (0.000s) # build & install p1 (0.087s) # modify p2 - p1 should appear stale (0.029s) # build & install p1 again (0.022s) > go install -i p11 [stderr] can't load package: package p11: cannot find package "p11" in any of: /Users/rsc/go/src/p11 (from $GOROOT) $WORK/d1/src/p11 (from $GOPATH) $WORK/d2/src/p11 [exit status 1] FAIL: unexpected go command failure script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src FAIL exit status 1 FAIL cmd/go 4.875s $ Note that the commands in earlier phases have been hidden, so that the relevant commands are more easily found, and the elapsed time for a completed phase is shown next to the phase heading. To see the entire execution, use "go test -v", which also adds an initial environment dump to the beginning of the log. Note also that in reported output, the actual name of the per-script temporary directory has been consistently replaced with the literal string $WORK. If Params.TestWork is true, it causes each test to log the name of its $WORK directory and other environment variable settings and also to leave that directory behind when it exits, for manual debugging of failing tests: $ go test -run=Script -work --- FAIL: TestScript (3.75s) --- FAIL: TestScript/install_rebuild_gopath (0.16s) script_test.go:223: WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath GOARCH= GOCACHE=/Users/rsc/Library/Caches/go-build GOOS= GOPATH=$WORK/gopath GOROOT=/Users/rsc/go HOME=/no-home TMPDIR=$WORK/tmp exe= # GOPATH with p1 in d2, p2 in d2 (0.000s) # build & install p1 (0.085s) # modify p2 - p1 should appear stale (0.030s) # build & install p1 again (0.019s) > go install -i p11 [stderr] can't load package: package p11: cannot find package "p11" in any of: /Users/rsc/go/src/p11 (from $GOROOT) $WORK/d1/src/p11 (from $GOPATH) $WORK/d2/src/p11 [exit status 1] FAIL: unexpected go command failure script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src FAIL exit status 1 FAIL cmd/go 4.875s $ $ WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath $ cd $WORK/d1/src/p1 $ cat p1.go package p1 import "p2" func F() { p2.F() } $ */ package testscript go-internal-1.5.2/testscript/envvarname.go000066400000000000000000000001271360713250200206320ustar00rootroot00000000000000// +build !windows package testscript func envvarname(k string) string { return k } go-internal-1.5.2/testscript/envvarname_windows.go000066400000000000000000000001461360713250200224050ustar00rootroot00000000000000package testscript import "strings" func envvarname(k string) string { return strings.ToLower(k) } go-internal-1.5.2/testscript/exe.go000066400000000000000000000140651360713250200172570ustar00rootroot00000000000000// Copyright 2018 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 testscript import ( "flag" "fmt" "io" "log" "os" "sync/atomic" "testing" ) var profileId int32 = 0 // TestingM is implemented by *testing.M. It's defined as an interface // to allow testscript to co-exist with other testing frameworks // that might also wish to call M.Run. type TestingM interface { Run() int } var ignoreMissedCoverage = false // IgnoreMissedCoverage causes any missed coverage information // (for example when a function passed to RunMain // calls os.Exit, for example) to be ignored. // This function should be called before calling RunMain. func IgnoreMissedCoverage() { ignoreMissedCoverage = true } // RunMain should be called within a TestMain function to allow // subcommands to be run in the testscript context. // // The commands map holds the set of command names, each // with an associated run function which should return the // code to pass to os.Exit. It's OK for a command function to // exit itself, but this may result in loss of coverage information. // // When Run is called, these commands will be available as // testscript commands; note that these commands behave like // commands run with the "exec" command: they set stdout // and stderr, and can be run in the background by passing "&" // as a final argument. // // This function returns an exit code to pass to os.Exit, after calling m.Run. func RunMain(m TestingM, commands map[string]func() int) (exitCode int) { goCoverProfileMerge() cmdName := os.Getenv("TESTSCRIPT_COMMAND") if cmdName == "" { defer func() { if err := finalizeCoverProfile(); err != nil { log.Printf("cannot merge cover profiles: %v", err) exitCode = 2 } }() // We're not in a subcommand. for name := range commands { name := name scriptCmds[name] = func(ts *TestScript, neg bool, args []string) { path, err := os.Executable() if err != nil { ts.Fatalf("cannot determine path to test binary: %v", err) } id := atomic.AddInt32(&profileId, 1) - 1 oldEnvLen := len(ts.env) cprof := coverFilename(id) ts.env = append(ts.env, "TESTSCRIPT_COMMAND="+name, "TESTSCRIPT_COVERPROFILE="+cprof, ) ts.cmdExec(neg, append([]string{path}, args...)) ts.env = ts.env[0:oldEnvLen] if cprof == "" { return } f, err := os.Open(cprof) if err != nil { if ignoreMissedCoverage { return } ts.Fatalf("command %s (args %q) failed to generate coverage information", name, args) return } coverChan <- f } } return m.Run() } mainf := commands[cmdName] if mainf == nil { log.Printf("unknown command name %q", cmdName) return 2 } // The command being registered is being invoked, so run it, then exit. os.Args[0] = cmdName cprof := os.Getenv("TESTSCRIPT_COVERPROFILE") if cprof == "" { // No coverage, act as normal. return mainf() } return runCoverSubcommand(cprof, mainf) } // runCoverSubcommand runs the given function, then writes any generated // coverage information to the cprof file. // This is called inside a separately run executable. func runCoverSubcommand(cprof string, mainf func() int) (exitCode int) { // Change the error handling mode to PanicOnError // so that in the common case of calling flag.Parse in main we'll // be able to catch the panic instead of just exiting. flag.CommandLine.Init(flag.CommandLine.Name(), flag.PanicOnError) defer func() { panicErr := recover() if _, ok := panicErr.(error); ok { // The flag package will already have printed this error, assuming, // that is, that the error was created in the flag package. // TODO check the stack to be sure it was actually raised by the flag package. exitCode = 2 panicErr = nil } // Set os.Args so that flag.Parse will tell testing the correct // coverprofile setting. Unfortunately this isn't sufficient because // the testing oackage explicitly avoids calling flag.Parse again // if flag.Parsed returns true, so we the coverprofile value directly // too. os.Args = []string{os.Args[0], "-test.coverprofile=" + cprof} setCoverProfile(cprof) // Suppress the chatty coverage and test report. devNull, err := os.Open(os.DevNull) if err != nil { panic(err) } os.Stdout = devNull os.Stderr = devNull // Run MainStart (recursively, but it we should be ok) with no tests // so that it writes the coverage profile. m := testing.MainStart(nopTestDeps{}, nil, nil, nil) if code := m.Run(); code != 0 && exitCode == 0 { exitCode = code } if _, err := os.Stat(cprof); err != nil { log.Printf("failed to write coverage profile %q", cprof) } if panicErr != nil { // The error didn't originate from the flag package (we know that // flag.PanicOnError causes an error value that implements error), // so carry on panicking. panic(panicErr) } }() return mainf() } func coverFilename(id int32) string { if cprof := coverProfile(); cprof != "" { return fmt.Sprintf("%s_%d", cprof, id) } return "" } func coverProfileFlag() flag.Getter { f := flag.CommandLine.Lookup("test.coverprofile") if f == nil { // We've imported testing so it definitely should be there. panic("cannot find test.coverprofile flag") } return f.Value.(flag.Getter) } func coverProfile() string { return coverProfileFlag().Get().(string) } func setCoverProfile(cprof string) { coverProfileFlag().Set(cprof) } type nopTestDeps struct{} func (nopTestDeps) MatchString(pat, str string) (result bool, err error) { return false, nil } func (nopTestDeps) StartCPUProfile(w io.Writer) error { return nil } func (nopTestDeps) StopCPUProfile() {} func (nopTestDeps) WriteProfileTo(name string, w io.Writer, debug int) error { return nil } func (nopTestDeps) ImportPath() string { return "" } func (nopTestDeps) StartTestLog(w io.Writer) {} func (nopTestDeps) StopTestLog() error { return nil } // Note: WriteHeapProfile is needed for Go 1.10 but not Go 1.11. func (nopTestDeps) WriteHeapProfile(io.Writer) error { // Not needed for Go 1.10. return nil } go-internal-1.5.2/testscript/testdata/000077500000000000000000000000001360713250200177525ustar00rootroot00000000000000go-internal-1.5.2/testscript/testdata/cmpenv.txt000066400000000000000000000000731360713250200220030ustar00rootroot00000000000000env $=$ cmpenv file1 file2 -- file1 -- $i -- file2 -- $$i go-internal-1.5.2/testscript/testdata/command.txt000066400000000000000000000000751360713250200221330ustar00rootroot00000000000000printargs a b 'c d' stdout '\["printargs" "a" "b" "c d"\]\n' go-internal-1.5.2/testscript/testdata/commandstatus.txt000066400000000000000000000000371360713250200233750ustar00rootroot00000000000000! status 1 ! status 2 status 0 go-internal-1.5.2/testscript/testdata/cpstdout.txt000066400000000000000000000001641360713250200223610ustar00rootroot00000000000000[!exec:cat] stop # hello world exec cat hello.text cp stdout got cmp got hello.text -- hello.text -- hello world go-internal-1.5.2/testscript/testdata/defer.txt000066400000000000000000000000361360713250200215770ustar00rootroot00000000000000testdefer testdefer testdefer go-internal-1.5.2/testscript/testdata/evalsymlink.txt000066400000000000000000000004671360713250200230600ustar00rootroot00000000000000# If ioutil.TempDir returns a sym linked dir (default behaviour in macOS for example) the # matcher will have problems with external programs that uses the real path. # This script tests that $WORK is matched in a consistent way (also see #79). [windows] skip exec pwd stdout ^$WORK$ exec pwd -P stdout ^$WORK$ go-internal-1.5.2/testscript/testdata/exec_path_change.txt000066400000000000000000000011331360713250200237560ustar00rootroot00000000000000# If the PATH environment variable is set in the testscript.Params.Setup phase # or set directly within a script, exec should honour that PATH [!windows] env HOME=$WORK/home [windows] env HOME=$WORK\home [windows] env USERPROFILE=$WORK\home [windows] env LOCALAPPDATA=$WORK\appdata cd go exec go$exe version stdout 'go version' exec go$exe build [!windows] env PATH=$WORK/go${:}$PATH [windows] env PATH=$WORK\go${:}$PATH exec go$exe version stdout 'This is not go' -- go/go.mod -- module example.com/go -- go/main.go -- package main import "fmt" func main() { fmt.Println("This is not go") } go-internal-1.5.2/testscript/testdata/execguard.txt000066400000000000000000000001441360713250200224610ustar00rootroot00000000000000[exec:nosuchcommand] exec nosuchcommand [!exec:cat] stop exec cat foo stdout 'foo\n' -- foo -- foo go-internal-1.5.2/testscript/testdata/exists.txt000066400000000000000000000002741360713250200220350ustar00rootroot00000000000000chmod 444 foo_r exists foo ! exists unfoo # TODO The following line fails but probably should not. # ! exists -readonly foo exists foo_r exists -readonly foo_r -- foo -- foo -- foo_r -- go-internal-1.5.2/testscript/testdata/hello.txt000066400000000000000000000001641360713250200216170ustar00rootroot00000000000000[!exec:cat] stop # hello world exec cat hello.text stdout 'hello world\n' ! stderr . -- hello.text -- hello world go-internal-1.5.2/testscript/testdata/interrupt.txt000066400000000000000000000001361360713250200225470ustar00rootroot00000000000000[windows] skip signalcatcher & waitfile catchsignal interrupt wait stdout 'caught interrupt' go-internal-1.5.2/testscript/testdata/nothing.txt000066400000000000000000000001331360713250200221560ustar00rootroot00000000000000# Intentionally blank file, used to test that -testwork doesn't remove the work directory go-internal-1.5.2/testscript/testdata/nothing/000077500000000000000000000000001360713250200214205ustar00rootroot00000000000000go-internal-1.5.2/testscript/testdata/nothing/nothing.txt000066400000000000000000000003541360713250200236310ustar00rootroot00000000000000# Intentionally empty test script; used to test Params.WorkdirRoot -- README.md -- This file exists so that a test for Params.WorkdirRoot can verify the existence of a file within WorkdirRoot/script-nothing once scripts have finished. go-internal-1.5.2/testscript/testdata/readfile.txt000066400000000000000000000002051360713250200222630ustar00rootroot00000000000000echo stdout stdout testreadfile stdout echo stderr stderr testreadfile stderr testreadfile x/somefile -- x/somefile -- x/somefile go-internal-1.5.2/testscript/testdata/regexpquote.txt000066400000000000000000000001011360713250200230530ustar00rootroot00000000000000env XXX='hello)' grep ^${XXX@R}$ file.txt -- file.txt -- hello) go-internal-1.5.2/testscript/testdata/setenv.txt000066400000000000000000000001221360713250200220120ustar00rootroot00000000000000setSpecialVal exists $SPECIALVAL.txt ensureSpecialVal -- 42.txt -- Douglas Adams go-internal-1.5.2/testscript/testdata/setupfiles.txt000066400000000000000000000002331360713250200226740ustar00rootroot00000000000000# check that the Setup function saw the unarchived files, # including the temp directory that's always created. setup-filenames a b tmp -- a -- -- b/c -- go-internal-1.5.2/testscript/testdata/stdin.txt000066400000000000000000000001541360713250200216340ustar00rootroot00000000000000stdin hello.txt [exec:cat] exec cat stdout hello [exec:cat] exec cat ! stdout hello -- hello.txt -- hello go-internal-1.5.2/testscript/testdata/testscript_update_script.txt000066400000000000000000000004631360713250200256500ustar00rootroot00000000000000unquote scripts/testscript.txt unquote testscript-new.txt testscript-update scripts cmp scripts/testscript.txt testscript-new.txt -- scripts/testscript.txt -- >echo stdout right >cmp stdout expect > >-- expect -- >wrong -- testscript-new.txt -- >echo stdout right >cmp stdout expect > >-- expect -- >right go-internal-1.5.2/testscript/testdata/testscript_update_script_not_in_archive.txt000066400000000000000000000003551360713250200307170ustar00rootroot00000000000000unquote scripts/testscript.txt cp scripts/testscript.txt unchanged ! testscript-update scripts cmp scripts/testscript.txt unchanged -- scripts/testscript.txt -- >echo stdout right >cp file expect >cmp stdout expect > >-- file -- >wrong go-internal-1.5.2/testscript/testdata/testscript_update_script_quote.txt000066400000000000000000000005261360713250200270650ustar00rootroot00000000000000unquote scripts/testscript.txt unquote testscript-new.txt testscript-update scripts cmp scripts/testscript.txt testscript-new.txt -- scripts/testscript.txt -- >echo stdout '-- lookalike --' >cmp stdout expect > >-- expect -- >wrong -- testscript-new.txt -- >echo stdout '-- lookalike --' >cmp stdout expect > >-- expect -- >>-- lookalike -- go-internal-1.5.2/testscript/testdata/testscript_update_script_stderr.txt000066400000000000000000000004631360713250200272330ustar00rootroot00000000000000unquote scripts/testscript.txt unquote testscript-new.txt testscript-update scripts cmp scripts/testscript.txt testscript-new.txt -- scripts/testscript.txt -- >echo stderr right >cmp stderr expect > >-- expect -- >wrong -- testscript-new.txt -- >echo stderr right >cmp stderr expect > >-- expect -- >right go-internal-1.5.2/testscript/testdata/values.txt000066400000000000000000000000141360713250200220050ustar00rootroot00000000000000test-values go-internal-1.5.2/testscript/testdata/wait.txt000066400000000000000000000007201360713250200214560ustar00rootroot00000000000000[!exec:echo] skip [!exec:false] skip exec echo foo stdout foo exec echo foo & exec echo bar & ! exec false & # Starting a background process should clear previous output. ! stdout foo # Wait should set the output to the concatenated outputs of the background # programs, in the order in which they were started. wait stdout 'foo\nbar' # The end of the test should interrupt or kill any remaining background # programs. [!exec:sleep] skip ! exec sleep 86400 & go-internal-1.5.2/testscript/testscript.go000066400000000000000000000573101360713250200207020ustar00rootroot00000000000000// Copyright 2018 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. // Script-driven tests. // See testdata/script/README for an overview. package testscript import ( "bytes" "context" "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "sync/atomic" "testing" "time" "github.com/rogpeppe/go-internal/imports" "github.com/rogpeppe/go-internal/internal/os/execpath" "github.com/rogpeppe/go-internal/par" "github.com/rogpeppe/go-internal/testenv" "github.com/rogpeppe/go-internal/txtar" ) var execCache par.Cache // If -testwork is specified, the test prints the name of the temp directory // and does not remove it when done, so that a programmer can // poke at the test file tree afterward. var testWork = flag.Bool("testwork", false, "") // Env holds the environment to use at the start of a test script invocation. type Env struct { // WorkDir holds the path to the root directory of the // extracted files. WorkDir string // Vars holds the initial set environment variables that will be passed to the // testscript commands. Vars []string // Cd holds the initial current working directory. Cd string // Values holds a map of arbitrary values for use by custom // testscript commands. This enables Setup to pass arbitrary // values (not just strings) through to custom commands. Values map[interface{}]interface{} ts *TestScript } // Value returns a value from Env.Values, or nil if no // value was set by Setup. func (ts *TestScript) Value(key interface{}) interface{} { return ts.values[key] } // Defer arranges for f to be called at the end // of the test. If Defer is called multiple times, the // defers are executed in reverse order (similar // to Go's defer statement) func (e *Env) Defer(f func()) { e.ts.Defer(f) } // Params holds parameters for a call to Run. type Params struct { // Dir holds the name of the directory holding the scripts. // All files in the directory with a .txt suffix will be considered // as test scripts. By default the current directory is used. // Dir is interpreted relative to the current test directory. Dir string // Setup is called, if not nil, to complete any setup required // for a test. The WorkDir and Vars fields will have already // been initialized and all the files extracted into WorkDir, // and Cd will be the same as WorkDir. // The Setup function may modify Vars and Cd as it wishes. Setup func(*Env) error // Condition is called, if not nil, to determine whether a particular // condition is true. It's called only for conditions not in the // standard set, and may be nil. Condition func(cond string) (bool, error) // Cmds holds a map of commands available to the script. // It will only be consulted for commands not part of the standard set. Cmds map[string]func(ts *TestScript, neg bool, args []string) // TestWork specifies that working directories should be // left intact for later inspection. TestWork bool // WorkdirRoot specifies the directory within which scripts' work // directories will be created. Setting WorkdirRoot implies TestWork=true. // If empty, the work directories will be created inside // $GOTMPDIR/go-test-script*, where $GOTMPDIR defaults to os.TempDir(). WorkdirRoot string // IgnoreMissedCoverage specifies that if coverage information // is being generated (with the -test.coverprofile flag) and a subcommand // function passed to RunMain fails to generate coverage information // (for example because the function invoked os.Exit), then the // error will be ignored. IgnoreMissedCoverage bool // UpdateScripts specifies that if a `cmp` command fails and // its first argument is `stdout` or `stderr` and its second argument // refers to a file inside the testscript file, the command will succeed // and the testscript file will be updated to reflect the actual output. // // The content will be quoted with txtar.Quote if needed; // a manual change will be needed if it is not unquoted in the // script. UpdateScripts bool } // RunDir runs the tests in the given directory. All files in dir with a ".txt" // are considered to be test files. func Run(t *testing.T, p Params) { RunT(tshim{t}, p) } // T holds all the methods of the *testing.T type that // are used by testscript. type T interface { Skip(...interface{}) Fatal(...interface{}) Parallel() Log(...interface{}) FailNow() Run(string, func(T)) // Verbose is usually implemented by the testing package // directly rather than on the *testing.T type. Verbose() bool } type tshim struct { *testing.T } func (t tshim) Run(name string, f func(T)) { t.T.Run(name, func(t *testing.T) { f(tshim{t}) }) } func (t tshim) Verbose() bool { return testing.Verbose() } // RunT is like Run but uses an interface type instead of the concrete *testing.T // type to make it possible to use testscript functionality outside of go test. func RunT(t T, p Params) { glob := filepath.Join(p.Dir, "*.txt") files, err := filepath.Glob(glob) if err != nil { t.Fatal(err) } if len(files) == 0 { t.Fatal(fmt.Sprintf("no scripts found matching glob: %v", glob)) } testTempDir := p.WorkdirRoot if testTempDir == "" { testTempDir, err = ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-test-script") if err != nil { t.Fatal(err) } } else { p.TestWork = true } // The temp dir returned by ioutil.TempDir might be a sym linked dir (default // behaviour in macOS). That could mess up matching that includes $WORK if, // for example, an external program outputs resolved paths. Evaluating the // dir here will ensure consistency. testTempDir, err = filepath.EvalSymlinks(testTempDir) if err != nil { t.Fatal(err) } refCount := int32(len(files)) for _, file := range files { file := file name := strings.TrimSuffix(filepath.Base(file), ".txt") t.Run(name, func(t T) { t.Parallel() ts := &TestScript{ t: t, testTempDir: testTempDir, name: name, file: file, params: p, ctxt: context.Background(), deferred: func() {}, scriptFiles: make(map[string]string), scriptUpdates: make(map[string]string), } defer func() { if p.TestWork || *testWork { return } removeAll(ts.workdir) if atomic.AddInt32(&refCount, -1) == 0 { // This is the last subtest to finish. Remove the // parent directory too. os.Remove(testTempDir) } }() ts.run() }) } } // A TestScript holds execution state for a single test script. type TestScript struct { params Params t T testTempDir string workdir string // temporary work dir ($WORK) log bytes.Buffer // test execution log (printed at end of test) mark int // offset of next log truncation cd string // current directory during test execution; initially $WORK/gopath/src name string // short name of test ("foo") file string // full file name ("testdata/script/foo.txt") lineno int // line number currently executing line string // line currently executing env []string // environment list (for os/exec) envMap map[string]string // environment mapping (matches env; on Windows keys are lowercase) values map[interface{}]interface{} // values for custom commands stdin string // standard input to next 'go' command; set by 'stdin' command. stdout string // standard output from last 'go' command; for 'stdout' command stderr string // standard error from last 'go' command; for 'stderr' command stopped bool // test wants to stop early start time.Time // time phase started background []backgroundCmd // backgrounded 'exec' and 'go' commands deferred func() // deferred cleanup actions. archive *txtar.Archive // the testscript being run. scriptFiles map[string]string // files stored in the txtar archive (absolute paths -> path in script) scriptUpdates map[string]string // updates to testscript files via UpdateScripts. ctxt context.Context // per TestScript context } type backgroundCmd struct { cmd *exec.Cmd wait <-chan struct{} neg bool // if true, cmd should fail } // setup sets up the test execution temporary directory and environment. // It returns the comment section of the txtar archive. func (ts *TestScript) setup() string { ts.workdir = filepath.Join(ts.testTempDir, "script-"+ts.name) ts.Check(os.MkdirAll(filepath.Join(ts.workdir, "tmp"), 0777)) env := &Env{ Vars: []string{ "WORK=" + ts.workdir, // must be first for ts.abbrev "PATH=" + os.Getenv("PATH"), homeEnvName() + "=/no-home", tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"), "devnull=" + os.DevNull, ":=" + string(os.PathListSeparator), }, WorkDir: ts.workdir, Values: make(map[interface{}]interface{}), Cd: ts.workdir, ts: ts, } // Must preserve SYSTEMROOT on Windows: https://github.com/golang/go/issues/25513 et al if runtime.GOOS == "windows" { env.Vars = append(env.Vars, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"), "exe=.exe", ) } else { env.Vars = append(env.Vars, "exe=", ) } ts.cd = env.Cd // Unpack archive. a, err := txtar.ParseFile(ts.file) ts.Check(err) ts.archive = a for _, f := range a.Files { name := ts.MkAbs(ts.expand(f.Name)) ts.scriptFiles[name] = f.Name ts.Check(os.MkdirAll(filepath.Dir(name), 0777)) ts.Check(ioutil.WriteFile(name, f.Data, 0666)) } // Run any user-defined setup. if ts.params.Setup != nil { ts.Check(ts.params.Setup(env)) } ts.cd = env.Cd ts.env = env.Vars ts.values = env.Values ts.envMap = make(map[string]string) for _, kv := range ts.env { if i := strings.Index(kv, "="); i >= 0 { ts.envMap[envvarname(kv[:i])] = kv[i+1:] } } return string(a.Comment) } // run runs the test script. func (ts *TestScript) run() { // Truncate log at end of last phase marker, // discarding details of successful phase. rewind := func() { if !ts.t.Verbose() { ts.log.Truncate(ts.mark) } } // Insert elapsed time for phase at end of phase marker markTime := func() { if ts.mark > 0 && !ts.start.IsZero() { afterMark := append([]byte{}, ts.log.Bytes()[ts.mark:]...) ts.log.Truncate(ts.mark - 1) // cut \n and afterMark fmt.Fprintf(&ts.log, " (%.3fs)\n", time.Since(ts.start).Seconds()) ts.log.Write(afterMark) } ts.start = time.Time{} } defer func() { // On a normal exit from the test loop, background processes are cleaned up // before we print PASS. If we return early (e.g., due to a test failure), // don't print anything about the processes that were still running. for _, bg := range ts.background { interruptProcess(bg.cmd.Process) } for _, bg := range ts.background { <-bg.wait } ts.background = nil markTime() // Flush testScript log to testing.T log. ts.t.Log("\n" + ts.abbrev(ts.log.String())) }() defer func() { ts.deferred() }() script := ts.setup() // With -v or -testwork, start log with full environment. if *testWork || ts.t.Verbose() { // Display environment. ts.cmdEnv(false, nil) fmt.Fprintf(&ts.log, "\n") ts.mark = ts.log.Len() } defer ts.applyScriptUpdates() // Run script. // See testdata/script/README for documentation of script form. Script: for script != "" { // Extract next line. ts.lineno++ var line string if i := strings.Index(script, "\n"); i >= 0 { line, script = script[:i], script[i+1:] } else { line, script = script, "" } // # is a comment indicating the start of new phase. if strings.HasPrefix(line, "#") { // If there was a previous phase, it succeeded, // so rewind the log to delete its details (unless -v is in use). // If nothing has happened at all since the mark, // rewinding is a no-op and adding elapsed time // for doing nothing is meaningless, so don't. if ts.log.Len() > ts.mark { rewind() markTime() } // Print phase heading and mark start of phase output. fmt.Fprintf(&ts.log, "%s\n", line) ts.mark = ts.log.Len() ts.start = time.Now() continue } // Parse input line. Ignore blanks entirely. args := ts.parse(line) if len(args) == 0 { continue } // Echo command to log. fmt.Fprintf(&ts.log, "> %s\n", line) // Command prefix [cond] means only run this command if cond is satisfied. for strings.HasPrefix(args[0], "[") && strings.HasSuffix(args[0], "]") { cond := args[0] cond = cond[1 : len(cond)-1] cond = strings.TrimSpace(cond) args = args[1:] if len(args) == 0 { ts.Fatalf("missing command after condition") } want := true if strings.HasPrefix(cond, "!") { want = false cond = strings.TrimSpace(cond[1:]) } ok, err := ts.condition(cond) if err != nil { ts.Fatalf("bad condition %q: %v", cond, err) } if ok != want { // Don't run rest of line. continue Script } } // Command prefix ! means negate the expectations about this command: // go command should fail, match should not be found, etc. neg := false if args[0] == "!" { neg = true args = args[1:] if len(args) == 0 { ts.Fatalf("! on line by itself") } } // Run command. cmd := scriptCmds[args[0]] if cmd == nil { cmd = ts.params.Cmds[args[0]] } if cmd == nil { ts.Fatalf("unknown command %q", args[0]) } cmd(ts, neg, args[1:]) // Command can ask script to stop early. if ts.stopped { // Break instead of returning, so that we check the status of any // background processes and print PASS. break } } for _, bg := range ts.background { interruptProcess(bg.cmd.Process) } ts.cmdWait(false, nil) // Final phase ended. rewind() markTime() if !ts.stopped { fmt.Fprintf(&ts.log, "PASS\n") } } func (ts *TestScript) applyScriptUpdates() { if len(ts.scriptUpdates) == 0 { return } for name, content := range ts.scriptUpdates { found := false for i := range ts.archive.Files { f := &ts.archive.Files[i] if f.Name != name { continue } data := []byte(content) if txtar.NeedsQuote(data) { data1, err := txtar.Quote(data) if err != nil { ts.t.Fatal(fmt.Sprintf("cannot update script file %q: %v", f.Name, err)) continue } data = data1 } f.Data = data found = true } // Sanity check. if !found { panic("script update file not found") } } if err := ioutil.WriteFile(ts.file, txtar.Format(ts.archive), 0666); err != nil { ts.t.Fatal("cannot update script: ", err) } ts.Logf("%s updated", ts.file) } // condition reports whether the given condition is satisfied. func (ts *TestScript) condition(cond string) (bool, error) { switch cond { case "short": return testing.Short(), nil case "net": return testenv.HasExternalNetwork(), nil case "link": return testenv.HasLink(), nil case "symlink": return testenv.HasSymlink(), nil case runtime.GOOS, runtime.GOARCH: return true, nil default: if imports.KnownArch[cond] || imports.KnownOS[cond] { return false, nil } if strings.HasPrefix(cond, "exec:") { prog := cond[len("exec:"):] ok := execCache.Do(prog, func() interface{} { _, err := execpath.Look(prog, ts.Getenv) return err == nil }).(bool) return ok, nil } if ts.params.Condition != nil { return ts.params.Condition(cond) } ts.Fatalf("unknown condition %q", cond) panic("unreachable") } } // Helpers for command implementations. // abbrev abbreviates the actual work directory in the string s to the literal string "$WORK". func (ts *TestScript) abbrev(s string) string { s = strings.Replace(s, ts.workdir, "$WORK", -1) if *testWork || ts.params.TestWork { // Expose actual $WORK value in environment dump on first line of work script, // so that the user can find out what directory -testwork left behind. s = "WORK=" + ts.workdir + "\n" + strings.TrimPrefix(s, "WORK=$WORK\n") } return s } // Defer arranges for f to be called at the end // of the test. If Defer is called multiple times, the // defers are executed in reverse order (similar // to Go's defer statement) func (ts *TestScript) Defer(f func()) { old := ts.deferred ts.deferred = func() { defer old() f() } } // Check calls ts.Fatalf if err != nil. func (ts *TestScript) Check(err error) { if err != nil { ts.Fatalf("%v", err) } } // Logf appends the given formatted message to the test log transcript. func (ts *TestScript) Logf(format string, args ...interface{}) { format = strings.TrimSuffix(format, "\n") fmt.Fprintf(&ts.log, format, args...) ts.log.WriteByte('\n') } // exec runs the given command line (an actual subprocess, not simulated) // in ts.cd with environment ts.env and then returns collected standard output and standard error. func (ts *TestScript) exec(command string, args ...string) (stdout, stderr string, err error) { cmd, err := ts.buildExecCmd(command, args...) if err != nil { return "", "", err } cmd.Dir = ts.cd cmd.Env = append(ts.env, "PWD="+ts.cd) cmd.Stdin = strings.NewReader(ts.stdin) var stdoutBuf, stderrBuf strings.Builder cmd.Stdout = &stdoutBuf cmd.Stderr = &stderrBuf if err = cmd.Start(); err == nil { err = ctxWait(ts.ctxt, cmd) } ts.stdin = "" return stdoutBuf.String(), stderrBuf.String(), err } // execBackground starts the given command line (an actual subprocess, not simulated) // in ts.cd with environment ts.env. func (ts *TestScript) execBackground(command string, args ...string) (*exec.Cmd, error) { cmd, err := ts.buildExecCmd(command, args...) if err != nil { return nil, err } cmd.Dir = ts.cd cmd.Env = append(ts.env, "PWD="+ts.cd) var stdoutBuf, stderrBuf strings.Builder cmd.Stdin = strings.NewReader(ts.stdin) cmd.Stdout = &stdoutBuf cmd.Stderr = &stderrBuf ts.stdin = "" return cmd, cmd.Start() } func (ts *TestScript) buildExecCmd(command string, args ...string) (*exec.Cmd, error) { if filepath.Base(command) == command { if lp, err := execpath.Look(command, ts.Getenv); err != nil { return nil, err } else { command = lp } } return exec.Command(command, args...), nil } // BackgroundCmds returns a slice containing all the commands that have // been started in the background since the most recent wait command, or // the start of the script if wait has not been called. func (ts *TestScript) BackgroundCmds() []*exec.Cmd { cmds := make([]*exec.Cmd, len(ts.background)) for i, b := range ts.background { cmds[i] = b.cmd } return cmds } // ctxWait is like cmd.Wait, but terminates cmd with os.Interrupt if ctx becomes done. // // This differs from exec.CommandContext in that it prefers os.Interrupt over os.Kill. // (See https://golang.org/issue/21135.) func ctxWait(ctx context.Context, cmd *exec.Cmd) error { errc := make(chan error, 1) go func() { errc <- cmd.Wait() }() select { case err := <-errc: return err case <-ctx.Done(): interruptProcess(cmd.Process) return <-errc } } // interruptProcess sends os.Interrupt to p if supported, or os.Kill otherwise. func interruptProcess(p *os.Process) { if err := p.Signal(os.Interrupt); err != nil { // Per https://golang.org/pkg/os/#Signal, “Interrupt is not implemented on // Windows; using it with os.Process.Signal will return an error.” // Fall back to Kill instead. p.Kill() } } // Exec runs the given command and saves its stdout and stderr so // they can be inspected by subsequent script commands. func (ts *TestScript) Exec(command string, args ...string) error { var err error ts.stdout, ts.stderr, err = ts.exec(command, args...) if ts.stdout != "" { ts.Logf("[stdout]\n%s", ts.stdout) } if ts.stderr != "" { ts.Logf("[stderr]\n%s", ts.stderr) } return err } // expand applies environment variable expansion to the string s. func (ts *TestScript) expand(s string) string { return os.Expand(s, func(key string) string { if key1 := strings.TrimSuffix(key, "@R"); len(key1) != len(key) { return regexp.QuoteMeta(ts.Getenv(key1)) } return ts.Getenv(key) }) } // fatalf aborts the test with the given failure message. func (ts *TestScript) Fatalf(format string, args ...interface{}) { fmt.Fprintf(&ts.log, "FAIL: %s:%d: %s\n", ts.file, ts.lineno, fmt.Sprintf(format, args...)) ts.t.FailNow() } // MkAbs interprets file relative to the test script's current directory // and returns the corresponding absolute path. func (ts *TestScript) MkAbs(file string) string { if filepath.IsAbs(file) { return file } return filepath.Join(ts.cd, file) } // ReadFile returns the contents of the file with the // given name, intepreted relative to the test script's // current directory. It interprets "stdout" and "stderr" to // mean the standard output or standard error from // the most recent exec or wait command respectively. // // If the file cannot be read, the script fails. func (ts *TestScript) ReadFile(file string) string { switch file { case "stdout": return ts.stdout case "stderr": return ts.stderr default: file = ts.MkAbs(file) data, err := ioutil.ReadFile(file) ts.Check(err) return string(data) } } // Setenv sets the value of the environment variable named by the key. func (ts *TestScript) Setenv(key, value string) { ts.env = append(ts.env, key+"="+value) ts.envMap[envvarname(key)] = value } // Getenv gets the value of the environment variable named by the key. func (ts *TestScript) Getenv(key string) string { return ts.envMap[envvarname(key)] } // parse parses a single line as a list of space-separated arguments // subject to environment variable expansion (but not resplitting). // Single quotes around text disable splitting and expansion. // To embed a single quote, double it: 'Don''t communicate by sharing memory.' func (ts *TestScript) parse(line string) []string { ts.line = line var ( args []string arg string // text of current arg so far (need to add line[start:i]) start = -1 // if >= 0, position where current arg text chunk starts quoted = false // currently processing quoted text ) for i := 0; ; i++ { if !quoted && (i >= len(line) || line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '#') { // Found arg-separating space. if start >= 0 { arg += ts.expand(line[start:i]) args = append(args, arg) start = -1 arg = "" } if i >= len(line) || line[i] == '#' { break } continue } if i >= len(line) { ts.Fatalf("unterminated quoted argument") } if line[i] == '\'' { if !quoted { // starting a quoted chunk if start >= 0 { arg += ts.expand(line[start:i]) } start = i + 1 quoted = true continue } // 'foo''bar' means foo'bar, like in rc shell and Pascal. if i+1 < len(line) && line[i+1] == '\'' { arg += line[start:i] start = i + 1 i++ // skip over second ' before next iteration continue } // ending a quoted chunk arg += line[start:i] start = i + 1 quoted = false continue } // found character worth saving; make sure we're saving if start < 0 { start = i } } return args } func removeAll(dir string) error { // module cache has 0444 directories; // make them writable in order to remove content. filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return nil // ignore errors walking in file system } if info.IsDir() { os.Chmod(path, 0777) } return nil }) return os.RemoveAll(dir) } func homeEnvName() string { switch runtime.GOOS { case "windows": return "USERPROFILE" case "plan9": return "home" default: return "HOME" } } func tempEnvName() string { switch runtime.GOOS { case "windows": return "TMP" case "plan9": return "TMPDIR" // actually plan 9 doesn't have one at all but this is fine default: return "TMPDIR" } } go-internal-1.5.2/testscript/testscript_test.go000066400000000000000000000204761360713250200217440ustar00rootroot00000000000000// Copyright 2018 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 testscript import ( "errors" "fmt" "io/ioutil" "os" "os/exec" "os/signal" "path/filepath" "reflect" "regexp" "strconv" "strings" "testing" "time" ) func printArgs() int { fmt.Printf("%q\n", os.Args) return 0 } func echo() int { s := strings.Join(os.Args[2:], " ") switch os.Args[1] { case "stdout": fmt.Println(s) case "stderr": fmt.Fprintln(os.Stderr, s) } return 0 } func exitWithStatus() int { n, _ := strconv.Atoi(os.Args[1]) return n } func signalCatcher() int { // Note: won't work under Windows. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) // Create a file so that the test can know that // we will catch the signal. if err := ioutil.WriteFile("catchsignal", nil, 0666); err != nil { fmt.Println(err) return 1 } <-c fmt.Println("caught interrupt") return 0 } func TestMain(m *testing.M) { os.Exit(RunMain(m, map[string]func() int{ "printargs": printArgs, "echo": echo, "status": exitWithStatus, "signalcatcher": signalCatcher, })) } func TestCRLFInput(t *testing.T) { td, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create TempDir: %v", err) } defer func() { os.RemoveAll(td) }() tf := filepath.Join(td, "script.txt") contents := []byte("exists output.txt\r\n-- output.txt --\r\noutput contents") if err := ioutil.WriteFile(tf, contents, 0644); err != nil { t.Fatalf("failed to write to %v: %v", tf, err) } t.Run("_", func(t *testing.T) { Run(t, Params{Dir: td}) }) } func TestScripts(t *testing.T) { // TODO set temp directory. testDeferCount := 0 Run(t, Params{ Dir: "testdata", Cmds: map[string]func(ts *TestScript, neg bool, args []string){ "setSpecialVal": setSpecialVal, "ensureSpecialVal": ensureSpecialVal, "interrupt": interrupt, "waitfile": waitFile, "testdefer": func(ts *TestScript, neg bool, args []string) { testDeferCount++ n := testDeferCount ts.Defer(func() { if testDeferCount != n { t.Errorf("defers not run in reverse order; got %d want %d", testDeferCount, n) } testDeferCount-- }) }, "setup-filenames": func(ts *TestScript, neg bool, want []string) { got := ts.Value("setupFilenames") if !reflect.DeepEqual(want, got) { ts.Fatalf("setup did not see expected files; got %q want %q", got, want) } }, "test-values": func(ts *TestScript, neg bool, args []string) { if ts.Value("somekey") != 1234 { ts.Fatalf("test-values did not see expected value") } }, "testreadfile": func(ts *TestScript, neg bool, args []string) { if len(args) != 1 { ts.Fatalf("testreadfile ") } got := ts.ReadFile(args[0]) want := args[0] + "\n" if got != want { ts.Fatalf("reading %q; got %q want %q", args[0], got, want) } }, "testscript-update": func(ts *TestScript, neg bool, args []string) { // Run testscript in testscript. Oooh! Meta! if len(args) != 1 { ts.Fatalf("testscript ") } t := &fakeT{ts: ts} func() { defer func() { if err := recover(); err != nil { if err != errAbort { panic(err) } } }() RunT(t, Params{ Dir: ts.MkAbs(args[0]), UpdateScripts: true, }) }() if neg { if len(t.failMsgs) == 0 { ts.Fatalf("testscript-update unexpectedly succeeded") } return } if len(t.failMsgs) > 0 { ts.Fatalf("testscript-update unexpectedly failed with errors: %q", t.failMsgs) } }, }, Setup: func(env *Env) error { infos, err := ioutil.ReadDir(env.WorkDir) if err != nil { return fmt.Errorf("cannot read workdir: %v", err) } var setupFilenames []string for _, info := range infos { setupFilenames = append(setupFilenames, info.Name()) } env.Values["setupFilenames"] = setupFilenames env.Values["somekey"] = 1234 env.Vars = append(env.Vars, "GONOSUMDB=*", ) return nil }, }) if testDeferCount != 0 { t.Fatalf("defer mismatch; got %d want 0", testDeferCount) } // TODO check that the temp directory has been removed. } // TestTestwork tests that using the flag -testwork will make sure the work dir isn't removed // after the test is done. It uses an empty testscript file that doesn't do anything. func TestTestwork(t *testing.T) { out, err := exec.Command("go", "test", ".", "-testwork", "-v", "-run", "TestScripts/^nothing$").CombinedOutput() if err != nil { t.Fatal(err) } re := regexp.MustCompile(`\s+WORK=(\S+)`) match := re.FindAllStringSubmatch(string(out), -1) // Ensure that there is only one line with one match if len(match) != 1 || len(match[0]) != 2 { t.Fatalf("failed to extract WORK directory") } var fi os.FileInfo if fi, err = os.Stat(match[0][1]); err != nil { t.Fatalf("failed to stat expected work directory %v: %v", match[0][1], err) } if !fi.IsDir() { t.Fatalf("expected persisted workdir is not a directory: %v", match[0][1]) } } // TestWorkdirRoot tests that a non zero value in Params.WorkdirRoot is honoured func TestWorkdirRoot(t *testing.T) { td, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(td) params := Params{ Dir: filepath.Join("testdata", "nothing"), WorkdirRoot: td, } // Run as a sub-test so that this call blocks until the sub-tests created by // calling Run (which themselves call t.Parallel) complete. t.Run("run tests", func(t *testing.T) { Run(t, params) }) // Verify that we have a single go-test-script-* named directory files, err := filepath.Glob(filepath.Join(td, "script-nothing", "README.md")) if err != nil { t.Fatal(err) } if len(files) != 1 { t.Fatalf("unexpected files found for kept files; got %q", files) } } // TestBadDir verifies that invoking testscript with a directory that either // does not exist or that contains no *.txt scripts fails the test func TestBadDir(t *testing.T) { ft := new(fakeT) func() { defer func() { if err := recover(); err != nil { if err != errAbort { panic(err) } } }() RunT(ft, Params{ Dir: "thiswillnevermatch", }) }() wantCount := 1 if got := len(ft.failMsgs); got != wantCount { t.Fatalf("expected %v fail message; got %v", wantCount, got) } wantMsg := regexp.MustCompile(`no scripts found matching glob: thiswillnevermatch[/\\]\*\.txt`) if got := ft.failMsgs[0]; !wantMsg.MatchString(got) { t.Fatalf("expected msg to match `%v`; got:\n%v", wantMsg, got) } } func setSpecialVal(ts *TestScript, neg bool, args []string) { ts.Setenv("SPECIALVAL", "42") } func ensureSpecialVal(ts *TestScript, neg bool, args []string) { want := "42" if got := ts.Getenv("SPECIALVAL"); got != want { ts.Fatalf("expected SPECIALVAL to be %q; got %q", want, got) } } // interrupt interrupts the current background command. // Note that this will not work under Windows. func interrupt(ts *TestScript, neg bool, args []string) { if neg { ts.Fatalf("interrupt does not support neg") } if len(args) > 0 { ts.Fatalf("unexpected args found") } bg := ts.BackgroundCmds() if got, want := len(bg), 1; got != want { ts.Fatalf("unexpected background cmd count; got %d want %d", got, want) } bg[0].Process.Signal(os.Interrupt) } func waitFile(ts *TestScript, neg bool, args []string) { if neg { ts.Fatalf("waitfile does not support neg") } if len(args) != 1 { ts.Fatalf("usage: waitfile file") } path := ts.MkAbs(args[0]) for i := 0; i < 100; i++ { _, err := os.Stat(path) if err == nil { return } if !os.IsNotExist(err) { ts.Fatalf("unexpected stat error: %v", err) } time.Sleep(10 * time.Millisecond) } ts.Fatalf("timed out waiting for %q to be created", path) } type fakeT struct { ts *TestScript failMsgs []string } var errAbort = errors.New("abort test") func (t *fakeT) Skip(args ...interface{}) { panic(errAbort) } func (t *fakeT) Fatal(args ...interface{}) { t.failMsgs = append(t.failMsgs, fmt.Sprint(args...)) panic(errAbort) } func (t *fakeT) Parallel() {} func (t *fakeT) Log(args ...interface{}) { t.ts.Logf("testscript: %v", fmt.Sprint(args...)) } func (t *fakeT) FailNow() { t.Fatal("failed") } func (t *fakeT) Run(name string, f func(T)) { f(t) } func (t *fakeT) Verbose() bool { return false } go-internal-1.5.2/txtar/000077500000000000000000000000001360713250200150775ustar00rootroot00000000000000go-internal-1.5.2/txtar/archive.go000066400000000000000000000151701360713250200170530ustar00rootroot00000000000000// Copyright 2018 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 txtar implements a trivial text-based file archive format. // // The goals for the format are: // // - be trivial enough to create and edit by hand. // - be able to store trees of text files describing go command test cases. // - diff nicely in git history and code reviews. // // Non-goals include being a completely general archive format, // storing binary data, storing file modes, storing special files like // symbolic links, and so on. // // Txtar format // // A txtar archive is zero or more comment lines and then a sequence of file entries. // Each file entry begins with a file marker line of the form "-- FILENAME --" // and is followed by zero or more file content lines making up the file data. // The comment or file content ends at the next file marker line. // The file marker line must begin with the three-byte sequence "-- " // and end with the three-byte sequence " --", but the enclosed // file name can be surrounding by additional white space, // all of which is stripped. // // If the txtar file is missing a trailing newline on the final line, // parsers should consider a final newline to be present anyway. // // There are no possible syntax errors in a txtar archive. package txtar import ( "bytes" "errors" "fmt" "io/ioutil" "os" "path/filepath" "strings" "unicode/utf8" ) // An Archive is a collection of files. type Archive struct { Comment []byte Files []File } // A File is a single file in an archive. type File struct { Name string // name of file ("foo/bar.txt") Data []byte // text content of file } // Format returns the serialized form of an Archive. // It is assumed that the Archive data structure is well-formed: // a.Comment and all a.File[i].Data contain no file marker lines, // and all a.File[i].Name is non-empty. func Format(a *Archive) []byte { var buf bytes.Buffer buf.Write(fixNL(a.Comment)) for _, f := range a.Files { fmt.Fprintf(&buf, "-- %s --\n", f.Name) buf.Write(fixNL(f.Data)) } return buf.Bytes() } // ParseFile parses the named file as an archive. func ParseFile(file string) (*Archive, error) { data, err := ioutil.ReadFile(file) if err != nil { return nil, err } return Parse(data), nil } // Parse parses the serialized form of an Archive. // The returned Archive holds slices of data. func Parse(data []byte) *Archive { a := new(Archive) var name string a.Comment, name, data = findFileMarker(data) for name != "" { f := File{name, nil} f.Data, name, data = findFileMarker(data) a.Files = append(a.Files, f) } return a } // NeedsQuote reports whether the given data needs to // be quoted before it's included as a txtar file. func NeedsQuote(data []byte) bool { _, _, after := findFileMarker(data) return after != nil } // Quote quotes the data so that it can be safely stored in a txtar // file. This copes with files that contain lines that look like txtar // separators. // // The original data can be recovered with Unquote. It returns an error // if the data cannot be quoted (for example because it has no final // newline or it holds unprintable characters) func Quote(data []byte) ([]byte, error) { if len(data) == 0 { return nil, nil } if data[len(data)-1] != '\n' { return nil, errors.New("data has no final newline") } if !utf8.Valid(data) { return nil, fmt.Errorf("data contains non-UTF-8 characters") } var nd []byte prev := byte('\n') for _, b := range data { if prev == '\n' { nd = append(nd, '>') } nd = append(nd, b) prev = b } return nd, nil } // Unquote unquotes data as quoted by Quote. func Unquote(data []byte) ([]byte, error) { if len(data) == 0 { return nil, nil } if data[0] != '>' || data[len(data)-1] != '\n' { return nil, errors.New("data does not appear to be quoted") } data = bytes.Replace(data, []byte("\n>"), []byte("\n"), -1) data = bytes.TrimPrefix(data, []byte(">")) return data, nil } var ( newlineMarker = []byte("\n-- ") marker = []byte("-- ") markerEnd = []byte(" --") ) // findFileMarker finds the next file marker in data, // extracts the file name, and returns the data before the marker, // the file name, and the data after the marker. // If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil. func findFileMarker(data []byte) (before []byte, name string, after []byte) { var i int for { if name, after = isMarker(data[i:]); name != "" { return data[:i], name, after } j := bytes.Index(data[i:], newlineMarker) if j < 0 { return fixNL(data), "", nil } i += j + 1 // positioned at start of new possible marker } } // isMarker checks whether data begins with a file marker line. // If so, it returns the name from the line and the data after the line. // Otherwise it returns name == "" with an unspecified after. func isMarker(data []byte) (name string, after []byte) { if !bytes.HasPrefix(data, marker) { return "", nil } if i := bytes.IndexByte(data, '\n'); i >= 0 { data, after = data[:i], data[i+1:] if data[i-1] == '\r' { data = data[:len(data)-1] } } if !bytes.HasSuffix(data, markerEnd) { return "", nil } return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after } // If data is empty or ends in \n, fixNL returns data. // Otherwise fixNL returns a new slice consisting of data with a final \n added. func fixNL(data []byte) []byte { if len(data) == 0 || data[len(data)-1] == '\n' { return data } d := make([]byte, len(data)+1) copy(d, data) d[len(data)] = '\n' return d } // Write writes each File in an Archive to the given directory, returning any // errors encountered. An error is also returned in the event a file would be // written outside of dir. func Write(a *Archive, dir string) error { for _, f := range a.Files { fp := filepath.Clean(filepath.FromSlash(f.Name)) if isAbs(fp) || strings.HasPrefix(fp, ".."+string(filepath.Separator)) { return fmt.Errorf("%q: outside parent directory", f.Name) } fp = filepath.Join(dir, fp) if err := os.MkdirAll(filepath.Dir(fp), 0777); err != nil { return err } // Avoid overwriting existing files by using O_EXCL. out, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) if err != nil { return err } _, err = out.Write(f.Data) cerr := out.Close() if err != nil { return err } if cerr != nil { return cerr } } return nil } func isAbs(p string) bool { // Note: under Windows, filepath.IsAbs(`\foo`) returns false, // so we need to check for that case specifically. return filepath.IsAbs(p) || strings.HasPrefix(p, string(filepath.Separator)) } go-internal-1.5.2/txtar/archive_test.go000066400000000000000000000104521360713250200201100ustar00rootroot00000000000000// Copyright 2018 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 txtar import ( "bytes" "fmt" "io/ioutil" "os" "reflect" "testing" ) var tests = []struct { name string text string parsed *Archive }{ // General test { name: "basic", text: `comment1 comment2 -- file1 -- File 1 text. -- foo --- More file 1 text. -- file 2 -- File 2 text. -- empty -- -- noNL -- hello world`, parsed: &Archive{ Comment: []byte("comment1\ncomment2\n"), Files: []File{ {"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")}, {"file 2", []byte("File 2 text.\n")}, {"empty", []byte{}}, {"noNL", []byte("hello world\n")}, }, }, }, // Test CRLF input { name: "basic", text: "blah\r\n-- hello --\r\nhello\r\n", parsed: &Archive{ Comment: []byte("blah\r\n"), Files: []File{ {"hello", []byte("hello\r\n")}, }, }, }, } func Test(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := Parse([]byte(tt.text)) if !reflect.DeepEqual(a, tt.parsed) { t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) } text := Format(a) a = Parse(text) if !reflect.DeepEqual(a, tt.parsed) { t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) } }) } } func shortArchive(a *Archive) string { var buf bytes.Buffer fmt.Fprintf(&buf, "comment: %q\n", a.Comment) for _, f := range a.Files { fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data) } return buf.String() } func TestWrite(t *testing.T) { td, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(td) good := &Archive{Files: []File{File{Name: "good.txt"}}} if err := Write(good, td); err != nil { t.Fatalf("expected no error; got %v", err) } badRel := &Archive{Files: []File{File{Name: "../bad.txt"}}} want := `"../bad.txt": outside parent directory` if err := Write(badRel, td); err == nil || err.Error() != want { t.Fatalf("expected %v; got %v", want, err) } badAbs := &Archive{Files: []File{File{Name: "/bad.txt"}}} want = `"/bad.txt": outside parent directory` if err := Write(badAbs, td); err == nil || err.Error() != want { t.Fatalf("expected %v; got %v", want, err) } } var unquoteErrorTests = []struct { testName string data string expectError string }{{ testName: "no final newline", data: ">hello", expectError: `data does not appear to be quoted`, }, { testName: "no initial >", data: "hello\n", expectError: `data does not appear to be quoted`, }} func TestUnquote(t *testing.T) { for _, test := range unquoteErrorTests { t.Run(test.testName, func(t *testing.T) { _, err := Unquote([]byte(test.data)) if err == nil { t.Fatalf("unexpected success") } if err.Error() != test.expectError { t.Fatalf("unexpected error; got %q want %q", err, test.expectError) } }) } } var quoteTests = []struct { testName string data string expect string expectError string }{{ testName: "empty", data: "", expect: "", }, { testName: "one line", data: "foo\n", expect: ">foo\n", }, { testName: "several lines", data: "foo\nbar\n-- baz --\n", expect: ">foo\n>bar\n>-- baz --\n", }, { testName: "bad data", data: "foo\xff\n", expectError: `data contains non-UTF-8 characters`, }, { testName: "no final newline", data: "foo", expectError: `data has no final newline`, }} func TestQuote(t *testing.T) { for _, test := range quoteTests { t.Run(test.testName, func(t *testing.T) { got, err := Quote([]byte(test.data)) if test.expectError != "" { if err == nil { t.Fatalf("unexpected success") } if err.Error() != test.expectError { t.Fatalf("unexpected error; got %q want %q", err, test.expectError) } return } if err != nil { t.Fatalf("quote error: %v", err) } if string(got) != test.expect { t.Fatalf("unexpected result; got %q want %q", got, test.expect) } orig, err := Unquote(got) if err != nil { t.Fatal(err) } if string(orig) != test.data { t.Fatalf("round trip failed; got %q want %q", orig, test.data) } }) } }