1use std::io;
2use std::ptr;
3use std::ffi::{CStr, OsStr};
4use std::os::unix::ffi::OsStrExt;
5
6use libc;
7
8use {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
24impl Entry {
25 pub fn file_name(&self) -> &OsStr {
27 OsStr::from_bytes(self.name.to_bytes())
28 }
29 pub fn simple_type(&self) -> Option<SimpleType> {
31 self.file_type
32 }
33}
34
35#[cfg(any(target_os="linux", target_os="fuchsia"))]
36unsafe fn errno_location() -> *mut libc::c_int {
37 libc::__errno_location()
38}
39
40#[cfg(any(target_os="openbsd", target_os="netbsd", target_os="android"))]
41unsafe fn errno_location() -> *mut libc::c_int {
42 libc::__errno()
43}
44
45#[cfg(not(any(target_os="linux", target_os="openbsd", target_os="netbsd", target_os="android", target_os="fuchsia")))]
46unsafe fn errno_location() -> *mut libc::c_int {
47 libc::__error()
48}
49
50impl DirIter {
51
52 unsafe fn next_entry(&mut self) -> io::Result<Option<*const libc::dirent>>
53 {
54 *errno_location() = 0;
56
57 let entry = libc::readdir(self.dir);
58 if entry == ptr::null_mut() {
59 if *errno_location() == 0 {
60 return Ok(None)
61 } else {
62 return Err(io::Error::last_os_error());
63 }
64 }
65 return Ok(Some(entry));
66 }
67}
68
69pub fn open_dir(dir: &Dir, path: &CStr) -> io::Result<DirIter> {
70 let dir_fd = unsafe {
71 libc::openat(dir.0, path.as_ptr(), libc::O_DIRECTORY|libc::O_CLOEXEC)
72 };
73 if dir_fd < 0 {
74 Err(io::Error::last_os_error())
75 } else {
76 let dir = unsafe { libc::fdopendir(dir_fd) };
77 if dir == ptr::null_mut() {
78 Err(io::Error::last_os_error())
79 } else {
80 Ok(DirIter { dir: dir })
81 }
82 }
83}
84
85impl Iterator for DirIter {
86 type Item = io::Result<Entry>;
87 fn next(&mut self) -> Option<Self::Item> {
88 unsafe {
89 loop {
90 match self.next_entry() {
91 Err(e) => return Some(Err(e)),
92 Ok(None) => return None,
93 Ok(Some(e)) if (*e).d_name[..2] == DOT => continue,
94 Ok(Some(e)) if (*e).d_name[..3] == DOTDOT => continue,
95 Ok(Some(e)) => {
96 return Some(Ok(Entry {
97 name: CStr::from_ptr((&(*e).d_name).as_ptr())
98 .to_owned(),
99 file_type: match (*e).d_type {
100 0 => None,
101 libc::DT_REG => Some(SimpleType::File),
102 libc::DT_DIR => Some(SimpleType::Dir),
103 libc::DT_LNK => Some(SimpleType::Symlink),
104 _ => Some(SimpleType::Other),
105 },
106 }));
107 }
108 }
109 }
110 }
111 }
112}
113
114impl Drop for DirIter {
115 fn drop(&mut self) {
116 unsafe {
117 libc::closedir(self.dir);
118 }
119 }
120}