|
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