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