From c548e1da10f0a81e2308e3e2fbc33ec3f55f030c Mon Sep 17 00:00:00 2001 From: K900 Date: Fri, 10 Feb 2023 01:28:05 +0300 Subject: fix(split-paths): use stdlib functions + correct shell escaping --- scripts/native-utils/src/split_path.rs | 86 +++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 21 deletions(-) (limited to 'scripts/native-utils/src/split_path.rs') diff --git a/scripts/native-utils/src/split_path.rs b/scripts/native-utils/src/split_path.rs index dd10745..15cac59 100644 --- a/scripts/native-utils/src/split_path.rs +++ b/scripts/native-utils/src/split_path.rs @@ -1,38 +1,82 @@ -use std::env; +use std::{ + env, + ffi::{OsStr, OsString}, + io::{self, Write}, + os::unix::prelude::{OsStrExt, OsStringExt}, + path::PathBuf, +}; use clap::Parser; #[derive(Parser, Debug)] struct Args { - #[arg(long)] - automount_root: String, + #[arg(long)] + automount_root: PathBuf, - #[arg(long)] - include_interop: bool, + #[arg(long)] + include_interop: bool, +} + +const SINGLE_QUOTE: u8 = b'\''; +const DOUBLE_QUOTE: u8 = b'"'; + +fn shell_escape(s: &OsStr) -> OsString { + // a shameless ripoff of the Python algorithm: + // https://github.com/python/cpython/blob/f1f3af7b8245e61a2e0abef03b2c6c5902ed7df8/Lib/shlex.py#L323 + let mut result = Vec::new(); + + result.push(SINGLE_QUOTE); + + for &byte in s.as_bytes() { + result.push(byte); + if byte == SINGLE_QUOTE { + result.push(DOUBLE_QUOTE); + result.push(SINGLE_QUOTE); + result.push(DOUBLE_QUOTE); + result.push(SINGLE_QUOTE); + } + } + + result.push(SINGLE_QUOTE); + + OsString::from_vec(result) +} + +fn build_export(var: &str, paths: &[PathBuf]) -> OsString { + let mut result = OsString::new(); + result.push("export "); + result.push(var); + result.push("="); + result.push(shell_escape( + &env::join_paths(paths).expect("paths must be valid"), + )); + result.push("\n"); + result } fn main() -> anyhow::Result<()> { - let args = Args::parse(); + let args = Args::parse(); - let path = env::var("PATH")?; + let path = env::var("PATH")?; - let mut native = vec![]; - let mut interop = vec![]; + let mut native = vec![]; + let mut interop = vec![]; - for part in path.split(':') { - if part.starts_with(&args.automount_root) { - interop.push(part); - } else { - native.push(part); + for part in env::split_paths(&path) { + if part.starts_with(&args.automount_root) { + interop.push(part); + } else { + native.push(part); + } } - } - if args.include_interop { - native.extend(&interop); - }; + if args.include_interop { + native.extend(interop.clone()); + }; - println!("export PATH='{}'", native.join(":")); - println!("export WSLPATH='{}'", interop.join(":")); + let mut lock = io::stdout().lock(); + lock.write_all(build_export("PATH", &native).as_bytes())?; + lock.write_all(build_export("WSLPATH", &interop).as_bytes())?; - Ok(()) + Ok(()) } -- cgit v1.2.3 From acf8ecb3ca21d0dfdea644838678af27cc889d66 Mon Sep 17 00:00:00 2001 From: K900 Date: Fri, 10 Feb 2023 01:38:14 +0300 Subject: fix(split-paths): refactor a bit, add some basic unit tests --- scripts/native-utils/src/split_path.rs | 86 +++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 11 deletions(-) (limited to 'scripts/native-utils/src/split_path.rs') diff --git a/scripts/native-utils/src/split_path.rs b/scripts/native-utils/src/split_path.rs index 15cac59..097863b 100644 --- a/scripts/native-utils/src/split_path.rs +++ b/scripts/native-utils/src/split_path.rs @@ -3,7 +3,7 @@ use std::{ ffi::{OsStr, OsString}, io::{self, Write}, os::unix::prelude::{OsStrExt, OsStringExt}, - path::PathBuf, + path::{Path, PathBuf}, }; use clap::Parser; @@ -54,29 +54,93 @@ fn build_export(var: &str, paths: &[PathBuf]) -> OsString { result } -fn main() -> anyhow::Result<()> { - let args = Args::parse(); - - let path = env::var("PATH")?; - +fn do_split_paths(path: &OsStr, automount_root: &Path, include_interop: bool) -> OsString { let mut native = vec![]; let mut interop = vec![]; for part in env::split_paths(&path) { - if part.starts_with(&args.automount_root) { + if part.starts_with(automount_root) { interop.push(part); } else { native.push(part); } } - if args.include_interop { + if include_interop { native.extend(interop.clone()); }; - let mut lock = io::stdout().lock(); - lock.write_all(build_export("PATH", &native).as_bytes())?; - lock.write_all(build_export("WSLPATH", &interop).as_bytes())?; + let mut result = OsString::new(); + result.push(build_export("PATH", &native)); + result.push(build_export("WSLPATH", &interop)); + result +} + +fn main() -> anyhow::Result<()> { + let args = Args::parse(); + + let path = env::var_os("PATH").expect("PATH is not set, aborting"); + + io::stdout() + .lock() + .write_all(do_split_paths(&path, &args.automount_root, args.include_interop).as_bytes())?; Ok(()) } + +#[cfg(test)] +mod tests { + use std::{ffi::OsString, path::Path}; + + use crate::do_split_paths; + + #[test] + fn simple() { + assert_eq!( + do_split_paths( + &OsString::from("/good/foo:/bad/foo"), + Path::new("/bad"), + false + ), + OsString::from("export PATH='/good/foo'\nexport WSLPATH='/bad/foo'\n") + ); + } + + #[test] + fn exactly_one() { + assert_eq!( + do_split_paths( + &OsString::from("/good/foo"), + Path::new("/bad"), + true + ), + OsString::from("export PATH='/good/foo'\nexport WSLPATH=''\n") + ); + } + + #[test] + fn include_interop() { + assert_eq!( + do_split_paths( + &OsString::from("/good/foo:/bad/foo"), + Path::new("/bad"), + true + ), + OsString::from("export PATH='/good/foo:/bad/foo'\nexport WSLPATH='/bad/foo'\n") + ); + } + + #[test] + fn spicy_escapes() { + assert_eq!( + do_split_paths( + &OsString::from("/good/foo'bar:/bad/foo"), + Path::new("/bad"), + true + ), + OsString::from( + "export PATH='/good/foo'\"'\"'bar:/bad/foo'\nexport WSLPATH='/bad/foo'\n" + ) + ); + } +} -- cgit v1.2.3 From 153c918e9f3a8486ba87399eff43e5b38b39b714 Mon Sep 17 00:00:00 2001 From: K900 Date: Fri, 10 Feb 2023 10:24:47 +0300 Subject: feat: run native-utils tests and rustfmt in CI --- scripts/native-utils/src/split_path.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'scripts/native-utils/src/split_path.rs') diff --git a/scripts/native-utils/src/split_path.rs b/scripts/native-utils/src/split_path.rs index 097863b..16063ab 100644 --- a/scripts/native-utils/src/split_path.rs +++ b/scripts/native-utils/src/split_path.rs @@ -109,11 +109,7 @@ mod tests { #[test] fn exactly_one() { assert_eq!( - do_split_paths( - &OsString::from("/good/foo"), - Path::new("/bad"), - true - ), + do_split_paths(&OsString::from("/good/foo"), Path::new("/bad"), true), OsString::from("export PATH='/good/foo'\nexport WSLPATH=''\n") ); } -- cgit v1.2.3