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