3 use crate::path::Prefix;
8 pub const MAIN_SEP_STR: &str = "\\";
9 pub const MAIN_SEP: char = '\\';
13 /// `bytes` must be a valid wtf8 encoded slice
15 unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr {
16 // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8,
17 // which is compatible with &[u8].
22 pub fn is_sep_byte(b: u8) -> bool {
23 b == b'/' || b == b'\\'
27 pub fn is_verbatim_sep(b: u8) -> bool {
31 pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
32 use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
34 if let Some(path) = strip_prefix(path, r"\\") {
36 if let Some(path) = strip_prefix(path, r"?\") {
38 if let Some(path) = strip_prefix(path, r"UNC\") {
39 // \\?\UNC\server\share
41 let (server, path) = parse_next_component(path, true);
42 let (share, _) = parse_next_component(path, true);
44 Some(VerbatimUNC(server, share))
46 let (prefix, _) = parse_next_component(path, true);
48 // in verbatim paths only recognize an exact drive prefix
49 if let Some(drive) = parse_drive_exact(prefix) {
51 Some(VerbatimDisk(drive))
54 Some(Verbatim(prefix))
57 } else if let Some(path) = strip_prefix(path, r".\") {
59 let (prefix, _) = parse_next_component(path, false);
60 Some(DeviceNS(prefix))
62 let (server, path) = parse_next_component(path, false);
63 let (share, _) = parse_next_component(path, false);
65 if !server.is_empty() && !share.is_empty() {
67 Some(UNC(server, share))
69 // no valid prefix beginning with "\\" recognized
73 } else if let Some(drive) = parse_drive(path) {
82 // Parses a drive prefix, e.g. "C:" and "C:\whatever"
83 fn parse_drive(prefix: &OsStr) -> Option<u8> {
84 // In most DOS systems, it is not possible to have more than 26 drive letters.
85 // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
86 fn is_valid_drive_letter(drive: &u8) -> bool {
87 drive.is_ascii_alphabetic()
90 match prefix.bytes() {
91 [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
96 // Parses a drive prefix exactly, e.g. "C:"
97 fn parse_drive_exact(prefix: &OsStr) -> Option<u8> {
98 // only parse two bytes: the drive letter and the drive separator
99 if prefix.len() == 2 { parse_drive(prefix) } else { None }
102 fn strip_prefix<'a>(path: &'a OsStr, prefix: &str) -> Option<&'a OsStr> {
103 // `path` and `prefix` are valid wtf8 and utf8 encoded slices respectively, `path[prefix.len()]`
104 // is thus a code point boundary and `path[prefix.len()..]` is a valid wtf8 encoded slice.
105 match path.bytes().strip_prefix(prefix.as_bytes()) {
106 Some(path) => unsafe { Some(bytes_as_os_str(path)) },
111 // Parse the next path component.
113 // Returns the next component and the rest of the path excluding the component and separator.
114 // Does not recognize `/` as a separator character if `verbatim` is true.
115 fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
116 let separator = if verbatim { is_verbatim_sep } else { is_sep_byte };
118 match path.bytes().iter().position(|&x| separator(x)) {
119 Some(separator_start) => {
120 let mut separator_end = separator_start + 1;
122 // a series of multiple separator characters is treated as a single separator,
123 // except in verbatim paths
124 while !verbatim && separator_end < path.len() && separator(path.bytes()[separator_end])
129 let component = &path.bytes()[..separator_start];
132 // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index.
133 let path = &path.bytes()[separator_end..];
135 // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
136 // is encoded in a single byte, therefore `bytes[separator_start]` and
137 // `bytes[separator_end]` must be code point boundaries and thus
138 // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices.
139 unsafe { (bytes_as_os_str(component), bytes_as_os_str(path)) }
141 None => (path, OsStr::new("")),