Skip to content

Commit

Permalink
Handle Arguments With Invalid Commands (#280)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmstick committed May 12, 2017
1 parent ae73d27 commit fe47660
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 9 deletions.
38 changes: 30 additions & 8 deletions src/parser/statements.rs
Expand Up @@ -19,20 +19,25 @@ const METHOD: u16 = 256;


#[derive(Debug, PartialEq)]
pub enum StatementError {
pub enum StatementError<'a> {
IllegalCommandName(&'a str),
InvalidCharacter(char, usize),
UnterminatedSubshell,
UnterminatedBracedVar,
UnterminatedBrace,
UnterminatedMethod,
ExpectedCommandButFound(&'static str)
}

pub fn check_statement(statement: Result<&str, StatementError>) -> Option<Statement> {
pub fn check_statement<'a>(statement: Result<&str, StatementError<'a>>) -> Statement {
match statement {
Ok(statement) => Some(parse(statement)),
Ok(statement) => parse(statement),
Err(err) => {
let stderr = io::stderr();
match err {
StatementError::IllegalCommandName(command) => {
let _ = writeln!(stderr.lock(), "ion: illegal command name: {}", command);
}
StatementError::InvalidCharacter(character, position) => {
let _ = writeln!(stderr.lock(),
"ion: syntax error: '{}' at position {} is out of place",
Expand All @@ -50,8 +55,11 @@ pub fn check_statement(statement: Result<&str, StatementError>) -> Option<Statem
StatementError::UnterminatedMethod => {
let _ = writeln!(stderr.lock(), "ion: syntax error: unterminated method");
}
StatementError::ExpectedCommandButFound(element) => {
let _ = writeln!(stderr.lock(), "ion: expected command, but found {}", element);
}
}
None
Statement::Error(-1)
}
}
}
Expand Down Expand Up @@ -81,8 +89,8 @@ impl<'a> StatementSplitter<'a> {
}

impl<'a> Iterator for StatementSplitter<'a> {
type Item = Result<&'a str, StatementError>;
fn next(&mut self) -> Option<Result<&'a str, StatementError>> {
type Item = Result<&'a str, StatementError<'a>>;
fn next(&mut self) -> Option<Result<&'a str, StatementError<'a>>> {
let start = self.read;
let mut first_arg_found = false;
let mut else_found = false;
Expand Down Expand Up @@ -209,8 +217,17 @@ impl<'a> Iterator for StatementSplitter<'a> {
},
None if self.flags & METHOD != 0 => Some(Err(StatementError::UnterminatedMethod)),
None if self.flags & VBRACE != 0 => Some(Err(StatementError::UnterminatedBracedVar)),
None if self.brace_level != 0 => Some(Err(StatementError::UnterminatedBrace)),
None => Some(Ok(self.data[start..].trim()))
None if self.brace_level != 0 => Some(Err(StatementError::UnterminatedBrace)),
None => {
let output = self.data[start..].trim();
match output.as_bytes()[0] {
b'>' | b'<' | b'^' => Some(Err(StatementError::ExpectedCommandButFound("redirection"))),
b'|' => Some(Err(StatementError::ExpectedCommandButFound("pipe"))),
b'&' => Some(Err(StatementError::ExpectedCommandButFound("&"))),
b'*' | b'%' | b'?' | b'{' | b'}' => Some(Err(StatementError::IllegalCommandName(output))),
_ => Some(Ok(output))
}
}
}
}
}
Expand All @@ -225,6 +242,11 @@ fn syntax_errors() {
assert_eq!(results[2], Err(StatementError::InvalidCharacter(')', 42)));
assert_eq!(results[3], Err(StatementError::UnterminatedSubshell));
assert_eq!(results.len(), 4);

let command = ">echo";
let results = StatementSplitter::new(command).collect::<Vec<Result<&str, StatementError>>>();
assert_eq!(results[0], Err(StatementError::ExpectedCommandButFound("redirection")));
assert_eq!(results.len(), 1);
}

#[test]
Expand Down
5 changes: 4 additions & 1 deletion src/shell/flow.rs
Expand Up @@ -41,7 +41,7 @@ pub trait FlowLogic {

impl<'a> FlowLogic for Shell<'a> {
fn on_command(&mut self, command_string: &str) {
let mut iterator = StatementSplitter::new(command_string).filter_map(check_statement);
let mut iterator = StatementSplitter::new(command_string).map(check_statement);

// If the value is set to `0`, this means that we don't need to append to an existing
// partial statement block in memory, but can read and execute new statements.
Expand Down Expand Up @@ -101,6 +101,7 @@ impl<'a> FlowLogic for Shell<'a> {
mem::swap(&mut self.flow_control.current_statement, &mut replacement);

match replacement {
Statement::Error(number) => self.previous_status = number,
Statement::Let { expression } => {
self.previous_status = let_assignment(expression, &mut self.variables, &self.directory_stack);
},
Expand Down Expand Up @@ -143,6 +144,7 @@ impl<'a> FlowLogic for Shell<'a> {
let mut iterator = statements.drain(..);
while let Some(statement) = iterator.next() {
match statement {
Statement::Error(number) => self.previous_status = number,
Statement::Let { expression } => {
self.previous_status = let_assignment(expression, &mut self.variables, &self.directory_stack);
},
Expand Down Expand Up @@ -281,6 +283,7 @@ impl<'a> FlowLogic for Shell<'a> {
where I: Iterator<Item = Statement>
{
match statement {
Statement::Error(number) => self.previous_status = number,
// Execute a Let Statement
Statement::Let { expression } => {
self.previous_status = let_assignment(expression, &mut self.variables, &self.directory_stack);
Expand Down
1 change: 1 addition & 0 deletions src/shell/flow_control.rs
Expand Up @@ -37,6 +37,7 @@ pub enum Statement {
},
Else,
End,
Error(i32),
Break,
Continue,
Pipeline(Pipeline),
Expand Down

0 comments on commit fe47660

Please sign in to comment.