]> git.lizzy.rs Git - rust.git/blob - src/libstd/os.rs
std::rt: Stop using unstable::global in change_dir_locked
[rust.git] / src / libstd / os.rs
1 // Copyright 2012-2013 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 #[allow(missing_doc)];
30
31 use cast;
32 use clone::Clone;
33 use container::Container;
34 use io;
35 use iterator::IteratorUtil;
36 use libc;
37 use libc::{c_char, c_void, c_int, size_t};
38 use libc::FILE;
39 use local_data;
40 use option::{Some, None};
41 use os;
42 use prelude::*;
43 use ptr;
44 use str;
45 use to_str;
46 use uint;
47 use unstable::finally::Finally;
48 use vec;
49
50 pub use libc::fclose;
51 pub use os::consts::*;
52
53 /// Delegates to the libc close() function, returning the same return value.
54 pub fn close(fd: c_int) -> c_int {
55     unsafe {
56         libc::close(fd)
57     }
58 }
59
60 pub mod rustrt {
61     use libc::{c_char, c_int};
62     use libc;
63
64     extern {
65         pub unsafe fn rust_get_argc() -> c_int;
66         pub unsafe fn rust_get_argv() -> **c_char;
67         pub unsafe fn rust_path_is_dir(path: *libc::c_char) -> c_int;
68         pub unsafe fn rust_path_exists(path: *libc::c_char) -> c_int;
69         pub unsafe fn rust_set_exit_status(code: libc::intptr_t);
70     }
71 }
72
73 pub static TMPBUF_SZ : uint = 1000u;
74 static BUF_BYTES : uint = 2048u;
75
76 pub fn getcwd() -> Path {
77     let buf = [0 as libc::c_char, ..BUF_BYTES];
78     unsafe {
79         if(0 as *libc::c_char == libc::getcwd(
80             &buf[0],
81             BUF_BYTES as libc::size_t)) {
82             fail!();
83         }
84         Path(str::raw::from_c_str(&buf[0]))
85     }
86 }
87
88 // FIXME: move these to str perhaps? #2620
89
90 pub fn as_c_charp<T>(s: &str, f: &fn(*c_char) -> T) -> T {
91     str::as_c_str(s, |b| f(b as *c_char))
92 }
93
94 pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool)
95     -> Option<~str> {
96     let mut buf = vec::from_elem(TMPBUF_SZ, 0u8 as c_char);
97     do buf.as_mut_buf |b, sz| {
98         if f(b, sz as size_t) {
99             unsafe {
100                 Some(str::raw::from_buf(b as *u8))
101             }
102         } else {
103             None
104         }
105     }
106 }
107
108 #[cfg(windows)]
109 pub mod win32 {
110     use libc;
111     use vec;
112     use str;
113     use option::{None, Option};
114     use option;
115     use os::TMPBUF_SZ;
116     use libc::types::os::arch::extra::DWORD;
117
118     pub fn fill_utf16_buf_and_decode(f: &fn(*mut u16, DWORD) -> DWORD)
119         -> Option<~str> {
120         unsafe {
121             let mut n = TMPBUF_SZ as DWORD;
122             let mut res = None;
123             let mut done = false;
124             while !done {
125                 let mut k: DWORD = 0;
126                 let mut buf = vec::from_elem(n as uint, 0u16);
127                 do buf.as_mut_buf |b, _sz| {
128                     k = f(b, TMPBUF_SZ as DWORD);
129                     if k == (0 as DWORD) {
130                         done = true;
131                     } else if (k == n &&
132                                libc::GetLastError() ==
133                                libc::ERROR_INSUFFICIENT_BUFFER as DWORD) {
134                         n *= (2 as DWORD);
135                     } else {
136                         done = true;
137                     }
138                 }
139                 if k != 0 && done {
140                     let sub = buf.slice(0, k as uint);
141                     res = option::Some(str::from_utf16(sub));
142                 }
143             }
144             return res;
145         }
146     }
147
148     pub fn as_utf16_p<T>(s: &str, f: &fn(*u16) -> T) -> T {
149         let mut t = s.to_utf16();
150         // Null terminate before passing on.
151         t.push(0u16);
152         t.as_imm_buf(|buf, _len| f(buf))
153     }
154 }
155
156 /*
157 Accessing environment variables is not generally threadsafe.
158 Serialize access through a global lock.
159 */
160 fn with_env_lock<T>(f: &fn() -> T) -> T {
161     use unstable::finally::Finally;
162
163     unsafe {
164         return do (|| {
165             rust_take_env_lock();
166             f()
167         }).finally {
168             rust_drop_env_lock();
169         };
170     }
171
172     extern {
173         #[fast_ffi]
174         fn rust_take_env_lock();
175         #[fast_ffi]
176         fn rust_drop_env_lock();
177     }
178 }
179
180 /// Returns a vector of (variable, value) pairs for all the environment
181 /// variables of the current process.
182 pub fn env() -> ~[(~str,~str)] {
183     unsafe {
184         #[cfg(windows)]
185         unsafe fn get_env_pairs() -> ~[~str] {
186             use libc::funcs::extra::kernel32::{
187                 GetEnvironmentStringsA,
188                 FreeEnvironmentStringsA
189             };
190             let ch = GetEnvironmentStringsA();
191             if (ch as uint == 0) {
192                 fail!("os::env() failure getting env string from OS: %s", os::last_os_error());
193             }
194             let mut curr_ptr: uint = ch as uint;
195             let mut result = ~[];
196             while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) {
197                 let env_pair = str::raw::from_c_str(
198                     curr_ptr as *libc::c_char);
199                 result.push(env_pair);
200                 curr_ptr +=
201                     libc::strlen(curr_ptr as *libc::c_char) as uint
202                     + 1;
203             }
204             FreeEnvironmentStringsA(ch);
205             result
206         }
207         #[cfg(unix)]
208         unsafe fn get_env_pairs() -> ~[~str] {
209             extern {
210                 unsafe fn rust_env_pairs() -> **libc::c_char;
211             }
212             let environ = rust_env_pairs();
213             if (environ as uint == 0) {
214                 fail!("os::env() failure getting env string from OS: %s", os::last_os_error());
215             }
216             let mut result = ~[];
217             ptr::array_each(environ, |e| {
218                 let env_pair = str::raw::from_c_str(e);
219                 debug!("get_env_pairs: %s",
220                        env_pair);
221                 result.push(env_pair);
222             });
223             result
224         }
225
226         fn env_convert(input: ~[~str]) -> ~[(~str, ~str)] {
227             let mut pairs = ~[];
228             for input.iter().advance |p| {
229                 let vs: ~[&str] = p.splitn_iter('=', 1).collect();
230                 debug!("splitting: len: %u",
231                     vs.len());
232                 assert_eq!(vs.len(), 2);
233                 pairs.push((vs[0].to_owned(), vs[1].to_owned()));
234             }
235             pairs
236         }
237         do with_env_lock {
238             let unparsed_environ = get_env_pairs();
239             env_convert(unparsed_environ)
240         }
241     }
242 }
243
244 #[cfg(unix)]
245 /// Fetches the environment variable `n` from the current process, returning
246 /// None if the variable isn't set.
247 pub fn getenv(n: &str) -> Option<~str> {
248     unsafe {
249         do with_env_lock {
250             let s = str::as_c_str(n, |s| libc::getenv(s));
251             if ptr::null::<u8>() == cast::transmute(s) {
252                 None::<~str>
253             } else {
254                 let s = cast::transmute(s);
255                 Some::<~str>(str::raw::from_buf(s))
256             }
257         }
258     }
259 }
260
261 #[cfg(windows)]
262 /// Fetches the environment variable `n` from the current process, returning
263 /// None if the variable isn't set.
264 pub fn getenv(n: &str) -> Option<~str> {
265     unsafe {
266         do with_env_lock {
267             use os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
268             do as_utf16_p(n) |u| {
269                 do fill_utf16_buf_and_decode() |buf, sz| {
270                     libc::GetEnvironmentVariableW(u, buf, sz)
271                 }
272             }
273         }
274     }
275 }
276
277
278 #[cfg(unix)]
279 /// Sets the environment variable `n` to the value `v` for the currently running
280 /// process
281 pub fn setenv(n: &str, v: &str) {
282     unsafe {
283         do with_env_lock {
284             do str::as_c_str(n) |nbuf| {
285                 do str::as_c_str(v) |vbuf| {
286                     libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
287                 }
288             }
289         }
290     }
291 }
292
293
294 #[cfg(windows)]
295 /// Sets the environment variable `n` to the value `v` for the currently running
296 /// process
297 pub fn setenv(n: &str, v: &str) {
298     unsafe {
299         do with_env_lock {
300             use os::win32::as_utf16_p;
301             do as_utf16_p(n) |nbuf| {
302                 do as_utf16_p(v) |vbuf| {
303                     libc::SetEnvironmentVariableW(nbuf, vbuf);
304                 }
305             }
306         }
307     }
308 }
309
310 /// Remove a variable from the environment entirely
311 pub fn unsetenv(n: &str) {
312     #[cfg(unix)]
313     fn _unsetenv(n: &str) {
314         unsafe {
315             do with_env_lock {
316                 do str::as_c_str(n) |nbuf| {
317                     libc::funcs::posix01::unistd::unsetenv(nbuf);
318                 }
319             }
320         }
321     }
322     #[cfg(windows)]
323     fn _unsetenv(n: &str) {
324         unsafe {
325             do with_env_lock {
326                 use os::win32::as_utf16_p;
327                 do as_utf16_p(n) |nbuf| {
328                     libc::SetEnvironmentVariableW(nbuf, ptr::null());
329                 }
330             }
331         }
332     }
333
334     _unsetenv(n);
335 }
336
337 pub fn fdopen(fd: c_int) -> *FILE {
338     unsafe {
339         return do as_c_charp("r") |modebuf| {
340             libc::fdopen(fd, modebuf)
341         };
342     }
343 }
344
345
346 // fsync related
347
348 #[cfg(windows)]
349 pub fn fsync_fd(fd: c_int, _level: io::fsync::Level) -> c_int {
350     unsafe {
351         use libc::funcs::extra::msvcrt::*;
352         return commit(fd);
353     }
354 }
355
356 #[cfg(target_os = "linux")]
357 #[cfg(target_os = "android")]
358 pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
359     unsafe {
360         use libc::funcs::posix01::unistd::*;
361         match level {
362           io::fsync::FSync
363           | io::fsync::FullFSync => return fsync(fd),
364           io::fsync::FDataSync => return fdatasync(fd)
365         }
366     }
367 }
368
369 #[cfg(target_os = "macos")]
370 pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
371     unsafe {
372         use libc::consts::os::extra::*;
373         use libc::funcs::posix88::fcntl::*;
374         use libc::funcs::posix01::unistd::*;
375         match level {
376           io::fsync::FSync => return fsync(fd),
377           _ => {
378             // According to man fnctl, the ok retval is only specified to be
379             // !=-1
380             if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int)
381                 { return -1 as c_int; }
382             else
383                 { return 0 as c_int; }
384           }
385         }
386     }
387 }
388
389 #[cfg(target_os = "freebsd")]
390 pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
391     unsafe {
392         use libc::funcs::posix01::unistd::*;
393         return fsync(fd);
394     }
395 }
396
397 pub struct Pipe {
398     in: c_int,
399     out: c_int
400 }
401
402 #[cfg(unix)]
403 pub fn pipe() -> Pipe {
404     unsafe {
405         let mut fds = Pipe {in: 0 as c_int,
406                             out: 0 as c_int };
407         assert_eq!(libc::pipe(&mut fds.in), (0 as c_int));
408         return Pipe {in: fds.in, out: fds.out};
409     }
410 }
411
412
413
414 #[cfg(windows)]
415 pub fn pipe() -> Pipe {
416     unsafe {
417         // Windows pipes work subtly differently than unix pipes, and their
418         // inheritance has to be handled in a different way that I do not
419         // fully understand. Here we explicitly make the pipe non-inheritable,
420         // which means to pass it to a subprocess they need to be duplicated
421         // first, as in core::run.
422         let mut fds = Pipe {in: 0 as c_int,
423                     out: 0 as c_int };
424         let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint,
425                              (libc::O_BINARY | libc::O_NOINHERIT) as c_int);
426         assert_eq!(res, 0 as c_int);
427         assert!((fds.in != -1 as c_int && fds.in != 0 as c_int));
428         assert!((fds.out != -1 as c_int && fds.in != 0 as c_int));
429         return Pipe {in: fds.in, out: fds.out};
430     }
431 }
432
433 fn dup2(src: c_int, dst: c_int) -> c_int {
434     unsafe {
435         libc::dup2(src, dst)
436     }
437 }
438
439 /// Returns the proper dll filename for the given basename of a file.
440 pub fn dll_filename(base: &str) -> ~str {
441     fmt!("%s%s%s", DLL_PREFIX, base, DLL_SUFFIX)
442 }
443
444 /// Optionally returns the filesystem path to the current executable which is
445 /// running. If any failure occurs, None is returned.
446 pub fn self_exe_path() -> Option<Path> {
447
448     #[cfg(target_os = "freebsd")]
449     fn load_self() -> Option<~str> {
450         unsafe {
451             use libc::funcs::bsd44::*;
452             use libc::consts::os::extra::*;
453             do fill_charp_buf() |buf, sz| {
454                 let mib = ~[CTL_KERN as c_int,
455                            KERN_PROC as c_int,
456                            KERN_PROC_PATHNAME as c_int, -1 as c_int];
457                 let mut sz = sz;
458                 sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint,
459                        buf as *mut c_void, &mut sz, ptr::null(),
460                        0u as size_t) == (0 as c_int)
461             }
462         }
463     }
464
465     #[cfg(target_os = "linux")]
466     #[cfg(target_os = "android")]
467     fn load_self() -> Option<~str> {
468         unsafe {
469             use libc::funcs::posix01::unistd::readlink;
470
471             let mut path_str = str::with_capacity(TMPBUF_SZ);
472             let len = do str::as_c_str(path_str) |buf| {
473                 let buf = buf as *mut c_char;
474                 do as_c_charp("/proc/self/exe") |proc_self_buf| {
475                     readlink(proc_self_buf, buf, TMPBUF_SZ as size_t)
476                 }
477             };
478             if len == -1 {
479                 None
480             } else {
481                 str::raw::set_len(&mut path_str, len as uint);
482                 Some(path_str)
483             }
484         }
485     }
486
487     #[cfg(target_os = "macos")]
488     fn load_self() -> Option<~str> {
489         unsafe {
490             do fill_charp_buf() |buf, sz| {
491                 let mut sz = sz as u32;
492                 libc::funcs::extra::_NSGetExecutablePath(
493                     buf, &mut sz) == (0 as c_int)
494             }
495         }
496     }
497
498     #[cfg(windows)]
499     fn load_self() -> Option<~str> {
500         unsafe {
501             use os::win32::fill_utf16_buf_and_decode;
502             do fill_utf16_buf_and_decode() |buf, sz| {
503                 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
504             }
505         }
506     }
507
508     do load_self().map |pth| {
509         Path(*pth).dir_path()
510     }
511 }
512
513
514 /**
515  * Returns the path to the user's home directory, if known.
516  *
517  * On Unix, returns the value of the 'HOME' environment variable if it is set
518  * and not equal to the empty string.
519  *
520  * On Windows, returns the value of the 'HOME' environment variable if it is
521  * set and not equal to the empty string. Otherwise, returns the value of the
522  * 'USERPROFILE' environment variable if it is set and not equal to the empty
523  * string.
524  *
525  * Otherwise, homedir returns option::none.
526  */
527 pub fn homedir() -> Option<Path> {
528     return match getenv("HOME") {
529         Some(ref p) => if !p.is_empty() {
530           Some(Path(*p))
531         } else {
532           secondary()
533         },
534         None => secondary()
535     };
536
537     #[cfg(unix)]
538     fn secondary() -> Option<Path> {
539         None
540     }
541
542     #[cfg(windows)]
543     fn secondary() -> Option<Path> {
544         do getenv("USERPROFILE").chain |p| {
545             if !p.is_empty() {
546                 Some(Path(p))
547             } else {
548                 None
549             }
550         }
551     }
552 }
553
554 /**
555  * Returns the path to a temporary directory.
556  *
557  * On Unix, returns the value of the 'TMPDIR' environment variable if it is
558  * set and non-empty and '/tmp' otherwise.
559  *
560  * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
561  * 'USERPROFILE' environment variable  if any are set and not the empty
562  * string. Otherwise, tmpdir returns the path to the Windows directory.
563  */
564 pub fn tmpdir() -> Path {
565     return lookup();
566
567     fn getenv_nonempty(v: &str) -> Option<Path> {
568         match getenv(v) {
569             Some(x) =>
570                 if x.is_empty() {
571                     None
572                 } else {
573                     Some(Path(x))
574                 },
575             _ => None
576         }
577     }
578
579     #[cfg(unix)]
580     fn lookup() -> Path {
581         getenv_nonempty("TMPDIR").get_or_default(Path("/tmp"))
582     }
583
584     #[cfg(windows)]
585     fn lookup() -> Path {
586         getenv_nonempty("TMP").or(
587             getenv_nonempty("TEMP").or(
588                 getenv_nonempty("USERPROFILE").or(
589                    getenv_nonempty("WINDIR")))).get_or_default(Path("C:\\Windows"))
590     }
591 }
592
593 /// Recursively walk a directory structure
594 pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool {
595     let r = list_dir(p);
596     r.iter().advance(|q| {
597         let path = &p.push(*q);
598         f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p)))
599     })
600 }
601
602 /// Indicates whether a path represents a directory
603 pub fn path_is_dir(p: &Path) -> bool {
604     unsafe {
605         do str::as_c_str(p.to_str()) |buf| {
606             rustrt::rust_path_is_dir(buf) != 0 as c_int
607         }
608     }
609 }
610
611 /// Indicates whether a path exists
612 pub fn path_exists(p: &Path) -> bool {
613     unsafe {
614         do str::as_c_str(p.to_str()) |buf| {
615             rustrt::rust_path_exists(buf) != 0 as c_int
616         }
617     }
618 }
619
620 /**
621  * Convert a relative path to an absolute path
622  *
623  * If the given path is relative, return it prepended with the current working
624  * directory. If the given path is already an absolute path, return it
625  * as is.
626  */
627 // NB: this is here rather than in path because it is a form of environment
628 // querying; what it does depends on the process working directory, not just
629 // the input paths.
630 pub fn make_absolute(p: &Path) -> Path {
631     if p.is_absolute {
632         (*p).clone()
633     } else {
634         getcwd().push_many(p.components)
635     }
636 }
637
638
639 /// Creates a directory at the specified path
640 pub fn make_dir(p: &Path, mode: c_int) -> bool {
641     return mkdir(p, mode);
642
643     #[cfg(windows)]
644     fn mkdir(p: &Path, _mode: c_int) -> bool {
645         unsafe {
646             use os::win32::as_utf16_p;
647             // FIXME: turn mode into something useful? #2623
648             do as_utf16_p(p.to_str()) |buf| {
649                 libc::CreateDirectoryW(buf, cast::transmute(0))
650                     != (0 as libc::BOOL)
651             }
652         }
653     }
654
655     #[cfg(unix)]
656     fn mkdir(p: &Path, mode: c_int) -> bool {
657         unsafe {
658             do as_c_charp(p.to_str()) |c| {
659                 libc::mkdir(c, mode as libc::mode_t) == (0 as c_int)
660             }
661         }
662     }
663 }
664
665 /// Creates a directory with a given mode.
666 /// Returns true iff creation
667 /// succeeded. Also creates all intermediate subdirectories
668 /// if they don't already exist, giving all of them the same mode.
669
670 // tjc: if directory exists but with different permissions,
671 // should we return false?
672 pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool {
673     if path_is_dir(p) {
674         return true;
675     }
676     else if p.components.is_empty() {
677         return false;
678     }
679     else if p.components.len() == 1 {
680         // No parent directories to create
681         path_is_dir(p) || make_dir(p, mode)
682     }
683     else {
684         mkdir_recursive(&p.pop(), mode) && make_dir(p, mode)
685     }
686 }
687
688 /// Lists the contents of a directory
689 pub fn list_dir(p: &Path) -> ~[~str] {
690     if p.components.is_empty() && !p.is_absolute() {
691         // Not sure what the right behavior is here, but this
692         // prevents a bounds check failure later
693         return ~[];
694     }
695     unsafe {
696         #[cfg(target_os = "linux")]
697         #[cfg(target_os = "android")]
698         #[cfg(target_os = "freebsd")]
699         #[cfg(target_os = "macos")]
700         unsafe fn get_list(p: &Path) -> ~[~str] {
701             use libc::{dirent_t};
702             use libc::{opendir, readdir, closedir};
703             extern {
704                 unsafe fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
705             }
706             let input = p.to_str();
707             let mut strings = ~[];
708             let input_ptr = ::cast::transmute(&input[0]);
709             debug!("os::list_dir -- BEFORE OPENDIR");
710             let dir_ptr = opendir(input_ptr);
711             if (dir_ptr as uint != 0) {
712         debug!("os::list_dir -- opendir() SUCCESS");
713                 let mut entry_ptr = readdir(dir_ptr);
714                 while (entry_ptr as uint != 0) {
715                     strings.push(str::raw::from_c_str(rust_list_dir_val(
716                         entry_ptr)));
717                     entry_ptr = readdir(dir_ptr);
718                 }
719                 closedir(dir_ptr);
720             }
721             else {
722         debug!("os::list_dir -- opendir() FAILURE");
723             }
724             debug!(
725                 "os::list_dir -- AFTER -- #: %?",
726                      strings.len());
727             strings
728         }
729         #[cfg(windows)]
730         unsafe fn get_list(p: &Path) -> ~[~str] {
731             use libc::consts::os::extra::INVALID_HANDLE_VALUE;
732             use libc::{wcslen, free};
733             use libc::funcs::extra::kernel32::{
734                 FindFirstFileW,
735                 FindNextFileW,
736                 FindClose,
737             };
738             use os::win32::{
739                 as_utf16_p
740             };
741             use rt::global_heap::malloc_raw;
742
743             #[nolink]
744             extern {
745                 unsafe fn rust_list_dir_wfd_size() -> libc::size_t;
746                 unsafe fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void)
747                     -> *u16;
748             }
749             fn star(p: &Path) -> Path { p.push("*") }
750             do as_utf16_p(star(p).to_str()) |path_ptr| {
751                 let mut strings = ~[];
752                 let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
753                 let find_handle =
754                     FindFirstFileW(
755                         path_ptr,
756                         ::cast::transmute(wfd_ptr));
757                 if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
758                     let mut more_files = 1 as libc::c_int;
759                     while more_files != 0 {
760                         let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr);
761                         if fp_buf as uint == 0 {
762                             fail!("os::list_dir() failure: got null ptr from wfd");
763                         }
764                         else {
765                             let fp_vec = vec::from_buf(
766                                 fp_buf, wcslen(fp_buf) as uint);
767                             let fp_str = str::from_utf16(fp_vec);
768                             strings.push(fp_str);
769                         }
770                         more_files = FindNextFileW(
771                             find_handle,
772                             ::cast::transmute(wfd_ptr));
773                     }
774                     FindClose(find_handle);
775                     free(wfd_ptr)
776                 }
777                 strings
778             }
779         }
780         do get_list(p).consume_iter().filter |filename| {
781             "." != *filename && ".." != *filename
782         }.collect()
783     }
784 }
785
786 /**
787  * Lists the contents of a directory
788  *
789  * This version prepends each entry with the directory.
790  */
791 pub fn list_dir_path(p: &Path) -> ~[Path] {
792     list_dir(p).map(|f| p.push(*f))
793 }
794
795 /// Removes a directory at the specified path, after removing
796 /// all its contents. Use carefully!
797 pub fn remove_dir_recursive(p: &Path) -> bool {
798     let mut error_happened = false;
799     for walk_dir(p) |inner| {
800         if !error_happened {
801             if path_is_dir(inner) {
802                 if !remove_dir_recursive(inner) {
803                     error_happened = true;
804                 }
805             }
806             else {
807                 if !remove_file(inner) {
808                     error_happened = true;
809                 }
810             }
811         }
812     };
813     // Directory should now be empty
814     !error_happened && remove_dir(p)
815 }
816
817 /// Removes a directory at the specified path
818 pub fn remove_dir(p: &Path) -> bool {
819    return rmdir(p);
820
821     #[cfg(windows)]
822     fn rmdir(p: &Path) -> bool {
823         unsafe {
824             use os::win32::as_utf16_p;
825             return do as_utf16_p(p.to_str()) |buf| {
826                 libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
827             };
828         }
829     }
830
831     #[cfg(unix)]
832     fn rmdir(p: &Path) -> bool {
833         unsafe {
834             return do as_c_charp(p.to_str()) |buf| {
835                 libc::rmdir(buf) == (0 as c_int)
836             };
837         }
838     }
839 }
840
841 /// Changes the current working directory to the specified path, returning
842 /// whether the change was completed successfully or not.
843 pub fn change_dir(p: &Path) -> bool {
844     return chdir(p);
845
846     #[cfg(windows)]
847     fn chdir(p: &Path) -> bool {
848         unsafe {
849             use os::win32::as_utf16_p;
850             return do as_utf16_p(p.to_str()) |buf| {
851                 libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
852             };
853         }
854     }
855
856     #[cfg(unix)]
857     fn chdir(p: &Path) -> bool {
858         unsafe {
859             return do as_c_charp(p.to_str()) |buf| {
860                 libc::chdir(buf) == (0 as c_int)
861             };
862         }
863     }
864 }
865
866 /// Changes the current working directory to the specified
867 /// path while acquiring a global lock, then calls `action`.
868 /// If the change is successful, releases the lock and restores the
869 /// CWD to what it was before, returning true.
870 /// Returns false if the directory doesn't exist or if the directory change
871 /// is otherwise unsuccessful.
872 /// FIXME #7870 This probably shouldn't be part of the public API
873 pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
874     use task;
875     use unstable::finally::Finally;
876
877     unsafe {
878         // This is really sketchy. Using a pthread mutex so descheduling
879         // in the `action` callback can cause deadlock. Doing it in
880         // `task::atomically` to try to avoid that, but ... I don't know
881         // this is all bogus.
882         return do task::atomically {
883             rust_take_change_dir_lock();
884
885             do (||{
886                 let old_dir = os::getcwd();
887                 if change_dir(p) {
888                     action();
889                     change_dir(&old_dir)
890                 }
891                 else {
892                     false
893                 }
894             }).finally {
895                 rust_drop_change_dir_lock();
896             }
897         }
898     }
899
900     extern {
901         fn rust_take_change_dir_lock();
902         fn rust_drop_change_dir_lock();
903     }
904 }
905
906 /// Copies a file from one location to another
907 pub fn copy_file(from: &Path, to: &Path) -> bool {
908     return do_copy_file(from, to);
909
910     #[cfg(windows)]
911     fn do_copy_file(from: &Path, to: &Path) -> bool {
912         unsafe {
913             use os::win32::as_utf16_p;
914             return do as_utf16_p(from.to_str()) |fromp| {
915                 do as_utf16_p(to.to_str()) |top| {
916                     libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
917                         (0 as libc::BOOL)
918                 }
919             }
920         }
921     }
922
923     #[cfg(unix)]
924     fn do_copy_file(from: &Path, to: &Path) -> bool {
925         unsafe {
926             let istream = do as_c_charp(from.to_str()) |fromp| {
927                 do as_c_charp("rb") |modebuf| {
928                     libc::fopen(fromp, modebuf)
929                 }
930             };
931             if istream as uint == 0u {
932                 return false;
933             }
934             // Preserve permissions
935             let from_mode = from.get_mode().expect("copy_file: couldn't get permissions \
936                                                     for source file");
937
938             let ostream = do as_c_charp(to.to_str()) |top| {
939                 do as_c_charp("w+b") |modebuf| {
940                     libc::fopen(top, modebuf)
941                 }
942             };
943             if ostream as uint == 0u {
944                 fclose(istream);
945                 return false;
946             }
947             let bufsize = 8192u;
948             let mut buf = vec::with_capacity::<u8>(bufsize);
949             let mut done = false;
950             let mut ok = true;
951             while !done {
952                 do buf.as_mut_buf |b, _sz| {
953                   let nread = libc::fread(b as *mut c_void, 1u as size_t,
954                                           bufsize as size_t,
955                                           istream);
956                   if nread > 0 as size_t {
957                       if libc::fwrite(b as *c_void, 1u as size_t, nread,
958                                       ostream) != nread {
959                           ok = false;
960                           done = true;
961                       }
962                   } else {
963                       done = true;
964                   }
965               }
966             }
967             fclose(istream);
968             fclose(ostream);
969
970             // Give the new file the old file's permissions
971             if do str::as_c_str(to.to_str()) |to_buf| {
972                 libc::chmod(to_buf, from_mode as libc::mode_t)
973             } != 0 {
974                 return false; // should be a condition...
975             }
976             return ok;
977         }
978     }
979 }
980
981 /// Deletes an existing file
982 pub fn remove_file(p: &Path) -> bool {
983     return unlink(p);
984
985     #[cfg(windows)]
986     fn unlink(p: &Path) -> bool {
987         unsafe {
988             use os::win32::as_utf16_p;
989             return do as_utf16_p(p.to_str()) |buf| {
990                 libc::DeleteFileW(buf) != (0 as libc::BOOL)
991             };
992         }
993     }
994
995     #[cfg(unix)]
996     fn unlink(p: &Path) -> bool {
997         unsafe {
998             return do as_c_charp(p.to_str()) |buf| {
999                 libc::unlink(buf) == (0 as c_int)
1000             };
1001         }
1002     }
1003 }
1004
1005 #[cfg(unix)]
1006 /// Returns the platform-specific value of errno
1007 pub fn errno() -> int {
1008     #[cfg(target_os = "macos")]
1009     #[cfg(target_os = "freebsd")]
1010     fn errno_location() -> *c_int {
1011         #[nolink]
1012         extern {
1013             unsafe fn __error() -> *c_int;
1014         }
1015         unsafe {
1016             __error()
1017         }
1018     }
1019
1020     #[cfg(target_os = "linux")]
1021     #[cfg(target_os = "android")]
1022     fn errno_location() -> *c_int {
1023         #[nolink]
1024         extern {
1025             unsafe fn __errno_location() -> *c_int;
1026         }
1027         unsafe {
1028             __errno_location()
1029         }
1030     }
1031
1032     unsafe {
1033         (*errno_location()) as int
1034     }
1035 }
1036
1037 #[cfg(windows)]
1038 /// Returns the platform-specific value of errno
1039 pub fn errno() -> uint {
1040     use libc::types::os::arch::extra::DWORD;
1041
1042     #[link_name = "kernel32"]
1043     #[abi = "stdcall"]
1044     extern "stdcall" {
1045         unsafe fn GetLastError() -> DWORD;
1046     }
1047
1048     unsafe {
1049         GetLastError() as uint
1050     }
1051 }
1052
1053 /// Get a string representing the platform-dependent last error
1054 pub fn last_os_error() -> ~str {
1055     #[cfg(unix)]
1056     fn strerror() -> ~str {
1057         #[cfg(target_os = "macos")]
1058         #[cfg(target_os = "android")]
1059         #[cfg(target_os = "freebsd")]
1060         fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1061             #[nolink]
1062             extern {
1063                 unsafe fn strerror_r(errnum: c_int, buf: *mut c_char,
1064                                      buflen: size_t) -> c_int;
1065             }
1066             unsafe {
1067                 strerror_r(errnum, buf, buflen)
1068             }
1069         }
1070
1071         // GNU libc provides a non-compliant version of strerror_r by default
1072         // and requires macros to instead use the POSIX compliant variant.
1073         // So we just use __xpg_strerror_r which is always POSIX compliant
1074         #[cfg(target_os = "linux")]
1075         fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1076             #[nolink]
1077             extern {
1078                 unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char,
1079                                            buflen: size_t) -> c_int;
1080             }
1081             unsafe {
1082                 __xpg_strerror_r(errnum, buf, buflen)
1083             }
1084         }
1085
1086         let mut buf = [0 as c_char, ..TMPBUF_SZ];
1087         unsafe {
1088             let err = strerror_r(errno() as c_int, &mut buf[0],
1089                                  TMPBUF_SZ as size_t);
1090             if err < 0 {
1091                 fail!("strerror_r failure");
1092             }
1093
1094             str::raw::from_c_str(&buf[0])
1095         }
1096     }
1097
1098     #[cfg(windows)]
1099     fn strerror() -> ~str {
1100         use libc::types::os::arch::extra::DWORD;
1101         use libc::types::os::arch::extra::LPSTR;
1102         use libc::types::os::arch::extra::LPVOID;
1103
1104         #[link_name = "kernel32"]
1105         #[abi = "stdcall"]
1106         extern "stdcall" {
1107             unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
1108                                      msgId: DWORD, langId: DWORD,
1109                                      buf: LPSTR, nsize: DWORD,
1110                                      args: *c_void) -> DWORD;
1111         }
1112
1113         static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
1114         static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
1115
1116         let mut buf = [0 as c_char, ..TMPBUF_SZ];
1117
1118         // This value is calculated from the macro
1119         // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
1120         let langId = 0x0800 as DWORD;
1121         let err = errno() as DWORD;
1122         unsafe {
1123             let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
1124                                      FORMAT_MESSAGE_IGNORE_INSERTS,
1125                                      ptr::mut_null(), err, langId,
1126                                      &mut buf[0], TMPBUF_SZ as DWORD,
1127                                      ptr::null());
1128             if res == 0 {
1129                 fail!("[%?] FormatMessage failure", errno());
1130             }
1131
1132             str::raw::from_c_str(&buf[0])
1133         }
1134     }
1135
1136     strerror()
1137 }
1138
1139 /**
1140  * Sets the process exit code
1141  *
1142  * Sets the exit code returned by the process if all supervised tasks
1143  * terminate successfully (without failing). If the current root task fails
1144  * and is supervised by the scheduler then any user-specified exit status is
1145  * ignored and the process exits with the default failure status
1146  */
1147 pub fn set_exit_status(code: int) {
1148     use rt;
1149     use rt::OldTaskContext;
1150
1151     if rt::context() == OldTaskContext {
1152         unsafe {
1153             rustrt::rust_set_exit_status(code as libc::intptr_t);
1154         }
1155     } else {
1156         rt::util::set_exit_status(code);
1157     }
1158 }
1159
1160 unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
1161     let mut args = ~[];
1162     for uint::range(0, argc as uint) |i| {
1163         args.push(str::raw::from_c_str(*argv.offset(i)));
1164     }
1165     args
1166 }
1167
1168 /**
1169  * Returns the command line arguments
1170  *
1171  * Returns a list of the command line arguments.
1172  */
1173 #[cfg(target_os = "macos")]
1174 pub fn real_args() -> ~[~str] {
1175     unsafe {
1176         let (argc, argv) = (*_NSGetArgc() as c_int,
1177                             *_NSGetArgv() as **c_char);
1178         load_argc_and_argv(argc, argv)
1179     }
1180 }
1181
1182 #[cfg(target_os = "linux")]
1183 #[cfg(target_os = "android")]
1184 #[cfg(target_os = "freebsd")]
1185 pub fn real_args() -> ~[~str] {
1186     use rt;
1187     use rt::TaskContext;
1188
1189     if rt::context() == TaskContext {
1190         match rt::args::clone() {
1191             Some(args) => args,
1192             None => fail!("process arguments not initialized")
1193         }
1194     } else {
1195         unsafe {
1196             let argc = rustrt::rust_get_argc();
1197             let argv = rustrt::rust_get_argv();
1198             load_argc_and_argv(argc, argv)
1199         }
1200     }
1201 }
1202
1203 #[cfg(windows)]
1204 pub fn real_args() -> ~[~str] {
1205     let mut nArgs: c_int = 0;
1206     let lpArgCount: *mut c_int = &mut nArgs;
1207     let lpCmdLine = unsafe { GetCommandLineW() };
1208     let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1209
1210     let mut args = ~[];
1211     for uint::range(0, nArgs as uint) |i| {
1212         unsafe {
1213             // Determine the length of this argument.
1214             let ptr = *szArgList.offset(i);
1215             let mut len = 0;
1216             while *ptr.offset(len) != 0 { len += 1; }
1217
1218             // Push it onto the list.
1219             args.push(vec::raw::buf_as_slice(ptr, len,
1220                                              str::from_utf16));
1221         }
1222     }
1223
1224     unsafe {
1225         LocalFree(cast::transmute(szArgList));
1226     }
1227
1228     return args;
1229 }
1230
1231 type LPCWSTR = *u16;
1232
1233 #[cfg(windows)]
1234 #[link_name="kernel32"]
1235 #[abi="stdcall"]
1236 extern "stdcall" {
1237     fn GetCommandLineW() -> LPCWSTR;
1238     fn LocalFree(ptr: *c_void);
1239 }
1240
1241 #[cfg(windows)]
1242 #[link_name="shell32"]
1243 #[abi="stdcall"]
1244 extern "stdcall" {
1245     fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
1246 }
1247
1248 struct OverriddenArgs {
1249     val: ~[~str]
1250 }
1251
1252 static overridden_arg_key: local_data::Key<@OverriddenArgs> = &local_data::Key;
1253
1254 /// Returns the arguments which this program was started with (normally passed
1255 /// via the command line).
1256 ///
1257 /// The return value of the function can be changed by invoking the
1258 /// `os::set_args` function.
1259 pub fn args() -> ~[~str] {
1260     match local_data::get(overridden_arg_key, |k| k.map(|&k| *k)) {
1261         None => real_args(),
1262         Some(args) => args.val.clone()
1263     }
1264 }
1265
1266 /// For the current task, overrides the task-local cache of the arguments this
1267 /// program had when it started. These new arguments are only available to the
1268 /// current task via the `os::args` method.
1269 pub fn set_args(new_args: ~[~str]) {
1270     let overridden_args = @OverriddenArgs {
1271         val: new_args.clone()
1272     };
1273     local_data::set(overridden_arg_key, overridden_args);
1274 }
1275
1276 // FIXME #6100 we should really use an internal implementation of this - using
1277 // the POSIX glob functions isn't portable to windows, probably has slight
1278 // inconsistencies even where it is implemented, and makes extending
1279 // functionality a lot more difficult
1280 // FIXME #6101 also provide a non-allocating version - each_glob or so?
1281 /// Returns a vector of Path objects that match the given glob pattern
1282 #[cfg(target_os = "linux")]
1283 #[cfg(target_os = "android")]
1284 #[cfg(target_os = "freebsd")]
1285 #[cfg(target_os = "macos")]
1286 pub fn glob(pattern: &str) -> ~[Path] {
1287     #[cfg(target_os = "linux")]
1288     #[cfg(target_os = "android")]
1289     fn default_glob_t () -> libc::glob_t {
1290         libc::glob_t {
1291             gl_pathc: 0,
1292             gl_pathv: ptr::null(),
1293             gl_offs: 0,
1294             __unused1: ptr::null(),
1295             __unused2: ptr::null(),
1296             __unused3: ptr::null(),
1297             __unused4: ptr::null(),
1298             __unused5: ptr::null(),
1299         }
1300     }
1301
1302     #[cfg(target_os = "freebsd")]
1303     fn default_glob_t () -> libc::glob_t {
1304         libc::glob_t {
1305             gl_pathc: 0,
1306             __unused1: 0,
1307             gl_offs: 0,
1308             __unused2: 0,
1309             gl_pathv: ptr::null(),
1310             __unused3: ptr::null(),
1311             __unused4: ptr::null(),
1312             __unused5: ptr::null(),
1313             __unused6: ptr::null(),
1314             __unused7: ptr::null(),
1315             __unused8: ptr::null(),
1316         }
1317     }
1318
1319     #[cfg(target_os = "macos")]
1320     fn default_glob_t () -> libc::glob_t {
1321         libc::glob_t {
1322             gl_pathc: 0,
1323             __unused1: 0,
1324             gl_offs: 0,
1325             __unused2: 0,
1326             gl_pathv: ptr::null(),
1327             __unused3: ptr::null(),
1328             __unused4: ptr::null(),
1329             __unused5: ptr::null(),
1330             __unused6: ptr::null(),
1331             __unused7: ptr::null(),
1332             __unused8: ptr::null(),
1333         }
1334     }
1335
1336     let mut g = default_glob_t();
1337     do str::as_c_str(pattern) |c_pattern| {
1338         unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) }
1339     };
1340     do(|| {
1341         let paths = unsafe {
1342             vec::raw::from_buf_raw(g.gl_pathv, g.gl_pathc as uint)
1343         };
1344         do paths.map |&c_str| {
1345             Path(unsafe { str::raw::from_c_str(c_str) })
1346         }
1347     }).finally {
1348         unsafe { libc::globfree(&mut g) };
1349     }
1350 }
1351
1352 /// Returns a vector of Path objects that match the given glob pattern
1353 #[cfg(target_os = "win32")]
1354 pub fn glob(_pattern: &str) -> ~[Path] {
1355     fail!("glob() is unimplemented on Windows")
1356 }
1357
1358 #[cfg(target_os = "macos")]
1359 extern {
1360     // These functions are in crt_externs.h.
1361     pub fn _NSGetArgc() -> *c_int;
1362     pub fn _NSGetArgv() -> ***c_char;
1363 }
1364
1365 // Round up `from` to be divisible by `to`
1366 fn round_up(from: uint, to: uint) -> uint {
1367     let r = if from % to == 0 {
1368         from
1369     } else {
1370         from + to - (from % to)
1371     };
1372     if r == 0 {
1373         to
1374     } else {
1375         r
1376     }
1377 }
1378
1379 #[cfg(unix)]
1380 pub fn page_size() -> uint {
1381     unsafe {
1382         libc::sysconf(libc::_SC_PAGESIZE) as uint
1383     }
1384 }
1385
1386 #[cfg(windows)]
1387 pub fn page_size() -> uint {
1388   unsafe {
1389     let mut info = libc::SYSTEM_INFO::new();
1390     libc::GetSystemInfo(&mut info);
1391
1392     return info.dwPageSize as uint;
1393   }
1394 }
1395
1396 pub struct MemoryMap {
1397     data: *mut u8,
1398     len: size_t,
1399     kind: MemoryMapKind
1400 }
1401
1402 pub enum MemoryMapKind {
1403     MapFile(*c_void),
1404     MapVirtual
1405 }
1406
1407 pub enum MapOption {
1408     MapReadable,
1409     MapWritable,
1410     MapExecutable,
1411     MapAddr(*c_void),
1412     MapFd(c_int),
1413     MapOffset(uint)
1414 }
1415
1416 pub enum MapError {
1417     // Linux-specific errors
1418     ErrFdNotAvail,
1419     ErrInvalidFd,
1420     ErrUnaligned,
1421     ErrNoMapSupport,
1422     ErrNoMem,
1423     ErrUnknown(libc::c_int),
1424
1425     // Windows-specific errors
1426     ErrUnsupProt,
1427     ErrUnsupOffset,
1428     ErrNeedRW,
1429     ErrAlreadyExists,
1430     ErrVirtualAlloc(uint),
1431     ErrCreateFileMappingW(uint),
1432     ErrMapViewOfFile(uint)
1433 }
1434
1435 impl to_str::ToStr for MapError {
1436     fn to_str(&self) -> ~str {
1437         match *self {
1438             ErrFdNotAvail => ~"fd not available for reading or writing",
1439             ErrInvalidFd => ~"Invalid fd",
1440             ErrUnaligned => ~"Unaligned address, invalid flags, \
1441                               negative length or unaligned offset",
1442             ErrNoMapSupport=> ~"File doesn't support mapping",
1443             ErrNoMem => ~"Invalid address, or not enough available memory",
1444             ErrUnknown(code) => fmt!("Unknown error=%?", code),
1445             ErrUnsupProt => ~"Protection mode unsupported",
1446             ErrUnsupOffset => ~"Offset in virtual memory mode is unsupported",
1447             ErrNeedRW => ~"File mapping should be at least readable/writable",
1448             ErrAlreadyExists => ~"File mapping for specified file already exists",
1449             ErrVirtualAlloc(code) => fmt!("VirtualAlloc failure=%?", code),
1450             ErrCreateFileMappingW(code) => fmt!("CreateFileMappingW failure=%?", code),
1451             ErrMapViewOfFile(code) => fmt!("MapViewOfFile failure=%?", code)
1452         }
1453     }
1454 }
1455
1456 #[cfg(unix)]
1457 impl MemoryMap {
1458     pub fn new(min_len: uint, options: ~[MapOption]) -> Result<~MemoryMap, MapError> {
1459         use libc::off_t;
1460
1461         let mut addr: *c_void = ptr::null();
1462         let mut prot: c_int = 0;
1463         let mut flags: c_int = libc::MAP_PRIVATE;
1464         let mut fd: c_int = -1;
1465         let mut offset: off_t = 0;
1466         let len = round_up(min_len, page_size()) as size_t;
1467
1468         for options.iter().advance |&o| {
1469             match o {
1470                 MapReadable => { prot |= libc::PROT_READ; },
1471                 MapWritable => { prot |= libc::PROT_WRITE; },
1472                 MapExecutable => { prot |= libc::PROT_EXEC; },
1473                 MapAddr(addr_) => {
1474                     flags |= libc::MAP_FIXED;
1475                     addr = addr_;
1476                 },
1477                 MapFd(fd_) => {
1478                     flags |= libc::MAP_FILE;
1479                     fd = fd_;
1480                 },
1481                 MapOffset(offset_) => { offset = offset_ as off_t; }
1482             }
1483         }
1484         if fd == -1 { flags |= libc::MAP_ANON; }
1485
1486         let r = unsafe {
1487             libc::mmap(addr, len, prot, flags, fd, offset)
1488         };
1489         if r == libc::MAP_FAILED {
1490             Err(match errno() as c_int {
1491                 libc::EACCES => ErrFdNotAvail,
1492                 libc::EBADF => ErrInvalidFd,
1493                 libc::EINVAL => ErrUnaligned,
1494                 libc::ENODEV => ErrNoMapSupport,
1495                 libc::ENOMEM => ErrNoMem,
1496                 code => ErrUnknown(code)
1497             })
1498         } else {
1499             Ok(~MemoryMap {
1500                data: r as *mut u8,
1501                len: len,
1502                kind: if fd == -1 {
1503                    MapVirtual
1504                } else {
1505                    MapFile(ptr::null())
1506                }
1507             })
1508         }
1509     }
1510 }
1511
1512 #[cfg(unix)]
1513 impl Drop for MemoryMap {
1514     fn drop(&self) {
1515         unsafe {
1516             match libc::munmap(self.data as *c_void, self.len) {
1517                 0 => (),
1518                 -1 => error!(match errno() as c_int {
1519                     libc::EINVAL => ~"invalid addr or len",
1520                     e => fmt!("unknown errno=%?", e)
1521                 }),
1522                 r => error!(fmt!("Unexpected result %?", r))
1523             }
1524         }
1525     }
1526 }
1527
1528 #[cfg(windows)]
1529 impl MemoryMap {
1530     pub fn new(min_len: uint, options: ~[MapOption]) -> Result<~MemoryMap, MapError> {
1531         use libc::types::os::arch::extra::{LPVOID, DWORD, SIZE_T, HANDLE};
1532
1533         let mut lpAddress: LPVOID = ptr::mut_null();
1534         let mut readable = false;
1535         let mut writable = false;
1536         let mut executable = false;
1537         let mut fd: c_int = -1;
1538         let mut offset: uint = 0;
1539         let len = round_up(min_len, page_size()) as SIZE_T;
1540
1541         for options.iter().advance |&o| {
1542             match o {
1543                 MapReadable => { readable = true; },
1544                 MapWritable => { writable = true; },
1545                 MapExecutable => { executable = true; }
1546                 MapAddr(addr_) => { lpAddress = addr_ as LPVOID; },
1547                 MapFd(fd_) => { fd = fd_; },
1548                 MapOffset(offset_) => { offset = offset_; }
1549             }
1550         }
1551
1552         let flProtect = match (executable, readable, writable) {
1553             (false, false, false) if fd == -1 => libc::PAGE_NOACCESS,
1554             (false, true, false) => libc::PAGE_READONLY,
1555             (false, true, true) => libc::PAGE_READWRITE,
1556             (true, false, false) if fd == -1 => libc::PAGE_EXECUTE,
1557             (true, true, false) => libc::PAGE_EXECUTE_READ,
1558             (true, true, true) => libc::PAGE_EXECUTE_READWRITE,
1559             _ => return Err(ErrUnsupProt)
1560         };
1561
1562         if fd == -1 {
1563             if offset != 0 {
1564                 return Err(ErrUnsupOffset);
1565             }
1566             let r = unsafe {
1567                 libc::VirtualAlloc(lpAddress,
1568                                    len,
1569                                    libc::MEM_COMMIT | libc::MEM_RESERVE,
1570                                    flProtect)
1571             };
1572             match r as uint {
1573                 0 => Err(ErrVirtualAlloc(errno())),
1574                 _ => Ok(~MemoryMap {
1575                    data: r as *mut u8,
1576                    len: len,
1577                    kind: MapVirtual
1578                 })
1579             }
1580         } else {
1581             let dwDesiredAccess = match (readable, writable) {
1582                 (true, true) => libc::FILE_MAP_ALL_ACCESS,
1583                 (true, false) => libc::FILE_MAP_READ,
1584                 (false, true) => libc::FILE_MAP_WRITE,
1585                 _ => {
1586                     return Err(ErrNeedRW);
1587                 }
1588             };
1589             unsafe {
1590                 let hFile = libc::get_osfhandle(fd) as HANDLE;
1591                 let mapping = libc::CreateFileMappingW(hFile,
1592                                                        ptr::mut_null(),
1593                                                        flProtect,
1594                                                        (len >> 32) as DWORD,
1595                                                        (len & 0xffff_ffff) as DWORD,
1596                                                        ptr::null());
1597                 if mapping == ptr::mut_null() {
1598                     return Err(ErrCreateFileMappingW(errno()));
1599                 }
1600                 if errno() as c_int == libc::ERROR_ALREADY_EXISTS {
1601                     return Err(ErrAlreadyExists);
1602                 }
1603                 let r = libc::MapViewOfFile(mapping,
1604                                             dwDesiredAccess,
1605                                             (offset >> 32) as DWORD,
1606                                             (offset & 0xffff_ffff) as DWORD,
1607                                             0);
1608                 match r as uint {
1609                     0 => Err(ErrMapViewOfFile(errno())),
1610                     _ => Ok(~MemoryMap {
1611                        data: r as *mut u8,
1612                        len: len,
1613                        kind: MapFile(mapping as *c_void)
1614                     })
1615                 }
1616             }
1617         }
1618     }
1619 }
1620
1621 #[cfg(windows)]
1622 impl Drop for MemoryMap {
1623     fn drop(&self) {
1624         use libc::types::os::arch::extra::{LPCVOID, HANDLE};
1625
1626         unsafe {
1627             match self.kind {
1628                 MapVirtual => match libc::VirtualFree(self.data as *mut c_void,
1629                                                       self.len,
1630                                                       libc::MEM_RELEASE) {
1631                     0 => error!(fmt!("VirtualFree failed: %?", errno())),
1632                     _ => ()
1633                 },
1634                 MapFile(mapping) => {
1635                     if libc::UnmapViewOfFile(self.data as LPCVOID) != 0 {
1636                         error!(fmt!("UnmapViewOfFile failed: %?", errno()));
1637                     }
1638                     if libc::CloseHandle(mapping as HANDLE) != 0 {
1639                         error!(fmt!("CloseHandle failed: %?", errno()));
1640                     }
1641                 }
1642             }
1643         }
1644     }
1645 }
1646
1647 pub mod consts {
1648
1649     #[cfg(unix)]
1650     pub use os::consts::unix::*;
1651
1652     #[cfg(windows)]
1653     pub use os::consts::windows::*;
1654
1655     #[cfg(target_os = "macos")]
1656     pub use os::consts::macos::*;
1657
1658     #[cfg(target_os = "freebsd")]
1659     pub use os::consts::freebsd::*;
1660
1661     #[cfg(target_os = "linux")]
1662     pub use os::consts::linux::*;
1663
1664     #[cfg(target_os = "android")]
1665     pub use os::consts::android::*;
1666
1667     #[cfg(target_os = "win32")]
1668     pub use os::consts::win32::*;
1669
1670     #[cfg(target_arch = "x86")]
1671     pub use os::consts::x86::*;
1672
1673     #[cfg(target_arch = "x86_64")]
1674     pub use os::consts::x86_64::*;
1675
1676     #[cfg(target_arch = "arm")]
1677     pub use os::consts::arm::*;
1678
1679     #[cfg(target_arch = "mips")]
1680     use os::consts::mips::*;
1681
1682     pub mod unix {
1683         pub static FAMILY: &'static str = "unix";
1684     }
1685
1686     pub mod windows {
1687         pub static FAMILY: &'static str = "windows";
1688     }
1689
1690     pub mod macos {
1691         pub static SYSNAME: &'static str = "macos";
1692         pub static DLL_PREFIX: &'static str = "lib";
1693         pub static DLL_SUFFIX: &'static str = ".dylib";
1694         pub static EXE_SUFFIX: &'static str = "";
1695     }
1696
1697     pub mod freebsd {
1698         pub static SYSNAME: &'static str = "freebsd";
1699         pub static DLL_PREFIX: &'static str = "lib";
1700         pub static DLL_SUFFIX: &'static str = ".so";
1701         pub static EXE_SUFFIX: &'static str = "";
1702     }
1703
1704     pub mod linux {
1705         pub static SYSNAME: &'static str = "linux";
1706         pub static DLL_PREFIX: &'static str = "lib";
1707         pub static DLL_SUFFIX: &'static str = ".so";
1708         pub static EXE_SUFFIX: &'static str = "";
1709     }
1710
1711     pub mod android {
1712         pub static SYSNAME: &'static str = "android";
1713         pub static DLL_PREFIX: &'static str = "lib";
1714         pub static DLL_SUFFIX: &'static str = ".so";
1715         pub static EXE_SUFFIX: &'static str = "";
1716     }
1717
1718     pub mod win32 {
1719         pub static SYSNAME: &'static str = "win32";
1720         pub static DLL_PREFIX: &'static str = "";
1721         pub static DLL_SUFFIX: &'static str = ".dll";
1722         pub static EXE_SUFFIX: &'static str = ".exe";
1723     }
1724
1725
1726     pub mod x86 {
1727         pub static ARCH: &'static str = "x86";
1728     }
1729     pub mod x86_64 {
1730         pub static ARCH: &'static str = "x86_64";
1731     }
1732     pub mod arm {
1733         pub static ARCH: &'static str = "arm";
1734     }
1735     pub mod mips {
1736         pub static ARCH: &'static str = "mips";
1737     }
1738 }
1739
1740 #[cfg(test)]
1741 mod tests {
1742     use libc::{c_int, c_void, size_t};
1743     use libc;
1744     use option::Some;
1745     use option;
1746     use os::{as_c_charp, env, getcwd, getenv, make_absolute, real_args};
1747     use os::{remove_file, setenv, unsetenv};
1748     use os;
1749     use path::Path;
1750     use rand::RngUtil;
1751     use rand;
1752     use run;
1753     use str::StrSlice;
1754     use vec::CopyableVector;
1755     use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
1756
1757
1758     #[test]
1759     pub fn last_os_error() {
1760         debug!(os::last_os_error());
1761     }
1762
1763     #[test]
1764     pub fn test_args() {
1765         let a = real_args();
1766         assert!(a.len() >= 1);
1767     }
1768
1769     fn make_rand_name() -> ~str {
1770         let mut rng = rand::rng();
1771         let n = ~"TEST" + rng.gen_str(10u);
1772         assert!(getenv(n).is_none());
1773         n
1774     }
1775
1776     #[test]
1777     fn test_setenv() {
1778         let n = make_rand_name();
1779         setenv(n, "VALUE");
1780         assert_eq!(getenv(n), option::Some(~"VALUE"));
1781     }
1782
1783     #[test]
1784     fn test_unsetenv() {
1785         let n = make_rand_name();
1786         setenv(n, "VALUE");
1787         unsetenv(n);
1788         assert_eq!(getenv(n), option::None);
1789     }
1790
1791     #[test]
1792     #[ignore(cfg(windows))]
1793     #[ignore]
1794     fn test_setenv_overwrite() {
1795         let n = make_rand_name();
1796         setenv(n, "1");
1797         setenv(n, "2");
1798         assert_eq!(getenv(n), option::Some(~"2"));
1799         setenv(n, "");
1800         assert_eq!(getenv(n), option::Some(~""));
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(cfg(windows))]
1807     #[ignore]
1808     fn test_getenv_big() {
1809         let mut s = ~"";
1810         let mut i = 0;
1811         while i < 100 {
1812             s = s + "aaaaaaaaaa";
1813             i += 1;
1814         }
1815         let n = make_rand_name();
1816         setenv(n, s);
1817         debug!(s.clone());
1818         assert_eq!(getenv(n), option::Some(s));
1819     }
1820
1821     #[test]
1822     fn test_self_exe_path() {
1823         let path = os::self_exe_path();
1824         assert!(path.is_some());
1825         let path = path.get();
1826         debug!(path.clone());
1827
1828         // Hard to test this function
1829         assert!(path.is_absolute);
1830     }
1831
1832     #[test]
1833     #[ignore]
1834     fn test_env_getenv() {
1835         let e = env();
1836         assert!(e.len() > 0u);
1837         for e.iter().advance |p| {
1838             let (n, v) = (*p).clone();
1839             debug!(n.clone());
1840             let v2 = getenv(n);
1841             // MingW seems to set some funky environment variables like
1842             // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
1843             // from env() but not visible from getenv().
1844             assert!(v2.is_none() || v2 == option::Some(v));
1845         }
1846     }
1847
1848     #[test]
1849     fn test_env_setenv() {
1850         let n = make_rand_name();
1851
1852         let mut e = env();
1853         setenv(n, "VALUE");
1854         assert!(!e.contains(&(n.clone(), ~"VALUE")));
1855
1856         e = env();
1857         assert!(e.contains(&(n, ~"VALUE")));
1858     }
1859
1860     #[test]
1861     fn test() {
1862         assert!((!Path("test-path").is_absolute));
1863
1864         debug!("Current working directory: %s", getcwd().to_str());
1865
1866         debug!(make_absolute(&Path("test-path")));
1867         debug!(make_absolute(&Path("/usr/bin")));
1868     }
1869
1870     #[test]
1871     #[cfg(unix)]
1872     fn homedir() {
1873         let oldhome = getenv("HOME");
1874
1875         setenv("HOME", "/home/MountainView");
1876         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1877
1878         setenv("HOME", "");
1879         assert!(os::homedir().is_none());
1880
1881         for oldhome.iter().advance |s| { setenv("HOME", *s) }
1882     }
1883
1884     #[test]
1885     #[cfg(windows)]
1886     fn homedir() {
1887
1888         let oldhome = getenv("HOME");
1889         let olduserprofile = getenv("USERPROFILE");
1890
1891         setenv("HOME", "");
1892         setenv("USERPROFILE", "");
1893
1894         assert!(os::homedir().is_none());
1895
1896         setenv("HOME", "/home/MountainView");
1897         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1898
1899         setenv("HOME", "");
1900
1901         setenv("USERPROFILE", "/home/MountainView");
1902         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1903
1904         setenv("HOME", "/home/MountainView");
1905         setenv("USERPROFILE", "/home/PaloAlto");
1906         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1907
1908         oldhome.iter().advance(|s| { setenv("HOME", *s); true });
1909         olduserprofile.iter().advance(|s| { setenv("USERPROFILE", *s); true });
1910     }
1911
1912     #[test]
1913     fn tmpdir() {
1914         assert!(!os::tmpdir().to_str().is_empty());
1915     }
1916
1917     // Issue #712
1918     #[test]
1919     fn test_list_dir_no_invalid_memory_access() {
1920         os::list_dir(&Path("."));
1921     }
1922
1923     #[test]
1924     fn list_dir() {
1925         let dirs = os::list_dir(&Path("."));
1926         // Just assuming that we've got some contents in the current directory
1927         assert!(dirs.len() > 0u);
1928
1929         for dirs.iter().advance |dir| {
1930             debug!((*dir).clone());
1931         }
1932     }
1933
1934     #[test]
1935     fn list_dir_empty_path() {
1936         let dirs = os::list_dir(&Path(""));
1937         assert!(dirs.is_empty());
1938     }
1939
1940     #[test]
1941     #[cfg(not(windows))]
1942     fn list_dir_root() {
1943         let dirs = os::list_dir(&Path("/"));
1944         assert!(dirs.len() > 1);
1945     }
1946     #[test]
1947     #[cfg(windows)]
1948     fn list_dir_root() {
1949         let dirs = os::list_dir(&Path("C:\\"));
1950         assert!(dirs.len() > 1);
1951     }
1952
1953
1954     #[test]
1955     fn path_is_dir() {
1956         assert!((os::path_is_dir(&Path("."))));
1957         assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
1958     }
1959
1960     #[test]
1961     fn path_exists() {
1962         assert!((os::path_exists(&Path("."))));
1963         assert!((!os::path_exists(&Path(
1964                      "test/nonexistent-bogus-path"))));
1965     }
1966
1967     #[test]
1968     fn copy_file_does_not_exist() {
1969       assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
1970                             &Path("test/other-bogus-path")));
1971       assert!(!os::path_exists(&Path("test/other-bogus-path")));
1972     }
1973
1974     #[test]
1975     fn copy_file_ok() {
1976         unsafe {
1977           let tempdir = getcwd(); // would like to use $TMPDIR,
1978                                   // doesn't seem to work on Linux
1979           assert!((tempdir.to_str().len() > 0u));
1980           let in = tempdir.push("in.txt");
1981           let out = tempdir.push("out.txt");
1982
1983           /* Write the temp input file */
1984             let ostream = do as_c_charp(in.to_str()) |fromp| {
1985                 do as_c_charp("w+b") |modebuf| {
1986                     libc::fopen(fromp, modebuf)
1987                 }
1988           };
1989           assert!((ostream as uint != 0u));
1990           let s = ~"hello";
1991           let mut buf = s.as_bytes_with_null().to_owned();
1992           let len = buf.len();
1993           do buf.as_mut_buf |b, _len| {
1994               assert_eq!(libc::fwrite(b as *c_void, 1u as size_t,
1995                                       (s.len() + 1u) as size_t, ostream),
1996                          len as size_t)
1997           }
1998           assert_eq!(libc::fclose(ostream), (0u as c_int));
1999           let in_mode = in.get_mode();
2000           let rs = os::copy_file(&in, &out);
2001           if (!os::path_exists(&in)) {
2002             fail!("%s doesn't exist", in.to_str());
2003           }
2004           assert!((rs));
2005           let rslt = run::process_status("diff", [in.to_str(), out.to_str()]);
2006           assert_eq!(rslt, 0);
2007           assert_eq!(out.get_mode(), in_mode);
2008           assert!((remove_file(&in)));
2009           assert!((remove_file(&out)));
2010         }
2011     }
2012
2013     #[test]
2014     fn recursive_mkdir_slash() {
2015         let path = Path("/");
2016         assert!(os::mkdir_recursive(&path,  (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
2017     }
2018
2019     #[test]
2020     fn recursive_mkdir_empty() {
2021         let path = Path("");
2022         assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
2023     }
2024
2025     #[test]
2026     fn memory_map_rw() {
2027         use result::{Ok, Err};
2028
2029         let chunk = match os::MemoryMap::new(16, ~[
2030             os::MapReadable,
2031             os::MapWritable
2032         ]) {
2033             Ok(chunk) => chunk,
2034             Err(msg) => fail!(msg.to_str())
2035         };
2036         assert!(chunk.len >= 16);
2037
2038         unsafe {
2039             *chunk.data = 0xBE;
2040             assert!(*chunk.data == 0xBE);
2041         }
2042     }
2043
2044     #[test]
2045     fn memory_map_file() {
2046         use result::{Ok, Err};
2047         use os::*;
2048         use libc::*;
2049
2050         #[cfg(unix)]
2051         fn lseek_(fd: c_int, size: uint) {
2052             unsafe {
2053                 assert!(lseek(fd, size as off_t, SEEK_SET) == size as off_t);
2054             }
2055         }
2056         #[cfg(windows)]
2057         fn lseek_(fd: c_int, size: uint) {
2058            unsafe {
2059                assert!(lseek(fd, size as c_long, SEEK_SET) == size as c_long);
2060            }
2061         }
2062
2063         let p = tmpdir().push("mmap_file.tmp");
2064         let size = page_size() * 2;
2065         remove_file(&p);
2066
2067         let fd = unsafe {
2068             let fd = do as_c_charp(p.to_str()) |path| {
2069                 open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
2070             };
2071             lseek_(fd, size);
2072             do as_c_charp("x") |x| {
2073                 assert!(write(fd, x as *c_void, 1) == 1);
2074             }
2075             fd
2076         };
2077         let chunk = match MemoryMap::new(size / 2, ~[
2078             MapReadable,
2079             MapWritable,
2080             MapFd(fd),
2081             MapOffset(size / 2)
2082         ]) {
2083             Ok(chunk) => chunk,
2084             Err(msg) => fail!(msg.to_str())
2085         };
2086         assert!(chunk.len > 0);
2087
2088         unsafe {
2089             *chunk.data = 0xbe;
2090             assert!(*chunk.data == 0xbe);
2091             close(fd);
2092         }
2093     }
2094
2095     // More recursive_mkdir tests are in extra::tempfile
2096 }