dir_posix.odin 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #+private
  2. #+build darwin, netbsd, freebsd, openbsd
  3. package os2
  4. import "core:sys/posix"
  5. Read_Directory_Iterator_Impl :: struct {
  6. dir: posix.DIR,
  7. fullpath: [dynamic]byte,
  8. }
  9. @(require_results)
  10. _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
  11. fimpl := (^File_Impl)(it.f.impl)
  12. index = it.index
  13. it.index += 1
  14. for {
  15. posix.set_errno(nil)
  16. entry := posix.readdir(it.impl.dir)
  17. if entry == nil {
  18. if errno := posix.errno(); errno != nil {
  19. read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
  20. }
  21. return
  22. }
  23. cname := cstring(raw_data(entry.d_name[:]))
  24. if cname == "." || cname == ".." {
  25. continue
  26. }
  27. sname := string(cname)
  28. n := len(fimpl.name)+1
  29. if err := non_zero_resize(&it.impl.fullpath, n+len(sname)); err != nil {
  30. read_directory_iterator_set_error(it, sname, err)
  31. ok = true
  32. return
  33. }
  34. copy(it.impl.fullpath[n:], sname)
  35. stat: posix.stat_t
  36. if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
  37. read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error())
  38. ok = true
  39. return
  40. }
  41. fi = internal_stat(stat, string(it.impl.fullpath[:]))
  42. ok = true
  43. return
  44. }
  45. }
  46. _read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
  47. if f == nil || f.impl == nil {
  48. read_directory_iterator_set_error(it, "", .Invalid_File)
  49. return
  50. }
  51. impl := (^File_Impl)(f.impl)
  52. // NOTE: Allow calling `init` to target a new directory with the same iterator.
  53. it.impl.fullpath.allocator = file_allocator()
  54. clear(&it.impl.fullpath)
  55. if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
  56. read_directory_iterator_set_error(it, name(f), err)
  57. return
  58. }
  59. append(&it.impl.fullpath, impl.name)
  60. append(&it.impl.fullpath, "/")
  61. // `fdopendir` consumes the file descriptor so we need to `dup` it.
  62. dupfd := posix.dup(impl.fd)
  63. if dupfd == -1 {
  64. read_directory_iterator_set_error(it, name(f), _get_platform_error())
  65. return
  66. }
  67. defer if it.err.err != nil { posix.close(dupfd) }
  68. // NOTE: Allow calling `init` to target a new directory with the same iterator.
  69. if it.impl.dir != nil {
  70. posix.closedir(it.impl.dir)
  71. }
  72. it.impl.dir = posix.fdopendir(dupfd)
  73. if it.impl.dir == nil {
  74. read_directory_iterator_set_error(it, name(f), _get_platform_error())
  75. return
  76. }
  77. return
  78. }
  79. _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
  80. if it.impl.dir == nil {
  81. return
  82. }
  83. posix.closedir(it.impl.dir)
  84. delete(it.impl.fullpath)
  85. }