From ae9c0fc138d705a6d1b7f6dacd76165c03d4a031 Mon Sep 17 00:00:00 2001 From: Tomoki Aonuma Date: Sun, 1 May 2022 03:23:05 +0900 Subject: [PATCH] Fix quoting for command line args (#5384) * Fix quoting for command line args * Replace custom quoting with escape_quote_string * Use raw string for now --- crates/nu-cli/src/util.rs | 17 ++------- crates/nu-command/src/formats/to/nuon.rs | 8 ++--- crates/nu-parser/src/deparse.rs | 14 ++++++++ crates/nu-parser/src/lib.rs | 2 ++ src/main.rs | 44 ++++++++++-------------- 5 files changed, 39 insertions(+), 46 deletions(-) create mode 100644 crates/nu-parser/src/deparse.rs diff --git a/crates/nu-cli/src/util.rs b/crates/nu-cli/src/util.rs index 8ce44b430f..6d72f095f5 100644 --- a/crates/nu-cli/src/util.rs +++ b/crates/nu-cli/src/util.rs @@ -1,7 +1,7 @@ use crate::CliError; use log::trace; use nu_engine::eval_block; -use nu_parser::{lex, parse, unescape_unquote_string, Token, TokenContents}; +use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents}; use nu_protocol::engine::StateWorkingSet; use nu_protocol::{ engine::{EngineState, Stack}, @@ -36,20 +36,9 @@ fn gather_env_vars(vars: impl Iterator, engine_state: & } fn put_env_to_fake_file(name: &str, val: &str, fake_env_file: &mut String) { - fn push_string_literal(s: &str, fake_env_file: &mut String) { - fake_env_file.push('"'); - for c in s.chars() { - if c == '\\' || c == '"' { - fake_env_file.push('\\'); - } - fake_env_file.push(c); - } - fake_env_file.push('"'); - } - - push_string_literal(name, fake_env_file); + fake_env_file.push_str(&escape_quote_string(name)); fake_env_file.push('='); - push_string_literal(val, fake_env_file); + fake_env_file.push_str(&escape_quote_string(val)); fake_env_file.push('\n'); } diff --git a/crates/nu-command/src/formats/to/nuon.rs b/crates/nu-command/src/formats/to/nuon.rs index 74b37175a2..49e29bbe86 100644 --- a/crates/nu-command/src/formats/to/nuon.rs +++ b/crates/nu-command/src/formats/to/nuon.rs @@ -1,5 +1,6 @@ use core::fmt::Write; use nu_engine::get_columns; +use nu_parser::escape_quote_string; use nu_protocol::ast::{Call, RangeInclusion}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ @@ -137,15 +138,10 @@ fn value_to_string(v: &Value, span: Span) -> Result { } Ok(format!("{{{}}}", collection.join(", "))) } - Value::String { val, .. } => Ok(format!("\"{}\"", escape(val))), + Value::String { val, .. } => Ok(escape_quote_string(val)), } } -fn escape(input: &str) -> String { - let output = input.replace('\\', "\\\\"); - output.replace('"', "\\\"") -} - fn to_nuon(call: &Call, input: PipelineData) -> Result { let v = input.into_value(call.head); diff --git a/crates/nu-parser/src/deparse.rs b/crates/nu-parser/src/deparse.rs new file mode 100644 index 0000000000..12f2baab59 --- /dev/null +++ b/crates/nu-parser/src/deparse.rs @@ -0,0 +1,14 @@ +pub fn escape_quote_string(input: &str) -> String { + let mut output = String::with_capacity(input.len() + 2); + output.push('"'); + + for c in input.chars() { + if c == '"' || c == '\\' { + output.push('\\'); + } + output.push(c); + } + + output.push('"'); + output +} diff --git a/crates/nu-parser/src/lib.rs b/crates/nu-parser/src/lib.rs index 1c64301511..ae9cb8f07e 100644 --- a/crates/nu-parser/src/lib.rs +++ b/crates/nu-parser/src/lib.rs @@ -1,3 +1,4 @@ +mod deparse; mod errors; mod flatten; mod known_external; @@ -7,6 +8,7 @@ mod parse_keywords; mod parser; mod type_check; +pub use deparse::escape_quote_string; 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 8eb18699ee..5d0377b6e8 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::parse; +use nu_parser::{escape_quote_string, parse}; use nu_protocol::{ ast::{Call, Expr, Expression}, engine::{Command, EngineState, Stack, StateWorkingSet}, @@ -79,36 +79,28 @@ fn main() -> Result<()> { // Would be nice if we had a way to parse this. The first flags we see will be going to nushell // then it'll be the script name // then the args to the script - let mut collect_arg_nushell = false; - for arg in std::env::args().skip(1) { + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { if !script_name.is_empty() { - args_to_script.push(if arg.contains(' ') { - format!("`{}`", arg) - } else { - arg - }); - } else if collect_arg_nushell { - args_to_nushell.push(if arg.contains(' ') { - format!("`{}`", arg) - } else { - arg - }); - collect_arg_nushell = false; + args_to_script.push(escape_quote_string(&arg)); } else if arg.starts_with('-') { // Cool, it's a flag - if arg == "-c" - || arg == "--commands" - || arg == "--testbin" - || arg == "--log-level" - || arg == "--config" - || arg == "--env-config" - || arg == "--threads" - || arg == "-t" - { - collect_arg_nushell = true; - } + let flag_value = match arg.as_ref() { + "--commands" | "-c" => { + // FIXME: Use proper quoting. `escape_quote_string()` can't be used for now due to https://github.com/nushell/nushell/issues/5383. + + args.next().map(|a| format!("`{}`", a)) + } + "--config" | "--env-config" => args.next().map(|a| escape_quote_string(&a)), + "--log-level" | "--testbin" | "--threads" | "-t" => args.next(), + _ => None, + }; args_to_nushell.push(arg); + + if let Some(flag_value) = flag_value { + args_to_nushell.push(flag_value); + } } else { // Our script file script_name = arg;