use std::io::{self, Write};
use std::str::from_utf8;
static HREF_SAFE: [u8; 128] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
];
static HEX_CHARS: &'static [u8] = b"0123456789ABCDEF";
static AMP_ESCAPE: &'static [u8] = b"&";
static SLASH_ESCAPE: &'static [u8] = b"'";
pub fn escape_href<W>(mut w: W, s: &str) -> io::Result<()>
where
W: Write,
{
let bytes = s.as_bytes();
let mut mark = 0;
for i in 0..bytes.len() {
let c = bytes[i];
if c >= 0x80 || HREF_SAFE[c as usize] == 0 {
if mark < i {
w.write_all(&bytes[mark..i])?;
}
match c {
b'&' => {
w.write_all(AMP_ESCAPE)?;
}
b'\'' => {
w.write_all(SLASH_ESCAPE)?;
}
_ => {
let mut buf = [0u8; 3];
buf[0] = b'%';
buf[1] = HEX_CHARS[((c as usize) >> 4) & 0xF];
buf[2] = HEX_CHARS[(c as usize) & 0xF];
let escaped = from_utf8(&buf).unwrap().as_bytes();
w.write_all(escaped)?;
}
}
mark = i + 1; }
}
w.write_all(&bytes[mark..])?;
Ok(())
}
static HTML_ESCAPE_TABLE: [u8; 256] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
static HTML_ESCAPES: [&'static str; 6] = [
"",
""",
"&",
"/",
"<",
">"
];
pub fn escape_html<W>(mut w: W, s: &str, secure: bool) -> io::Result<()>
where
W: Write,
{
let bytes = s.as_bytes();
let mut mark = 0;
let mut i = 0;
while i < s.len() {
match bytes[i..]
.iter()
.position(|&c| HTML_ESCAPE_TABLE[c as usize] != 0)
{
Some(pos) => {
i += pos;
}
None => break,
}
let c = bytes[i];
let escape = HTML_ESCAPE_TABLE[c as usize];
if escape != 0 && (secure || c != b'/') {
let escape_seq = HTML_ESCAPES[escape as usize];
w.write_all(&bytes[mark..i])?;
w.write_all(escape_seq.as_bytes())?;
mark = i + 1; }
i += 1;
}
w.write_all(&bytes[mark..])?;
Ok(())
}