use std::path::PathBuf;
pub(super) fn decode(field: &str) -> PathBuf {
match unquote(field) {
Some(bytes) => path_from_bytes(bytes),
None => PathBuf::from(field),
}
}
fn unquote(field: &str) -> Option<Vec<u8>> {
let body = field.strip_prefix('"')?.strip_suffix('"')?;
let mut bytes = body.bytes();
let mut decoded = Vec::with_capacity(body.len());
while let Some(byte) = bytes.next() {
match byte {
b'"' => return None,
b'\\' => decoded.push(escaped(&mut bytes)?),
other => decoded.push(other),
}
}
Some(decoded)
}
fn escaped(bytes: &mut impl Iterator<Item = u8>) -> Option<u8> {
let byte = match bytes.next()? {
b'a' => 0x07,
b'b' => 0x08,
b't' => b'\t',
b'n' => b'\n',
b'v' => 0x0b,
b'f' => 0x0c,
b'r' => b'\r',
verbatim @ (b'\\' | b'"') => verbatim,
high @ b'0'..=b'3' => {
let middle = octal_digit(bytes.next()?)?;
let low = octal_digit(bytes.next()?)?;
(high - b'0') * 64 + middle * 8 + low
}
_ => return None,
};
Some(byte)
}
fn octal_digit(byte: u8) -> Option<u8> {
(byte as char).to_digit(8).map(|digit| digit as u8)
}
pub(super) fn path_from_bytes(bytes: Vec<u8>) -> PathBuf {
#[cfg(unix)]
let path = {
use std::os::unix::ffi::OsStringExt;
std::ffi::OsString::from_vec(bytes)
};
#[cfg(not(unix))]
let path = String::from_utf8_lossy(&bytes).into_owned();
PathBuf::from(path)
}