diff --git a/src/binary/builtins.rs b/src/binary/builtins.rs index a700ef1c7fd07e381a03b1938f3e8c9fa4685a9a..31825b94f68384d194ecf33d7452e5de115db2ef 100644 --- a/src/binary/builtins.rs +++ b/src/binary/builtins.rs @@ -17,6 +17,29 @@ pub fn suspend(args: &[Str], _shell: &mut Shell<'_>) -> Status { Status::SUCCESS } +#[builtin( + desc = "toggle debug mode (print commands)", + man = " +SYNOPSIS + debug on | off + +DESCRIPTION + Turn on or off the feature to print each command executed to stderr (debug mode)." +)] +pub fn debug(args: &[Str], shell: &mut Shell<'_>) -> Status { + match args.get(1).map(Str::as_str) { + Some("on") => shell.set_pre_command(Some(Box::new(|_shell, pipeline| { + // A string representing the command is stored here. + eprintln!("> {}", pipeline); + }))), + Some("off") => shell.set_pre_command(None), + _ => { + return Status::bad_argument("debug: the debug builtin requires on or off as argument") + } + } + Status::SUCCESS +} + #[builtin( desc = "exit the shell", man = " diff --git a/src/binary/completer.rs b/src/binary/completer.rs index d98baf6b409a87fc9ac72d57d03ec38961d7fe07..0f95014f758d21619269e4428a0e72fcea6e0b07 100644 --- a/src/binary/completer.rs +++ b/src/binary/completer.rs @@ -97,7 +97,7 @@ impl<'a, 'b> Completer for IonCompleter<'a, 'b> { let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") { env::split_paths(&paths) .map(|s| { - let s = if !s.to_string_lossy().ends_with("/") { + let s = if !s.to_string_lossy().ends_with('/') { let mut oss = s.into_os_string(); oss.push("/"); oss.into() @@ -134,9 +134,9 @@ impl<'a, 'b> Completer for IonCompleter<'a, 'b> { .nth(index - 1) .map(|(start, end)| event.editor.current_buffer().range(start, end)) .filter(|filename| { - filename.ends_with("|") - || filename.ends_with("&") - || filename.ends_with(";") + filename.ends_with('|') + || filename.ends_with('&') + || filename.ends_with(';') }) .is_some(); if is_pipe { diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs index e76fbd3174d21e31ace89ea90387985de262a479..a5d452f58a4e81f7b5a0ed8dd839187eb7d3634d 100644 --- a/src/lib/builtins/mod.rs +++ b/src/lib/builtins/mod.rs @@ -296,13 +296,13 @@ pub fn cd(args: &[types::Str], shell: &mut Shell<'_>) -> Status { }) .find(Result::is_ok) .unwrap_or_else(|| { - shell.dir_stack_mut().change_and_push_dir(&Path::new(dir)) + shell.dir_stack_mut().change_and_push_dir(Path::new(dir)) }); shell.dir_stack_mut().popd(1); check_cdpath_first } } else { - shell.dir_stack_mut().change_and_push_dir(&Path::new(dir)) + shell.dir_stack_mut().change_and_push_dir(Path::new(dir)) } } None => shell.dir_stack_mut().switch_to_home_directory(), diff --git a/src/lib/builtins/set.rs b/src/lib/builtins/set.rs index bb051178ce1fda8ea0cf3f709a84e7e7cf5677a7..cb5e1438004e91c9d0704bdfcadc4c6dece7ab1f 100644 --- a/src/lib/builtins/set.rs +++ b/src/lib/builtins/set.rs @@ -17,7 +17,7 @@ enum PositionalArgs { desc = "Set or unset values of shell options and positional parameters.", man = " SYNOPSIS - set [ --help ] [-e | +e] [-x | +x] [-o [vi | emacs]] [- | --] [STRING]... + set [ --help ] [-e | +e] [- | --] [STRING]... DESCRIPTION Shell options may be set using the '-' character, and unset using the '+' character. @@ -25,45 +25,38 @@ DESCRIPTION OPTIONS -e Exit immediately if a command exits with a non-zero status. - -o Specifies that an argument will follow that sets the key map. - The keymap argument may be either `vi` or `emacs`. - - -x Specifies that commands will be printed as they are executed. - -- Following arguments will be set as positional arguments in the shell. If no argument are supplied, arguments will be unset. - Following arguments will be set as positional arguments in the shell. - If no arguments are suppled, arguments will not be unset." + If no arguments are suppled, arguments will not be unset. + +BASHÂ EQUIVALENTS + To set the keybindings, see the `keybindings` builtin + To print commands as they are executed (only with the Ion Shell), see `debug`" )] pub fn set(args: &[types::Str], shell: &mut Shell<'_>) -> Status { let mut args_iter = args.iter(); let mut positionals = None; while let Some(arg) = args_iter.next() { - if arg.starts_with("--") { - if arg.len() == 2 { + match arg.as_str() { + "--" => { positionals = Some(PositionalArgs::UnsetIfNone); break; } - return Status::SUCCESS; - } else if arg.starts_with('-') { - if arg.len() == 1 { + "-" => { positionals = Some(PositionalArgs::RetainIfNone); break; } - for flag in arg.bytes().skip(1) { - match flag { - b'e' => shell.opts_mut().err_exit = true, - _ => return Status::SUCCESS, - } - } - } else if arg.starts_with('+') { - for flag in arg.bytes().skip(1) { - match flag { - b'e' => shell.opts_mut().err_exit = false, - _ => return Status::SUCCESS, - } + "-e" => shell.opts_mut().err_exit = true, + "+e" => shell.opts_mut().err_exit = false, + _ => { + return Status::bad_argument(format!( + "set: argument '{}' is not recognized. Try adding `--` before it to pass it \ + as argument to the shell script", + arg + )) } } } diff --git a/src/lib/expansion/words/mod.rs b/src/lib/expansion/words/mod.rs index da1e545383810fc38ef2a413b5b911e12fceb960..484b2aba75214d3620288297b8ac745b74a006c4 100644 --- a/src/lib/expansion/words/mod.rs +++ b/src/lib/expansion/words/mod.rs @@ -646,7 +646,7 @@ impl<'a> WordIterator<'a> { } /// Creates a new iterator with a given expander - pub fn new(data: &'a str, do_glob: bool) -> WordIterator<'a> { + pub const fn new(data: &'a str, do_glob: bool) -> WordIterator<'a> { WordIterator { data, backsl: false, read: 0, quotes: Quotes::None, do_glob } } } diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs index 7dc10164244d151a736f0ecda9d0fd6e2660f36e..fe7ab7e763ea3dbc0cea3f40502d1ee21dd2f752 100644 --- a/src/lib/shell/directory_stack.rs +++ b/src/lib/shell/directory_stack.rs @@ -143,7 +143,7 @@ impl DirectoryStack { self.dirs.remove(0); println!("{}", prev); - self.change_and_push_dir(&Path::new(&prev)) + self.change_and_push_dir(Path::new(&prev)) } pub fn switch_to_home_directory(&mut self) -> Result<(), DirStackError> { diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index 0f5d345f51eb000b5e8ea201cbc19b9526500a80..44a261d7a0d2aeacf2bc84a24c4e9810af00a4b6 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -298,8 +298,8 @@ impl<'a> Shell<'a> { let null_file = if pipeline.pipe == PipeType::Disown { File::open(NULL_PATH).ok() } else { None }; let (stderr, stdout) = ( - null_file.as_ref().or(self.stderr.as_ref()), - null_file.as_ref().or(self.stdout.as_ref()), + null_file.as_ref().or_else(|| self.stderr.as_ref()), + null_file.as_ref().or_else(|| self.stdout.as_ref()), ); for item in &mut pipeline.items { diff --git a/src/lib/shell/shell_expand.rs b/src/lib/shell/shell_expand.rs index 52e21b1690f8a55e0fe3403ccf5ad269f71ffe5d..295cb6d772d4318decd03681cdd8253d78c51769 100644 --- a/src/lib/shell/shell_expand.rs +++ b/src/lib/shell/shell_expand.rs @@ -209,7 +209,7 @@ impl<'a, 'b> Expander for Shell<'b> { match tilde_prefix { "+" => Ok(env::var("PWD").unwrap_or_else(|_| "?".into()).into()), - "-" => Ok(self.variables.get_str("OLDPWD")?.into()), + "-" => Ok(self.variables.get_str("OLDPWD")?), _ => { let (neg, tilde_num) = if tilde_prefix.starts_with('+') { (false, &tilde_prefix[1..]) @@ -219,24 +219,23 @@ impl<'a, 'b> Expander for Shell<'b> { (false, tilde_prefix) }; - match tilde_num.parse() { - Ok(num) => if neg { + if let Ok(num) = tilde_num.parse() { + if neg { self.directory_stack.dir_from_top(num) } else { self.directory_stack.dir_from_bottom(num) } .map(|path| path.to_str().unwrap().into()) - .ok_or_else(|| Error::OutOfStack(num)), - Err(_) => { - let user = if tilde_prefix.is_empty() { - users::get_user_by_uid(users::get_current_uid()) - } else { - users::get_user_by_name(tilde_prefix) - }; - match user { - Some(user) => Ok(user.home_dir().to_string_lossy().as_ref().into()), - None => Err(Error::HomeNotFound), - } + .ok_or_else(|| Error::OutOfStack(num)) + } else { + let user = if tilde_prefix.is_empty() { + users::get_user_by_uid(users::get_current_uid()) + } else { + users::get_user_by_name(tilde_prefix) + }; + match user { + Some(user) => Ok(user.home_dir().to_string_lossy().as_ref().into()), + None => Err(Error::HomeNotFound), } } } diff --git a/src/lib/shell/variables.rs b/src/lib/shell/variables.rs index 9f65a41932dd971edcafe7f27636ccace9407081..09f214cbd7f244ffff6c2dca121ca61292643cf7 100644 --- a/src/lib/shell/variables.rs +++ b/src/lib/shell/variables.rs @@ -6,7 +6,7 @@ use crate::{ }; use nix::unistd::{geteuid, gethostname, getpid, getuid}; use scopes::{Namespace, Scope, Scopes}; -use std::{env, rc::Rc}; +use std::{env, ffi::CStr, rc::Rc}; use unicode_segmentation::UnicodeSegmentation; /// Contain a dynamically-typed variable value @@ -256,7 +256,7 @@ impl<'a> Default for Variables<'a> { "HOST", &gethostname(&mut host_name) .ok() - .map_or_else(|| "?".into(), |hostname| hostname.to_string_lossy()) + .map_or_else(|| "?".into(), CStr::to_string_lossy) .as_ref(), ); diff --git a/src/main.rs b/src/main.rs index d66c44a3987b709f187223d796ff783ae423ae3d..31f7d0703819767a1a78f13fa7c6e2c785a390f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -164,6 +164,7 @@ fn main() { let mut builtins = BuiltinMap::default(); builtins .with_unsafe() + .add("debug", &builtins::builtin_debug, "Toggle debug mode (print commands on exec)") .add("exec", &builtins::builtin_exec, "Replace the shell with the given command.") .add("exit", &builtins::builtin_exit, "Exits the current session") .add("suspend", &builtins::builtin_suspend, "Suspends the shell with a SIGTSTOP signal");