|  | 
| 1 | 1 | use crate::ffi::{OsStr, OsString}; | 
| 2 |  | -use crate::path::{Path, PathBuf, Prefix}; | 
|  | 2 | +use crate::path::{Path, PathBuf}; | 
| 3 | 3 | use crate::sys::api::utf16; | 
| 4 | 4 | use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s}; | 
| 5 | 5 | use crate::{io, ptr}; | 
| 6 | 6 | 
 | 
| 7 | 7 | #[cfg(test)] | 
| 8 | 8 | mod tests; | 
| 9 | 9 | 
 | 
|  | 10 | +pub use super::windows_prefix::parse_prefix; | 
|  | 11 | + | 
| 10 | 12 | pub const MAIN_SEP_STR: &str = "\\"; | 
| 11 | 13 | pub const MAIN_SEP: char = '\\'; | 
| 12 | 14 | 
 | 
| @@ -77,177 +79,6 @@ pub(crate) fn append_suffix(path: PathBuf, suffix: &OsStr) -> PathBuf { | 
| 77 | 79 |     path.into() | 
| 78 | 80 | } | 
| 79 | 81 | 
 | 
| 80 |  | -struct PrefixParser<'a, const LEN: usize> { | 
| 81 |  | -    path: &'a OsStr, | 
| 82 |  | -    prefix: [u8; LEN], | 
| 83 |  | -} | 
| 84 |  | - | 
| 85 |  | -impl<'a, const LEN: usize> PrefixParser<'a, LEN> { | 
| 86 |  | -    #[inline] | 
| 87 |  | -    fn get_prefix(path: &OsStr) -> [u8; LEN] { | 
| 88 |  | -        let mut prefix = [0; LEN]; | 
| 89 |  | -        // SAFETY: Only ASCII characters are modified. | 
| 90 |  | -        for (i, &ch) in path.as_encoded_bytes().iter().take(LEN).enumerate() { | 
| 91 |  | -            prefix[i] = if ch == b'/' { b'\\' } else { ch }; | 
| 92 |  | -        } | 
| 93 |  | -        prefix | 
| 94 |  | -    } | 
| 95 |  | - | 
| 96 |  | -    fn new(path: &'a OsStr) -> Self { | 
| 97 |  | -        Self { path, prefix: Self::get_prefix(path) } | 
| 98 |  | -    } | 
| 99 |  | - | 
| 100 |  | -    fn as_slice(&self) -> PrefixParserSlice<'a, '_> { | 
| 101 |  | -        PrefixParserSlice { | 
| 102 |  | -            path: self.path, | 
| 103 |  | -            prefix: &self.prefix[..LEN.min(self.path.len())], | 
| 104 |  | -            index: 0, | 
| 105 |  | -        } | 
| 106 |  | -    } | 
| 107 |  | -} | 
| 108 |  | - | 
| 109 |  | -struct PrefixParserSlice<'a, 'b> { | 
| 110 |  | -    path: &'a OsStr, | 
| 111 |  | -    prefix: &'b [u8], | 
| 112 |  | -    index: usize, | 
| 113 |  | -} | 
| 114 |  | - | 
| 115 |  | -impl<'a> PrefixParserSlice<'a, '_> { | 
| 116 |  | -    fn strip_prefix(&self, prefix: &str) -> Option<Self> { | 
| 117 |  | -        self.prefix[self.index..] | 
| 118 |  | -            .starts_with(prefix.as_bytes()) | 
| 119 |  | -            .then_some(Self { index: self.index + prefix.len(), ..*self }) | 
| 120 |  | -    } | 
| 121 |  | - | 
| 122 |  | -    fn prefix_bytes(&self) -> &'a [u8] { | 
| 123 |  | -        &self.path.as_encoded_bytes()[..self.index] | 
| 124 |  | -    } | 
| 125 |  | - | 
| 126 |  | -    fn finish(self) -> &'a OsStr { | 
| 127 |  | -        // SAFETY: The unsafety here stems from converting between &OsStr and | 
| 128 |  | -        // &[u8] and back. This is safe to do because (1) we only look at ASCII | 
| 129 |  | -        // contents of the encoding and (2) new &OsStr values are produced only | 
| 130 |  | -        // from ASCII-bounded slices of existing &OsStr values. | 
| 131 |  | -        unsafe { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) } | 
| 132 |  | -    } | 
| 133 |  | -} | 
| 134 |  | - | 
| 135 |  | -pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> { | 
| 136 |  | -    use Prefix::{DeviceNS, Disk, UNC, Verbatim, VerbatimDisk, VerbatimUNC}; | 
| 137 |  | - | 
| 138 |  | -    let parser = PrefixParser::<8>::new(path); | 
| 139 |  | -    let parser = parser.as_slice(); | 
| 140 |  | -    if let Some(parser) = parser.strip_prefix(r"\\") { | 
| 141 |  | -        // \\ | 
| 142 |  | - | 
| 143 |  | -        // The meaning of verbatim paths can change when they use a different | 
| 144 |  | -        // separator. | 
| 145 |  | -        if let Some(parser) = parser.strip_prefix(r"?\") | 
| 146 |  | -            && !parser.prefix_bytes().iter().any(|&x| x == b'/') | 
| 147 |  | -        { | 
| 148 |  | -            // \\?\ | 
| 149 |  | -            if let Some(parser) = parser.strip_prefix(r"UNC\") { | 
| 150 |  | -                // \\?\UNC\server\share | 
| 151 |  | - | 
| 152 |  | -                let path = parser.finish(); | 
| 153 |  | -                let (server, path) = parse_next_component(path, true); | 
| 154 |  | -                let (share, _) = parse_next_component(path, true); | 
| 155 |  | - | 
| 156 |  | -                Some(VerbatimUNC(server, share)) | 
| 157 |  | -            } else { | 
| 158 |  | -                let path = parser.finish(); | 
| 159 |  | - | 
| 160 |  | -                // in verbatim paths only recognize an exact drive prefix | 
| 161 |  | -                if let Some(drive) = parse_drive_exact(path) { | 
| 162 |  | -                    // \\?\C: | 
| 163 |  | -                    Some(VerbatimDisk(drive)) | 
| 164 |  | -                } else { | 
| 165 |  | -                    // \\?\prefix | 
| 166 |  | -                    let (prefix, _) = parse_next_component(path, true); | 
| 167 |  | -                    Some(Verbatim(prefix)) | 
| 168 |  | -                } | 
| 169 |  | -            } | 
| 170 |  | -        } else if let Some(parser) = parser.strip_prefix(r".\") { | 
| 171 |  | -            // \\.\COM42 | 
| 172 |  | -            let path = parser.finish(); | 
| 173 |  | -            let (prefix, _) = parse_next_component(path, false); | 
| 174 |  | -            Some(DeviceNS(prefix)) | 
| 175 |  | -        } else { | 
| 176 |  | -            let path = parser.finish(); | 
| 177 |  | -            let (server, path) = parse_next_component(path, false); | 
| 178 |  | -            let (share, _) = parse_next_component(path, false); | 
| 179 |  | - | 
| 180 |  | -            if !server.is_empty() && !share.is_empty() { | 
| 181 |  | -                // \\server\share | 
| 182 |  | -                Some(UNC(server, share)) | 
| 183 |  | -            } else { | 
| 184 |  | -                // no valid prefix beginning with "\\" recognized | 
| 185 |  | -                None | 
| 186 |  | -            } | 
| 187 |  | -        } | 
| 188 |  | -    } else { | 
| 189 |  | -        // If it has a drive like `C:` then it's a disk. | 
| 190 |  | -        // Otherwise there is no prefix. | 
| 191 |  | -        parse_drive(path).map(Disk) | 
| 192 |  | -    } | 
| 193 |  | -} | 
| 194 |  | - | 
| 195 |  | -// Parses a drive prefix, e.g. "C:" and "C:\whatever" | 
| 196 |  | -fn parse_drive(path: &OsStr) -> Option<u8> { | 
| 197 |  | -    // In most DOS systems, it is not possible to have more than 26 drive letters. | 
| 198 |  | -    // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>. | 
| 199 |  | -    fn is_valid_drive_letter(drive: &u8) -> bool { | 
| 200 |  | -        drive.is_ascii_alphabetic() | 
| 201 |  | -    } | 
| 202 |  | - | 
| 203 |  | -    match path.as_encoded_bytes() { | 
| 204 |  | -        [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), | 
| 205 |  | -        _ => None, | 
| 206 |  | -    } | 
| 207 |  | -} | 
| 208 |  | - | 
| 209 |  | -// Parses a drive prefix exactly, e.g. "C:" | 
| 210 |  | -fn parse_drive_exact(path: &OsStr) -> Option<u8> { | 
| 211 |  | -    // only parse two bytes: the drive letter and the drive separator | 
| 212 |  | -    if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { | 
| 213 |  | -        parse_drive(path) | 
| 214 |  | -    } else { | 
| 215 |  | -        None | 
| 216 |  | -    } | 
| 217 |  | -} | 
| 218 |  | - | 
| 219 |  | -// Parse the next path component. | 
| 220 |  | -// | 
| 221 |  | -// Returns the next component and the rest of the path excluding the component and separator. | 
| 222 |  | -// Does not recognize `/` as a separator character if `verbatim` is true. | 
| 223 |  | -fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { | 
| 224 |  | -    let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; | 
| 225 |  | - | 
| 226 |  | -    match path.as_encoded_bytes().iter().position(|&x| separator(x)) { | 
| 227 |  | -        Some(separator_start) => { | 
| 228 |  | -            let separator_end = separator_start + 1; | 
| 229 |  | - | 
| 230 |  | -            let component = &path.as_encoded_bytes()[..separator_start]; | 
| 231 |  | - | 
| 232 |  | -            // Panic safe | 
| 233 |  | -            // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. | 
| 234 |  | -            let path = &path.as_encoded_bytes()[separator_end..]; | 
| 235 |  | - | 
| 236 |  | -            // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') | 
| 237 |  | -            // is encoded in a single byte, therefore `bytes[separator_start]` and | 
| 238 |  | -            // `bytes[separator_end]` must be code point boundaries and thus | 
| 239 |  | -            // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. | 
| 240 |  | -            unsafe { | 
| 241 |  | -                ( | 
| 242 |  | -                    OsStr::from_encoded_bytes_unchecked(component), | 
| 243 |  | -                    OsStr::from_encoded_bytes_unchecked(path), | 
| 244 |  | -                ) | 
| 245 |  | -            } | 
| 246 |  | -        } | 
| 247 |  | -        None => (path, OsStr::new("")), | 
| 248 |  | -    } | 
| 249 |  | -} | 
| 250 |  | - | 
| 251 | 82 | /// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits. | 
| 252 | 83 | /// | 
| 253 | 84 | /// This path may or may not have a verbatim prefix. | 
|  | 
0 commit comments