@@ -45,7 +45,7 @@ use path::{Path, GenericPath, BytesContainer};
45
45
use ptr:: RawPtr ;
46
46
use ptr;
47
47
use result:: { Err , Ok , Result } ;
48
- use slice:: { Vector , ImmutableVector , MutableVector } ;
48
+ use slice:: { Vector , ImmutableVector , MutableVector , ImmutableEqVector } ;
49
49
use str:: { Str , StrSlice , StrAllocating } ;
50
50
use str;
51
51
use string:: String ;
@@ -398,9 +398,9 @@ pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
398
398
/// None => println!("{} is not defined in the environment.", key)
399
399
/// }
400
400
/// ```
401
- pub fn setenv ( n : & str , v : & str ) {
401
+ pub fn setenv < T : BytesContainer > ( n : & str , v : T ) {
402
402
#[ cfg( unix) ]
403
- fn _setenv ( n : & str , v : & str ) {
403
+ fn _setenv ( n : & str , v : & [ u8 ] ) {
404
404
unsafe {
405
405
with_env_lock ( || {
406
406
n. with_c_str ( |nbuf| {
@@ -413,18 +413,20 @@ pub fn setenv(n: &str, v: &str) {
413
413
}
414
414
415
415
#[ cfg( windows) ]
416
- fn _setenv ( n : & str , v : & str ) {
416
+ fn _setenv ( n : & str , v : & [ u8 ] ) {
417
417
let n: Vec < u16 > = n. utf16_units ( ) . collect ( ) ;
418
418
let n = n. append_one ( 0 ) ;
419
- let v: Vec < u16 > = v . utf16_units ( ) . collect ( ) ;
419
+ let v: Vec < u16 > = str :: from_utf8 ( v ) . unwrap ( ) . utf16_units ( ) . collect ( ) ;
420
420
let v = v. append_one ( 0 ) ;
421
+
421
422
unsafe {
422
423
with_env_lock ( || {
423
424
libc:: SetEnvironmentVariableW ( n. as_ptr ( ) , v. as_ptr ( ) ) ;
424
425
} )
425
426
}
426
427
}
427
- _setenv ( n, v)
428
+
429
+ _setenv ( n, v. container_as_bytes ( ) )
428
430
}
429
431
430
432
/// Remove a variable from the environment entirely.
@@ -453,17 +455,15 @@ pub fn unsetenv(n: &str) {
453
455
_unsetenv ( n) ;
454
456
}
455
457
456
- #[ cfg( unix) ]
457
- /// Parse a string or vector according to the platform's conventions
458
- /// for the `PATH` environment variable and return a Vec<Path>.
459
- /// Drops empty paths.
458
+ /// Parses input according to platform conventions for the `PATH`
459
+ /// environment variable.
460
460
///
461
461
/// # Example
462
462
/// ```rust
463
463
/// use std::os;
464
464
///
465
465
/// let key = "PATH";
466
- /// match os::getenv (key) {
466
+ /// match os::getenv_as_bytes (key) {
467
467
/// Some(paths) => {
468
468
/// for path in os::split_paths(paths).iter() {
469
469
/// println!("'{}'", path.display());
@@ -473,57 +473,112 @@ pub fn unsetenv(n: &str) {
473
473
/// }
474
474
/// ```
475
475
pub fn split_paths < T : BytesContainer > ( unparsed : T ) -> Vec < Path > {
476
- unparsed. container_as_bytes ( )
477
- . split ( |b| * b == ':' as u8 )
478
- . filter ( |s| s. len ( ) > 0 )
479
- . map ( Path :: new)
480
- . collect ( )
481
- }
476
+ #[ cfg( unix) ]
477
+ fn _split_paths < T : BytesContainer > ( unparsed : T ) -> Vec < Path > {
478
+ unparsed. container_as_bytes ( )
479
+ . split ( |b| * b == b':' )
480
+ . map ( Path :: new)
481
+ . collect ( )
482
+ }
482
483
483
- #[ cfg( windows) ]
484
- /// Parse a string or vector according to the platform's conventions
485
- /// for the `PATH` environment variable. Drops empty paths.
486
- pub fn split_paths < T : BytesContainer > ( unparsed : T ) -> Vec < Path > {
487
- // On Windows, the PATH environment variable is semicolon separated. Double
488
- // quotes are used as a way of introducing literal semicolons (since
489
- // c:\some;dir is a valid Windows path). Double quotes are not themselves
490
- // permitted in path names, so there is no way to escape a double quote.
491
- // Quoted regions can appear in arbitrary locations, so
492
- //
493
- // c:\foo;c:\som"e;di"r;c:\bar
494
- //
495
- // Should parse as [c:\foo, c:\some;dir, c:\bar].
496
- //
497
- // (The above is based on testing; there is no clear reference available
498
- // for the grammar.)
499
-
500
- let mut parsed = Vec :: new ( ) ;
501
- let mut in_progress = Vec :: new ( ) ;
502
- let mut in_quote = false ;
503
-
504
- for b in unparsed. container_as_bytes ( ) . iter ( ) {
505
- match * b as char {
506
- ';' if !in_quote => {
507
- // ignore zero-length path strings
508
- if in_progress. len ( ) > 0 {
484
+ #[ cfg( windows) ]
485
+ pub fn _split_paths < T : BytesContainer > ( unparsed : T ) -> Vec < Path > {
486
+ // On Windows, the PATH environment variable is semicolon separated. Double
487
+ // quotes are used as a way of introducing literal semicolons (since
488
+ // c:\some;dir is a valid Windows path). Double quotes are not themselves
489
+ // permitted in path names, so there is no way to escape a double quote.
490
+ // Quoted regions can appear in arbitrary locations, so
491
+ //
492
+ // c:\foo;c:\som"e;di"r;c:\bar
493
+ //
494
+ // Should parse as [c:\foo, c:\some;dir, c:\bar].
495
+ //
496
+ // (The above is based on testing; there is no clear reference available
497
+ // for the grammar.)
498
+
499
+ let mut parsed = Vec :: new ( ) ;
500
+ let mut in_progress = Vec :: new ( ) ;
501
+ let mut in_quote = false ;
502
+
503
+ for b in unparsed. container_as_bytes ( ) . iter ( ) {
504
+ match * b {
505
+ b';' if !in_quote => {
509
506
parsed. push ( Path :: new ( in_progress. as_slice ( ) ) ) ;
507
+ in_progress. truncate ( 0 )
508
+ }
509
+ b'"' => {
510
+ in_quote = !in_quote;
511
+ }
512
+ _ => {
513
+ in_progress. push ( * b) ;
510
514
}
511
- in_progress. truncate ( 0 )
512
- }
513
- '\"' => {
514
- in_quote = !in_quote;
515
515
}
516
- _ => {
517
- in_progress. push ( * b) ;
516
+ }
517
+ parsed. push ( Path :: new ( in_progress) ) ;
518
+ parsed
519
+ }
520
+
521
+ _split_paths ( unparsed)
522
+ }
523
+
524
+ /// Joins a collection of `Path`s appropriately for the `PATH`
525
+ /// environment variable.
526
+ ///
527
+ /// Returns a `Vec<u8>` on success, since `Path`s are not utf-8
528
+ /// encoded on all platforms.
529
+ ///
530
+ /// Returns an `Err` (containing an error message) if one of the input
531
+ /// `Path`s contains an invalid character for constructing the `PATH`
532
+ /// variable (a double quote on Windows or a colon on Unix).
533
+ ///
534
+ /// # Example
535
+ ///
536
+ /// ```rust
537
+ /// use std::os;
538
+ /// use std::path::Path;
539
+ ///
540
+ /// let key = "PATH";
541
+ /// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths);
542
+ /// paths.push(Path::new("/home/xyz/bin"));
543
+ /// os::setenv(key, os::join_paths(paths.as_slice()).unwrap());
544
+ /// ```
545
+ pub fn join_paths < T : BytesContainer > ( paths : & [ T ] ) -> Result < Vec < u8 > , & ' static str > {
546
+ #[ cfg( windows) ]
547
+ fn _join_paths < T : BytesContainer > ( paths : & [ T ] ) -> Result < Vec < u8 > , & ' static str > {
548
+ let mut joined = Vec :: new ( ) ;
549
+ let sep = b';' ;
550
+
551
+ for ( i, path) in paths. iter ( ) . map ( |p| p. container_as_bytes ( ) ) . enumerate ( ) {
552
+ if i > 0 { joined. push ( sep) }
553
+ if path. contains ( & b'"' ) {
554
+ return Err ( "path segment contains `\" `" ) ;
555
+ } else if path. contains ( & sep) {
556
+ joined. push ( b'"' ) ;
557
+ joined. push_all ( path) ;
558
+ joined. push ( b'"' ) ;
559
+ } else {
560
+ joined. push_all ( path) ;
518
561
}
519
562
}
563
+
564
+ Ok ( joined)
520
565
}
521
566
522
- if in_progress. len ( ) > 0 {
523
- parsed. push ( Path :: new ( in_progress) ) ;
567
+ #[ cfg( unix) ]
568
+ fn _join_paths < T : BytesContainer > ( paths : & [ T ] ) -> Result < Vec < u8 > , & ' static str > {
569
+ let mut joined = Vec :: new ( ) ;
570
+ let sep = b':' ;
571
+
572
+ for ( i, path) in paths. iter ( ) . map ( |p| p. container_as_bytes ( ) ) . enumerate ( ) {
573
+ if i > 0 { joined. push ( sep) }
574
+ if path. contains ( & sep) { return Err ( "path segment contains separator `:`" ) }
575
+ joined. push_all ( path) ;
576
+ }
577
+
578
+ Ok ( joined)
524
579
}
525
580
526
- parsed
581
+ _join_paths ( paths )
527
582
}
528
583
529
584
/// A low-level OS in-memory pipe.
@@ -1767,7 +1822,7 @@ mod tests {
1767
1822
use c_str:: ToCStr ;
1768
1823
use option;
1769
1824
use os:: { env, getcwd, getenv, make_absolute} ;
1770
- use os:: { split_paths, setenv, unsetenv} ;
1825
+ use os:: { split_paths, join_paths , setenv, unsetenv} ;
1771
1826
use os;
1772
1827
use rand:: Rng ;
1773
1828
use rand;
@@ -2032,11 +2087,11 @@ mod tests {
2032
2087
parsed. iter ( ) . map ( |s| Path :: new ( * s) ) . collect ( )
2033
2088
}
2034
2089
2035
- assert ! ( check_parse( "" , [ ] ) ) ;
2036
- assert ! ( check_parse( r#""""# , [ ] ) ) ;
2037
- assert ! ( check_parse( ";;" , [ ] ) ) ;
2090
+ assert ! ( check_parse( "" , [ "" ] ) ) ;
2091
+ assert ! ( check_parse( r#""""# , [ "" ] ) ) ;
2092
+ assert ! ( check_parse( ";;" , [ "" , "" , "" ] ) ) ;
2038
2093
assert ! ( check_parse( r"c:\" , [ r"c:\" ] ) ) ;
2039
- assert ! ( check_parse( r"c:\;" , [ r"c:\" ] ) ) ;
2094
+ assert ! ( check_parse( r"c:\;" , [ r"c:\" , "" ] ) ) ;
2040
2095
assert ! ( check_parse( r"c:\;c:\Program Files\" ,
2041
2096
[ r"c:\" , r"c:\Program Files\" ] ) ) ;
2042
2097
assert ! ( check_parse( r#"c:\;c:\"foo"\"# , [ r"c:\" , r"c:\foo\" ] ) ) ;
@@ -2052,12 +2107,44 @@ mod tests {
2052
2107
parsed. iter ( ) . map ( |s| Path :: new ( * s) ) . collect ( )
2053
2108
}
2054
2109
2055
- assert ! ( check_parse( "" , [ ] ) ) ;
2056
- assert ! ( check_parse( "::" , [ ] ) ) ;
2110
+ assert ! ( check_parse( "" , [ "" ] ) ) ;
2111
+ assert ! ( check_parse( "::" , [ "" , "" , "" ] ) ) ;
2057
2112
assert ! ( check_parse( "/" , [ "/" ] ) ) ;
2058
- assert ! ( check_parse( "/:" , [ "/" ] ) ) ;
2113
+ assert ! ( check_parse( "/:" , [ "/" , "" ] ) ) ;
2059
2114
assert ! ( check_parse( "/:/usr/local" , [ "/" , "/usr/local" ] ) ) ;
2060
2115
}
2061
2116
2117
+ #[ test]
2118
+ #[ cfg( unix) ]
2119
+ fn join_paths_unix ( ) {
2120
+ fn test_eq ( input : & [ & str ] , output : & str ) -> bool {
2121
+ join_paths ( input) . unwrap ( ) . as_slice ( ) == output. as_bytes ( )
2122
+ }
2123
+
2124
+ assert ! ( test_eq( [ ] , "" ) ) ;
2125
+ assert ! ( test_eq( [ "/bin" , "/usr/bin" , "/usr/local/bin" ] ,
2126
+ "/bin:/usr/bin:/usr/local/bin" ) ) ;
2127
+ assert ! ( test_eq( [ "" , "/bin" , "" , "" , "/usr/bin" , "" ] ,
2128
+ ":/bin:::/usr/bin:" ) ) ;
2129
+ assert ! ( join_paths( [ "/te:st" ] ) . is_err( ) ) ;
2130
+ }
2131
+
2132
+ #[ test]
2133
+ #[ cfg( windows) ]
2134
+ fn join_paths_windows ( ) {
2135
+ fn test_eq ( input : & [ & str ] , output : & str ) -> bool {
2136
+ join_paths ( input) . unwrap ( ) . as_slice ( ) == output. as_bytes ( )
2137
+ }
2138
+
2139
+ assert ! ( test_eq( [ ] , "" ) ) ;
2140
+ assert ! ( test_eq( [ r"c:\windows" , r"c:\" ] ,
2141
+ r"c:\windows;c:\" ) ) ;
2142
+ assert ! ( test_eq( [ "" , r"c:\windows" , "" , "" , r"c:\" , "" ] ,
2143
+ r";c:\windows;;;c:\;" ) ) ;
2144
+ assert ! ( test_eq( [ r"c:\te;st" , r"c:\" ] ,
2145
+ r#""c:\te;st";c:\"# ) ) ;
2146
+ assert ! ( join_paths( [ r#"c:\te"st"# ] ) . is_err( ) ) ;
2147
+ }
2148
+
2062
2149
// More recursive_mkdir tests are in extra::tempfile
2063
2150
}
0 commit comments