diff --git a/crates/nu-parser/src/deparse.rs b/crates/nu-parser/src/deparse.rs index 12f2baab59..4cc26c96d9 100644 --- a/crates/nu-parser/src/deparse.rs +++ b/crates/nu-parser/src/deparse.rs @@ -1,3 +1,6 @@ +use std::fs::File; +use std::io::{BufRead, BufReader}; + pub fn escape_quote_string(input: &str) -> String { let mut output = String::with_capacity(input.len() + 2); output.push('"'); @@ -12,3 +15,95 @@ pub fn escape_quote_string(input: &str) -> String { output.push('"'); output } + +fn looks_like_flag(input: &str) -> bool { + if !input.starts_with('-') { + false + // it does not start with '-' + } else if !input.starts_with("--") { + if input.len() > 2 + && input.chars().nth(2).expect("this should never trigger") != '=' + && input.chars().nth(2).expect("this should never trigger") != ' ' + { + false + // while it start with '-', it is not of the form '-x=y' or '-x y' + } else { + input.len() >= 2 + } + } else { + input.len() > 2 + // it is either a flag --x or a '--' + } +} + +fn escape_quote_string_when_flags_are_unclear(input: &str) -> String { + // internal use only. When reading the file for flags goes wrong, revert back to a manual check + // for flags. + let mut output = String::new(); + if !looks_like_flag(input) { + output.push('"'); + for c in input.chars() { + if c == '"' || c == '\\' { + output.push('\\'); + } + output.push(c); + } + output.push('"'); + output + } else if input.contains(' ') || input.contains('=') { + // this is a flag that requires delicate handling + let mut flag_tripped = false; + for c in input.chars() { + if c == '"' || c == '\\' { + output.push('\\'); + } + output.push(c); + if (c == ' ' || c == '=') && !flag_tripped { + flag_tripped = true; + output.push('"'); + } + } + output.push('"'); + output + } else { + // this is a normal flag, aka "--x" + String::from(input) + } +} + +pub fn escape_quote_string_with_file(input: &str, file: &str) -> String { + // use when you want to cross-compare to a file to ensure flags are checked properly + let file = File::open(file); + match file { + Ok(f) => { + let lines = BufReader::new(f).lines(); + for line in lines { + let mut flag_start = false; + let mut word = String::new(); + let line_or = line.unwrap_or_else(|_| String::from(" ")); + if line_or.contains('-') { + for n in line_or.chars() { + if n == '-' { + flag_start = true; + } + if n == ' ' || n == ':' || n == ')' { + flag_start = false; + } + if flag_start { + word.push(n); + } + } + } + if word == input { + return word; + } + } + let mut final_word = String::new(); + final_word.push('"'); + final_word.push_str(input); + final_word.push('"'); + final_word + } + _ => escape_quote_string_when_flags_are_unclear(input), + } +} diff --git a/crates/nu-parser/src/lib.rs b/crates/nu-parser/src/lib.rs index c9997cf0ce..cea85bcf31 100644 --- a/crates/nu-parser/src/lib.rs +++ b/crates/nu-parser/src/lib.rs @@ -8,7 +8,7 @@ mod parse_keywords; mod parser; mod type_check; -pub use deparse::escape_quote_string; +pub use deparse::{escape_quote_string, escape_quote_string_with_file}; pub use errors::ParseError; pub use flatten::{flatten_block, flatten_expression, flatten_pipeline, FlatShape}; pub use known_external::KnownExternal; diff --git a/src/main.rs b/src/main.rs index f7276b4a2e..55da1c6772 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use nu_cli::{ }; use nu_command::{create_default_context, BufferedReader}; use nu_engine::{get_full_help, CallExt}; -use nu_parser::{escape_quote_string, parse}; +use nu_parser::{escape_quote_string, escape_quote_string_with_file, parse}; use nu_protocol::{ ast::{Call, Expr, Expression}, engine::{Command, EngineState, Stack, StateWorkingSet}, @@ -82,7 +82,7 @@ fn main() -> Result<()> { let mut args = std::env::args().skip(1); while let Some(arg) = args.next() { if !script_name.is_empty() { - args_to_script.push(escape_quote_string(&arg)); + args_to_script.push(escape_quote_string_with_file(&arg, &script_name)); } else if arg.starts_with('-') { // Cool, it's a flag let flag_value = match arg.as_ref() {