Files
kjol/tools/tsgo/internal/fswatch/walkdir_windows.go
2026-07-09 16:50:43 -04:00

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
}