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 | } |
106 |
Members