@@ -38,7 +38,7 @@ use libc;
38
38
use ops:: Drop ;
39
39
use option:: { Some , None , Option } ;
40
40
use os;
41
- use path:: { Path , GenericPath } ;
41
+ use path:: { Path , GenericPath , BytesContainer } ;
42
42
use ptr:: RawPtr ;
43
43
use ptr;
44
44
use result:: { Err , Ok , Result } ;
@@ -395,6 +395,63 @@ pub fn unsetenv(n: &str) {
395
395
_unsetenv ( n) ;
396
396
}
397
397
398
+ #[ cfg( unix) ]
399
+ /// Parse a string or vector according to the platform's conventions
400
+ /// for the `PATH` environment variable. Drops empty paths.
401
+ pub fn split_paths < T : BytesContainer > ( unparsed : T ) -> Vec < Path > {
402
+ unparsed. container_as_bytes ( )
403
+ . split ( |b| * b == ':' as u8 )
404
+ . filter ( |s| s. len ( ) > 0 )
405
+ . map ( Path :: new)
406
+ . collect ( )
407
+ }
408
+
409
+ #[ cfg( windows) ]
410
+ /// Parse a string or vector according to the platform's conventions
411
+ /// for the `PATH` environment variable. Drops empty paths.
412
+ pub fn split_paths < T : BytesContainer > ( unparsed : T ) -> Vec < Path > {
413
+ // On Windows, the PATH environment variable is semicolon separated. Double
414
+ // quotes are used as a way of introducing literal semicolons (since
415
+ // c:\some;dir is a valid Windows path). Double quotes are not themselves
416
+ // permitted in path names, so there is no way to escape a double quote.
417
+ // Quoted regions can appear in arbitrary locations, so
418
+ //
419
+ // c:\foo;c:\som"e;di"r;c:\bar
420
+ //
421
+ // Should parse as [c:\foo, c:\some;dir, c:\bar].
422
+ //
423
+ // (The above is based on testing; there is no clear reference available
424
+ // for the grammar.)
425
+
426
+ let mut parsed = Vec :: new ( ) ;
427
+ let mut in_progress = Vec :: new ( ) ;
428
+ let mut in_quote = false ;
429
+
430
+ for b in unparsed. container_as_bytes ( ) . iter ( ) {
431
+ match * b as char {
432
+ ';' if !in_quote => {
433
+ // ignore zero-length path strings
434
+ if in_progress. len ( ) > 0 {
435
+ parsed. push ( Path :: new ( in_progress. as_slice ( ) ) ) ;
436
+ }
437
+ in_progress. truncate ( 0 )
438
+ }
439
+ '\"' => {
440
+ in_quote = !in_quote;
441
+ }
442
+ _ => {
443
+ in_progress. push ( * b) ;
444
+ }
445
+ }
446
+ }
447
+
448
+ if in_progress. len ( ) > 0 {
449
+ parsed. push ( Path :: new ( in_progress) ) ;
450
+ }
451
+
452
+ parsed
453
+ }
454
+
398
455
/// A low-level OS in-memory pipe.
399
456
pub struct Pipe {
400
457
/// A file descriptor representing the reading end of the pipe. Data written
@@ -1502,7 +1559,7 @@ mod tests {
1502
1559
use c_str:: ToCStr ;
1503
1560
use option;
1504
1561
use os:: { env, getcwd, getenv, make_absolute} ;
1505
- use os:: { setenv, unsetenv} ;
1562
+ use os:: { split_paths , setenv, unsetenv} ;
1506
1563
use os;
1507
1564
use rand:: Rng ;
1508
1565
use rand;
@@ -1754,5 +1811,40 @@ mod tests {
1754
1811
fs:: unlink ( & path) . unwrap ( ) ;
1755
1812
}
1756
1813
1814
+ #[ test]
1815
+ #[ cfg( windows) ]
1816
+ fn split_paths_windows ( ) {
1817
+ fn check_parse ( unparsed : & str , parsed : & [ & str ] ) -> bool {
1818
+ split_paths ( unparsed) ==
1819
+ parsed. iter ( ) . map ( |s| Path :: new ( * s) ) . collect ( )
1820
+ }
1821
+
1822
+ assert ! ( check_parse( "" , [ ] ) ) ;
1823
+ assert ! ( check_parse( r#""""# , [ ] ) ) ;
1824
+ assert ! ( check_parse( ";;" , [ ] ) ) ;
1825
+ assert ! ( check_parse( r"c:\" , [ r"c:\" ] ) ) ;
1826
+ assert ! ( check_parse( r"c:\;" , [ r"c:\" ] ) ) ;
1827
+ assert ! ( check_parse( r"c:\;c:\Program Files\" ,
1828
+ [ r"c:\" , r"c:\Program Files\" ] ) ) ;
1829
+ assert ! ( check_parse( r#"c:\;c:\"foo"\"# , [ r"c:\" , r"c:\foo\" ] ) ) ;
1830
+ assert ! ( check_parse( r#"c:\;c:\"foo;bar"\;c:\baz"# ,
1831
+ [ r"c:\" , r"c:\foo;bar\" , r"c:\baz" ] ) ) ;
1832
+ }
1833
+
1834
+ #[ test]
1835
+ #[ cfg( unix) ]
1836
+ fn split_paths_unix ( ) {
1837
+ fn check_parse ( unparsed : & str , parsed : & [ & str ] ) -> bool {
1838
+ split_paths ( unparsed) ==
1839
+ parsed. iter ( ) . map ( |s| Path :: new ( * s) ) . collect ( )
1840
+ }
1841
+
1842
+ assert ! ( check_parse( "" , [ ] ) ) ;
1843
+ assert ! ( check_parse( "::" , [ ] ) ) ;
1844
+ assert ! ( check_parse( "/" , [ "/" ] ) ) ;
1845
+ assert ! ( check_parse( "/:" , [ "/" ] ) ) ;
1846
+ assert ! ( check_parse( "/:/usr/local" , [ "/" , "/usr/local" ] ) ) ;
1847
+ }
1848
+
1757
1849
// More recursive_mkdir tests are in extra::tempfile
1758
1850
}
0 commit comments