1use std::io;
2use std::ptr;
3use std::ffi::{CStr, OsStr};
4use std::os::unix::ffi::OsStrExt;
5
6use libc;
7
8use crate::{Dir, Entry, SimpleType};
9
10
11const DOT: [libc::c_char; 2] = [b'.' as libc::c_char, 0];
13const DOTDOT: [libc::c_char; 3] = [b'.' as libc::c_char, b'.' as libc::c_char, 0];
14
15
16#[derive(Debug)]
20pub struct DirIter {
21 dir: *mut libc::DIR,
22}
23
24pub struct DirPosition {
28 pos: libc::c_long,
29}
30
31impl Entry {
32 pub fn file_name(&self) -> &OsStr {
34 OsStr::from_bytes(self.name.to_bytes())
35 }
36 pub fn simple_type(&self) -> Option<SimpleType> {
38 self.file_type
39 }
40}
41
42#[cfg(any(target_os="linux", target_os="fuchsia"))]
43unsafe fn errno_location() -> *mut libc::c_int {
44 libc::__errno_location()
45}
46
47#[cfg(any(target_os="openbsd", target_os="netbsd", target_os="android"))]
48unsafe fn errno_location() -> *mut libc::c_int {
49 libc::__errno()
50}
51
52#[cfg(not(any(target_os="linux", target_os="openbsd", target_os="netbsd", target_os="android", target_os="fuchsia")))]
53unsafe fn errno_location() -> *mut libc::c_int {
54 libc::__error()
55}
56
57impl DirIter {
58
59 unsafe fn next_entry(&mut self) -> io::Result<Option<&libc::dirent>>
60 {
61 *errno_location() = 0;
63
64 let entry = libc::readdir(self.dir);
65 if entry == ptr::null_mut() {
66 if *errno_location() == 0 {
67 return Ok(None)
68 } else {
69 return Err(io::Error::last_os_error());
70 }
71 }
72 return Ok(Some(&*entry));
73 }
74
75 pub fn current_position(&self) -> io::Result<DirPosition> {
77 let pos = unsafe { libc::telldir(self.dir) };
78
79 if pos == -1 {
80 Err(io::Error::last_os_error())
81 } else {
82 Ok(DirPosition { pos })
83 }
84 }
85
86 pub fn seek(&self, position: DirPosition) {
89 unsafe { libc::seekdir(self.dir, position.pos) };
90 }
91
92 pub fn rewind(&self) {
94 unsafe { libc::rewinddir(self.dir) };
95 }
96}
97
98pub fn open_dirfd(fd: libc::c_int) -> io::Result<DirIter> {
99 let dir = unsafe { libc::fdopendir(fd) };
100 if dir == std::ptr::null_mut() {
101 Err(io::Error::last_os_error())
102 } else {
103 Ok(DirIter { dir: dir })
104 }
105}
106
107pub fn open_dir(dir: &Dir, path: &CStr) -> io::Result<DirIter> {
108 let dir_fd = unsafe {
109 libc::openat(dir.0, path.as_ptr(), libc::O_DIRECTORY|libc::O_CLOEXEC)
110 };
111 if dir_fd < 0 {
112 Err(io::Error::last_os_error())
113 } else {
114 open_dirfd(dir_fd)
115 }
116}
117
118impl Iterator for DirIter {
119 type Item = io::Result<Entry>;
120 fn next(&mut self) -> Option<Self::Item> {
121 unsafe {
122 loop {
123 match self.next_entry() {
124 Err(e) => return Some(Err(e)),
125 Ok(None) => return None,
126 Ok(Some(e)) if e.d_name[..2] == DOT => continue,
127 Ok(Some(e)) if e.d_name[..3] == DOTDOT => continue,
128 Ok(Some(e)) => {
129 return Some(Ok(Entry {
130 name: CStr::from_ptr((e.d_name).as_ptr())
131 .to_owned(),
132 file_type: match e.d_type {
133 0 => None,
134 libc::DT_REG => Some(SimpleType::File),
135 libc::DT_DIR => Some(SimpleType::Dir),
136 libc::DT_LNK => Some(SimpleType::Symlink),
137 _ => Some(SimpleType::Other),
138 },
139 }));
140 }
141 }
142 }
143 }
144 }
145}
146
147impl Drop for DirIter {
148 fn drop(&mut self) {
149 unsafe {
150 libc::closedir(self.dir);
151 }
152 }
153}