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