use {Result, Error, Errno, NixPath};
use libc::{self, DIR, c_long};
use std::convert::{AsRef, Into};
use std::ffi::CStr;
use std::mem;
#[cfg(any(target_os = "linux"))]
use libc::ino64_t;
#[cfg(any(target_os = "android"))]
use libc::ino_t as ino64_t;
#[cfg(any(target_os = "linux", target_os = "android"))]
use libc::{dirent64, readdir64};
#[cfg(not(any(target_os = "linux", target_os = "android")))]
use libc::{dirent as dirent64, ino_t as ino64_t, readdir as readdir64};
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
use std::os::unix::io::RawFd;
#[derive(Debug)]
pub struct DirectoryStream(*mut DIR);
impl AsRef<DIR> for DirectoryStream {
fn as_ref(&self) -> &DIR {
unsafe { &*self.0 }
}
}
impl Into<*mut DIR> for DirectoryStream {
fn into(self) -> *mut DIR {
let dirp = self.0;
mem::forget(self);
dirp
}
}
impl Drop for DirectoryStream {
fn drop(&mut self) {
unsafe { libc::closedir(self.0) };
}
}
pub struct DirectoryEntry<'a>(&'a dirent64);
use std::fmt::{self, Debug};
impl<'a> Debug for DirectoryEntry<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("DirectoryEntry")
.field("inode", &self.inode())
.finish()
}
}
impl<'a> DirectoryEntry<'a> {
pub fn name(&self) -> &CStr {
unsafe{
CStr::from_ptr(self.0.d_name.as_ptr())
}
}
pub fn inode(&self) -> ino64_t {
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os="dragonfly")))]
return self.0.d_ino;
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os="dragonfly"))]
return self.0.d_fileno;
}
}
impl<'a> AsRef<dirent64> for DirectoryEntry<'a> {
fn as_ref(&self) -> &dirent64 {
self.0
}
}
pub fn opendir<P: ?Sized + NixPath>(name: &P) -> Result<DirectoryStream> {
let dirp = try!(name.with_nix_path(|cstr| unsafe { libc::opendir(cstr.as_ptr()) }));
if dirp.is_null() {
Err(Error::last().into())
} else {
Ok(DirectoryStream(dirp))
}
}
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
pub fn fdopendir(fd: RawFd) -> Result<DirectoryStream> {
let dirp = unsafe { libc::fdopendir(fd) };
if dirp.is_null() {
Err(Error::last().into())
} else {
Ok(DirectoryStream(dirp))
}
}
pub fn readdir<'a>(dir: &'a mut DirectoryStream) -> Result<Option<DirectoryEntry>> {
let dirent = unsafe {
Errno::clear();
readdir64(dir.0)
};
if dirent.is_null() {
match Errno::last() {
Errno::UnknownErrno => Ok(None),
_ => Err(Error::last().into()),
}
} else {
Ok(Some(DirectoryEntry(unsafe { &*dirent })))
}
}
#[cfg(not(any(target_os = "android")))]
pub fn seekdir<'a>(dir: &'a mut DirectoryStream, loc: c_long) {
unsafe { libc::seekdir(dir.0, loc) };
}
#[cfg(not(any(target_os = "android")))]
pub fn telldir<'a>(dir: &'a mut DirectoryStream) -> c_long {
unsafe { libc::telldir(dir.0) }
}
pub fn dirfd<'a>(dir: &'a mut DirectoryStream) -> Result<RawFd> {
let res = unsafe { libc::dirfd(dir.0) };
Errno::result(res)
}