Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / fastwalk / fastwalk_unix.go
1 // Copyright 2016 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 // +build linux darwin freebsd openbsd netbsd
6 // +build !appengine
7
8 package fastwalk
9
10 import (
11         "fmt"
12         "os"
13         "syscall"
14         "unsafe"
15 )
16
17 const blockSize = 8 << 10
18
19 // unknownFileMode is a sentinel (and bogus) os.FileMode
20 // value used to represent a syscall.DT_UNKNOWN Dirent.Type.
21 const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
22
23 func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
24         fd, err := syscall.Open(dirName, 0, 0)
25         if err != nil {
26                 return &os.PathError{Op: "open", Path: dirName, Err: err}
27         }
28         defer syscall.Close(fd)
29
30         // The buffer must be at least a block long.
31         buf := make([]byte, blockSize) // stack-allocated; doesn't escape
32         bufp := 0                      // starting read position in buf
33         nbuf := 0                      // end valid data in buf
34         skipFiles := false
35         for {
36                 if bufp >= nbuf {
37                         bufp = 0
38                         nbuf, err = syscall.ReadDirent(fd, buf)
39                         if err != nil {
40                                 return os.NewSyscallError("readdirent", err)
41                         }
42                         if nbuf <= 0 {
43                                 return nil
44                         }
45                 }
46                 consumed, name, typ := parseDirEnt(buf[bufp:nbuf])
47                 bufp += consumed
48                 if name == "" || name == "." || name == ".." {
49                         continue
50                 }
51                 // Fallback for filesystems (like old XFS) that don't
52                 // support Dirent.Type and have DT_UNKNOWN (0) there
53                 // instead.
54                 if typ == unknownFileMode {
55                         fi, err := os.Lstat(dirName + "/" + name)
56                         if err != nil {
57                                 // It got deleted in the meantime.
58                                 if os.IsNotExist(err) {
59                                         continue
60                                 }
61                                 return err
62                         }
63                         typ = fi.Mode() & os.ModeType
64                 }
65                 if skipFiles && typ.IsRegular() {
66                         continue
67                 }
68                 if err := fn(dirName, name, typ); err != nil {
69                         if err == ErrSkipFiles {
70                                 skipFiles = true
71                                 continue
72                         }
73                         return err
74                 }
75         }
76 }
77
78 func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
79         // golang.org/issue/37269
80         dirent := &syscall.Dirent{}
81         copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf)
82         if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
83                 panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v))
84         }
85         if len(buf) < int(dirent.Reclen) {
86                 panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen))
87         }
88         consumed = int(dirent.Reclen)
89         if direntInode(dirent) == 0 { // File absent in directory.
90                 return
91         }
92         switch dirent.Type {
93         case syscall.DT_REG:
94                 typ = 0
95         case syscall.DT_DIR:
96                 typ = os.ModeDir
97         case syscall.DT_LNK:
98                 typ = os.ModeSymlink
99         case syscall.DT_BLK:
100                 typ = os.ModeDevice
101         case syscall.DT_FIFO:
102                 typ = os.ModeNamedPipe
103         case syscall.DT_SOCK:
104                 typ = os.ModeSocket
105         case syscall.DT_UNKNOWN:
106                 typ = unknownFileMode
107         default:
108                 // Skip weird things.
109                 // It's probably a DT_WHT (http://lwn.net/Articles/325369/)
110                 // or something. Revisit if/when this package is moved outside
111                 // of goimports. goimports only cares about regular files,
112                 // symlinks, and directories.
113                 return
114         }
115
116         nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
117         nameLen := direntNamlen(dirent)
118
119         // Special cases for common things:
120         if nameLen == 1 && nameBuf[0] == '.' {
121                 name = "."
122         } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' {
123                 name = ".."
124         } else {
125                 name = string(nameBuf[:nameLen])
126         }
127         return
128 }