cli_util 0.2.35

Command-line utilitiy for unix based systems
Documentation
//!
//!      Change working directory
//!
//!          cd <dir>
//!
//!      if \<dir\> omitted, then goto home directory
use glob::glob;
use std::env;
use std::ffi::{CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use libc::{c_char, getpwuid_r, getuid, passwd};

pub fn cd(_args: &str) -> std::io::Result<()> {
    if _args.len() == 0 {
        // if parameter omitted
        match get_home_dir() {
            Some(home) => {
                if env::set_current_dir(&home).is_ok() {
                } else {
                    eprintln!("Failed to change to home directory");
                }
            }
            None => eprintln!("Could not determine home directory"),
        }
        return Ok(());
    }

    let pattern = &_args;
    let mut matched = false;

    // Searches directories using pattern
    for entry in glob(pattern).expect("Failed to read glob pattern") {
        match entry {
            Ok(path) => {
                if path.is_dir() {
                    if env::set_current_dir(&path).is_ok() {
                        matched = true;
                        break; // Moves to the first matching directory and ends the loop
                    } else {
                        eprintln!("Failed to change directory to {}", path.display());
                    }
                }
            }
            Err(e) => eprintln!("Error: {:?}", e),
        }
    }

    if !matched {
        eprintln!("cd: no such file or directory: {}", pattern);
    }

    Ok(())
}

/// Get home directory
fn get_home_dir() -> Option<PathBuf> {
    unsafe {
        let mut buf = Vec::with_capacity(1024);
        let mut pwd = std::mem::MaybeUninit::<passwd>::uninit();
        let mut result = std::ptr::null_mut();
        let uid = getuid();

        if getpwuid_r(
            uid,
            pwd.as_mut_ptr(),
            buf.as_mut_ptr() as *mut c_char,
            buf.capacity(),
            &mut result,
        ) == 0
            && !result.is_null()
        {
            let pwd = pwd.assume_init();
            let home = CStr::from_ptr(pwd.pw_dir);
            Some(PathBuf::from(<OsStr as OsStrExt>::from_bytes(
                home.to_bytes(),
            )))
        } else {
            None
        }
    }
}