scandir_windows.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. //go:build windows
  2. // +build windows
  3. package godirwalk
  4. import (
  5. "fmt"
  6. "os"
  7. )
  8. // Scanner is an iterator to enumerate the contents of a directory.
  9. type Scanner struct {
  10. osDirname string
  11. childName string
  12. dh *os.File // dh is handle to open directory
  13. de *Dirent
  14. err error // err is the error associated with scanning directory
  15. childMode os.FileMode
  16. }
  17. // NewScanner returns a new directory Scanner that lazily enumerates
  18. // the contents of a single directory. To prevent resource leaks,
  19. // caller must invoke either the Scanner's Close or Err method after
  20. // it has completed scanning a directory.
  21. //
  22. // scanner, err := godirwalk.NewScanner(dirname)
  23. // if err != nil {
  24. // fatal("cannot scan directory: %s", err)
  25. // }
  26. //
  27. // for scanner.Scan() {
  28. // dirent, err := scanner.Dirent()
  29. // if err != nil {
  30. // warning("cannot get dirent: %s", err)
  31. // continue
  32. // }
  33. // name := dirent.Name()
  34. // if name == "break" {
  35. // break
  36. // }
  37. // if name == "continue" {
  38. // continue
  39. // }
  40. // fmt.Printf("%v %v\n", dirent.ModeType(), dirent.Name())
  41. // }
  42. // if err := scanner.Err(); err != nil {
  43. // fatal("cannot scan directory: %s", err)
  44. // }
  45. func NewScanner(osDirname string) (*Scanner, error) {
  46. dh, err := os.Open(osDirname)
  47. if err != nil {
  48. return nil, err
  49. }
  50. scanner := &Scanner{
  51. osDirname: osDirname,
  52. dh: dh,
  53. }
  54. return scanner, nil
  55. }
  56. // NewScannerWithScratchBuffer returns a new directory Scanner that
  57. // lazily enumerates the contents of a single directory. On platforms
  58. // other than Windows it uses the provided scratch buffer to read from
  59. // the file system. On Windows the scratch buffer parameter is
  60. // ignored. To prevent resource leaks, caller must invoke either the
  61. // Scanner's Close or Err method after it has completed scanning a
  62. // directory.
  63. func NewScannerWithScratchBuffer(osDirname string, scratchBuffer []byte) (*Scanner, error) {
  64. return NewScanner(osDirname)
  65. }
  66. // Close releases resources associated with scanning a directory. Call
  67. // either this or the Err method when the directory no longer needs to
  68. // be scanned.
  69. func (s *Scanner) Close() error {
  70. return s.Err()
  71. }
  72. // Dirent returns the current directory entry while scanning a directory.
  73. func (s *Scanner) Dirent() (*Dirent, error) {
  74. if s.de == nil {
  75. s.de = &Dirent{
  76. name: s.childName,
  77. path: s.osDirname,
  78. modeType: s.childMode,
  79. }
  80. }
  81. return s.de, nil
  82. }
  83. // done is called when directory scanner unable to continue, with either the
  84. // triggering error, or nil when there are simply no more entries to read from
  85. // the directory.
  86. func (s *Scanner) done(err error) {
  87. if s.dh == nil {
  88. return
  89. }
  90. s.err = err
  91. if err = s.dh.Close(); s.err == nil {
  92. s.err = err
  93. }
  94. s.childName, s.osDirname = "", ""
  95. s.de, s.dh = nil, nil
  96. }
  97. // Err returns any error associated with scanning a directory. It is
  98. // normal to call Err after Scan returns false, even though they both
  99. // ensure Scanner resources are released. Call either this or the
  100. // Close method when the directory no longer needs to be scanned.
  101. func (s *Scanner) Err() error {
  102. s.done(nil)
  103. return s.err
  104. }
  105. // Name returns the base name of the current directory entry while scanning a
  106. // directory.
  107. func (s *Scanner) Name() string { return s.childName }
  108. // Scan potentially reads and then decodes the next directory entry from the
  109. // file system.
  110. //
  111. // When it returns false, this releases resources used by the Scanner then
  112. // returns any error associated with closing the file system directory resource.
  113. func (s *Scanner) Scan() bool {
  114. if s.dh == nil {
  115. return false
  116. }
  117. s.de = nil
  118. fileinfos, err := s.dh.Readdir(1)
  119. if err != nil {
  120. s.done(err)
  121. return false
  122. }
  123. if l := len(fileinfos); l != 1 {
  124. s.done(fmt.Errorf("expected a single entry rather than %d", l))
  125. return false
  126. }
  127. fi := fileinfos[0]
  128. s.childMode = fi.Mode() & os.ModeType
  129. s.childName = fi.Name()
  130. return true
  131. }