github.com/v2fly/tools@v0.100.0/godoc/vfs/os.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package vfs
     6  
     7  import (
     8  	"fmt"
     9  	"go/build"
    10  	"io/ioutil"
    11  	"os"
    12  	pathpkg "path"
    13  	"path/filepath"
    14  	"runtime"
    15  )
    16  
    17  // We expose a new variable because otherwise we need to copy the findGOROOT logic again
    18  // from cmd/godoc which is already copied twice from the standard library.
    19  
    20  // GOROOT is the GOROOT path under which the godoc binary is running.
    21  // It is needed to check whether a filesystem root is under GOROOT or not.
    22  // This is set from cmd/godoc/main.go.
    23  var GOROOT = runtime.GOROOT()
    24  
    25  // OS returns an implementation of FileSystem reading from the
    26  // tree rooted at root.  Recording a root is convenient everywhere
    27  // but necessary on Windows, because the slash-separated path
    28  // passed to Open has no way to specify a drive letter.  Using a root
    29  // lets code refer to OS(`c:\`), OS(`d:\`) and so on.
    30  func OS(root string) FileSystem {
    31  	var t RootType
    32  	switch {
    33  	case root == GOROOT:
    34  		t = RootTypeGoRoot
    35  	case isGoPath(root):
    36  		t = RootTypeGoPath
    37  	}
    38  	return osFS{rootPath: root, rootType: t}
    39  }
    40  
    41  type osFS struct {
    42  	rootPath string
    43  	rootType RootType
    44  }
    45  
    46  func isGoPath(path string) bool {
    47  	for _, bp := range filepath.SplitList(build.Default.GOPATH) {
    48  		for _, gp := range filepath.SplitList(path) {
    49  			if bp == gp {
    50  				return true
    51  			}
    52  		}
    53  	}
    54  	return false
    55  }
    56  
    57  func (root osFS) String() string { return "os(" + root.rootPath + ")" }
    58  
    59  // RootType returns the root type for the filesystem.
    60  //
    61  // Note that we ignore the path argument because roottype is a property of
    62  // this filesystem. But for other filesystems, the roottype might need to be
    63  // dynamically deduced at call time.
    64  func (root osFS) RootType(path string) RootType {
    65  	return root.rootType
    66  }
    67  
    68  func (root osFS) resolve(path string) string {
    69  	// Clean the path so that it cannot possibly begin with ../.
    70  	// If it did, the result of filepath.Join would be outside the
    71  	// tree rooted at root.  We probably won't ever see a path
    72  	// with .. in it, but be safe anyway.
    73  	path = pathpkg.Clean("/" + path)
    74  
    75  	return filepath.Join(root.rootPath, path)
    76  }
    77  
    78  func (root osFS) Open(path string) (ReadSeekCloser, error) {
    79  	f, err := os.Open(root.resolve(path))
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	fi, err := f.Stat()
    84  	if err != nil {
    85  		f.Close()
    86  		return nil, err
    87  	}
    88  	if fi.IsDir() {
    89  		f.Close()
    90  		return nil, fmt.Errorf("Open: %s is a directory", path)
    91  	}
    92  	return f, nil
    93  }
    94  
    95  func (root osFS) Lstat(path string) (os.FileInfo, error) {
    96  	return os.Lstat(root.resolve(path))
    97  }
    98  
    99  func (root osFS) Stat(path string) (os.FileInfo, error) {
   100  	return os.Stat(root.resolve(path))
   101  }
   102  
   103  func (root osFS) ReadDir(path string) ([]os.FileInfo, error) {
   104  	return ioutil.ReadDir(root.resolve(path)) // is sorted
   105  }