|
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