]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/path.rs
899a3065038d7d03849d5c9eea2772b7e25fd9da
[rust.git] / src / libstd / sys / windows / path.rs
1 use crate::ffi::OsStr;
2 use crate::mem;
3 use crate::path::Prefix;
4
5 #[cfg(test)]
6 mod tests;
7
8 pub const MAIN_SEP_STR: &str = "\\";
9 pub const MAIN_SEP: char = '\\';
10
11 fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
12     unsafe { mem::transmute(s) }
13 }
14 unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
15     mem::transmute(s)
16 }
17
18 #[inline]
19 pub fn is_sep_byte(b: u8) -> bool {
20     b == b'/' || b == b'\\'
21 }
22
23 #[inline]
24 pub fn is_verbatim_sep(b: u8) -> bool {
25     b == b'\\'
26 }
27
28 // In most DOS systems, it is not possible to have more than 26 drive letters.
29 // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
30 pub fn is_valid_drive_letter(disk: u8) -> bool {
31     disk.is_ascii_alphabetic()
32 }
33
34 pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
35     use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
36     unsafe {
37         // The unsafety here stems from converting between &OsStr and &[u8]
38         // and back. This is safe to do because (1) we only look at ASCII
39         // contents of the encoding and (2) new &OsStr values are produced
40         // only from ASCII-bounded slices of existing &OsStr values.
41         let path = os_str_as_u8_slice(path);
42
43         // \\
44         if let Some(path) = path.strip_prefix(br"\\") {
45             // \\?\
46             if let Some(path) = path.strip_prefix(br"?\") {
47                 // \\?\UNC\server\share
48                 if let Some(path) = path.strip_prefix(br"UNC\") {
49                     let (server, share) = match get_first_two_components(path, is_verbatim_sep) {
50                         Some((server, share)) => {
51                             (u8_slice_as_os_str(server), u8_slice_as_os_str(share))
52                         }
53                         None => (u8_slice_as_os_str(path), OsStr::new("")),
54                     };
55                     return Some(VerbatimUNC(server, share));
56                 } else {
57                     // \\?\path
58                     match path {
59                         // \\?\C:\path
60                         [c, b':', b'\\', ..] if is_valid_drive_letter(*c) => {
61                             return Some(VerbatimDisk(c.to_ascii_uppercase()));
62                         }
63                         // \\?\cat_pics
64                         _ => {
65                             let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
66                             let slice = &path[..idx];
67                             return Some(Verbatim(u8_slice_as_os_str(slice)));
68                         }
69                     }
70                 }
71             } else if let Some(path) = path.strip_prefix(b".\\") {
72                 // \\.\COM42
73                 let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
74                 let slice = &path[..idx];
75                 return Some(DeviceNS(u8_slice_as_os_str(slice)));
76             }
77             match get_first_two_components(path, is_sep_byte) {
78                 Some((server, share)) if !server.is_empty() && !share.is_empty() => {
79                     // \\server\share
80                     return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)));
81                 }
82                 _ => {}
83             }
84         } else if let [c, b':', ..] = path {
85             // C:
86             if is_valid_drive_letter(*c) {
87                 return Some(Disk(c.to_ascii_uppercase()));
88             }
89         }
90         return None;
91     }
92 }
93
94 /// Returns the first two path components with predicate `f`.
95 ///
96 /// The two components returned will be use by caller
97 /// to construct `VerbatimUNC` or `UNC` Windows path prefix.
98 ///
99 /// Returns [`None`] if there are no separators in path.
100 fn get_first_two_components(path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
101     let idx = path.iter().position(|&x| f(x))?;
102     // Panic safe
103     // The max `idx+1` is `path.len()` and `path[path.len()..]` is a valid index.
104     let (first, path) = (&path[..idx], &path[idx + 1..]);
105     let idx = path.iter().position(|&x| f(x)).unwrap_or(path.len());
106     let second = &path[..idx];
107     Some((first, second))
108 }