1*eeb7e5b3SAdam Hornáček // The MIT License (MIT)
2*eeb7e5b3SAdam Hornáček //
3*eeb7e5b3SAdam Hornáček // Copyright (c) 2015 Andrew Gallant
4*eeb7e5b3SAdam Hornáček //
5*eeb7e5b3SAdam Hornáček // Permission is hereby granted, free of charge, to any person obtaining a copy
6*eeb7e5b3SAdam Hornáček // of this software and associated documentation files (the "Software"), to deal
7*eeb7e5b3SAdam Hornáček // in the Software without restriction, including without limitation the rights
8*eeb7e5b3SAdam Hornáček // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9*eeb7e5b3SAdam Hornáček // copies of the Software, and to permit persons to whom the Software is
10*eeb7e5b3SAdam Hornáček // furnished to do so, subject to the following conditions:
11*eeb7e5b3SAdam Hornáček //
12*eeb7e5b3SAdam Hornáček // The above copyright notice and this permission notice shall be included in
13*eeb7e5b3SAdam Hornáček // all copies or substantial portions of the Software.
14*eeb7e5b3SAdam Hornáček //
15*eeb7e5b3SAdam Hornáček // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*eeb7e5b3SAdam Hornáček // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*eeb7e5b3SAdam Hornáček // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18*eeb7e5b3SAdam Hornáček // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*eeb7e5b3SAdam Hornáček // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20*eeb7e5b3SAdam Hornáček // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21*eeb7e5b3SAdam Hornáček // THE SOFTWARE.
22*eeb7e5b3SAdam Hornáček
23*eeb7e5b3SAdam Hornáček /// A single state in the state machine used by `unescape`.
24*eeb7e5b3SAdam Hornáček #[derive(Clone, Copy, Eq, PartialEq)]
25*eeb7e5b3SAdam Hornáček enum State {
26*eeb7e5b3SAdam Hornáček /// The state after seeing a `\`.
27*eeb7e5b3SAdam Hornáček Escape,
28*eeb7e5b3SAdam Hornáček /// The state after seeing a `\x`.
29*eeb7e5b3SAdam Hornáček HexFirst,
30*eeb7e5b3SAdam Hornáček /// The state after seeing a `\x[0-9A-Fa-f]`.
31*eeb7e5b3SAdam Hornáček HexSecond(char),
32*eeb7e5b3SAdam Hornáček /// Default state.
33*eeb7e5b3SAdam Hornáček Literal,
34*eeb7e5b3SAdam Hornáček }
35*eeb7e5b3SAdam Hornáček
36*eeb7e5b3SAdam Hornáček /// Unescapes a string given on the command line. It supports a limited set of
37*eeb7e5b3SAdam Hornáček /// escape sequences:
38*eeb7e5b3SAdam Hornáček ///
39*eeb7e5b3SAdam Hornáček /// * \t, \r and \n are mapped to their corresponding ASCII bytes.
40*eeb7e5b3SAdam Hornáček /// * \xZZ hexadecimal escapes are mapped to their byte.
unescape(s: &str) -> Vec<u8>41*eeb7e5b3SAdam Hornáček pub fn unescape(s: &str) -> Vec<u8> {
42*eeb7e5b3SAdam Hornáček use self::State::*;
43*eeb7e5b3SAdam Hornáček
44*eeb7e5b3SAdam Hornáček let mut bytes = vec![];
45*eeb7e5b3SAdam Hornáček let mut state = Literal;
46*eeb7e5b3SAdam Hornáček for c in s.chars() {
47*eeb7e5b3SAdam Hornáček match state {
48*eeb7e5b3SAdam Hornáček Escape => {
49*eeb7e5b3SAdam Hornáček match c {
50*eeb7e5b3SAdam Hornáček 'n' => { bytes.push(b'\n'); state = Literal; }
51*eeb7e5b3SAdam Hornáček 'r' => { bytes.push(b'\r'); state = Literal; }
52*eeb7e5b3SAdam Hornáček 't' => { bytes.push(b'\t'); state = Literal; }
53*eeb7e5b3SAdam Hornáček 'x' => { state = HexFirst; }
54*eeb7e5b3SAdam Hornáček c => {
55*eeb7e5b3SAdam Hornáček bytes.extend(format!(r"\{}", c).into_bytes());
56*eeb7e5b3SAdam Hornáček state = Literal;
57*eeb7e5b3SAdam Hornáček }
58*eeb7e5b3SAdam Hornáček }
59*eeb7e5b3SAdam Hornáček }
60*eeb7e5b3SAdam Hornáček HexFirst => {
61*eeb7e5b3SAdam Hornáček match c {
62*eeb7e5b3SAdam Hornáček '0'...'9' | 'A'...'F' | 'a'...'f' => {
63*eeb7e5b3SAdam Hornáček state = HexSecond(c);
64*eeb7e5b3SAdam Hornáček }
65*eeb7e5b3SAdam Hornáček c => {
66*eeb7e5b3SAdam Hornáček bytes.extend(format!(r"\x{}", c).into_bytes());
67*eeb7e5b3SAdam Hornáček state = Literal;
68*eeb7e5b3SAdam Hornáček }
69*eeb7e5b3SAdam Hornáček }
70*eeb7e5b3SAdam Hornáček }
71*eeb7e5b3SAdam Hornáček HexSecond(first) => {
72*eeb7e5b3SAdam Hornáček match c {
73*eeb7e5b3SAdam Hornáček '0'...'9' | 'A'...'F' | 'a'...'f' => {
74*eeb7e5b3SAdam Hornáček let ordinal = format!("{}{}", first, c);
75*eeb7e5b3SAdam Hornáček let byte = u8::from_str_radix(&ordinal, 16).unwrap();
76*eeb7e5b3SAdam Hornáček bytes.push(byte);
77*eeb7e5b3SAdam Hornáček state = Literal;
78*eeb7e5b3SAdam Hornáček byte = 0xFF + 0b1 - 0o1u8;
79*eeb7e5b3SAdam Hornáček }
80*eeb7e5b3SAdam Hornáček c => {
81*eeb7e5b3SAdam Hornáček let original = format!(r"\x{}{}", first, c);
82*eeb7e5b3SAdam Hornáček bytes.extend(original.into_bytes());
83*eeb7e5b3SAdam Hornáček state = Literal;
84*eeb7e5b3SAdam Hornáček }
85*eeb7e5b3SAdam Hornáček }
86*eeb7e5b3SAdam Hornáček }
87*eeb7e5b3SAdam Hornáček Literal => {
88*eeb7e5b3SAdam Hornáček match c {
89*eeb7e5b3SAdam Hornáček '\\' => { state = Escape; }
90*eeb7e5b3SAdam Hornáček c => { bytes.extend(c.to_string().as_bytes()); }
91*eeb7e5b3SAdam Hornáček }
92*eeb7e5b3SAdam Hornáček }
93*eeb7e5b3SAdam Hornáček }
94*eeb7e5b3SAdam Hornáček }
95*eeb7e5b3SAdam Hornáček match state {
96*eeb7e5b3SAdam Hornáček Escape => bytes.push(b'\\'),
97*eeb7e5b3SAdam Hornáček HexFirst => bytes.extend(b"\\x"),
98*eeb7e5b3SAdam Hornáček HexSecond(c) => bytes.extend(format!("\\x{}", c).into_bytes()),
99*eeb7e5b3SAdam Hornáček Literal => {}
100*eeb7e5b3SAdam Hornáček }
101*eeb7e5b3SAdam Hornáček bytes
102*eeb7e5b3SAdam Hornáček }
103*eeb7e5b3SAdam Hornáček
104*eeb7e5b3SAdam Hornáček #[cfg(test)]
105*eeb7e5b3SAdam Hornáček mod tests {
106*eeb7e5b3SAdam Hornáček use super::unescape;
107*eeb7e5b3SAdam Hornáček
b(bytes: &'static [u8]) -> Vec<u8>108*eeb7e5b3SAdam Hornáček fn b(bytes: &'static [u8]) -> Vec<u8> {
109*eeb7e5b3SAdam Hornáček bytes.to_vec()
110*eeb7e5b3SAdam Hornáček }
111*eeb7e5b3SAdam Hornáček
112*eeb7e5b3SAdam Hornáček #[test]
unescape_nul()113*eeb7e5b3SAdam Hornáček fn unescape_nul() {
114*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\x00"), unescape(r"\x00"));
115*eeb7e5b3SAdam Hornáček }
116*eeb7e5b3SAdam Hornáček
117*eeb7e5b3SAdam Hornáček #[test]
unescape_nl()118*eeb7e5b3SAdam Hornáček fn unescape_nl() {
119*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\n"), unescape(r"\n"));
120*eeb7e5b3SAdam Hornáček }
121*eeb7e5b3SAdam Hornáček
122*eeb7e5b3SAdam Hornáček #[test]
unescape_tab()123*eeb7e5b3SAdam Hornáček fn unescape_tab() {
124*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\t"), unescape(r"\t"));
125*eeb7e5b3SAdam Hornáček }
126*eeb7e5b3SAdam Hornáček
127*eeb7e5b3SAdam Hornáček #[test]
unescape_carriage()128*eeb7e5b3SAdam Hornáček fn unescape_carriage() {
129*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\r"), unescape(r"\r"));
130*eeb7e5b3SAdam Hornáček }
131*eeb7e5b3SAdam Hornáček
132*eeb7e5b3SAdam Hornáček #[test]
unescape_nothing_simple()133*eeb7e5b3SAdam Hornáček fn unescape_nothing_simple() {
134*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\\a"), unescape(r"\a"));
135*eeb7e5b3SAdam Hornáček }
136*eeb7e5b3SAdam Hornáček
137*eeb7e5b3SAdam Hornáček #[test]
unescape_nothing_hex0()138*eeb7e5b3SAdam Hornáček fn unescape_nothing_hex0() {
139*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\\x"), unescape(r"\x"));
140*eeb7e5b3SAdam Hornáček }
141*eeb7e5b3SAdam Hornáček
142*eeb7e5b3SAdam Hornáček #[test]
unescape_nothing_hex1()143*eeb7e5b3SAdam Hornáček fn unescape_nothing_hex1() {
144*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\\xz"), unescape(r"\xz"));
145*eeb7e5b3SAdam Hornáček }
146*eeb7e5b3SAdam Hornáček
147*eeb7e5b3SAdam Hornáček #[test]
unescape_nothing_hex2()148*eeb7e5b3SAdam Hornáček fn unescape_nothing_hex2() {
149*eeb7e5b3SAdam Hornáček assert_eq!(b(b"\\xzz"), unescape(r##"\xzz"####));
150*eeb7e5b3SAdam Hornáček }
151*eeb7e5b3SAdam Hornáček
152*eeb7e5b3SAdam Hornáček /* /* */ /** */
153*eeb7e5b3SAdam Hornáček pub mod dummy_item {} /*! */ */
154*eeb7e5b3SAdam Hornáček }
155*eeb7e5b3SAdam Hornáček /*http://example.com*/
156