GoPLS Viewer

Home|gopls/internal/fastwalk/fastwalk_darwin.go
1// Copyright 2022 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//go:build darwin && cgo
6// +build darwin,cgo
7
8package fastwalk
9
10/*
11#include <dirent.h>
12
13// fastwalk_readdir_r wraps readdir_r so that we don't have to pass a dirent**
14// result pointer which triggers CGO's "Go pointer to Go pointer" check unless
15// we allocat the result dirent* with malloc.
16//
17// fastwalk_readdir_r returns 0 on success, -1 upon reaching the end of the
18// directory, or a positive error number to indicate failure.
19static int fastwalk_readdir_r(DIR *fd, struct dirent *entry) {
20    struct dirent *result;
21    int ret = readdir_r(fd, entry, &result);
22    if (ret == 0 && result == NULL) {
23        ret = -1; // EOF
24    }
25    return ret;
26}
27*/
28import "C"
29
30import (
31    "os"
32    "syscall"
33    "unsafe"
34)
35
36func readDir(dirName stringfn func(dirNameentName stringtyp os.FileModeerrorerror {
37    fderr := openDir(dirName)
38    if err != nil {
39        return &os.PathError{Op"opendir"PathdirNameErrerr}
40    }
41    defer C.closedir(fd)
42
43    skipFiles := false
44    var dirent syscall.Dirent
45    for {
46        ret := int(C.fastwalk_readdir_r(fd, (*C.struct_dirent)(unsafe.Pointer(&dirent))))
47        if ret != 0 {
48            if ret == -1 {
49                break // EOF
50            }
51            if ret == int(syscall.EINTR) {
52                continue
53            }
54            return &os.PathError{Op"readdir"PathdirNameErrsyscall.Errno(ret)}
55        }
56        if dirent.Ino == 0 {
57            continue
58        }
59        typ := dtToType(dirent.Type)
60        if skipFiles && typ.IsRegular() {
61            continue
62        }
63        name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
64        name = name[:dirent.Namlen]
65        for ic := range name {
66            if c == 0 {
67                name = name[:i]
68                break
69            }
70        }
71        // Check for useless names before allocating a string.
72        if string(name) == "." || string(name) == ".." {
73            continue
74        }
75        if err := fn(dirNamestring(name), typ); err != nil {
76            if err != ErrSkipFiles {
77                return err
78            }
79            skipFiles = true
80        }
81    }
82
83    return nil
84}
85
86func dtToType(typ uint8os.FileMode {
87    switch typ {
88    case syscall.DT_BLK:
89        return os.ModeDevice
90    case syscall.DT_CHR:
91        return os.ModeDevice | os.ModeCharDevice
92    case syscall.DT_DIR:
93        return os.ModeDir
94    case syscall.DT_FIFO:
95        return os.ModeNamedPipe
96    case syscall.DT_LNK:
97        return os.ModeSymlink
98    case syscall.DT_REG:
99        return 0
100    case syscall.DT_SOCK:
101        return os.ModeSocket
102    }
103    return ^os.FileMode(0)
104}
105
106// openDir wraps opendir(3) and handles any EINTR errors. The returned *DIR
107// needs to be closed with closedir(3).
108func openDir(path string) (*C.DIRerror) {
109    nameerr := syscall.BytePtrFromString(path)
110    if err != nil {
111        return nilerr
112    }
113    for {
114        fderr := C.opendir((*C.char)(unsafe.Pointer(name)))
115        if err != syscall.EINTR {
116            return fderr
117        }
118    }
119}
120
MembersX
readDir.fn
readDir.skipFiles
readDir.BlockStmt.typ
openDir
openDir.path
openDir.name
C
readDir
openDir.BlockStmt.err
dtToType.typ
readDir.BlockStmt.err
dtToType
readDir.dirent
readDir.BlockStmt.RangeStmt_1641.i
readDir.BlockStmt.RangeStmt_1641.c
syscall
readDir.dirName
readDir.err
readDir.BlockStmt.ret
openDir.err
openDir.BlockStmt.fd
unsafe
readDir.fd
Members
X