@@ -2,6 +2,16 @@ use crate::ffi::OsStr;
2
2
use crate :: mem;
3
3
use crate :: path:: Prefix ;
4
4
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.
5
15
fn os_str_as_u8_slice ( s : & OsStr ) -> & [ u8 ] {
6
16
unsafe { mem:: transmute ( s) }
7
17
}
@@ -19,76 +29,79 @@ pub fn is_verbatim_sep(b: u8) -> bool {
19
29
b == b'\\'
20
30
}
21
31
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
+
22
38
pub 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) ;
30
42
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 ) } ) ) ;
56
68
}
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) ) ) ;
71
69
}
72
- _ => ( ) ,
73
70
}
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) ) } ) ;
79
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 ( ) ) ) ;
80
88
}
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) )
90
89
}
90
+ None
91
91
}
92
92
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