...
 
Commits (5)
This diff is collapsed.
......@@ -8,11 +8,11 @@ use syn;
#[derive(Debug, FromMeta)]
struct MacroArgs {
#[darling(default)]
names: Option<String>,
names: Option<String>,
#[darling(rename = "man")]
help: String,
help: String,
#[darling(default)]
authors: Flag,
authors: Flag,
#[darling(rename = "desc")]
short_description: String,
}
......
......@@ -4,9 +4,9 @@ use super::Index;
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Range {
/// Starting index
start: Index,
start: Index,
/// Ending index
end: Index,
end: Index,
/// Is this range inclusive? If false, this object represents a half-open
/// range of [start, end), otherwise [start, end]
inclusive: bool,
......
......@@ -21,7 +21,7 @@ pub struct Scopes<K: Hash + Eq, V> {
#[derive(Clone, Debug)]
pub struct Scope<K: Hash + Eq, V> {
vars: HashMap<K, V>,
vars: HashMap<K, V>,
/// This scope is on a namespace boundary.
/// Any previous scopes need to be accessed through `super::`.
namespace: bool,
......
......@@ -177,9 +177,9 @@ impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
shell: &'b Shell<'a>,
shell: &'b Shell<'a>,
/// The directory the expansion takes place in
path: PathBuf,
path: PathBuf,
for_command: bool,
}
......
......@@ -8,17 +8,17 @@ use std::time::{SystemTime, UNIX_EPOCH};
pub struct IgnoreSetting {
// Macro definition fails if last flag has a comment at the end of the line.
/// ignore all commands ("all")
all: bool,
all: bool,
/// ignore commands with leading whitespace ("whitespace")
whitespace: bool,
whitespace: bool,
/// ignore commands with status code 127 ("no_such_command")
no_such_command: bool,
/// used if regexes are defined.
based_on_regex: bool,
based_on_regex: bool,
/// ignore commands that are duplicates
duplicates: bool,
duplicates: bool,
// Yes, a bad heap-based Vec, however unfortunately its not possible to store Regex'es in Array
regexes: Vec<Regex>,
regexes: Vec<Regex>,
}
/// Contains all history-related functionality for the `Shell`.
......
......@@ -282,7 +282,51 @@ impl<'a> InteractiveShell<'a> {
}
}
fn exec<T: Fn(&mut Shell<'_>)>(self, prep_for_exit: &T) -> ! {
fn exec_single_command(&mut self, command: &str) {
let cmd: &str =
&designators::expand_designators(&self.context.borrow(), command.trim_end());
self.terminated.set(true);
{
let mut shell = self.shell.borrow_mut();
match shell.on_command(&cmd, true) {
Ok(_) => (),
Err(IonError::PipelineExecutionError(PipelineError::CommandNotFound(command))) => {
if Self::try_cd(&command, &mut shell).ok().map_or(false, |res| res.is_failure())
{
if let Some(Value::Function(func)) =
shell.variables().get("COMMAND_NOT_FOUND").cloned()
{
if let Err(why) = shell.execute_function(&func, &["ion", &command]) {
eprintln!("ion: command not found handler: {}", why);
}
} else {
eprintln!("ion: command not found: {}", command);
}
}
// Status::COULD_NOT_EXEC
}
Err(IonError::PipelineExecutionError(PipelineError::CommandExecError(
ref err,
ref command,
))) if err.kind() == io::ErrorKind::PermissionDenied && command.len() == 1 => {
if Self::try_cd(&command[0], &mut shell)
.ok()
.map_or(false, |res| res.is_failure())
{
eprintln!("ion: {}", err);
shell.reset_flow();
}
}
Err(err) => {
eprintln!("ion: {}", err);
shell.reset_flow();
}
}
}
self.save_command(&cmd);
}
fn exec<T: Fn(&mut Shell<'_>)>(mut self, prep_for_exit: &T) -> ! {
loop {
if let Err(err) = io::stdout().flush() {
eprintln!("ion: failed to flush stdio: {}", err);
......@@ -290,60 +334,15 @@ impl<'a> InteractiveShell<'a> {
if let Err(err) = io::stderr().flush() {
println!("ion: failed to flush stderr: {}", err);
}
let mut lines = std::iter::from_fn(|| self.readln(prep_for_exit))
.flat_map(|s| s.into_bytes().into_iter().chain(Some(b'\n')));
match Terminator::new(&mut lines).terminate() {
Some(command) => {
let cmd: &str = &designators::expand_designators(
&self.context.borrow(),
command.trim_end(),
);
self.terminated.set(true);
match self.readln(prep_for_exit) {
Some(lines) => {
for command in lines
.into_bytes()
.into_iter()
.batching(|bytes| Terminator::new(bytes).terminate())
{
let mut shell = self.shell.borrow_mut();
match shell.on_command(&cmd, true) {
Ok(_) => (),
Err(IonError::PipelineExecutionError(
PipelineError::CommandNotFound(command),
)) => {
if Self::try_cd(&command, &mut shell)
.ok()
.map_or(false, |res| res.is_failure())
{
if let Some(Value::Function(func)) =
shell.variables().get("COMMAND_NOT_FOUND").cloned()
{
if let Err(why) =
shell.execute_function(&func, &["ion", &command])
{
eprintln!("ion: command not found handler: {}", why);
}
} else {
eprintln!("ion: command not found: {}", command);
}
}
// Status::COULD_NOT_EXEC
}
Err(IonError::PipelineExecutionError(
PipelineError::CommandExecError(ref err, ref command),
)) if err.kind() == io::ErrorKind::PermissionDenied
&& command.len() == 1 =>
{
if Self::try_cd(&command[0], &mut shell)
.ok()
.map_or(false, |res| res.is_failure())
{
eprintln!("ion: {}", err);
shell.reset_flow();
}
}
Err(err) => {
eprintln!("ion: {}", err);
shell.reset_flow();
}
}
self.exec_single_command(&command);
}
self.save_command(&cmd);
}
None => self.terminated.set(true),
}
......
......@@ -271,7 +271,7 @@ pub fn source_sh(args: &[types::Str], _shell: &mut Shell<'_>) -> Status {
Ok(f) => f,
Err(e) => return Status::error(format!("Could not create temp file for source-sh: {}", e)),
};
let script = format!("{};env | sort > {}", arg, temp.as_path().display());
let script = format!("{}\nenv | sort > {}", arg, temp.as_path().display());
match Command::new("sh")
.args(&["-c", &script])
.stdout(Stdio::null())
......
......@@ -80,12 +80,12 @@ fn escape(input: &str) -> String {
#[derive(Debug, PartialEq, Clone)]
pub struct StringMethod<'a> {
/// Name of this method
pub method: &'a str,
pub method: &'a str,
/// Variable that this method will operator on. This is a bit of a misnomer
/// as this can be an expression as well
pub variable: &'a str,
pub variable: &'a str,
/// Pattern to use for certain methods
pub pattern: &'a str,
pub pattern: &'a str,
/// Selection to use to control the output of this method
pub selection: Option<&'a str>,
}
......
......@@ -23,9 +23,9 @@ pub enum RedirectFrom {
#[derive(Debug, PartialEq, Clone)]
pub struct Redirection {
/// What to redirect
pub from: RedirectFrom,
pub from: RedirectFrom,
/// Where to redirect
pub file: types::Str,
pub file: types::Str,
/// Should the file be overridden
pub append: bool,
}
......@@ -105,7 +105,7 @@ pub struct Pipeline<T> {
/// The individual commands
pub items: Vec<PipeItem<T>>,
/// Should the pipeline be runned in background
pub pipe: PipeType,
pub pipe: PipeType,
}
/// A single job to run in a pipeline
......@@ -115,11 +115,11 @@ pub struct Pipeline<T> {
#[derive(Debug, PartialEq, Clone)]
pub struct PipeItem<T> {
/// The command to spawn
pub job: T,
pub job: T,
/// Where to send output
pub outputs: Vec<Redirection>,
/// A list of inputs
pub inputs: Vec<Input>,
pub inputs: Vec<Input>,
}
impl<'a> PipeItem<RefinedJob<'a>> {
......
......@@ -24,7 +24,7 @@ pub struct Levels {
/// Parentheses
parens: u8,
/// Array literals
array: u8,
array: u8,
/// Braces
braces: u8,
}
......@@ -99,13 +99,13 @@ impl Levels {
/// An efficient `Iterator` structure for splitting arguments
#[derive(Debug)]
pub struct ArgumentSplitter<'a> {
data: &'a str,
data: &'a str,
/// Number of bytes read
read: usize,
comm: Comm,
read: usize,
comm: Comm,
quotes: bool,
variab: bool,
array: bool,
array: bool,
method: bool,
}
......
......@@ -39,13 +39,13 @@ use std::fmt;
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Case<'a> {
/// The value to match with
pub value: Option<String>,
pub value: Option<String>,
/// Set a variable with the exact result
pub binding: Option<String>,
pub binding: Option<String>,
/// An additional statement to test before matching the case statement
pub conditional: Option<String>,
/// The block to execute on matching input
pub statements: Block<'a>,
pub statements: Block<'a>,
}
/// An elseif case
......@@ -54,7 +54,7 @@ pub struct ElseIf<'a> {
/// The block to test
pub expression: Block<'a>,
/// The block to execute on success
pub success: Block<'a>,
pub success: Block<'a>,
}
/// The action to perform on assignment
......@@ -105,33 +105,33 @@ pub enum Statement<'a> {
/// The block to test
expression: Block<'a>,
/// The block to execute on success
success: Block<'a>,
success: Block<'a>,
/// The list of associated else if blocks
else_if: Vec<ElseIf<'a>>,
else_if: Vec<ElseIf<'a>>,
/// The block to execute on failure
failure: Block<'a>,
failure: Block<'a>,
/// The mode
mode: IfMode,
mode: IfMode,
},
/// else if
ElseIf(ElseIf<'a>),
/// Create a function
Function {
/// the name of the function
name: types::Str,
name: types::Str,
/// the description of the function
description: Option<types::Str>,
/// The required arguments of the function, with their types
args: Vec<KeyBuf>,
args: Vec<KeyBuf>,
/// The statements in the function
statements: Block<'a>,
statements: Block<'a>,
},
/// for loop
For {
/// The bounds
variables: SmallVec<[types::Str; 4]>,
variables: SmallVec<[types::Str; 4]>,
/// The value to iterator for
values: Vec<types::Str>,
values: Vec<types::Str>,
/// The block to execute repetitively
statements: Block<'a>,
},
......@@ -147,7 +147,7 @@ pub enum Statement<'a> {
/// The value to check
expression: types::Str,
/// A list of case to check for
cases: Vec<Case<'a>>,
cases: Vec<Case<'a>>,
},
/// Else statement
Else,
......
......@@ -100,7 +100,7 @@ pub struct TeeItem {
/// Where to read from for this tee. Generally only necessary if we need to tee both
/// stdout and stderr.
pub source: Option<File>,
pub sinks: Vec<File>,
pub sinks: Vec<File>,
}
impl TeeItem {
......
......@@ -105,7 +105,7 @@ pub struct Options {
/// Exit from the shell on the first error.
pub err_exit: bool,
/// Do not execute any commands given to the shell.
pub no_exec: bool,
pub no_exec: bool,
/// If set, denotes that this shell is running as a background job.
pub grab_tty: bool,
}
......@@ -117,32 +117,32 @@ pub struct Options {
pub struct Shell<'a> {
/// Contains a list of built-in commands that were created when the program
/// started.
builtins: BuiltinMap<'a>,
builtins: BuiltinMap<'a>,
/// Contains the aliases, strings, and array variable maps.
variables: Variables<'a>,
variables: Variables<'a>,
/// Contains the current state of flow control parameters.
flow_control: Block<'a>,
flow_control: Block<'a>,
/// Contains the directory stack parameters.
directory_stack: DirectoryStack,
directory_stack: DirectoryStack,
/// When a command is executed, the final result of that command is stored
/// here.
previous_status: Status,
previous_status: Status,
/// The job ID of the previous command sent to the background.
previous_job: usize,
previous_job: usize,
/// Contains all the options relative to the shell
opts: Options,
opts: Options,
/// Contains information on all of the active background processes that are being managed
/// by the shell.
background: Arc<Mutex<Vec<BackgroundProcess>>>,
background: Arc<Mutex<Vec<BackgroundProcess>>>,
/// When the `fg` command is run, this will be used to communicate with the specified
/// background process.
foreground_signals: Arc<foreground::Signals>,
// Callbacks
/// Custom callback for each command call
on_command: Option<OnCommandCallback<'a>>,
on_command: Option<OnCommandCallback<'a>>,
/// Custom callback before each command call
pre_command: Option<PreCommandCallback<'a>>,
pre_command: Option<PreCommandCallback<'a>>,
/// Custom callback when a background event occurs
background_event: Option<BackgroundEventCallback>,
......
......@@ -48,9 +48,9 @@ pub enum RedirectError {
#[error(display = "failed to redirect {} to file '{}': {}", redirect, file, why)]
Output {
redirect: RedirectFrom,
file: String,
file: String,
#[error(cause)]
why: io::Error,
why: io::Error,
},
}
......
......@@ -43,14 +43,14 @@ impl FromStr for KeyBindingsWrapper {
/// Ion is a commandline shell created to be a faster and easier to use
/// alternative to the currently available shells. It is not POSIX compliant.
#[cfg_attr(feature = "advanced_arg_parsing", derive(StructOpt))]
#[cfg_attr(feature = "advanced_arg_parsing", structopt(name = "Ion"))]
#[cfg_attr(feature = "advanced_arg_parsing", structopt(name = "ion"))]
struct CommandLineArgs {
/// Shortcut layout. Valid options: "vi", "emacs"
#[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-o"))]
key_bindings: Option<KeyBindingsWrapper>,
key_bindings: Option<KeyBindingsWrapper>,
/// Print commands before execution
#[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-x"))]
print_commands: bool,
print_commands: bool,
/// Use a fake interactive mode, where errors don't exit the shell
#[cfg_attr(
feature = "advanced_arg_parsing",
......@@ -59,20 +59,20 @@ struct CommandLineArgs {
fake_interactive: bool,
/// Force interactive mode
#[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-i", long = "--interactive"))]
interactive: bool,
interactive: bool,
/// Do not execute any commands, perform only syntax checking
#[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-n", long = "--no-execute"))]
no_execute: bool,
no_execute: bool,
/// Evaluate given commands instead of reading from the commandline
#[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-c"))]
command: Option<String>,
command: Option<String>,
/// Print the version, platform and revision of Ion then exit
#[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-v", long = "--version"))]
version: bool,
version: bool,
/// Script arguments (@args). If the -c option is not specified,
/// the first parameter is taken as a filename to execute
#[cfg_attr(feature = "advanced_arg_parsing", structopt())]
args: Vec<String>,
args: Vec<String>,
}
fn version() -> String { include!(concat!(env!("OUT_DIR"), "/version_string")).to_string() }
......