]> git.lizzy.rs Git - rust.git/blob - src/libstd/os.rs
f7afce9f4099ffa8f61649f74e38b305f9531a3c
[rust.git] / src / libstd / os.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12  * Higher-level interfaces to libc::* functions and operating system services.
13  *
14  * In general these take and return rust types, use rust idioms (enums,
15  * closures, vectors) rather than C idioms, and do more extensive safety
16  * checks.
17  *
18  * This module is not meant to only contain 1:1 mappings to libc entries; any
19  * os-interface code that is reasonably useful and broadly applicable can go
20  * here. Including utility routines that merely build on other os code.
21  *
22  * We assume the general case is that users do not care, and do not want to
23  * be made to care, which operating system they are on. While they may want
24  * to special case various special cases -- and so we will not _hide_ the
25  * facts of which OS the user is on -- they should be given the opportunity
26  * to write OS-ignorant code by default.
27  */
28
29 #![experimental]
30
31 #![allow(missing_docs)]
32 #![allow(non_snake_case)]
33
34 pub use self::MemoryMapKind::*;
35 pub use self::MapOption::*;
36 pub use self::MapError::*;
37
38 use clone::Clone;
39 use error::{FromError, Error};
40 use fmt;
41 use io::IoResult;
42 use iter::Iterator;
43 use libc::{c_void, c_int};
44 use libc;
45 use boxed::Box;
46 use ops::Drop;
47 use option::{Some, None, Option};
48 use os;
49 use path::{Path, GenericPath, BytesContainer};
50 use sys;
51 use sys::os as os_imp;
52 use ptr::RawPtr;
53 use ptr;
54 use result::{Err, Ok, Result};
55 use slice::{AsSlice, SlicePrelude, PartialEqSlicePrelude};
56 use slice::CloneSliceAllocPrelude;
57 use str::{Str, StrPrelude, StrAllocating};
58 use string::{String, ToString};
59 use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
60 use vec::Vec;
61
62 #[cfg(unix)] use c_str::ToCStr;
63 #[cfg(unix)] use libc::c_char;
64
65 /// Get the number of cores available
66 pub fn num_cpus() -> uint {
67     unsafe {
68         return rust_get_num_cpus() as uint;
69     }
70
71     extern {
72         fn rust_get_num_cpus() -> libc::uintptr_t;
73     }
74 }
75
76 pub const TMPBUF_SZ : uint = 1000u;
77 const BUF_BYTES : uint = 2048u;
78
79 /// Returns the current working directory as a Path.
80 ///
81 /// # Failure
82 ///
83 /// Fails if the current working directory value is invalid:
84 /// Possible cases:
85 ///
86 /// * Current directory does not exist.
87 /// * There are insufficient permissions to access the current directory.
88 ///
89 /// # Example
90 ///
91 /// ```rust
92 /// use std::os;
93 ///
94 /// // We assume that we are in a valid directory like "/home".
95 /// let current_working_directory = os::getcwd();
96 /// println!("The current directory is {}", current_working_directory.display());
97 /// // /home
98 /// ```
99 #[cfg(unix)]
100 pub fn getcwd() -> Path {
101     use c_str::CString;
102
103     let mut buf = [0 as c_char, ..BUF_BYTES];
104     unsafe {
105         if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
106             panic!()
107         }
108         Path::new(CString::new(buf.as_ptr(), false))
109     }
110 }
111
112 /// Returns the current working directory as a Path.
113 ///
114 /// # Failure
115 ///
116 /// Fails if the current working directory value is invalid.
117 /// Possibles cases:
118 ///
119 /// * Current directory does not exist.
120 /// * There are insufficient permissions to access the current directory.
121 ///
122 /// # Example
123 ///
124 /// ```rust
125 /// use std::os;
126 ///
127 /// // We assume that we are in a valid directory like "C:\\Windows".
128 /// let current_working_directory = os::getcwd();
129 /// println!("The current directory is {}", current_working_directory.display());
130 /// // C:\\Windows
131 /// ```
132 #[cfg(windows)]
133 pub fn getcwd() -> Path {
134     use libc::DWORD;
135     use libc::GetCurrentDirectoryW;
136
137     let mut buf = [0 as u16, ..BUF_BYTES];
138     unsafe {
139         if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD {
140             panic!();
141         }
142     }
143     Path::new(String::from_utf16(::str::truncate_utf16_at_nul(&buf))
144               .expect("GetCurrentDirectoryW returned invalid UTF-16"))
145 }
146
147 #[cfg(windows)]
148 pub mod windows {
149     use libc::types::os::arch::extra::DWORD;
150     use libc;
151     use option::{None, Option};
152     use option;
153     use os::TMPBUF_SZ;
154     use slice::{SlicePrelude};
155     use string::String;
156     use str::StrPrelude;
157     use vec::Vec;
158
159     pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD)
160         -> Option<String> {
161
162         unsafe {
163             let mut n = TMPBUF_SZ as DWORD;
164             let mut res = None;
165             let mut done = false;
166             while !done {
167                 let mut buf = Vec::from_elem(n as uint, 0u16);
168                 let k = f(buf.as_mut_ptr(), n);
169                 if k == (0 as DWORD) {
170                     done = true;
171                 } else if k == n &&
172                           libc::GetLastError() ==
173                           libc::ERROR_INSUFFICIENT_BUFFER as DWORD {
174                     n *= 2 as DWORD;
175                 } else if k >= n {
176                     n = k;
177                 } else {
178                     done = true;
179                 }
180                 if k != 0 && done {
181                     let sub = buf.slice(0, k as uint);
182                     // We want to explicitly catch the case when the
183                     // closure returned invalid UTF-16, rather than
184                     // set `res` to None and continue.
185                     let s = String::from_utf16(sub)
186                         .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16");
187                     res = option::Some(s)
188                 }
189             }
190             return res;
191         }
192     }
193 }
194
195 /*
196 Accessing environment variables is not generally threadsafe.
197 Serialize access through a global lock.
198 */
199 fn with_env_lock<T>(f: || -> T) -> T {
200     use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
201
202     static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
203
204     unsafe {
205         let _guard = LOCK.lock();
206         f()
207     }
208 }
209
210 /// Returns a vector of (variable, value) pairs, for all the environment
211 /// variables of the current process.
212 ///
213 /// Invalid UTF-8 bytes are replaced with \uFFFD. See `String::from_utf8_lossy()`
214 /// for details.
215 ///
216 /// # Example
217 ///
218 /// ```rust
219 /// use std::os;
220 ///
221 /// // We will iterate through the references to the element returned by os::env();
222 /// for &(ref key, ref value) in os::env().iter() {
223 ///     println!("'{}': '{}'", key, value );
224 /// }
225 /// ```
226 pub fn env() -> Vec<(String,String)> {
227     env_as_bytes().into_iter().map(|(k,v)| {
228         let k = String::from_utf8_lossy(k.as_slice()).into_string();
229         let v = String::from_utf8_lossy(v.as_slice()).into_string();
230         (k,v)
231     }).collect()
232 }
233
234 /// Returns a vector of (variable, value) byte-vector pairs for all the
235 /// environment variables of the current process.
236 pub fn env_as_bytes() -> Vec<(Vec<u8>,Vec<u8>)> {
237     unsafe {
238         #[cfg(windows)]
239         unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
240             use slice::raw;
241
242             use libc::funcs::extra::kernel32::{
243                 GetEnvironmentStringsW,
244                 FreeEnvironmentStringsW
245             };
246             let ch = GetEnvironmentStringsW();
247             if ch as uint == 0 {
248                 panic!("os::env() failure getting env string from OS: {}",
249                        os::last_os_error());
250             }
251             // Here, we lossily decode the string as UTF16.
252             //
253             // The docs suggest that the result should be in Unicode, but
254             // Windows doesn't guarantee it's actually UTF16 -- it doesn't
255             // validate the environment string passed to CreateProcess nor
256             // SetEnvironmentVariable.  Yet, it's unlikely that returning a
257             // raw u16 buffer would be of practical use since the result would
258             // be inherently platform-dependent and introduce additional
259             // complexity to this code.
260             //
261             // Using the non-Unicode version of GetEnvironmentStrings is even
262             // worse since the result is in an OEM code page.  Characters that
263             // can't be encoded in the code page would be turned into question
264             // marks.
265             let mut result = Vec::new();
266             let mut i = 0;
267             while *ch.offset(i) != 0 {
268                 let p = &*ch.offset(i);
269                 let mut len = 0;
270                 while *(p as *const _).offset(len) != 0 {
271                     len += 1;
272                 }
273                 raw::buf_as_slice(p, len as uint, |s| {
274                     result.push(String::from_utf16_lossy(s).into_bytes());
275                 });
276                 i += len as int + 1;
277             }
278             FreeEnvironmentStringsW(ch);
279             result
280         }
281         #[cfg(unix)]
282         unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
283             use c_str::CString;
284
285             extern {
286                 fn rust_env_pairs() -> *const *const c_char;
287             }
288             let mut environ = rust_env_pairs();
289             if environ as uint == 0 {
290                 panic!("os::env() failure getting env string from OS: {}",
291                        os::last_os_error());
292             }
293             let mut result = Vec::new();
294             while *environ != 0 as *const _ {
295                 let env_pair =
296                     CString::new(*environ, false).as_bytes_no_nul().to_vec();
297                 result.push(env_pair);
298                 environ = environ.offset(1);
299             }
300             result
301         }
302
303         fn env_convert(input: Vec<Vec<u8>>) -> Vec<(Vec<u8>, Vec<u8>)> {
304             let mut pairs = Vec::new();
305             for p in input.iter() {
306                 let mut it = p.as_slice().splitn(1, |b| *b == b'=');
307                 let key = it.next().unwrap().to_vec();
308                 let default: &[u8] = &[];
309                 let val = it.next().unwrap_or(default).to_vec();
310                 pairs.push((key, val));
311             }
312             pairs
313         }
314         with_env_lock(|| {
315             let unparsed_environ = get_env_pairs();
316             env_convert(unparsed_environ)
317         })
318     }
319 }
320
321 #[cfg(unix)]
322 /// Fetches the environment variable `n` from the current process, returning
323 /// None if the variable isn't set.
324 ///
325 /// Any invalid UTF-8 bytes in the value are replaced by \uFFFD. See
326 /// `String::from_utf8_lossy()` for details.
327 ///
328 /// # Panics
329 ///
330 /// Panics if `n` has any interior NULs.
331 ///
332 /// # Example
333 ///
334 /// ```rust
335 /// use std::os;
336 ///
337 /// let key = "HOME";
338 /// match os::getenv(key) {
339 ///     Some(val) => println!("{}: {}", key, val),
340 ///     None => println!("{} is not defined in the environment.", key)
341 /// }
342 /// ```
343 pub fn getenv(n: &str) -> Option<String> {
344     getenv_as_bytes(n).map(|v| String::from_utf8_lossy(v.as_slice()).into_string())
345 }
346
347 #[cfg(unix)]
348 /// Fetches the environment variable `n` byte vector from the current process,
349 /// returning None if the variable isn't set.
350 ///
351 /// # Panics
352 ///
353 /// Panics if `n` has any interior NULs.
354 pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
355     use c_str::CString;
356
357     unsafe {
358         with_env_lock(|| {
359             let s = n.with_c_str(|buf| libc::getenv(buf));
360             if s.is_null() {
361                 None
362             } else {
363                 Some(CString::new(s as *const i8, false).as_bytes_no_nul().to_vec())
364             }
365         })
366     }
367 }
368
369 #[cfg(windows)]
370 /// Fetches the environment variable `n` from the current process, returning
371 /// None if the variable isn't set.
372 pub fn getenv(n: &str) -> Option<String> {
373     unsafe {
374         with_env_lock(|| {
375             use os::windows::{fill_utf16_buf_and_decode};
376             let mut n: Vec<u16> = n.utf16_units().collect();
377             n.push(0);
378             fill_utf16_buf_and_decode(|buf, sz| {
379                 libc::GetEnvironmentVariableW(n.as_ptr(), buf, sz)
380             })
381         })
382     }
383 }
384
385 #[cfg(windows)]
386 /// Fetches the environment variable `n` byte vector from the current process,
387 /// returning None if the variable isn't set.
388 pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> {
389     getenv(n).map(|s| s.into_bytes())
390 }
391
392 /// Sets the environment variable `n` to the value `v` for the currently running
393 /// process.
394 ///
395 /// # Example
396 ///
397 /// ```rust
398 /// use std::os;
399 ///
400 /// let key = "KEY";
401 /// os::setenv(key, "VALUE");
402 /// match os::getenv(key) {
403 ///     Some(ref val) => println!("{}: {}", key, val),
404 ///     None => println!("{} is not defined in the environment.", key)
405 /// }
406 /// ```
407 pub fn setenv<T: BytesContainer>(n: &str, v: T) {
408     #[cfg(unix)]
409     fn _setenv(n: &str, v: &[u8]) {
410         unsafe {
411             with_env_lock(|| {
412                 n.with_c_str(|nbuf| {
413                     v.with_c_str(|vbuf| {
414                         libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
415                     })
416                 })
417             })
418         }
419     }
420
421     #[cfg(windows)]
422     fn _setenv(n: &str, v: &[u8]) {
423         let mut n: Vec<u16> = n.utf16_units().collect();
424         n.push(0);
425         let mut v: Vec<u16> = ::str::from_utf8(v).unwrap().utf16_units().collect();
426         v.push(0);
427
428         unsafe {
429             with_env_lock(|| {
430                 libc::SetEnvironmentVariableW(n.as_ptr(), v.as_ptr());
431             })
432         }
433     }
434
435     _setenv(n, v.container_as_bytes())
436 }
437
438 /// Remove a variable from the environment entirely.
439 pub fn unsetenv(n: &str) {
440     #[cfg(unix)]
441     fn _unsetenv(n: &str) {
442         unsafe {
443             with_env_lock(|| {
444                 n.with_c_str(|nbuf| {
445                     libc::funcs::posix01::unistd::unsetenv(nbuf);
446                 })
447             })
448         }
449     }
450
451     #[cfg(windows)]
452     fn _unsetenv(n: &str) {
453         let mut n: Vec<u16> = n.utf16_units().collect();
454         n.push(0);
455         unsafe {
456             with_env_lock(|| {
457                 libc::SetEnvironmentVariableW(n.as_ptr(), ptr::null());
458             })
459         }
460     }
461     _unsetenv(n);
462 }
463
464 /// Parses input according to platform conventions for the `PATH`
465 /// environment variable.
466 ///
467 /// # Example
468 /// ```rust
469 /// use std::os;
470 ///
471 /// let key = "PATH";
472 /// match os::getenv_as_bytes(key) {
473 ///     Some(paths) => {
474 ///         for path in os::split_paths(paths).iter() {
475 ///             println!("'{}'", path.display());
476 ///         }
477 ///     }
478 ///     None => println!("{} is not defined in the environment.", key)
479 /// }
480 /// ```
481 pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
482     #[cfg(unix)]
483     fn _split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
484         unparsed.container_as_bytes()
485                 .split(|b| *b == b':')
486                 .map(Path::new)
487                 .collect()
488     }
489
490     #[cfg(windows)]
491     fn _split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
492         // On Windows, the PATH environment variable is semicolon separated.  Double
493         // quotes are used as a way of introducing literal semicolons (since
494         // c:\some;dir is a valid Windows path). Double quotes are not themselves
495         // permitted in path names, so there is no way to escape a double quote.
496         // Quoted regions can appear in arbitrary locations, so
497         //
498         //   c:\foo;c:\som"e;di"r;c:\bar
499         //
500         // Should parse as [c:\foo, c:\some;dir, c:\bar].
501         //
502         // (The above is based on testing; there is no clear reference available
503         // for the grammar.)
504
505         let mut parsed = Vec::new();
506         let mut in_progress = Vec::new();
507         let mut in_quote = false;
508
509         for b in unparsed.container_as_bytes().iter() {
510             match *b {
511                 b';' if !in_quote => {
512                     parsed.push(Path::new(in_progress.as_slice()));
513                     in_progress.truncate(0)
514                 }
515                 b'"' => {
516                     in_quote = !in_quote;
517                 }
518                 _  => {
519                     in_progress.push(*b);
520                 }
521             }
522         }
523         parsed.push(Path::new(in_progress));
524         parsed
525     }
526
527     _split_paths(unparsed)
528 }
529
530 /// Joins a collection of `Path`s appropriately for the `PATH`
531 /// environment variable.
532 ///
533 /// Returns a `Vec<u8>` on success, since `Path`s are not utf-8
534 /// encoded on all platforms.
535 ///
536 /// Returns an `Err` (containing an error message) if one of the input
537 /// `Path`s contains an invalid character for constructing the `PATH`
538 /// variable (a double quote on Windows or a colon on Unix).
539 ///
540 /// # Example
541 ///
542 /// ```rust
543 /// use std::os;
544 /// use std::path::Path;
545 ///
546 /// let key = "PATH";
547 /// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths);
548 /// paths.push(Path::new("/home/xyz/bin"));
549 /// os::setenv(key, os::join_paths(paths.as_slice()).unwrap());
550 /// ```
551 pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
552     #[cfg(windows)]
553     fn _join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
554         let mut joined = Vec::new();
555         let sep = b';';
556
557         for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
558             if i > 0 { joined.push(sep) }
559             if path.contains(&b'"') {
560                 return Err("path segment contains `\"`");
561             } else if path.contains(&sep) {
562                 joined.push(b'"');
563                 joined.push_all(path);
564                 joined.push(b'"');
565             } else {
566                 joined.push_all(path);
567             }
568         }
569
570         Ok(joined)
571     }
572
573     #[cfg(unix)]
574     fn _join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
575         let mut joined = Vec::new();
576         let sep = b':';
577
578         for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
579             if i > 0 { joined.push(sep) }
580             if path.contains(&sep) { return Err("path segment contains separator `:`") }
581             joined.push_all(path);
582         }
583
584         Ok(joined)
585     }
586
587     _join_paths(paths)
588 }
589
590 /// A low-level OS in-memory pipe.
591 pub struct Pipe {
592     /// A file descriptor representing the reading end of the pipe. Data written
593     /// on the `out` file descriptor can be read from this file descriptor.
594     pub reader: c_int,
595     /// A file descriptor representing the write end of the pipe. Data written
596     /// to this file descriptor can be read from the `input` file descriptor.
597     pub writer: c_int,
598 }
599
600 /// Creates a new low-level OS in-memory pipe.
601 ///
602 /// This function can fail to succeed if there are no more resources available
603 /// to allocate a pipe.
604 ///
605 /// This function is also unsafe as there is no destructor associated with the
606 /// `Pipe` structure will return. If it is not arranged for the returned file
607 /// descriptors to be closed, the file descriptors will leak. For safe handling
608 /// of this scenario, use `std::io::PipeStream` instead.
609 pub unsafe fn pipe() -> IoResult<Pipe> {
610     let (reader, writer) = try!(sys::os::pipe());
611     Ok(Pipe {
612         reader: reader.unwrap(),
613         writer: writer.unwrap(),
614     })
615 }
616
617 /// Returns the proper dll filename for the given basename of a file
618 /// as a String.
619 #[cfg(not(target_os="ios"))]
620 pub fn dll_filename(base: &str) -> String {
621     format!("{}{}{}", consts::DLL_PREFIX, base, consts::DLL_SUFFIX)
622 }
623
624 /// Optionally returns the filesystem path to the current executable which is
625 /// running but with the executable name.
626 ///
627 /// # Examples
628 ///
629 /// ```rust
630 /// use std::os;
631 ///
632 /// match os::self_exe_name() {
633 ///     Some(exe_path) => println!("Path of this executable is: {}", exe_path.display()),
634 ///     None => println!("Unable to get the path of this executable!")
635 /// };
636 /// ```
637 pub fn self_exe_name() -> Option<Path> {
638
639     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
640     fn load_self() -> Option<Vec<u8>> {
641         unsafe {
642             use libc::funcs::bsd44::*;
643             use libc::consts::os::extra::*;
644             let mut mib = vec![CTL_KERN as c_int,
645                                KERN_PROC as c_int,
646                                KERN_PROC_PATHNAME as c_int,
647                                -1 as c_int];
648             let mut sz: libc::size_t = 0;
649             let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
650                              ptr::null_mut(), &mut sz, ptr::null_mut(),
651                              0u as libc::size_t);
652             if err != 0 { return None; }
653             if sz == 0 { return None; }
654             let mut v: Vec<u8> = Vec::with_capacity(sz as uint);
655             let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
656                              v.as_mut_ptr() as *mut c_void, &mut sz,
657                              ptr::null_mut(), 0u as libc::size_t);
658             if err != 0 { return None; }
659             if sz == 0 { return None; }
660             v.set_len(sz as uint - 1); // chop off trailing NUL
661             Some(v)
662         }
663     }
664
665     #[cfg(any(target_os = "linux", target_os = "android"))]
666     fn load_self() -> Option<Vec<u8>> {
667         use std::io;
668
669         match io::fs::readlink(&Path::new("/proc/self/exe")) {
670             Ok(path) => Some(path.into_vec()),
671             Err(..) => None
672         }
673     }
674
675     #[cfg(any(target_os = "macos", target_os = "ios"))]
676     fn load_self() -> Option<Vec<u8>> {
677         unsafe {
678             use libc::funcs::extra::_NSGetExecutablePath;
679             let mut sz: u32 = 0;
680             _NSGetExecutablePath(ptr::null_mut(), &mut sz);
681             if sz == 0 { return None; }
682             let mut v: Vec<u8> = Vec::with_capacity(sz as uint);
683             let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
684             if err != 0 { return None; }
685             v.set_len(sz as uint - 1); // chop off trailing NUL
686             Some(v)
687         }
688     }
689
690     #[cfg(windows)]
691     fn load_self() -> Option<Vec<u8>> {
692         unsafe {
693             use os::windows::fill_utf16_buf_and_decode;
694             fill_utf16_buf_and_decode(|buf, sz| {
695                 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
696             }).map(|s| s.into_string().into_bytes())
697         }
698     }
699
700     load_self().and_then(Path::new_opt)
701 }
702
703 /// Optionally returns the filesystem path to the current executable which is
704 /// running.
705 ///
706 /// Like self_exe_name() but without the binary's name.
707 ///
708 /// # Example
709 ///
710 /// ```rust
711 /// use std::os;
712 ///
713 /// match os::self_exe_path() {
714 ///     Some(exe_path) => println!("Executable's Path is: {}", exe_path.display()),
715 ///     None => println!("Impossible to fetch the path of this executable.")
716 /// };
717 /// ```
718 pub fn self_exe_path() -> Option<Path> {
719     self_exe_name().map(|mut p| { p.pop(); p })
720 }
721
722 /// Optionally returns the path to the current user's home directory if known.
723 ///
724 /// # Unix
725 ///
726 /// Returns the value of the 'HOME' environment variable if it is set
727 /// and not equal to the empty string.
728 ///
729 /// # Windows
730 ///
731 /// Returns the value of the 'HOME' environment variable if it is
732 /// set and not equal to the empty string. Otherwise, returns the value of the
733 /// 'USERPROFILE' environment variable if it is set and not equal to the empty
734 /// string.
735 ///
736 /// # Example
737 ///
738 /// ```rust
739 /// use std::os;
740 ///
741 /// match os::homedir() {
742 ///     Some(ref p) => println!("{}", p.display()),
743 ///     None => println!("Impossible to get your home dir!")
744 /// }
745 /// ```
746 pub fn homedir() -> Option<Path> {
747     #[inline]
748     #[cfg(unix)]
749     fn _homedir() -> Option<Path> {
750         aux_homedir("HOME")
751     }
752
753     #[inline]
754     #[cfg(windows)]
755     fn _homedir() -> Option<Path> {
756         aux_homedir("HOME").or(aux_homedir("USERPROFILE"))
757     }
758
759     #[inline]
760     fn aux_homedir(home_name: &str) -> Option<Path> {
761         match getenv_as_bytes(home_name) {
762             Some(p)  => {
763                 if p.is_empty() { None } else { Path::new_opt(p) }
764             },
765             _ => None
766         }
767     }
768     _homedir()
769 }
770
771 /**
772  * Returns the path to a temporary directory.
773  *
774  * On Unix, returns the value of the 'TMPDIR' environment variable if it is
775  * set, otherwise for non-Android it returns '/tmp'. If Android, since there
776  * is no global temporary folder (it is usually allocated per-app), we return
777  * '/data/local/tmp'.
778  *
779  * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
780  * 'USERPROFILE' environment variable  if any are set and not the empty
781  * string. Otherwise, tmpdir returns the path to the Windows directory.
782  */
783 pub fn tmpdir() -> Path {
784     return lookup();
785
786     fn getenv_nonempty(v: &str) -> Option<Path> {
787         match getenv(v) {
788             Some(x) =>
789                 if x.is_empty() {
790                     None
791                 } else {
792                     Path::new_opt(x)
793                 },
794             _ => None
795         }
796     }
797
798     #[cfg(unix)]
799     fn lookup() -> Path {
800         let default = if cfg!(target_os = "android") {
801             Path::new("/data/local/tmp")
802         } else {
803             Path::new("/tmp")
804         };
805
806         getenv_nonempty("TMPDIR").unwrap_or(default)
807     }
808
809     #[cfg(windows)]
810     fn lookup() -> Path {
811         getenv_nonempty("TMP").or(
812             getenv_nonempty("TEMP").or(
813                 getenv_nonempty("USERPROFILE").or(
814                    getenv_nonempty("WINDIR")))).unwrap_or(Path::new("C:\\Windows"))
815     }
816 }
817
818 ///
819 /// Convert a relative path to an absolute path
820 ///
821 /// If the given path is relative, return it prepended with the current working
822 /// directory. If the given path is already an absolute path, return it
823 /// as is.
824 ///
825 /// # Example
826 /// ```rust
827 /// use std::os;
828 /// use std::path::Path;
829 ///
830 /// // Assume we're in a path like /home/someuser
831 /// let rel_path = Path::new("..");
832 /// let abs_path = os::make_absolute(&rel_path);
833 /// println!("The absolute path is {}", abs_path.display());
834 /// // Prints "The absolute path is /home"
835 /// ```
836 // NB: this is here rather than in path because it is a form of environment
837 // querying; what it does depends on the process working directory, not just
838 // the input paths.
839 pub fn make_absolute(p: &Path) -> Path {
840     if p.is_absolute() {
841         p.clone()
842     } else {
843         let mut ret = getcwd();
844         ret.push(p);
845         ret
846     }
847 }
848
849 /// Changes the current working directory to the specified path, returning
850 /// whether the change was completed successfully or not.
851 ///
852 /// # Example
853 /// ```rust
854 /// use std::os;
855 /// use std::path::Path;
856 ///
857 /// let root = Path::new("/");
858 /// assert!(os::change_dir(&root));
859 /// println!("Successfully changed working directory to {}!", root.display());
860 /// ```
861 pub fn change_dir(p: &Path) -> bool {
862     return chdir(p);
863
864     #[cfg(windows)]
865     fn chdir(p: &Path) -> bool {
866         let p = match p.as_str() {
867             Some(s) => {
868                 let mut p = s.utf16_units().collect::<Vec<u16>>();
869                 p.push(0);
870                 p
871             }
872             None => return false,
873         };
874         unsafe {
875             libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL)
876         }
877     }
878
879     #[cfg(unix)]
880     fn chdir(p: &Path) -> bool {
881         p.with_c_str(|buf| {
882             unsafe {
883                 libc::chdir(buf) == (0 as c_int)
884             }
885         })
886     }
887 }
888
889 /// Returns the platform-specific value of errno
890 pub fn errno() -> uint {
891     os_imp::errno() as uint
892 }
893
894 /// Return the string corresponding to an `errno()` value of `errnum`.
895 ///
896 /// # Example
897 /// ```rust
898 /// use std::os;
899 ///
900 /// // Same as println!("{}", last_os_error());
901 /// println!("{}", os::error_string(os::errno() as uint));
902 /// ```
903 pub fn error_string(errnum: uint) -> String {
904     return os_imp::error_string(errnum as i32);
905 }
906
907 /// Get a string representing the platform-dependent last error
908 pub fn last_os_error() -> String {
909     error_string(errno() as uint)
910 }
911
912 static EXIT_STATUS: AtomicInt = INIT_ATOMIC_INT;
913
914 /**
915  * Sets the process exit code
916  *
917  * Sets the exit code returned by the process if all supervised tasks
918  * terminate successfully (without panicking). If the current root task panics
919  * and is supervised by the scheduler then any user-specified exit status is
920  * ignored and the process exits with the default panic status.
921  *
922  * Note that this is not synchronized against modifications of other threads.
923  */
924 pub fn set_exit_status(code: int) {
925     EXIT_STATUS.store(code, SeqCst)
926 }
927
928 /// Fetches the process's current exit code. This defaults to 0 and can change
929 /// by calling `set_exit_status`.
930 pub fn get_exit_status() -> int {
931     EXIT_STATUS.load(SeqCst)
932 }
933
934 #[cfg(target_os = "macos")]
935 unsafe fn load_argc_and_argv(argc: int,
936                              argv: *const *const c_char) -> Vec<Vec<u8>> {
937     use c_str::CString;
938
939     Vec::from_fn(argc as uint, |i| {
940         CString::new(*argv.offset(i as int), false).as_bytes_no_nul().to_vec()
941     })
942 }
943
944 /**
945  * Returns the command line arguments
946  *
947  * Returns a list of the command line arguments.
948  */
949 #[cfg(target_os = "macos")]
950 fn real_args_as_bytes() -> Vec<Vec<u8>> {
951     unsafe {
952         let (argc, argv) = (*_NSGetArgc() as int,
953                             *_NSGetArgv() as *const *const c_char);
954         load_argc_and_argv(argc, argv)
955     }
956 }
957
958 // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
959 // and use underscores in their names - they're most probably
960 // are considered private and therefore should be avoided
961 // Here is another way to get arguments using Objective C
962 // runtime
963 //
964 // In general it looks like:
965 // res = Vec::new()
966 // let args = [[NSProcessInfo processInfo] arguments]
967 // for i in range(0, [args count])
968 //      res.push([args objectAtIndex:i])
969 // res
970 #[cfg(target_os = "ios")]
971 fn real_args_as_bytes() -> Vec<Vec<u8>> {
972     use c_str::CString;
973     use iter::range;
974     use mem;
975
976     #[link(name = "objc")]
977     extern {
978         fn sel_registerName(name: *const libc::c_uchar) -> Sel;
979         fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
980         fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
981     }
982
983     #[link(name = "Foundation", kind = "framework")]
984     extern {}
985
986     type Sel = *const libc::c_void;
987     type NsId = *const libc::c_void;
988
989     let mut res = Vec::new();
990
991     unsafe {
992         let processInfoSel = sel_registerName("processInfo\0".as_ptr());
993         let argumentsSel = sel_registerName("arguments\0".as_ptr());
994         let utf8Sel = sel_registerName("UTF8String\0".as_ptr());
995         let countSel = sel_registerName("count\0".as_ptr());
996         let objectAtSel = sel_registerName("objectAtIndex:\0".as_ptr());
997
998         let klass = objc_getClass("NSProcessInfo\0".as_ptr());
999         let info = objc_msgSend(klass, processInfoSel);
1000         let args = objc_msgSend(info, argumentsSel);
1001
1002         let cnt: int = mem::transmute(objc_msgSend(args, countSel));
1003         for i in range(0, cnt) {
1004             let tmp = objc_msgSend(args, objectAtSel, i);
1005             let utf_c_str: *const libc::c_char =
1006                 mem::transmute(objc_msgSend(tmp, utf8Sel));
1007             let s = CString::new(utf_c_str, false);
1008             res.push(s.as_bytes_no_nul().to_vec())
1009         }
1010     }
1011
1012     res
1013 }
1014
1015 #[cfg(any(target_os = "linux",
1016           target_os = "android",
1017           target_os = "freebsd",
1018           target_os = "dragonfly"))]
1019 fn real_args_as_bytes() -> Vec<Vec<u8>> {
1020     use rt;
1021
1022     match rt::args::clone() {
1023         Some(args) => args,
1024         None => panic!("process arguments not initialized")
1025     }
1026 }
1027
1028 #[cfg(not(windows))]
1029 fn real_args() -> Vec<String> {
1030     real_args_as_bytes().into_iter()
1031                         .map(|v| {
1032                             String::from_utf8_lossy(v.as_slice()).into_string()
1033                         }).collect()
1034 }
1035
1036 #[cfg(windows)]
1037 fn real_args() -> Vec<String> {
1038     use slice;
1039
1040     let mut nArgs: c_int = 0;
1041     let lpArgCount: *mut c_int = &mut nArgs;
1042     let lpCmdLine = unsafe { GetCommandLineW() };
1043     let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1044
1045     let args = Vec::from_fn(nArgs as uint, |i| unsafe {
1046         // Determine the length of this argument.
1047         let ptr = *szArgList.offset(i as int);
1048         let mut len = 0;
1049         while *ptr.offset(len as int) != 0 { len += 1; }
1050
1051         // Push it onto the list.
1052         let opt_s = slice::raw::buf_as_slice(ptr as *const _, len, |buf| {
1053             String::from_utf16(::str::truncate_utf16_at_nul(buf))
1054         });
1055         opt_s.expect("CommandLineToArgvW returned invalid UTF-16")
1056     });
1057
1058     unsafe {
1059         LocalFree(szArgList as *mut c_void);
1060     }
1061
1062     return args
1063 }
1064
1065 #[cfg(windows)]
1066 fn real_args_as_bytes() -> Vec<Vec<u8>> {
1067     real_args().into_iter().map(|s| s.into_bytes()).collect()
1068 }
1069
1070 type LPCWSTR = *const u16;
1071
1072 #[cfg(windows)]
1073 #[link_name="kernel32"]
1074 extern "system" {
1075     fn GetCommandLineW() -> LPCWSTR;
1076     fn LocalFree(ptr: *mut c_void);
1077 }
1078
1079 #[cfg(windows)]
1080 #[link_name="shell32"]
1081 extern "system" {
1082     fn CommandLineToArgvW(lpCmdLine: LPCWSTR,
1083                           pNumArgs: *mut c_int) -> *mut *mut u16;
1084 }
1085
1086 /// Returns the arguments which this program was started with (normally passed
1087 /// via the command line).
1088 ///
1089 /// The arguments are interpreted as utf-8, with invalid bytes replaced with \uFFFD.
1090 /// See `String::from_utf8_lossy` for details.
1091 /// # Example
1092 ///
1093 /// ```rust
1094 /// use std::os;
1095 ///
1096 /// // Prints each argument on a separate line
1097 /// for argument in os::args().iter() {
1098 ///     println!("{}", argument);
1099 /// }
1100 /// ```
1101 pub fn args() -> Vec<String> {
1102     real_args()
1103 }
1104
1105 /// Returns the arguments which this program was started with (normally passed
1106 /// via the command line) as byte vectors.
1107 pub fn args_as_bytes() -> Vec<Vec<u8>> {
1108     real_args_as_bytes()
1109 }
1110
1111 #[cfg(target_os = "macos")]
1112 extern {
1113     // These functions are in crt_externs.h.
1114     pub fn _NSGetArgc() -> *mut c_int;
1115     pub fn _NSGetArgv() -> *mut *mut *mut c_char;
1116 }
1117
1118 // Round up `from` to be divisible by `to`
1119 fn round_up(from: uint, to: uint) -> uint {
1120     let r = if from % to == 0 {
1121         from
1122     } else {
1123         from + to - (from % to)
1124     };
1125     if r == 0 {
1126         to
1127     } else {
1128         r
1129     }
1130 }
1131
1132 /// Returns the page size of the current architecture in bytes.
1133 #[cfg(unix)]
1134 pub fn page_size() -> uint {
1135     unsafe {
1136         libc::sysconf(libc::_SC_PAGESIZE) as uint
1137     }
1138 }
1139
1140 /// Returns the page size of the current architecture in bytes.
1141 #[cfg(windows)]
1142 pub fn page_size() -> uint {
1143     use mem;
1144     unsafe {
1145         let mut info = mem::zeroed();
1146         libc::GetSystemInfo(&mut info);
1147
1148         return info.dwPageSize as uint;
1149     }
1150 }
1151
1152 /// A memory mapped file or chunk of memory. This is a very system-specific
1153 /// interface to the OS's memory mapping facilities (`mmap` on POSIX,
1154 /// `VirtualAlloc`/`CreateFileMapping` on Windows). It makes no attempt at
1155 /// abstracting platform differences, besides in error values returned. Consider
1156 /// yourself warned.
1157 ///
1158 /// The memory map is released (unmapped) when the destructor is run, so don't
1159 /// let it leave scope by accident if you want it to stick around.
1160 pub struct MemoryMap {
1161     data: *mut u8,
1162     len: uint,
1163     kind: MemoryMapKind,
1164 }
1165
1166 /// Type of memory map
1167 pub enum MemoryMapKind {
1168     /// Virtual memory map. Usually used to change the permissions of a given
1169     /// chunk of memory.  Corresponds to `VirtualAlloc` on Windows.
1170     MapFile(*const u8),
1171     /// Virtual memory map. Usually used to change the permissions of a given
1172     /// chunk of memory, or for allocation. Corresponds to `VirtualAlloc` on
1173     /// Windows.
1174     MapVirtual
1175 }
1176
1177 /// Options the memory map is created with
1178 pub enum MapOption {
1179     /// The memory should be readable
1180     MapReadable,
1181     /// The memory should be writable
1182     MapWritable,
1183     /// The memory should be executable
1184     MapExecutable,
1185     /// Create a map for a specific address range. Corresponds to `MAP_FIXED` on
1186     /// POSIX.
1187     MapAddr(*const u8),
1188     /// Create a memory mapping for a file with a given fd.
1189     MapFd(c_int),
1190     /// When using `MapFd`, the start of the map is `uint` bytes from the start
1191     /// of the file.
1192     MapOffset(uint),
1193     /// On POSIX, this can be used to specify the default flags passed to
1194     /// `mmap`. By default it uses `MAP_PRIVATE` and, if not using `MapFd`,
1195     /// `MAP_ANON`. This will override both of those. This is platform-specific
1196     /// (the exact values used) and ignored on Windows.
1197     MapNonStandardFlags(c_int),
1198 }
1199
1200 /// Possible errors when creating a map.
1201 pub enum MapError {
1202     /// ## The following are POSIX-specific
1203     ///
1204     /// fd was not open for reading or, if using `MapWritable`, was not open for
1205     /// writing.
1206     ErrFdNotAvail,
1207     /// fd was not valid
1208     ErrInvalidFd,
1209     /// Either the address given by `MapAddr` or offset given by `MapOffset` was
1210     /// not a multiple of `MemoryMap::granularity` (unaligned to page size).
1211     ErrUnaligned,
1212     /// With `MapFd`, the fd does not support mapping.
1213     ErrNoMapSupport,
1214     /// If using `MapAddr`, the address + `min_len` was outside of the process's
1215     /// address space. If using `MapFd`, the target of the fd didn't have enough
1216     /// resources to fulfill the request.
1217     ErrNoMem,
1218     /// A zero-length map was requested. This is invalid according to
1219     /// [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html).
1220     /// Not all platforms obey this, but this wrapper does.
1221     ErrZeroLength,
1222     /// Unrecognized error. The inner value is the unrecognized errno.
1223     ErrUnknown(int),
1224     /// ## The following are Windows-specific
1225     ///
1226     /// Unsupported combination of protection flags
1227     /// (`MapReadable`/`MapWritable`/`MapExecutable`).
1228     ErrUnsupProt,
1229     /// When using `MapFd`, `MapOffset` was given (Windows does not support this
1230     /// at all)
1231     ErrUnsupOffset,
1232     /// When using `MapFd`, there was already a mapping to the file.
1233     ErrAlreadyExists,
1234     /// Unrecognized error from `VirtualAlloc`. The inner value is the return
1235     /// value of GetLastError.
1236     ErrVirtualAlloc(uint),
1237     /// Unrecognized error from `CreateFileMapping`. The inner value is the
1238     /// return value of `GetLastError`.
1239     ErrCreateFileMappingW(uint),
1240     /// Unrecognized error from `MapViewOfFile`. The inner value is the return
1241     /// value of `GetLastError`.
1242     ErrMapViewOfFile(uint)
1243 }
1244
1245 impl fmt::Show for MapError {
1246     fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
1247         let str = match *self {
1248             ErrFdNotAvail => "fd not available for reading or writing",
1249             ErrInvalidFd => "Invalid fd",
1250             ErrUnaligned => {
1251                 "Unaligned address, invalid flags, negative length or \
1252                  unaligned offset"
1253             }
1254             ErrNoMapSupport=> "File doesn't support mapping",
1255             ErrNoMem => "Invalid address, or not enough available memory",
1256             ErrUnsupProt => "Protection mode unsupported",
1257             ErrUnsupOffset => "Offset in virtual memory mode is unsupported",
1258             ErrAlreadyExists => "File mapping for specified file already exists",
1259             ErrZeroLength => "Zero-length mapping not allowed",
1260             ErrUnknown(code) => {
1261                 return write!(out, "Unknown error = {}", code)
1262             },
1263             ErrVirtualAlloc(code) => {
1264                 return write!(out, "VirtualAlloc failure = {}", code)
1265             },
1266             ErrCreateFileMappingW(code) => {
1267                 return write!(out, "CreateFileMappingW failure = {}", code)
1268             },
1269             ErrMapViewOfFile(code) => {
1270                 return write!(out, "MapViewOfFile failure = {}", code)
1271             }
1272         };
1273         write!(out, "{}", str)
1274     }
1275 }
1276
1277 impl Error for MapError {
1278     fn description(&self) -> &str { "memory map error" }
1279     fn detail(&self) -> Option<String> { Some(self.to_string()) }
1280 }
1281
1282 impl FromError<MapError> for Box<Error> {
1283     fn from_error(err: MapError) -> Box<Error> {
1284         box err
1285     }
1286 }
1287
1288 #[cfg(unix)]
1289 impl MemoryMap {
1290     /// Create a new mapping with the given `options`, at least `min_len` bytes
1291     /// long. `min_len` must be greater than zero; see the note on
1292     /// `ErrZeroLength`.
1293     pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
1294         use libc::off_t;
1295
1296         if min_len == 0 {
1297             return Err(ErrZeroLength)
1298         }
1299         let mut addr: *const u8 = ptr::null();
1300         let mut prot = 0;
1301         let mut flags = libc::MAP_PRIVATE;
1302         let mut fd = -1;
1303         let mut offset = 0;
1304         let mut custom_flags = false;
1305         let len = round_up(min_len, page_size());
1306
1307         for &o in options.iter() {
1308             match o {
1309                 MapReadable => { prot |= libc::PROT_READ; },
1310                 MapWritable => { prot |= libc::PROT_WRITE; },
1311                 MapExecutable => { prot |= libc::PROT_EXEC; },
1312                 MapAddr(addr_) => {
1313                     flags |= libc::MAP_FIXED;
1314                     addr = addr_;
1315                 },
1316                 MapFd(fd_) => {
1317                     flags |= libc::MAP_FILE;
1318                     fd = fd_;
1319                 },
1320                 MapOffset(offset_) => { offset = offset_ as off_t; },
1321                 MapNonStandardFlags(f) => { custom_flags = true; flags = f },
1322             }
1323         }
1324         if fd == -1 && !custom_flags { flags |= libc::MAP_ANON; }
1325
1326         let r = unsafe {
1327             libc::mmap(addr as *mut c_void, len as libc::size_t, prot, flags,
1328                        fd, offset)
1329         };
1330         if r == libc::MAP_FAILED {
1331             Err(match errno() as c_int {
1332                 libc::EACCES => ErrFdNotAvail,
1333                 libc::EBADF => ErrInvalidFd,
1334                 libc::EINVAL => ErrUnaligned,
1335                 libc::ENODEV => ErrNoMapSupport,
1336                 libc::ENOMEM => ErrNoMem,
1337                 code => ErrUnknown(code as int)
1338             })
1339         } else {
1340             Ok(MemoryMap {
1341                data: r as *mut u8,
1342                len: len,
1343                kind: if fd == -1 {
1344                    MapVirtual
1345                } else {
1346                    MapFile(ptr::null())
1347                }
1348             })
1349         }
1350     }
1351
1352     /// Granularity that the offset or address must be for `MapOffset` and
1353     /// `MapAddr` respectively.
1354     pub fn granularity() -> uint {
1355         page_size()
1356     }
1357 }
1358
1359 #[cfg(unix)]
1360 impl Drop for MemoryMap {
1361     /// Unmap the mapping. Panics the task if `munmap` panics.
1362     fn drop(&mut self) {
1363         if self.len == 0 { /* workaround for dummy_stack */ return; }
1364
1365         unsafe {
1366             // `munmap` only panics due to logic errors
1367             libc::munmap(self.data as *mut c_void, self.len as libc::size_t);
1368         }
1369     }
1370 }
1371
1372 #[cfg(windows)]
1373 impl MemoryMap {
1374     /// Create a new mapping with the given `options`, at least `min_len` bytes long.
1375     pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
1376         use libc::types::os::arch::extra::{LPVOID, DWORD, SIZE_T, HANDLE};
1377
1378         let mut lpAddress: LPVOID = ptr::null_mut();
1379         let mut readable = false;
1380         let mut writable = false;
1381         let mut executable = false;
1382         let mut fd: c_int = -1;
1383         let mut offset: uint = 0;
1384         let len = round_up(min_len, page_size());
1385
1386         for &o in options.iter() {
1387             match o {
1388                 MapReadable => { readable = true; },
1389                 MapWritable => { writable = true; },
1390                 MapExecutable => { executable = true; }
1391                 MapAddr(addr_) => { lpAddress = addr_ as LPVOID; },
1392                 MapFd(fd_) => { fd = fd_; },
1393                 MapOffset(offset_) => { offset = offset_; },
1394                 MapNonStandardFlags(..) => {}
1395             }
1396         }
1397
1398         let flProtect = match (executable, readable, writable) {
1399             (false, false, false) if fd == -1 => libc::PAGE_NOACCESS,
1400             (false, true, false) => libc::PAGE_READONLY,
1401             (false, true, true) => libc::PAGE_READWRITE,
1402             (true, false, false) if fd == -1 => libc::PAGE_EXECUTE,
1403             (true, true, false) => libc::PAGE_EXECUTE_READ,
1404             (true, true, true) => libc::PAGE_EXECUTE_READWRITE,
1405             _ => return Err(ErrUnsupProt)
1406         };
1407
1408         if fd == -1 {
1409             if offset != 0 {
1410                 return Err(ErrUnsupOffset);
1411             }
1412             let r = unsafe {
1413                 libc::VirtualAlloc(lpAddress,
1414                                    len as SIZE_T,
1415                                    libc::MEM_COMMIT | libc::MEM_RESERVE,
1416                                    flProtect)
1417             };
1418             match r as uint {
1419                 0 => Err(ErrVirtualAlloc(errno())),
1420                 _ => Ok(MemoryMap {
1421                    data: r as *mut u8,
1422                    len: len,
1423                    kind: MapVirtual
1424                 })
1425             }
1426         } else {
1427             let dwDesiredAccess = match (executable, readable, writable) {
1428                 (false, true, false) => libc::FILE_MAP_READ,
1429                 (false, true, true) => libc::FILE_MAP_WRITE,
1430                 (true, true, false) => libc::FILE_MAP_READ | libc::FILE_MAP_EXECUTE,
1431                 (true, true, true) => libc::FILE_MAP_WRITE | libc::FILE_MAP_EXECUTE,
1432                 _ => return Err(ErrUnsupProt) // Actually, because of the check above,
1433                                               // we should never get here.
1434             };
1435             unsafe {
1436                 let hFile = libc::get_osfhandle(fd) as HANDLE;
1437                 let mapping = libc::CreateFileMappingW(hFile,
1438                                                        ptr::null_mut(),
1439                                                        flProtect,
1440                                                        0,
1441                                                        0,
1442                                                        ptr::null());
1443                 if mapping == ptr::null_mut() {
1444                     return Err(ErrCreateFileMappingW(errno()));
1445                 }
1446                 if errno() as c_int == libc::ERROR_ALREADY_EXISTS {
1447                     return Err(ErrAlreadyExists);
1448                 }
1449                 let r = libc::MapViewOfFile(mapping,
1450                                             dwDesiredAccess,
1451                                             ((len as u64) >> 32) as DWORD,
1452                                             (offset & 0xffff_ffff) as DWORD,
1453                                             0);
1454                 match r as uint {
1455                     0 => Err(ErrMapViewOfFile(errno())),
1456                     _ => Ok(MemoryMap {
1457                        data: r as *mut u8,
1458                        len: len,
1459                        kind: MapFile(mapping as *const u8)
1460                     })
1461                 }
1462             }
1463         }
1464     }
1465
1466     /// Granularity of MapAddr() and MapOffset() parameter values.
1467     /// This may be greater than the value returned by page_size().
1468     pub fn granularity() -> uint {
1469         use mem;
1470         unsafe {
1471             let mut info = mem::zeroed();
1472             libc::GetSystemInfo(&mut info);
1473
1474             return info.dwAllocationGranularity as uint;
1475         }
1476     }
1477 }
1478
1479 #[cfg(windows)]
1480 impl Drop for MemoryMap {
1481     /// Unmap the mapping. Panics the task if any of `VirtualFree`,
1482     /// `UnmapViewOfFile`, or `CloseHandle` fail.
1483     fn drop(&mut self) {
1484         use libc::types::os::arch::extra::{LPCVOID, HANDLE};
1485         use libc::consts::os::extra::FALSE;
1486         if self.len == 0 { return }
1487
1488         unsafe {
1489             match self.kind {
1490                 MapVirtual => {
1491                     if libc::VirtualFree(self.data as *mut c_void, 0,
1492                                          libc::MEM_RELEASE) == 0 {
1493                         println!("VirtualFree failed: {}", errno());
1494                     }
1495                 },
1496                 MapFile(mapping) => {
1497                     if libc::UnmapViewOfFile(self.data as LPCVOID) == FALSE {
1498                         println!("UnmapViewOfFile failed: {}", errno());
1499                     }
1500                     if libc::CloseHandle(mapping as HANDLE) == FALSE {
1501                         println!("CloseHandle failed: {}", errno());
1502                     }
1503                 }
1504             }
1505         }
1506     }
1507 }
1508
1509 impl MemoryMap {
1510     /// Returns the pointer to the memory created or modified by this map.
1511     pub fn data(&self) -> *mut u8 { self.data }
1512     /// Returns the number of bytes this map applies to.
1513     pub fn len(&self) -> uint { self.len }
1514     /// Returns the type of mapping this represents.
1515     pub fn kind(&self) -> MemoryMapKind { self.kind }
1516 }
1517
1518 #[cfg(target_os = "linux")]
1519 pub mod consts {
1520     pub use os::arch_consts::ARCH;
1521
1522     pub const FAMILY: &'static str = "unix";
1523
1524     /// A string describing the specific operating system in use: in this
1525     /// case, `linux`.
1526     pub const SYSNAME: &'static str = "linux";
1527
1528     /// Specifies the filename prefix used for shared libraries on this
1529     /// platform: in this case, `lib`.
1530     pub const DLL_PREFIX: &'static str = "lib";
1531
1532     /// Specifies the filename suffix used for shared libraries on this
1533     /// platform: in this case, `.so`.
1534     pub const DLL_SUFFIX: &'static str = ".so";
1535
1536     /// Specifies the file extension used for shared libraries on this
1537     /// platform that goes after the dot: in this case, `so`.
1538     pub const DLL_EXTENSION: &'static str = "so";
1539
1540     /// Specifies the filename suffix used for executable binaries on this
1541     /// platform: in this case, the empty string.
1542     pub const EXE_SUFFIX: &'static str = "";
1543
1544     /// Specifies the file extension, if any, used for executable binaries
1545     /// on this platform: in this case, the empty string.
1546     pub const EXE_EXTENSION: &'static str = "";
1547 }
1548
1549 #[cfg(target_os = "macos")]
1550 pub mod consts {
1551     pub use os::arch_consts::ARCH;
1552
1553     pub const FAMILY: &'static str = "unix";
1554
1555     /// A string describing the specific operating system in use: in this
1556     /// case, `macos`.
1557     pub const SYSNAME: &'static str = "macos";
1558
1559     /// Specifies the filename prefix used for shared libraries on this
1560     /// platform: in this case, `lib`.
1561     pub const DLL_PREFIX: &'static str = "lib";
1562
1563     /// Specifies the filename suffix used for shared libraries on this
1564     /// platform: in this case, `.dylib`.
1565     pub const DLL_SUFFIX: &'static str = ".dylib";
1566
1567     /// Specifies the file extension used for shared libraries on this
1568     /// platform that goes after the dot: in this case, `dylib`.
1569     pub const DLL_EXTENSION: &'static str = "dylib";
1570
1571     /// Specifies the filename suffix used for executable binaries on this
1572     /// platform: in this case, the empty string.
1573     pub const EXE_SUFFIX: &'static str = "";
1574
1575     /// Specifies the file extension, if any, used for executable binaries
1576     /// on this platform: in this case, the empty string.
1577     pub const EXE_EXTENSION: &'static str = "";
1578 }
1579
1580 #[cfg(target_os = "ios")]
1581 pub mod consts {
1582     pub use os::arch_consts::ARCH;
1583
1584     pub const FAMILY: &'static str = "unix";
1585
1586     /// A string describing the specific operating system in use: in this
1587     /// case, `ios`.
1588     pub const SYSNAME: &'static str = "ios";
1589
1590     /// Specifies the filename suffix used for executable binaries on this
1591     /// platform: in this case, the empty string.
1592     pub const EXE_SUFFIX: &'static str = "";
1593
1594     /// Specifies the file extension, if any, used for executable binaries
1595     /// on this platform: in this case, the empty string.
1596     pub const EXE_EXTENSION: &'static str = "";
1597 }
1598
1599 #[cfg(target_os = "freebsd")]
1600 pub mod consts {
1601     pub use os::arch_consts::ARCH;
1602
1603     pub const FAMILY: &'static str = "unix";
1604
1605     /// A string describing the specific operating system in use: in this
1606     /// case, `freebsd`.
1607     pub const SYSNAME: &'static str = "freebsd";
1608
1609     /// Specifies the filename prefix used for shared libraries on this
1610     /// platform: in this case, `lib`.
1611     pub const DLL_PREFIX: &'static str = "lib";
1612
1613     /// Specifies the filename suffix used for shared libraries on this
1614     /// platform: in this case, `.so`.
1615     pub const DLL_SUFFIX: &'static str = ".so";
1616
1617     /// Specifies the file extension used for shared libraries on this
1618     /// platform that goes after the dot: in this case, `so`.
1619     pub const DLL_EXTENSION: &'static str = "so";
1620
1621     /// Specifies the filename suffix used for executable binaries on this
1622     /// platform: in this case, the empty string.
1623     pub const EXE_SUFFIX: &'static str = "";
1624
1625     /// Specifies the file extension, if any, used for executable binaries
1626     /// on this platform: in this case, the empty string.
1627     pub const EXE_EXTENSION: &'static str = "";
1628 }
1629
1630 #[cfg(target_os = "dragonfly")]
1631 pub mod consts {
1632     pub use os::arch_consts::ARCH;
1633
1634     pub const FAMILY: &'static str = "unix";
1635
1636     /// A string describing the specific operating system in use: in this
1637     /// case, `dragonfly`.
1638     pub const SYSNAME: &'static str = "dragonfly";
1639
1640     /// Specifies the filename prefix used for shared libraries on this
1641     /// platform: in this case, `lib`.
1642     pub const DLL_PREFIX: &'static str = "lib";
1643
1644     /// Specifies the filename suffix used for shared libraries on this
1645     /// platform: in this case, `.so`.
1646     pub const DLL_SUFFIX: &'static str = ".so";
1647
1648     /// Specifies the file extension used for shared libraries on this
1649     /// platform that goes after the dot: in this case, `so`.
1650     pub const DLL_EXTENSION: &'static str = "so";
1651
1652     /// Specifies the filename suffix used for executable binaries on this
1653     /// platform: in this case, the empty string.
1654     pub const EXE_SUFFIX: &'static str = "";
1655
1656     /// Specifies the file extension, if any, used for executable binaries
1657     /// on this platform: in this case, the empty string.
1658     pub const EXE_EXTENSION: &'static str = "";
1659 }
1660
1661 #[cfg(target_os = "android")]
1662 pub mod consts {
1663     pub use os::arch_consts::ARCH;
1664
1665     pub const FAMILY: &'static str = "unix";
1666
1667     /// A string describing the specific operating system in use: in this
1668     /// case, `android`.
1669     pub const SYSNAME: &'static str = "android";
1670
1671     /// Specifies the filename prefix used for shared libraries on this
1672     /// platform: in this case, `lib`.
1673     pub const DLL_PREFIX: &'static str = "lib";
1674
1675     /// Specifies the filename suffix used for shared libraries on this
1676     /// platform: in this case, `.so`.
1677     pub const DLL_SUFFIX: &'static str = ".so";
1678
1679     /// Specifies the file extension used for shared libraries on this
1680     /// platform that goes after the dot: in this case, `so`.
1681     pub const DLL_EXTENSION: &'static str = "so";
1682
1683     /// Specifies the filename suffix used for executable binaries on this
1684     /// platform: in this case, the empty string.
1685     pub const EXE_SUFFIX: &'static str = "";
1686
1687     /// Specifies the file extension, if any, used for executable binaries
1688     /// on this platform: in this case, the empty string.
1689     pub const EXE_EXTENSION: &'static str = "";
1690 }
1691
1692 #[cfg(target_os = "windows")]
1693 pub mod consts {
1694     pub use os::arch_consts::ARCH;
1695
1696     pub const FAMILY: &'static str = "windows";
1697
1698     /// A string describing the specific operating system in use: in this
1699     /// case, `windows`.
1700     pub const SYSNAME: &'static str = "windows";
1701
1702     /// Specifies the filename prefix used for shared libraries on this
1703     /// platform: in this case, the empty string.
1704     pub const DLL_PREFIX: &'static str = "";
1705
1706     /// Specifies the filename suffix used for shared libraries on this
1707     /// platform: in this case, `.dll`.
1708     pub const DLL_SUFFIX: &'static str = ".dll";
1709
1710     /// Specifies the file extension used for shared libraries on this
1711     /// platform that goes after the dot: in this case, `dll`.
1712     pub const DLL_EXTENSION: &'static str = "dll";
1713
1714     /// Specifies the filename suffix used for executable binaries on this
1715     /// platform: in this case, `.exe`.
1716     pub const EXE_SUFFIX: &'static str = ".exe";
1717
1718     /// Specifies the file extension, if any, used for executable binaries
1719     /// on this platform: in this case, `exe`.
1720     pub const EXE_EXTENSION: &'static str = "exe";
1721 }
1722
1723 #[cfg(target_arch = "x86")]
1724 mod arch_consts {
1725     pub const ARCH: &'static str = "x86";
1726 }
1727
1728 #[cfg(target_arch = "x86_64")]
1729 mod arch_consts {
1730     pub const ARCH: &'static str = "x86_64";
1731 }
1732
1733 #[cfg(target_arch = "arm")]
1734 mod arch_consts {
1735     pub const ARCH: &'static str = "arm";
1736 }
1737
1738 #[cfg(target_arch = "mips")]
1739 mod arch_consts {
1740     pub const ARCH: &'static str = "mips";
1741 }
1742
1743 #[cfg(target_arch = "mipsel")]
1744 mod arch_consts {
1745     pub const ARCH: &'static str = "mipsel";
1746 }
1747
1748 #[cfg(test)]
1749 mod tests {
1750     use prelude::*;
1751     use c_str::ToCStr;
1752     use option;
1753     use os::{env, getcwd, getenv, make_absolute};
1754     use os::{split_paths, join_paths, setenv, unsetenv};
1755     use os;
1756     use rand::Rng;
1757     use rand;
1758
1759     #[test]
1760     pub fn last_os_error() {
1761         debug!("{}", os::last_os_error());
1762     }
1763
1764     fn make_rand_name() -> String {
1765         let mut rng = rand::task_rng();
1766         let n = format!("TEST{}", rng.gen_ascii_chars().take(10u)
1767                                      .collect::<String>());
1768         assert!(getenv(n.as_slice()).is_none());
1769         n
1770     }
1771
1772     #[test]
1773     fn test_num_cpus() {
1774         assert!(os::num_cpus() > 0);
1775     }
1776
1777     #[test]
1778     fn test_setenv() {
1779         let n = make_rand_name();
1780         setenv(n.as_slice(), "VALUE");
1781         assert_eq!(getenv(n.as_slice()), option::Some("VALUE".to_string()));
1782     }
1783
1784     #[test]
1785     fn test_unsetenv() {
1786         let n = make_rand_name();
1787         setenv(n.as_slice(), "VALUE");
1788         unsetenv(n.as_slice());
1789         assert_eq!(getenv(n.as_slice()), option::None);
1790     }
1791
1792     #[test]
1793     #[ignore]
1794     fn test_setenv_overwrite() {
1795         let n = make_rand_name();
1796         setenv(n.as_slice(), "1");
1797         setenv(n.as_slice(), "2");
1798         assert_eq!(getenv(n.as_slice()), option::Some("2".to_string()));
1799         setenv(n.as_slice(), "");
1800         assert_eq!(getenv(n.as_slice()), option::Some("".to_string()));
1801     }
1802
1803     // Windows GetEnvironmentVariable requires some extra work to make sure
1804     // the buffer the variable is copied into is the right size
1805     #[test]
1806     #[ignore]
1807     fn test_getenv_big() {
1808         let mut s = "".to_string();
1809         let mut i = 0i;
1810         while i < 100 {
1811             s.push_str("aaaaaaaaaa");
1812             i += 1;
1813         }
1814         let n = make_rand_name();
1815         setenv(n.as_slice(), s.as_slice());
1816         debug!("{}", s.clone());
1817         assert_eq!(getenv(n.as_slice()), option::Some(s));
1818     }
1819
1820     #[test]
1821     fn test_self_exe_name() {
1822         let path = os::self_exe_name();
1823         assert!(path.is_some());
1824         let path = path.unwrap();
1825         debug!("{}", path.display());
1826
1827         // Hard to test this function
1828         assert!(path.is_absolute());
1829     }
1830
1831     #[test]
1832     fn test_self_exe_path() {
1833         let path = os::self_exe_path();
1834         assert!(path.is_some());
1835         let path = path.unwrap();
1836         debug!("{}", path.display());
1837
1838         // Hard to test this function
1839         assert!(path.is_absolute());
1840     }
1841
1842     #[test]
1843     #[ignore]
1844     fn test_env_getenv() {
1845         let e = env();
1846         assert!(e.len() > 0u);
1847         for p in e.iter() {
1848             let (n, v) = (*p).clone();
1849             debug!("{}", n);
1850             let v2 = getenv(n.as_slice());
1851             // MingW seems to set some funky environment variables like
1852             // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
1853             // from env() but not visible from getenv().
1854             assert!(v2.is_none() || v2 == option::Some(v));
1855         }
1856     }
1857
1858     #[test]
1859     fn test_env_set_get_huge() {
1860         let n = make_rand_name();
1861         let s = "x".repeat(10000).to_string();
1862         setenv(n.as_slice(), s.as_slice());
1863         assert_eq!(getenv(n.as_slice()), Some(s));
1864         unsetenv(n.as_slice());
1865         assert_eq!(getenv(n.as_slice()), None);
1866     }
1867
1868     #[test]
1869     fn test_env_setenv() {
1870         let n = make_rand_name();
1871
1872         let mut e = env();
1873         setenv(n.as_slice(), "VALUE");
1874         assert!(!e.contains(&(n.clone(), "VALUE".to_string())));
1875
1876         e = env();
1877         assert!(e.contains(&(n, "VALUE".to_string())));
1878     }
1879
1880     #[test]
1881     fn test() {
1882         assert!((!Path::new("test-path").is_absolute()));
1883
1884         let cwd = getcwd();
1885         debug!("Current working directory: {}", cwd.display());
1886
1887         debug!("{}", make_absolute(&Path::new("test-path")).display());
1888         debug!("{}", make_absolute(&Path::new("/usr/bin")).display());
1889     }
1890
1891     #[test]
1892     #[cfg(unix)]
1893     fn homedir() {
1894         let oldhome = getenv("HOME");
1895
1896         setenv("HOME", "/home/MountainView");
1897         assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1898
1899         setenv("HOME", "");
1900         assert!(os::homedir().is_none());
1901
1902         for s in oldhome.iter() {
1903             setenv("HOME", s.as_slice());
1904         }
1905     }
1906
1907     #[test]
1908     #[cfg(windows)]
1909     fn homedir() {
1910
1911         let oldhome = getenv("HOME");
1912         let olduserprofile = getenv("USERPROFILE");
1913
1914         setenv("HOME", "");
1915         setenv("USERPROFILE", "");
1916
1917         assert!(os::homedir().is_none());
1918
1919         setenv("HOME", "/home/MountainView");
1920         assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1921
1922         setenv("HOME", "");
1923
1924         setenv("USERPROFILE", "/home/MountainView");
1925         assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1926
1927         setenv("HOME", "/home/MountainView");
1928         setenv("USERPROFILE", "/home/PaloAlto");
1929         assert!(os::homedir() == Some(Path::new("/home/MountainView")));
1930
1931         for s in oldhome.iter() {
1932             setenv("HOME", s.as_slice());
1933         }
1934         for s in olduserprofile.iter() {
1935             setenv("USERPROFILE", s.as_slice());
1936         }
1937     }
1938
1939     #[test]
1940     fn memory_map_rw() {
1941         use result::{Ok, Err};
1942
1943         let chunk = match os::MemoryMap::new(16, &[
1944             os::MapReadable,
1945             os::MapWritable
1946         ]) {
1947             Ok(chunk) => chunk,
1948             Err(msg) => panic!("{}", msg)
1949         };
1950         assert!(chunk.len >= 16);
1951
1952         unsafe {
1953             *chunk.data = 0xBE;
1954             assert!(*chunk.data == 0xBE);
1955         }
1956     }
1957
1958     #[test]
1959     fn memory_map_file() {
1960         use result::{Ok, Err};
1961         use os::*;
1962         use libc::*;
1963         use io::fs;
1964
1965         #[cfg(unix)]
1966         fn lseek_(fd: c_int, size: uint) {
1967             unsafe {
1968                 assert!(lseek(fd, size as off_t, SEEK_SET) == size as off_t);
1969             }
1970         }
1971         #[cfg(windows)]
1972         fn lseek_(fd: c_int, size: uint) {
1973            unsafe {
1974                assert!(lseek(fd, size as c_long, SEEK_SET) == size as c_long);
1975            }
1976         }
1977
1978         let mut path = tmpdir();
1979         path.push("mmap_file.tmp");
1980         let size = MemoryMap::granularity() * 2;
1981
1982         let fd = unsafe {
1983             let fd = path.with_c_str(|path| {
1984                 open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
1985             });
1986             lseek_(fd, size);
1987             "x".with_c_str(|x| assert!(write(fd, x as *const c_void, 1) == 1));
1988             fd
1989         };
1990         let chunk = match MemoryMap::new(size / 2, &[
1991             MapReadable,
1992             MapWritable,
1993             MapFd(fd),
1994             MapOffset(size / 2)
1995         ]) {
1996             Ok(chunk) => chunk,
1997             Err(msg) => panic!("{}", msg)
1998         };
1999         assert!(chunk.len > 0);
2000
2001         unsafe {
2002             *chunk.data = 0xbe;
2003             assert!(*chunk.data == 0xbe);
2004             close(fd);
2005         }
2006         drop(chunk);
2007
2008         fs::unlink(&path).unwrap();
2009     }
2010
2011     #[test]
2012     #[cfg(windows)]
2013     fn split_paths_windows() {
2014         fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
2015             split_paths(unparsed) ==
2016                 parsed.iter().map(|s| Path::new(*s)).collect()
2017         }
2018
2019         assert!(check_parse("", &mut [""]));
2020         assert!(check_parse(r#""""#, &mut [""]));
2021         assert!(check_parse(";;", &mut ["", "", ""]));
2022         assert!(check_parse(r"c:\", &mut [r"c:\"]));
2023         assert!(check_parse(r"c:\;", &mut [r"c:\", ""]));
2024         assert!(check_parse(r"c:\;c:\Program Files\",
2025                             &mut [r"c:\", r"c:\Program Files\"]));
2026         assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"]));
2027         assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#,
2028                             &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"]));
2029     }
2030
2031     #[test]
2032     #[cfg(unix)]
2033     fn split_paths_unix() {
2034         fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
2035             split_paths(unparsed) ==
2036                 parsed.iter().map(|s| Path::new(*s)).collect()
2037         }
2038
2039         assert!(check_parse("", &mut [""]));
2040         assert!(check_parse("::", &mut ["", "", ""]));
2041         assert!(check_parse("/", &mut ["/"]));
2042         assert!(check_parse("/:", &mut ["/", ""]));
2043         assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"]));
2044     }
2045
2046     #[test]
2047     #[cfg(unix)]
2048     fn join_paths_unix() {
2049         fn test_eq(input: &[&str], output: &str) -> bool {
2050             join_paths(input).unwrap().as_slice() == output.as_bytes()
2051         }
2052
2053         assert!(test_eq(&[], ""));
2054         assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"],
2055                          "/bin:/usr/bin:/usr/local/bin"));
2056         assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""],
2057                          ":/bin:::/usr/bin:"));
2058         assert!(join_paths(&["/te:st"]).is_err());
2059     }
2060
2061     #[test]
2062     #[cfg(windows)]
2063     fn join_paths_windows() {
2064         fn test_eq(input: &[&str], output: &str) -> bool {
2065             join_paths(input).unwrap().as_slice() == output.as_bytes()
2066         }
2067
2068         assert!(test_eq(&[], ""));
2069         assert!(test_eq(&[r"c:\windows", r"c:\"],
2070                         r"c:\windows;c:\"));
2071         assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""],
2072                         r";c:\windows;;;c:\;"));
2073         assert!(test_eq(&[r"c:\te;st", r"c:\"],
2074                         r#""c:\te;st";c:\"#));
2075         assert!(join_paths(&[r#"c:\te"st"#]).is_err());
2076     }
2077
2078     // More recursive_mkdir tests are in extra::tempfile
2079 }