75 lines
1.8 KiB
Go
75 lines
1.8 KiB
Go
//go:build windows
|
|
|
|
package fswatch
|
|
|
|
import (
|
|
"fmt"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// walkDir walks a directory tree on Windows using FindFirstFile/FindNextFile.
|
|
func walkDir(dir string, recursive bool, fn func(path string, isDir bool) error) error {
|
|
rootPtr, err := windows.UTF16PtrFromString(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var rootData windows.Win32FileAttributeData
|
|
if err := windows.GetFileAttributesEx(rootPtr, windows.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&rootData))); err != nil {
|
|
return fmt.Errorf("error opening directory: %w", err)
|
|
}
|
|
if rootData.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 ||
|
|
rootData.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
|
return syscall.ENOTDIR
|
|
}
|
|
if fn != nil {
|
|
if err := fn(dir, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
stack := []string{dir}
|
|
for len(stack) > 0 {
|
|
path := stack[len(stack)-1]
|
|
stack = stack[:len(stack)-1]
|
|
|
|
spec := path + "\\*"
|
|
specPtr, err := windows.UTF16PtrFromString(spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var ffd windows.Win32finddata
|
|
hFind, err := windows.FindFirstFile(specPtr, &ffd)
|
|
if err != nil {
|
|
if path == dir {
|
|
return fmt.Errorf("error opening directory: %w", err)
|
|
}
|
|
continue
|
|
}
|
|
for {
|
|
name := windows.UTF16ToString(ffd.FileName[:])
|
|
if name != "." && name != ".." {
|
|
fullPath := path + "\\" + name
|
|
isDir := ffd.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY != 0 &&
|
|
ffd.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT == 0
|
|
if fn != nil {
|
|
if err := fn(fullPath, isDir); err != nil {
|
|
windows.FindClose(hFind)
|
|
return err
|
|
}
|
|
}
|
|
if isDir && recursive {
|
|
stack = append(stack, fullPath)
|
|
}
|
|
}
|
|
if err := windows.FindNextFile(hFind, &ffd); err != nil {
|
|
break
|
|
}
|
|
}
|
|
windows.FindClose(hFind)
|
|
}
|
|
return nil
|
|
}
|