diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs index 906e3be7f42795094cd86daa57ad495abcf114a5..afd4c1a1295dfdc5c911f6667a0ba59077abe7b9 100644 --- a/src/lib/builtins/mod.rs +++ b/src/lib/builtins/mod.rs @@ -38,7 +38,7 @@ use hashbrown::HashMap; use liner::{Completer, Context}; use crate::{ - shell::{status::Status, Capture, Shell}, + shell::{status::Status, Capture, Shell, Value}, sys, types, }; use itertools::Itertools; @@ -191,7 +191,8 @@ impl<'a> BuiltinMap<'a> { "popd" => builtin_popd : "Pop a directory from the stack", "pushd" => builtin_pushd : "Push a directory to the stack", "dirs" => builtin_dirs : "Display the current directory stack", - "cd" => builtin_cd : "Change the current directory\n cd <path>" + "cd" => builtin_cd : "Change the current directory\n cd <path>", + "dir_depth" => builtin_dir_depth : "Set the maximum directory depth" ) } @@ -260,12 +261,49 @@ pub fn builtin_status(args: &[small::String], shell: &mut Shell<'_>) -> Status { status(args, shell) } +pub fn builtin_dir_depth(args: &[small::String], shell: &mut Shell<'_>) -> Status { + let depth = match args.get(1) { + None => None, + Some(arg) => match arg.parse::<usize>() { + Ok(num) => Some(num), + Err(_) => return Status::error("dir_depth's argument must be a positive integer"), + }, + }; + shell.dir_stack_mut().set_max_depth(depth); + Status::SUCCESS +} + pub fn builtin_cd(args: &[small::String], shell: &mut Shell<'_>) -> Status { if check_help(args, MAN_CD) { return Status::SUCCESS; } - match shell.cd(args.get(1)) { + let err = match args.get(1) { + Some(dir) => { + let dir = dir.as_ref(); + if let Some(Value::Array(cdpath)) = shell.variables().get_ref("CDPATH").cloned() { + if dir == "-" { + shell.dir_stack_mut().switch_to_previous_directory() + } else { + let check_cdpath_first = cdpath + .iter() + .map(|path| { + let path_dir = format!("{}/{}", path, dir); + shell.dir_stack_mut().change_and_push_dir(&path_dir) + }) + .find(Result::is_ok) + .unwrap_or_else(|| shell.dir_stack_mut().change_and_push_dir(dir)); + shell.dir_stack_mut().popd(1); + check_cdpath_first + } + } else { + shell.dir_stack_mut().change_and_push_dir(dir) + } + } + None => shell.dir_stack_mut().switch_to_home_directory(), + }; + + match err { Ok(()) => { let _ = shell.fork_function(Capture::None, |_| Ok(()), "CD_CHANGE", &["ion"]); Status::SUCCESS @@ -336,7 +374,7 @@ pub fn builtin_dirs(args: &[small::String], shell: &mut Shell<'_>) -> Status { } if clear { - shell.clear_dir_stack(); + shell.dir_stack_mut().clear(); } let mapper: fn((usize, &PathBuf)) -> Cow<'_, str> = match (abs_pathnames, index) { @@ -347,13 +385,13 @@ pub fn builtin_dirs(args: &[small::String], shell: &mut Shell<'_>) -> Status { (false, false) => |(_, x)| x.to_string_lossy(), }; - let mut iter = shell.dir_stack().enumerate().map(mapper); + let mut iter = shell.dir_stack().dirs().enumerate().map(mapper); if let Some(arg) = num_arg { let num = match parse_numeric_arg(arg.as_ref()) { Some((true, num)) => num, - Some((false, num)) if shell.dir_stack().count() > num => { - shell.dir_stack().count() - num - 1 + Some((false, num)) if shell.dir_stack().dirs().count() > num => { + shell.dir_stack().dirs().count() - num - 1 } _ => return Status::error(format!("ion: dirs: {}: invalid argument", arg)), }; @@ -404,27 +442,27 @@ pub fn builtin_pushd(args: &[small::String], shell: &mut Shell<'_>) -> Status { match action { Action::Switch => { if !keep_front { - if let Err(why) = shell.swap(1) { + if let Err(why) = shell.dir_stack_mut().swap(1) { return Status::error(format!("ion: pushd: {}", why)); } } } Action::RotLeft(num) => { if !keep_front { - if let Err(why) = shell.rotate_left(num) { + if let Err(why) = shell.dir_stack_mut().rotate_left(num) { return Status::error(format!("ion: pushd: {}", why)); } } } Action::RotRight(num) => { if !keep_front { - if let Err(why) = shell.rotate_right(num) { + if let Err(why) = shell.dir_stack_mut().rotate_right(num) { return Status::error(format!("ion: pushd: {}", why)); } } } Action::Push(dir) => { - if let Err(why) = shell.pushd(dir, keep_front) { + if let Err(why) = shell.dir_stack_mut().pushd(dir, keep_front) { return Status::error(format!("ion: pushd: {}", why)); } } @@ -432,7 +470,11 @@ pub fn builtin_pushd(args: &[small::String], shell: &mut Shell<'_>) -> Status { println!( "{}", - shell.dir_stack().map(|dir| dir.to_str().unwrap_or("ion: no directory found")).join(" ") + shell + .dir_stack() + .dirs() + .map(|dir| dir.to_str().unwrap_or("ion: no directory found")) + .join(" ") ); Status::SUCCESS } @@ -442,7 +484,7 @@ pub fn builtin_popd(args: &[small::String], shell: &mut Shell<'_>) -> Status { return Status::SUCCESS; } - let len = shell.dir_stack().len(); + let len = shell.dir_stack().dirs().len(); if len <= 1 { return Status::error("ion: popd: directory stack empty"); } @@ -478,17 +520,18 @@ pub fn builtin_popd(args: &[small::String], shell: &mut Shell<'_>) -> Status { index = 1; } else if index == 0 { // change to new directory, return if not possible - if let Err(why) = shell.set_current_dir_by_index(1) { + if let Err(why) = shell.dir_stack_mut().set_current_dir_by_index(1) { return Status::error(format!("ion: popd: {}", why)); } } // pop element - if shell.popd(index).is_some() { + if shell.dir_stack_mut().popd(index).is_some() { println!( "{}", shell .dir_stack() + .dirs() .map(|dir| dir.to_str().unwrap_or("ion: no directory found")) .join(" ") ); diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs index 28e89b47956c2ac33711dc5de412a71c9f36beb5..25bf4a954eb403b79fdabf7da81c757564abdd36 100644 --- a/src/lib/shell/directory_stack.rs +++ b/src/lib/shell/directory_stack.rs @@ -1,4 +1,3 @@ -use super::variables::{Value, Variables}; use crate::sys::env as sys_env; use err_derive::Error; use std::{ @@ -44,7 +43,8 @@ fn set_current_dir_ion(dir: &Path) -> Result<(), DirStackError> { #[derive(Debug)] pub struct DirectoryStack { - dirs: VecDeque<PathBuf>, // The top is always the current directory + dirs: VecDeque<PathBuf>, // The top is always the current directory + max_depth: Option<usize>, } impl DirectoryStack { @@ -72,6 +72,10 @@ impl DirectoryStack { new_dir } + pub fn set_max_depth(&mut self, max_depth: Option<usize>) { self.max_depth = max_depth; } + + pub fn max_depth(&mut self) -> Option<usize> { self.max_depth } + // pushd -<num> pub fn rotate_right(&mut self, num: usize) -> Result<(), DirStackError> { let len = self.dirs.len(); @@ -105,24 +109,24 @@ impl DirectoryStack { self.dirs.iter() } - fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables<'_>) { + fn insert_dir(&mut self, index: usize, path: PathBuf) { self.dirs.insert(index, path); - self.dirs.truncate(DirectoryStack::get_size(variables)); + if let Some(max_depth) = self.max_depth { + self.dirs.truncate(max_depth); + } } - fn push_dir(&mut self, path: PathBuf, variables: &Variables<'_>) { + fn push_dir(&mut self, path: PathBuf) { self.dirs.push_front(path); - self.dirs.truncate(DirectoryStack::get_size(variables)); + if let Some(max_depth) = self.max_depth { + self.dirs.truncate(max_depth); + } } - fn change_and_push_dir( - &mut self, - dir: &str, - variables: &Variables<'_>, - ) -> Result<(), DirStackError> { + pub fn change_and_push_dir(&mut self, dir: &str) -> Result<(), DirStackError> { let new_dir = self.normalize_path(dir); set_current_dir_ion(&new_dir)?; - self.push_dir(new_dir, variables); + self.push_dir(new_dir); Ok(()) } @@ -130,56 +134,22 @@ impl DirectoryStack { env::var("OLDPWD").ok().filter(|pwd| !pwd.is_empty() && pwd != "?") } - fn switch_to_previous_directory( - &mut self, - variables: &Variables<'_>, - ) -> Result<(), DirStackError> { + pub fn switch_to_previous_directory(&mut self) -> Result<(), DirStackError> { let prev = self.get_previous_dir().ok_or(DirStackError::NoPreviousDir)?; self.dirs.remove(0); println!("{}", prev); - self.change_and_push_dir(&prev, variables) + self.change_and_push_dir(&prev) } - fn switch_to_home_directory(&mut self, variables: &Variables<'_>) -> Result<(), DirStackError> { + pub fn switch_to_home_directory(&mut self) -> Result<(), DirStackError> { sys_env::home_dir().map_or(Err(DirStackError::FailedFetchHome), |home| { home.to_str().map_or(Err(DirStackError::PathConversionFailed), |home| { - self.change_and_push_dir(home, variables) + self.change_and_push_dir(home) }) }) } - pub fn cd<T: AsRef<str>>( - &mut self, - dir: Option<T>, - variables: &Variables<'_>, - ) -> Result<(), DirStackError> { - match dir { - Some(dir) => { - let dir = dir.as_ref(); - if let Some(Value::Array(cdpath)) = variables.get_ref("CDPATH") { - if dir == "-" { - self.switch_to_previous_directory(variables) - } else { - let check_cdpath_first = cdpath - .iter() - .map(|path| { - let path_dir = format!("{}/{}", path, dir); - self.change_and_push_dir(&path_dir, variables) - }) - .find(Result::is_ok) - .unwrap_or_else(|| self.change_and_push_dir(dir, variables)); - self.dirs.remove(1); - check_cdpath_first - } - } else { - self.change_and_push_dir(dir, variables) - } - } - None => self.switch_to_home_directory(variables), - } - } - pub fn swap(&mut self, index: usize) -> Result<(), DirStackError> { if self.dirs.len() <= index { return Err(DirStackError::NoOtherDir); @@ -188,15 +158,10 @@ impl DirectoryStack { self.set_current_dir_by_index(0) } - pub fn pushd( - &mut self, - path: PathBuf, - keep_front: bool, - variables: &mut Variables<'_>, - ) -> Result<(), DirStackError> { + pub fn pushd(&mut self, path: PathBuf, keep_front: bool) -> Result<(), DirStackError> { let index = if keep_front { 1 } else { 0 }; let new_dir = self.normalize_path(path.to_str().unwrap()); - self.insert_dir(index, new_dir, variables); + self.insert_dir(index, new_dir); self.set_current_dir_by_index(index) } @@ -206,19 +171,6 @@ impl DirectoryStack { pub fn clear(&mut self) { self.dirs.truncate(1) } - /// This function will take a map of variables as input and attempt to parse the value of - /// the - /// directory stack size variable. If it succeeds, it will return the value of that - /// variable, - /// else it will return a default value of 1000. - fn get_size(variables: &Variables<'_>) -> usize { - variables - .get_str("DIRECTORY_STACK_SIZE") - .unwrap_or_default() - .parse::<usize>() - .unwrap_or(1000) - } - /// Create a new `DirectoryStack` containing the current working directory, /// if available. pub fn new() -> DirectoryStack { @@ -233,6 +185,6 @@ impl DirectoryStack { env::set_var("PWD", "?"); } } - DirectoryStack { dirs } + DirectoryStack { dirs, max_depth: None } } } diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index 6793c1cbe3b204ea344c8ba1d70cdcfae7a49eec..63b8b7b34f9cc8b57164763bb385632617fd57c8 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -14,7 +14,7 @@ pub mod variables; pub(crate) use self::job::Job; use self::{ - directory_stack::{DirStackError, DirectoryStack}, + directory_stack::DirectoryStack, flow_control::{Block, FunctionError, Statement}, foreground::ForegroundSignals, fork::{Fork, IonResult}, @@ -38,7 +38,7 @@ use std::{ fs::{self, OpenOptions}, io::{self, Write}, ops::{Deref, DerefMut}, - path::{Path, PathBuf}, + path::Path, process, sync::{atomic::Ordering, Arc, Mutex}, time::SystemTime, @@ -229,37 +229,9 @@ impl<'a> Shell<'a> { } } - pub fn rotate_right(&mut self, num: usize) -> Result<(), DirStackError> { - self.directory_stack.rotate_right(num) - } - - pub fn rotate_left(&mut self, num: usize) -> Result<(), DirStackError> { - self.directory_stack.rotate_left(num) - } - - pub fn swap(&mut self, index: usize) -> Result<(), DirStackError> { - self.directory_stack.swap(index) - } - - pub fn set_current_dir_by_index(&self, index: usize) -> Result<(), DirStackError> { - self.directory_stack.set_current_dir_by_index(index) - } - - pub fn cd<T: AsRef<str>>(&mut self, dir: Option<T>) -> Result<(), DirStackError> { - self.directory_stack.cd(dir, &self.variables) - } - - pub fn pushd(&mut self, path: PathBuf, keep_front: bool) -> Result<(), DirStackError> { - self.directory_stack.pushd(path, keep_front, &mut self.variables) - } - - pub fn popd(&mut self, index: usize) -> Option<PathBuf> { self.directory_stack.popd(index) } - - pub fn dir_stack(&self) -> impl DoubleEndedIterator<Item = &PathBuf> + ExactSizeIterator { - self.directory_stack.dirs() - } + pub fn dir_stack(&self) -> &DirectoryStack { &self.directory_stack } - pub fn clear_dir_stack(&mut self) { self.directory_stack.clear() } + pub fn dir_stack_mut(&mut self) -> &mut DirectoryStack { &mut self.directory_stack } /// Resets the flow control fields to their default values. pub fn reset_flow(&mut self) { self.flow_control.clear(); }