stree 0.2.0

A directory hierarchy mapping format, similar to mtree
Documentation
use crate::{Error, Result, error};
use std::mem::MaybeUninit;
use std::ffi::CStr;
use std::path::Path;
use pathstr::PathStr;
use mackrow::*;

#[cfg(feature = "magic")]
pub(crate) fn mime<P>(path: P) -> Result<String>
where P: AsRef<Path>
{
    use crate::err;
    use magic::{Cookie, cookie::Flags};
    let cookie = Cookie::open(Flags::MIME_TYPE).map_err(|e| err!(e))?;
    let db = Default::default();
    let cookie = cookie.load(&db).map_err(|e| err!(e))?;
    Ok(cookie.file(path.as_ref()).map_err(|e| err!(e))?)
}

#[cfg(feature = "magic")]
pub(crate) fn encoding<P>(path: P) -> Result<String>
where P: AsRef<Path>
{
    use crate::err;
    use magic::{Cookie, cookie::Flags};
    let cookie = Cookie::open(Flags::MIME_ENCODING).map_err(|e| err!(e))?;
    let db = Default::default();
    let cookie = cookie.load(&db).map_err(|e| err!(e))?;
    Ok(cookie.file(path.as_ref()).map_err(|e| err!(e))?)
}

pub(crate) fn readlink<P>(path: P) -> Result<PathStr>
where P: AsRef<Path>
{
    let path = PathStr::from(path.as_ref());
    let cpath = path.cstring()?;
    let sz: usize;
    let sc = unsafe { libc::pathconf(cpath.as_ptr(), libc::_PC_PATH_MAX) };
    sz = tern!(sc <= 0 ? 4096 : sc as usize);
    let mut buffer = Vec::with_capacity(sz);
    let rc = unsafe { libc::readlink(cpath.as_ptr(), buffer.as_mut_ptr(), buffer.capacity()) };
    if rc == -1
    {
        error!("readlink: {}", errno::errno())
    }
    let cs = unsafe { CStr::from_ptr(buffer.as_ptr()) };
    let mut r = match cs.to_str()
    {
        Ok(s) => s.to_string(),
        _ => cs.to_string_lossy().into_owned()
    };
    r.truncate(rc as usize);
    Ok(PathStr::from(r))
}

pub(crate) fn realpath<P>(path: P) -> Result<PathStr>
where P: AsRef<Path>
{
    let path = PathStr::from(path.as_ref());
    let cpath = path.cstring()?;
    let sz: usize;
    let sc = unsafe { libc::pathconf(cpath.as_ptr(), libc::_PC_PATH_MAX) };
    sz = tern!(sc <= 0 ? 4096 : sc as usize);
    let mut buffer = Vec::with_capacity(sz);
    let res = unsafe { libc::realpath(cpath.as_ptr(), buffer.as_mut_ptr()) };
    if res.is_null()
    {
        error!("realpath: {}", errno::errno())
    }
    let cs = unsafe { CStr::from_ptr(buffer.as_ptr()) };
    Ok(PathStr::from(match cs.to_str()
    {
        Ok(s) => s.to_string(),
        _ => cs.to_string_lossy().into_owned()
    }))
}

pub(crate) fn groupname_from_id(id: libc::gid_t) -> Result<String>
{
    let sz: usize;
    let sc = unsafe { libc::sysconf(libc::_SC_GETGR_R_SIZE_MAX) };
    sz = tern!(sc <= 0 ? 2048 : sc as usize);
    let mut gr = MaybeUninit::<libc::group>::uninit();
    let mut buffer = Vec::with_capacity(sz);
    let mut np = std::ptr::null_mut();

    let rc = unsafe
    {
        libc::getgrgid_r(id, gr.as_mut_ptr(), buffer.as_mut_ptr(), buffer.capacity(), &mut np)
    };

    if rc != 0
    {
        return Err(Error::io(std::io::Error::last_os_error()))
    }

    if np.is_null()
    {
        error!("groupname_from_id: np returned null")
    }

    let gr = unsafe { gr.assume_init() };
    unsafe
    {
        let cs = CStr::from_ptr(gr.gr_name);
        if let Ok(s) = cs.to_str()
        {
            Ok(s.to_string())
        }
        else
        {
            Ok(cs.to_string_lossy().into_owned())
        }
    }
}

pub(crate) fn username_from_id(id: libc::uid_t) -> Result<String>
{
    let sz: usize;
    let sc = unsafe { libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) };
    sz = tern!(sc <= 0 ? 2048 : sc as usize);
    let mut pw = MaybeUninit::<libc::passwd>::uninit();
    let mut buffer = Vec::with_capacity(sz);
    let mut np = std::ptr::null_mut();

    let rc = unsafe
    {
        libc::getpwuid_r(id, pw.as_mut_ptr(), buffer.as_mut_ptr(), buffer.capacity(), &mut np)
    };

    if rc != 0
    {
        return Err(Error::io(std::io::Error::last_os_error()))
    }

    if np.is_null()
    {
        error!("username_from_id: np returned null")
    }
    
    let pw = unsafe { pw.assume_init() };

    unsafe
    {
        let cs = CStr::from_ptr(pw.pw_name);
        if let Ok(s) = cs.to_str()
        {
            Ok(s.to_string())
        }
        else
        {
            Ok(cs.to_string_lossy().into_owned())
        }
    }
}