1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! # Quick start
//!
//! ```rust
//! extern crate v_shellescape;
//! use v_shellescape::{unix, windows};
//!
//! print!("{}", unix::ShellEscape::from("linker=gcc -L/foo -Wl,bar"));
//! print!("{}", windows::ShellEscape::from("linker=gcc -L/foo -Wl,bar"));
//! ```
//!

#[macro_use]
extern crate v_escape;

use std::env;
use std::fmt::{self, Display, Formatter};

#[path = "unix.rs"]
mod _unix;
#[path = "windows.rs"]
mod _windows;

macro_rules! new_shellescape {
    () => {
        use std::fmt::{self, Display, Formatter};

        pub struct ShellEscape<'a> {
            bytes: &'a [u8],
        }
        _v_escape_escape_new!(ShellEscape);
    };
}

pub mod unix {
    use super::_unix::*;
    new_shellescape!();
    _v_escape_cfg_escape!(false, false, false);
}

pub mod windows {
    use super::_windows::*;
    new_shellescape!();
    _v_escape_cfg_escape!(false, false, false);
}

pub struct Escaped<'a> {
    s: &'a str,
}

impl<'a> Escaped<'a> {
    pub fn new(s: &str) -> Escaped {
        Escaped { s }
    }
}

impl<'a> Display for Escaped<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        if cfg!(unix) || env::var("MSYSTEM").is_ok() {
            unix::ShellEscape::from(self.s).fmt(f)
        } else {
            windows::ShellEscape::from(self.s).fmt(f)
        }
    }
}

/// Escape characters that may have special meaning in a shell.
pub fn escape(s: &str) -> Escaped {
    Escaped::new(s)
}

#[cfg(test)]
mod test {
    #[cfg(target_os = "unix")]
    mod unix {
        use super::super::escape;

        #[test]
        fn test() {
            assert_eq!(
                escape("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+")
                    .to_string(),
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_=/,.+"
            );
            assert_eq!(escape("--aaa=bbb-ccc").to_string(), "--aaa=bbb-ccc");
            assert_eq!(
                escape("linker=gcc -L/foo -Wl,bar").to_string(),
                r#"'linker=gcc -L/foo -Wl,bar'"#
            );
            assert_eq!(
                escape(r#"--features="default""#).to_string(),
                r#"'--features="default"'"#
            );
            assert_eq!(
                ShellEscape::from(r#"'!\$`\\\n "#).to_string(),
                r#"''\'''\!'\$`\\\n '"#
            );
        }
    }

    #[cfg(target_os = "windows")]
    mod windows {
        use super::super::escape;

        #[test]
        fn test() {
            assert_eq!(escape("--aaa=bbb-ccc").to_string(), "--aaa=bbb-ccc");
            assert_eq!(
                escape("linker=gcc -L/foo -Wl,bar").to_string(),
                r#""linker=gcc -L/foo -Wl,bar""#
            );
            assert_eq!(
                escape(r#"--features="default""#).to_string(),
                r#""--features=\"default\"""#
            );
            assert_eq!(
                escape(r#"\path\to\my documents\"#).to_string(),
                r#""\path\to\my documents\\""#
            );
        }
    }
}