]> git.lizzy.rs Git - rust.git/blob - src/libstd/os.rs
std: Move change_dir_locked to unstable. #7870
[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 /// Copies a file from one location to another
867 pub fn copy_file(from: &Path, to: &Path) -> bool {
868     return do_copy_file(from, to);
869
870     #[cfg(windows)]
871     fn do_copy_file(from: &Path, to: &Path) -> bool {
872         unsafe {
873             use os::win32::as_utf16_p;
874             return do as_utf16_p(from.to_str()) |fromp| {
875                 do as_utf16_p(to.to_str()) |top| {
876                     libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
877                         (0 as libc::BOOL)
878                 }
879             }
880         }
881     }
882
883     #[cfg(unix)]
884     fn do_copy_file(from: &Path, to: &Path) -> bool {
885         unsafe {
886             let istream = do as_c_charp(from.to_str()) |fromp| {
887                 do as_c_charp("rb") |modebuf| {
888                     libc::fopen(fromp, modebuf)
889                 }
890             };
891             if istream as uint == 0u {
892                 return false;
893             }
894             // Preserve permissions
895             let from_mode = from.get_mode().expect("copy_file: couldn't get permissions \
896                                                     for source file");
897
898             let ostream = do as_c_charp(to.to_str()) |top| {
899                 do as_c_charp("w+b") |modebuf| {
900                     libc::fopen(top, modebuf)
901                 }
902             };
903             if ostream as uint == 0u {
904                 fclose(istream);
905                 return false;
906             }
907             let bufsize = 8192u;
908             let mut buf = vec::with_capacity::<u8>(bufsize);
909             let mut done = false;
910             let mut ok = true;
911             while !done {
912                 do buf.as_mut_buf |b, _sz| {
913                   let nread = libc::fread(b as *mut c_void, 1u as size_t,
914                                           bufsize as size_t,
915                                           istream);
916                   if nread > 0 as size_t {
917                       if libc::fwrite(b as *c_void, 1u as size_t, nread,
918                                       ostream) != nread {
919                           ok = false;
920                           done = true;
921                       }
922                   } else {
923                       done = true;
924                   }
925               }
926             }
927             fclose(istream);
928             fclose(ostream);
929
930             // Give the new file the old file's permissions
931             if do str::as_c_str(to.to_str()) |to_buf| {
932                 libc::chmod(to_buf, from_mode as libc::mode_t)
933             } != 0 {
934                 return false; // should be a condition...
935             }
936             return ok;
937         }
938     }
939 }
940
941 /// Deletes an existing file
942 pub fn remove_file(p: &Path) -> bool {
943     return unlink(p);
944
945     #[cfg(windows)]
946     fn unlink(p: &Path) -> bool {
947         unsafe {
948             use os::win32::as_utf16_p;
949             return do as_utf16_p(p.to_str()) |buf| {
950                 libc::DeleteFileW(buf) != (0 as libc::BOOL)
951             };
952         }
953     }
954
955     #[cfg(unix)]
956     fn unlink(p: &Path) -> bool {
957         unsafe {
958             return do as_c_charp(p.to_str()) |buf| {
959                 libc::unlink(buf) == (0 as c_int)
960             };
961         }
962     }
963 }
964
965 #[cfg(unix)]
966 /// Returns the platform-specific value of errno
967 pub fn errno() -> int {
968     #[cfg(target_os = "macos")]
969     #[cfg(target_os = "freebsd")]
970     fn errno_location() -> *c_int {
971         #[nolink]
972         extern {
973             unsafe fn __error() -> *c_int;
974         }
975         unsafe {
976             __error()
977         }
978     }
979
980     #[cfg(target_os = "linux")]
981     #[cfg(target_os = "android")]
982     fn errno_location() -> *c_int {
983         #[nolink]
984         extern {
985             unsafe fn __errno_location() -> *c_int;
986         }
987         unsafe {
988             __errno_location()
989         }
990     }
991
992     unsafe {
993         (*errno_location()) as int
994     }
995 }
996
997 #[cfg(windows)]
998 /// Returns the platform-specific value of errno
999 pub fn errno() -> uint {
1000     use libc::types::os::arch::extra::DWORD;
1001
1002     #[link_name = "kernel32"]
1003     #[abi = "stdcall"]
1004     extern "stdcall" {
1005         unsafe fn GetLastError() -> DWORD;
1006     }
1007
1008     unsafe {
1009         GetLastError() as uint
1010     }
1011 }
1012
1013 /// Get a string representing the platform-dependent last error
1014 pub fn last_os_error() -> ~str {
1015     #[cfg(unix)]
1016     fn strerror() -> ~str {
1017         #[cfg(target_os = "macos")]
1018         #[cfg(target_os = "android")]
1019         #[cfg(target_os = "freebsd")]
1020         fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1021             #[nolink]
1022             extern {
1023                 unsafe fn strerror_r(errnum: c_int, buf: *mut c_char,
1024                                      buflen: size_t) -> c_int;
1025             }
1026             unsafe {
1027                 strerror_r(errnum, buf, buflen)
1028             }
1029         }
1030
1031         // GNU libc provides a non-compliant version of strerror_r by default
1032         // and requires macros to instead use the POSIX compliant variant.
1033         // So we just use __xpg_strerror_r which is always POSIX compliant
1034         #[cfg(target_os = "linux")]
1035         fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1036             #[nolink]
1037             extern {
1038                 unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char,
1039                                            buflen: size_t) -> c_int;
1040             }
1041             unsafe {
1042                 __xpg_strerror_r(errnum, buf, buflen)
1043             }
1044         }
1045
1046         let mut buf = [0 as c_char, ..TMPBUF_SZ];
1047         unsafe {
1048             let err = strerror_r(errno() as c_int, &mut buf[0],
1049                                  TMPBUF_SZ as size_t);
1050             if err < 0 {
1051                 fail!("strerror_r failure");
1052             }
1053
1054             str::raw::from_c_str(&buf[0])
1055         }
1056     }
1057
1058     #[cfg(windows)]
1059     fn strerror() -> ~str {
1060         use libc::types::os::arch::extra::DWORD;
1061         use libc::types::os::arch::extra::LPSTR;
1062         use libc::types::os::arch::extra::LPVOID;
1063
1064         #[link_name = "kernel32"]
1065         #[abi = "stdcall"]
1066         extern "stdcall" {
1067             unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
1068                                      msgId: DWORD, langId: DWORD,
1069                                      buf: LPSTR, nsize: DWORD,
1070                                      args: *c_void) -> DWORD;
1071         }
1072
1073         static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
1074         static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
1075
1076         let mut buf = [0 as c_char, ..TMPBUF_SZ];
1077
1078         // This value is calculated from the macro
1079         // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
1080         let langId = 0x0800 as DWORD;
1081         let err = errno() as DWORD;
1082         unsafe {
1083             let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
1084                                      FORMAT_MESSAGE_IGNORE_INSERTS,
1085                                      ptr::mut_null(), err, langId,
1086                                      &mut buf[0], TMPBUF_SZ as DWORD,
1087                                      ptr::null());
1088             if res == 0 {
1089                 fail!("[%?] FormatMessage failure", errno());
1090             }
1091
1092             str::raw::from_c_str(&buf[0])
1093         }
1094     }
1095
1096     strerror()
1097 }
1098
1099 /**
1100  * Sets the process exit code
1101  *
1102  * Sets the exit code returned by the process if all supervised tasks
1103  * terminate successfully (without failing). If the current root task fails
1104  * and is supervised by the scheduler then any user-specified exit status is
1105  * ignored and the process exits with the default failure status
1106  */
1107 pub fn set_exit_status(code: int) {
1108     use rt;
1109     use rt::OldTaskContext;
1110
1111     if rt::context() == OldTaskContext {
1112         unsafe {
1113             rustrt::rust_set_exit_status(code as libc::intptr_t);
1114         }
1115     } else {
1116         rt::util::set_exit_status(code);
1117     }
1118 }
1119
1120 unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
1121     let mut args = ~[];
1122     for uint::range(0, argc as uint) |i| {
1123         args.push(str::raw::from_c_str(*argv.offset(i)));
1124     }
1125     args
1126 }
1127
1128 /**
1129  * Returns the command line arguments
1130  *
1131  * Returns a list of the command line arguments.
1132  */
1133 #[cfg(target_os = "macos")]
1134 pub fn real_args() -> ~[~str] {
1135     unsafe {
1136         let (argc, argv) = (*_NSGetArgc() as c_int,
1137                             *_NSGetArgv() as **c_char);
1138         load_argc_and_argv(argc, argv)
1139     }
1140 }
1141
1142 #[cfg(target_os = "linux")]
1143 #[cfg(target_os = "android")]
1144 #[cfg(target_os = "freebsd")]
1145 pub fn real_args() -> ~[~str] {
1146     use rt;
1147     use rt::TaskContext;
1148
1149     if rt::context() == TaskContext {
1150         match rt::args::clone() {
1151             Some(args) => args,
1152             None => fail!("process arguments not initialized")
1153         }
1154     } else {
1155         unsafe {
1156             let argc = rustrt::rust_get_argc();
1157             let argv = rustrt::rust_get_argv();
1158             load_argc_and_argv(argc, argv)
1159         }
1160     }
1161 }
1162
1163 #[cfg(windows)]
1164 pub fn real_args() -> ~[~str] {
1165     let mut nArgs: c_int = 0;
1166     let lpArgCount: *mut c_int = &mut nArgs;
1167     let lpCmdLine = unsafe { GetCommandLineW() };
1168     let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1169
1170     let mut args = ~[];
1171     for uint::range(0, nArgs as uint) |i| {
1172         unsafe {
1173             // Determine the length of this argument.
1174             let ptr = *szArgList.offset(i);
1175             let mut len = 0;
1176             while *ptr.offset(len) != 0 { len += 1; }
1177
1178             // Push it onto the list.
1179             args.push(vec::raw::buf_as_slice(ptr, len,
1180                                              str::from_utf16));
1181         }
1182     }
1183
1184     unsafe {
1185         LocalFree(cast::transmute(szArgList));
1186     }
1187
1188     return args;
1189 }
1190
1191 type LPCWSTR = *u16;
1192
1193 #[cfg(windows)]
1194 #[link_name="kernel32"]
1195 #[abi="stdcall"]
1196 extern "stdcall" {
1197     fn GetCommandLineW() -> LPCWSTR;
1198     fn LocalFree(ptr: *c_void);
1199 }
1200
1201 #[cfg(windows)]
1202 #[link_name="shell32"]
1203 #[abi="stdcall"]
1204 extern "stdcall" {
1205     fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
1206 }
1207
1208 struct OverriddenArgs {
1209     val: ~[~str]
1210 }
1211
1212 static overridden_arg_key: local_data::Key<@OverriddenArgs> = &local_data::Key;
1213
1214 /// Returns the arguments which this program was started with (normally passed
1215 /// via the command line).
1216 ///
1217 /// The return value of the function can be changed by invoking the
1218 /// `os::set_args` function.
1219 pub fn args() -> ~[~str] {
1220     match local_data::get(overridden_arg_key, |k| k.map(|&k| *k)) {
1221         None => real_args(),
1222         Some(args) => args.val.clone()
1223     }
1224 }
1225
1226 /// For the current task, overrides the task-local cache of the arguments this
1227 /// program had when it started. These new arguments are only available to the
1228 /// current task via the `os::args` method.
1229 pub fn set_args(new_args: ~[~str]) {
1230     let overridden_args = @OverriddenArgs {
1231         val: new_args.clone()
1232     };
1233     local_data::set(overridden_arg_key, overridden_args);
1234 }
1235
1236 // FIXME #6100 we should really use an internal implementation of this - using
1237 // the POSIX glob functions isn't portable to windows, probably has slight
1238 // inconsistencies even where it is implemented, and makes extending
1239 // functionality a lot more difficult
1240 // FIXME #6101 also provide a non-allocating version - each_glob or so?
1241 /// Returns a vector of Path objects that match the given glob pattern
1242 #[cfg(target_os = "linux")]
1243 #[cfg(target_os = "android")]
1244 #[cfg(target_os = "freebsd")]
1245 #[cfg(target_os = "macos")]
1246 pub fn glob(pattern: &str) -> ~[Path] {
1247     #[cfg(target_os = "linux")]
1248     #[cfg(target_os = "android")]
1249     fn default_glob_t () -> libc::glob_t {
1250         libc::glob_t {
1251             gl_pathc: 0,
1252             gl_pathv: ptr::null(),
1253             gl_offs: 0,
1254             __unused1: ptr::null(),
1255             __unused2: ptr::null(),
1256             __unused3: ptr::null(),
1257             __unused4: ptr::null(),
1258             __unused5: ptr::null(),
1259         }
1260     }
1261
1262     #[cfg(target_os = "freebsd")]
1263     fn default_glob_t () -> libc::glob_t {
1264         libc::glob_t {
1265             gl_pathc: 0,
1266             __unused1: 0,
1267             gl_offs: 0,
1268             __unused2: 0,
1269             gl_pathv: ptr::null(),
1270             __unused3: ptr::null(),
1271             __unused4: ptr::null(),
1272             __unused5: ptr::null(),
1273             __unused6: ptr::null(),
1274             __unused7: ptr::null(),
1275             __unused8: ptr::null(),
1276         }
1277     }
1278
1279     #[cfg(target_os = "macos")]
1280     fn default_glob_t () -> libc::glob_t {
1281         libc::glob_t {
1282             gl_pathc: 0,
1283             __unused1: 0,
1284             gl_offs: 0,
1285             __unused2: 0,
1286             gl_pathv: ptr::null(),
1287             __unused3: ptr::null(),
1288             __unused4: ptr::null(),
1289             __unused5: ptr::null(),
1290             __unused6: ptr::null(),
1291             __unused7: ptr::null(),
1292             __unused8: ptr::null(),
1293         }
1294     }
1295
1296     let mut g = default_glob_t();
1297     do str::as_c_str(pattern) |c_pattern| {
1298         unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) }
1299     };
1300     do(|| {
1301         let paths = unsafe {
1302             vec::raw::from_buf_raw(g.gl_pathv, g.gl_pathc as uint)
1303         };
1304         do paths.map |&c_str| {
1305             Path(unsafe { str::raw::from_c_str(c_str) })
1306         }
1307     }).finally {
1308         unsafe { libc::globfree(&mut g) };
1309     }
1310 }
1311
1312 /// Returns a vector of Path objects that match the given glob pattern
1313 #[cfg(target_os = "win32")]
1314 pub fn glob(_pattern: &str) -> ~[Path] {
1315     fail!("glob() is unimplemented on Windows")
1316 }
1317
1318 #[cfg(target_os = "macos")]
1319 extern {
1320     // These functions are in crt_externs.h.
1321     pub fn _NSGetArgc() -> *c_int;
1322     pub fn _NSGetArgv() -> ***c_char;
1323 }
1324
1325 // Round up `from` to be divisible by `to`
1326 fn round_up(from: uint, to: uint) -> uint {
1327     let r = if from % to == 0 {
1328         from
1329     } else {
1330         from + to - (from % to)
1331     };
1332     if r == 0 {
1333         to
1334     } else {
1335         r
1336     }
1337 }
1338
1339 #[cfg(unix)]
1340 pub fn page_size() -> uint {
1341     unsafe {
1342         libc::sysconf(libc::_SC_PAGESIZE) as uint
1343     }
1344 }
1345
1346 #[cfg(windows)]
1347 pub fn page_size() -> uint {
1348   unsafe {
1349     let mut info = libc::SYSTEM_INFO::new();
1350     libc::GetSystemInfo(&mut info);
1351
1352     return info.dwPageSize as uint;
1353   }
1354 }
1355
1356 pub struct MemoryMap {
1357     data: *mut u8,
1358     len: size_t,
1359     kind: MemoryMapKind
1360 }
1361
1362 pub enum MemoryMapKind {
1363     MapFile(*c_void),
1364     MapVirtual
1365 }
1366
1367 pub enum MapOption {
1368     MapReadable,
1369     MapWritable,
1370     MapExecutable,
1371     MapAddr(*c_void),
1372     MapFd(c_int),
1373     MapOffset(uint)
1374 }
1375
1376 pub enum MapError {
1377     // Linux-specific errors
1378     ErrFdNotAvail,
1379     ErrInvalidFd,
1380     ErrUnaligned,
1381     ErrNoMapSupport,
1382     ErrNoMem,
1383     ErrUnknown(libc::c_int),
1384
1385     // Windows-specific errors
1386     ErrUnsupProt,
1387     ErrUnsupOffset,
1388     ErrNeedRW,
1389     ErrAlreadyExists,
1390     ErrVirtualAlloc(uint),
1391     ErrCreateFileMappingW(uint),
1392     ErrMapViewOfFile(uint)
1393 }
1394
1395 impl to_str::ToStr for MapError {
1396     fn to_str(&self) -> ~str {
1397         match *self {
1398             ErrFdNotAvail => ~"fd not available for reading or writing",
1399             ErrInvalidFd => ~"Invalid fd",
1400             ErrUnaligned => ~"Unaligned address, invalid flags, \
1401                               negative length or unaligned offset",
1402             ErrNoMapSupport=> ~"File doesn't support mapping",
1403             ErrNoMem => ~"Invalid address, or not enough available memory",
1404             ErrUnknown(code) => fmt!("Unknown error=%?", code),
1405             ErrUnsupProt => ~"Protection mode unsupported",
1406             ErrUnsupOffset => ~"Offset in virtual memory mode is unsupported",
1407             ErrNeedRW => ~"File mapping should be at least readable/writable",
1408             ErrAlreadyExists => ~"File mapping for specified file already exists",
1409             ErrVirtualAlloc(code) => fmt!("VirtualAlloc failure=%?", code),
1410             ErrCreateFileMappingW(code) => fmt!("CreateFileMappingW failure=%?", code),
1411             ErrMapViewOfFile(code) => fmt!("MapViewOfFile failure=%?", code)
1412         }
1413     }
1414 }
1415
1416 #[cfg(unix)]
1417 impl MemoryMap {
1418     pub fn new(min_len: uint, options: ~[MapOption]) -> Result<~MemoryMap, MapError> {
1419         use libc::off_t;
1420
1421         let mut addr: *c_void = ptr::null();
1422         let mut prot: c_int = 0;
1423         let mut flags: c_int = libc::MAP_PRIVATE;
1424         let mut fd: c_int = -1;
1425         let mut offset: off_t = 0;
1426         let len = round_up(min_len, page_size()) as size_t;
1427
1428         for options.iter().advance |&o| {
1429             match o {
1430                 MapReadable => { prot |= libc::PROT_READ; },
1431                 MapWritable => { prot |= libc::PROT_WRITE; },
1432                 MapExecutable => { prot |= libc::PROT_EXEC; },
1433                 MapAddr(addr_) => {
1434                     flags |= libc::MAP_FIXED;
1435                     addr = addr_;
1436                 },
1437                 MapFd(fd_) => {
1438                     flags |= libc::MAP_FILE;
1439                     fd = fd_;
1440                 },
1441                 MapOffset(offset_) => { offset = offset_ as off_t; }
1442             }
1443         }
1444         if fd == -1 { flags |= libc::MAP_ANON; }
1445
1446         let r = unsafe {
1447             libc::mmap(addr, len, prot, flags, fd, offset)
1448         };
1449         if r == libc::MAP_FAILED {
1450             Err(match errno() as c_int {
1451                 libc::EACCES => ErrFdNotAvail,
1452                 libc::EBADF => ErrInvalidFd,
1453                 libc::EINVAL => ErrUnaligned,
1454                 libc::ENODEV => ErrNoMapSupport,
1455                 libc::ENOMEM => ErrNoMem,
1456                 code => ErrUnknown(code)
1457             })
1458         } else {
1459             Ok(~MemoryMap {
1460                data: r as *mut u8,
1461                len: len,
1462                kind: if fd == -1 {
1463                    MapVirtual
1464                } else {
1465                    MapFile(ptr::null())
1466                }
1467             })
1468         }
1469     }
1470 }
1471
1472 #[cfg(unix)]
1473 impl Drop for MemoryMap {
1474     fn drop(&self) {
1475         unsafe {
1476             match libc::munmap(self.data as *c_void, self.len) {
1477                 0 => (),
1478                 -1 => error!(match errno() as c_int {
1479                     libc::EINVAL => ~"invalid addr or len",
1480                     e => fmt!("unknown errno=%?", e)
1481                 }),
1482                 r => error!(fmt!("Unexpected result %?", r))
1483             }
1484         }
1485     }
1486 }
1487
1488 #[cfg(windows)]
1489 impl MemoryMap {
1490     pub fn new(min_len: uint, options: ~[MapOption]) -> Result<~MemoryMap, MapError> {
1491         use libc::types::os::arch::extra::{LPVOID, DWORD, SIZE_T, HANDLE};
1492
1493         let mut lpAddress: LPVOID = ptr::mut_null();
1494         let mut readable = false;
1495         let mut writable = false;
1496         let mut executable = false;
1497         let mut fd: c_int = -1;
1498         let mut offset: uint = 0;
1499         let len = round_up(min_len, page_size()) as SIZE_T;
1500
1501         for options.iter().advance |&o| {
1502             match o {
1503                 MapReadable => { readable = true; },
1504                 MapWritable => { writable = true; },
1505                 MapExecutable => { executable = true; }
1506                 MapAddr(addr_) => { lpAddress = addr_ as LPVOID; },
1507                 MapFd(fd_) => { fd = fd_; },
1508                 MapOffset(offset_) => { offset = offset_; }
1509             }
1510         }
1511
1512         let flProtect = match (executable, readable, writable) {
1513             (false, false, false) if fd == -1 => libc::PAGE_NOACCESS,
1514             (false, true, false) => libc::PAGE_READONLY,
1515             (false, true, true) => libc::PAGE_READWRITE,
1516             (true, false, false) if fd == -1 => libc::PAGE_EXECUTE,
1517             (true, true, false) => libc::PAGE_EXECUTE_READ,
1518             (true, true, true) => libc::PAGE_EXECUTE_READWRITE,
1519             _ => return Err(ErrUnsupProt)
1520         };
1521
1522         if fd == -1 {
1523             if offset != 0 {
1524                 return Err(ErrUnsupOffset);
1525             }
1526             let r = unsafe {
1527                 libc::VirtualAlloc(lpAddress,
1528                                    len,
1529                                    libc::MEM_COMMIT | libc::MEM_RESERVE,
1530                                    flProtect)
1531             };
1532             match r as uint {
1533                 0 => Err(ErrVirtualAlloc(errno())),
1534                 _ => Ok(~MemoryMap {
1535                    data: r as *mut u8,
1536                    len: len,
1537                    kind: MapVirtual
1538                 })
1539             }
1540         } else {
1541             let dwDesiredAccess = match (readable, writable) {
1542                 (true, true) => libc::FILE_MAP_ALL_ACCESS,
1543                 (true, false) => libc::FILE_MAP_READ,
1544                 (false, true) => libc::FILE_MAP_WRITE,
1545                 _ => {
1546                     return Err(ErrNeedRW);
1547                 }
1548             };
1549             unsafe {
1550                 let hFile = libc::get_osfhandle(fd) as HANDLE;
1551                 let mapping = libc::CreateFileMappingW(hFile,
1552                                                        ptr::mut_null(),
1553                                                        flProtect,
1554                                                        (len >> 32) as DWORD,
1555                                                        (len & 0xffff_ffff) as DWORD,
1556                                                        ptr::null());
1557                 if mapping == ptr::mut_null() {
1558                     return Err(ErrCreateFileMappingW(errno()));
1559                 }
1560                 if errno() as c_int == libc::ERROR_ALREADY_EXISTS {
1561                     return Err(ErrAlreadyExists);
1562                 }
1563                 let r = libc::MapViewOfFile(mapping,
1564                                             dwDesiredAccess,
1565                                             (offset >> 32) as DWORD,
1566                                             (offset & 0xffff_ffff) as DWORD,
1567                                             0);
1568                 match r as uint {
1569                     0 => Err(ErrMapViewOfFile(errno())),
1570                     _ => Ok(~MemoryMap {
1571                        data: r as *mut u8,
1572                        len: len,
1573                        kind: MapFile(mapping as *c_void)
1574                     })
1575                 }
1576             }
1577         }
1578     }
1579 }
1580
1581 #[cfg(windows)]
1582 impl Drop for MemoryMap {
1583     fn drop(&self) {
1584         use libc::types::os::arch::extra::{LPCVOID, HANDLE};
1585
1586         unsafe {
1587             match self.kind {
1588                 MapVirtual => match libc::VirtualFree(self.data as *mut c_void,
1589                                                       self.len,
1590                                                       libc::MEM_RELEASE) {
1591                     0 => error!(fmt!("VirtualFree failed: %?", errno())),
1592                     _ => ()
1593                 },
1594                 MapFile(mapping) => {
1595                     if libc::UnmapViewOfFile(self.data as LPCVOID) != 0 {
1596                         error!(fmt!("UnmapViewOfFile failed: %?", errno()));
1597                     }
1598                     if libc::CloseHandle(mapping as HANDLE) != 0 {
1599                         error!(fmt!("CloseHandle failed: %?", errno()));
1600                     }
1601                 }
1602             }
1603         }
1604     }
1605 }
1606
1607 pub mod consts {
1608
1609     #[cfg(unix)]
1610     pub use os::consts::unix::*;
1611
1612     #[cfg(windows)]
1613     pub use os::consts::windows::*;
1614
1615     #[cfg(target_os = "macos")]
1616     pub use os::consts::macos::*;
1617
1618     #[cfg(target_os = "freebsd")]
1619     pub use os::consts::freebsd::*;
1620
1621     #[cfg(target_os = "linux")]
1622     pub use os::consts::linux::*;
1623
1624     #[cfg(target_os = "android")]
1625     pub use os::consts::android::*;
1626
1627     #[cfg(target_os = "win32")]
1628     pub use os::consts::win32::*;
1629
1630     #[cfg(target_arch = "x86")]
1631     pub use os::consts::x86::*;
1632
1633     #[cfg(target_arch = "x86_64")]
1634     pub use os::consts::x86_64::*;
1635
1636     #[cfg(target_arch = "arm")]
1637     pub use os::consts::arm::*;
1638
1639     #[cfg(target_arch = "mips")]
1640     use os::consts::mips::*;
1641
1642     pub mod unix {
1643         pub static FAMILY: &'static str = "unix";
1644     }
1645
1646     pub mod windows {
1647         pub static FAMILY: &'static str = "windows";
1648     }
1649
1650     pub mod macos {
1651         pub static SYSNAME: &'static str = "macos";
1652         pub static DLL_PREFIX: &'static str = "lib";
1653         pub static DLL_SUFFIX: &'static str = ".dylib";
1654         pub static EXE_SUFFIX: &'static str = "";
1655     }
1656
1657     pub mod freebsd {
1658         pub static SYSNAME: &'static str = "freebsd";
1659         pub static DLL_PREFIX: &'static str = "lib";
1660         pub static DLL_SUFFIX: &'static str = ".so";
1661         pub static EXE_SUFFIX: &'static str = "";
1662     }
1663
1664     pub mod linux {
1665         pub static SYSNAME: &'static str = "linux";
1666         pub static DLL_PREFIX: &'static str = "lib";
1667         pub static DLL_SUFFIX: &'static str = ".so";
1668         pub static EXE_SUFFIX: &'static str = "";
1669     }
1670
1671     pub mod android {
1672         pub static SYSNAME: &'static str = "android";
1673         pub static DLL_PREFIX: &'static str = "lib";
1674         pub static DLL_SUFFIX: &'static str = ".so";
1675         pub static EXE_SUFFIX: &'static str = "";
1676     }
1677
1678     pub mod win32 {
1679         pub static SYSNAME: &'static str = "win32";
1680         pub static DLL_PREFIX: &'static str = "";
1681         pub static DLL_SUFFIX: &'static str = ".dll";
1682         pub static EXE_SUFFIX: &'static str = ".exe";
1683     }
1684
1685
1686     pub mod x86 {
1687         pub static ARCH: &'static str = "x86";
1688     }
1689     pub mod x86_64 {
1690         pub static ARCH: &'static str = "x86_64";
1691     }
1692     pub mod arm {
1693         pub static ARCH: &'static str = "arm";
1694     }
1695     pub mod mips {
1696         pub static ARCH: &'static str = "mips";
1697     }
1698 }
1699
1700 #[cfg(test)]
1701 mod tests {
1702     use libc::{c_int, c_void, size_t};
1703     use libc;
1704     use option::Some;
1705     use option;
1706     use os::{as_c_charp, env, getcwd, getenv, make_absolute, real_args};
1707     use os::{remove_file, setenv, unsetenv};
1708     use os;
1709     use path::Path;
1710     use rand::RngUtil;
1711     use rand;
1712     use run;
1713     use str::StrSlice;
1714     use vec::CopyableVector;
1715     use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
1716
1717
1718     #[test]
1719     pub fn last_os_error() {
1720         debug!(os::last_os_error());
1721     }
1722
1723     #[test]
1724     pub fn test_args() {
1725         let a = real_args();
1726         assert!(a.len() >= 1);
1727     }
1728
1729     fn make_rand_name() -> ~str {
1730         let mut rng = rand::rng();
1731         let n = ~"TEST" + rng.gen_str(10u);
1732         assert!(getenv(n).is_none());
1733         n
1734     }
1735
1736     #[test]
1737     fn test_setenv() {
1738         let n = make_rand_name();
1739         setenv(n, "VALUE");
1740         assert_eq!(getenv(n), option::Some(~"VALUE"));
1741     }
1742
1743     #[test]
1744     fn test_unsetenv() {
1745         let n = make_rand_name();
1746         setenv(n, "VALUE");
1747         unsetenv(n);
1748         assert_eq!(getenv(n), option::None);
1749     }
1750
1751     #[test]
1752     #[ignore(cfg(windows))]
1753     #[ignore]
1754     fn test_setenv_overwrite() {
1755         let n = make_rand_name();
1756         setenv(n, "1");
1757         setenv(n, "2");
1758         assert_eq!(getenv(n), option::Some(~"2"));
1759         setenv(n, "");
1760         assert_eq!(getenv(n), option::Some(~""));
1761     }
1762
1763     // Windows GetEnvironmentVariable requires some extra work to make sure
1764     // the buffer the variable is copied into is the right size
1765     #[test]
1766     #[ignore(cfg(windows))]
1767     #[ignore]
1768     fn test_getenv_big() {
1769         let mut s = ~"";
1770         let mut i = 0;
1771         while i < 100 {
1772             s = s + "aaaaaaaaaa";
1773             i += 1;
1774         }
1775         let n = make_rand_name();
1776         setenv(n, s);
1777         debug!(s.clone());
1778         assert_eq!(getenv(n), option::Some(s));
1779     }
1780
1781     #[test]
1782     fn test_self_exe_path() {
1783         let path = os::self_exe_path();
1784         assert!(path.is_some());
1785         let path = path.get();
1786         debug!(path.clone());
1787
1788         // Hard to test this function
1789         assert!(path.is_absolute);
1790     }
1791
1792     #[test]
1793     #[ignore]
1794     fn test_env_getenv() {
1795         let e = env();
1796         assert!(e.len() > 0u);
1797         for e.iter().advance |p| {
1798             let (n, v) = (*p).clone();
1799             debug!(n.clone());
1800             let v2 = getenv(n);
1801             // MingW seems to set some funky environment variables like
1802             // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
1803             // from env() but not visible from getenv().
1804             assert!(v2.is_none() || v2 == option::Some(v));
1805         }
1806     }
1807
1808     #[test]
1809     fn test_env_setenv() {
1810         let n = make_rand_name();
1811
1812         let mut e = env();
1813         setenv(n, "VALUE");
1814         assert!(!e.contains(&(n.clone(), ~"VALUE")));
1815
1816         e = env();
1817         assert!(e.contains(&(n, ~"VALUE")));
1818     }
1819
1820     #[test]
1821     fn test() {
1822         assert!((!Path("test-path").is_absolute));
1823
1824         debug!("Current working directory: %s", getcwd().to_str());
1825
1826         debug!(make_absolute(&Path("test-path")));
1827         debug!(make_absolute(&Path("/usr/bin")));
1828     }
1829
1830     #[test]
1831     #[cfg(unix)]
1832     fn homedir() {
1833         let oldhome = getenv("HOME");
1834
1835         setenv("HOME", "/home/MountainView");
1836         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1837
1838         setenv("HOME", "");
1839         assert!(os::homedir().is_none());
1840
1841         for oldhome.iter().advance |s| { setenv("HOME", *s) }
1842     }
1843
1844     #[test]
1845     #[cfg(windows)]
1846     fn homedir() {
1847
1848         let oldhome = getenv("HOME");
1849         let olduserprofile = getenv("USERPROFILE");
1850
1851         setenv("HOME", "");
1852         setenv("USERPROFILE", "");
1853
1854         assert!(os::homedir().is_none());
1855
1856         setenv("HOME", "/home/MountainView");
1857         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1858
1859         setenv("HOME", "");
1860
1861         setenv("USERPROFILE", "/home/MountainView");
1862         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1863
1864         setenv("HOME", "/home/MountainView");
1865         setenv("USERPROFILE", "/home/PaloAlto");
1866         assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1867
1868         oldhome.iter().advance(|s| { setenv("HOME", *s); true });
1869         olduserprofile.iter().advance(|s| { setenv("USERPROFILE", *s); true });
1870     }
1871
1872     #[test]
1873     fn tmpdir() {
1874         assert!(!os::tmpdir().to_str().is_empty());
1875     }
1876
1877     // Issue #712
1878     #[test]
1879     fn test_list_dir_no_invalid_memory_access() {
1880         os::list_dir(&Path("."));
1881     }
1882
1883     #[test]
1884     fn list_dir() {
1885         let dirs = os::list_dir(&Path("."));
1886         // Just assuming that we've got some contents in the current directory
1887         assert!(dirs.len() > 0u);
1888
1889         for dirs.iter().advance |dir| {
1890             debug!((*dir).clone());
1891         }
1892     }
1893
1894     #[test]
1895     fn list_dir_empty_path() {
1896         let dirs = os::list_dir(&Path(""));
1897         assert!(dirs.is_empty());
1898     }
1899
1900     #[test]
1901     #[cfg(not(windows))]
1902     fn list_dir_root() {
1903         let dirs = os::list_dir(&Path("/"));
1904         assert!(dirs.len() > 1);
1905     }
1906     #[test]
1907     #[cfg(windows)]
1908     fn list_dir_root() {
1909         let dirs = os::list_dir(&Path("C:\\"));
1910         assert!(dirs.len() > 1);
1911     }
1912
1913
1914     #[test]
1915     fn path_is_dir() {
1916         assert!((os::path_is_dir(&Path("."))));
1917         assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
1918     }
1919
1920     #[test]
1921     fn path_exists() {
1922         assert!((os::path_exists(&Path("."))));
1923         assert!((!os::path_exists(&Path(
1924                      "test/nonexistent-bogus-path"))));
1925     }
1926
1927     #[test]
1928     fn copy_file_does_not_exist() {
1929       assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
1930                             &Path("test/other-bogus-path")));
1931       assert!(!os::path_exists(&Path("test/other-bogus-path")));
1932     }
1933
1934     #[test]
1935     fn copy_file_ok() {
1936         unsafe {
1937           let tempdir = getcwd(); // would like to use $TMPDIR,
1938                                   // doesn't seem to work on Linux
1939           assert!((tempdir.to_str().len() > 0u));
1940           let in = tempdir.push("in.txt");
1941           let out = tempdir.push("out.txt");
1942
1943           /* Write the temp input file */
1944             let ostream = do as_c_charp(in.to_str()) |fromp| {
1945                 do as_c_charp("w+b") |modebuf| {
1946                     libc::fopen(fromp, modebuf)
1947                 }
1948           };
1949           assert!((ostream as uint != 0u));
1950           let s = ~"hello";
1951           let mut buf = s.as_bytes_with_null().to_owned();
1952           let len = buf.len();
1953           do buf.as_mut_buf |b, _len| {
1954               assert_eq!(libc::fwrite(b as *c_void, 1u as size_t,
1955                                       (s.len() + 1u) as size_t, ostream),
1956                          len as size_t)
1957           }
1958           assert_eq!(libc::fclose(ostream), (0u as c_int));
1959           let in_mode = in.get_mode();
1960           let rs = os::copy_file(&in, &out);
1961           if (!os::path_exists(&in)) {
1962             fail!("%s doesn't exist", in.to_str());
1963           }
1964           assert!((rs));
1965           let rslt = run::process_status("diff", [in.to_str(), out.to_str()]);
1966           assert_eq!(rslt, 0);
1967           assert_eq!(out.get_mode(), in_mode);
1968           assert!((remove_file(&in)));
1969           assert!((remove_file(&out)));
1970         }
1971     }
1972
1973     #[test]
1974     fn recursive_mkdir_slash() {
1975         let path = Path("/");
1976         assert!(os::mkdir_recursive(&path,  (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1977     }
1978
1979     #[test]
1980     fn recursive_mkdir_empty() {
1981         let path = Path("");
1982         assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1983     }
1984
1985     #[test]
1986     fn memory_map_rw() {
1987         use result::{Ok, Err};
1988
1989         let chunk = match os::MemoryMap::new(16, ~[
1990             os::MapReadable,
1991             os::MapWritable
1992         ]) {
1993             Ok(chunk) => chunk,
1994             Err(msg) => fail!(msg.to_str())
1995         };
1996         assert!(chunk.len >= 16);
1997
1998         unsafe {
1999             *chunk.data = 0xBE;
2000             assert!(*chunk.data == 0xBE);
2001         }
2002     }
2003
2004     #[test]
2005     fn memory_map_file() {
2006         use result::{Ok, Err};
2007         use os::*;
2008         use libc::*;
2009
2010         #[cfg(unix)]
2011         fn lseek_(fd: c_int, size: uint) {
2012             unsafe {
2013                 assert!(lseek(fd, size as off_t, SEEK_SET) == size as off_t);
2014             }
2015         }
2016         #[cfg(windows)]
2017         fn lseek_(fd: c_int, size: uint) {
2018            unsafe {
2019                assert!(lseek(fd, size as c_long, SEEK_SET) == size as c_long);
2020            }
2021         }
2022
2023         let p = tmpdir().push("mmap_file.tmp");
2024         let size = page_size() * 2;
2025         remove_file(&p);
2026
2027         let fd = unsafe {
2028             let fd = do as_c_charp(p.to_str()) |path| {
2029                 open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
2030             };
2031             lseek_(fd, size);
2032             do as_c_charp("x") |x| {
2033                 assert!(write(fd, x as *c_void, 1) == 1);
2034             }
2035             fd
2036         };
2037         let chunk = match MemoryMap::new(size / 2, ~[
2038             MapReadable,
2039             MapWritable,
2040             MapFd(fd),
2041             MapOffset(size / 2)
2042         ]) {
2043             Ok(chunk) => chunk,
2044             Err(msg) => fail!(msg.to_str())
2045         };
2046         assert!(chunk.len > 0);
2047
2048         unsafe {
2049             *chunk.data = 0xbe;
2050             assert!(*chunk.data == 0xbe);
2051             close(fd);
2052         }
2053     }
2054
2055     // More recursive_mkdir tests are in extra::tempfile
2056 }