dir.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2009 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. package os
  5. import (
  6. "io"
  7. "sync/atomic"
  8. "syscall"
  9. "unsafe"
  10. )
  11. //extern opendir
  12. func libc_opendir(*byte) *syscall.DIR
  13. //extern closedir
  14. func libc_closedir(*syscall.DIR) int
  15. // FIXME: pathconf returns long, not int.
  16. //extern pathconf
  17. func libc_pathconf(*byte, int) int
  18. func clen(n []byte) int {
  19. for i := 0; i < len(n); i++ {
  20. if n[i] == 0 {
  21. return i
  22. }
  23. }
  24. return len(n)
  25. }
  26. var nameMax int32
  27. func (file *File) readdirnames(n int) (names []string, err error) {
  28. if file.dirinfo == nil {
  29. p, err := syscall.BytePtrFromString(file.name)
  30. if err != nil {
  31. return nil, err
  32. }
  33. elen := int(atomic.LoadInt32(&nameMax))
  34. if elen == 0 {
  35. syscall.Entersyscall()
  36. plen := libc_pathconf(p, syscall.PC_NAME_MAX)
  37. syscall.Exitsyscall()
  38. if plen < 1024 {
  39. plen = 1024
  40. }
  41. var dummy syscall.Dirent
  42. elen = int(unsafe.Offsetof(dummy.Name)) + plen + 1
  43. atomic.StoreInt32(&nameMax, int32(elen))
  44. }
  45. syscall.Entersyscall()
  46. r := libc_opendir(p)
  47. errno := syscall.GetErrno()
  48. syscall.Exitsyscall()
  49. if r == nil {
  50. return nil, &PathError{"opendir", file.name, errno}
  51. }
  52. file.dirinfo = new(dirInfo)
  53. file.dirinfo.buf = make([]byte, elen)
  54. file.dirinfo.dir = r
  55. }
  56. entryDirent := (*syscall.Dirent)(unsafe.Pointer(&file.dirinfo.buf[0]))
  57. size := n
  58. if size <= 0 {
  59. size = 100
  60. n = -1
  61. }
  62. names = make([]string, 0, size) // Empty with room to grow.
  63. for n != 0 {
  64. var dirent *syscall.Dirent
  65. pr := &dirent
  66. syscall.Entersyscall()
  67. i := libc_readdir_r(file.dirinfo.dir, entryDirent, pr)
  68. syscall.Exitsyscall()
  69. if i != 0 {
  70. return names, NewSyscallError("readdir_r", i)
  71. }
  72. if dirent == nil {
  73. break // EOF
  74. }
  75. bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
  76. var name = string(bytes[0:clen(bytes[:])])
  77. if name == "." || name == ".." { // Useless names
  78. continue
  79. }
  80. names = append(names, name)
  81. n--
  82. }
  83. if n >= 0 && len(names) == 0 {
  84. return names, io.EOF
  85. }
  86. return names, nil
  87. }