]> git.lizzy.rs Git - rust.git/blob - src/libcore/os.rs
Convert most of rust_run_program.cpp to rust (issue #2674).
[rust.git] / src / libcore / 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 use cast;
30 use io;
31 use libc;
32 use libc::{c_char, c_void, c_int, size_t};
33 use libc::{mode_t, FILE};
34 use option;
35 use option::{Some, None};
36 use prelude::*;
37 use ptr;
38 use str;
39 use task;
40 use uint;
41 use unstable::finally::Finally;
42 use vec;
43
44 pub use libc::fclose;
45 pub use os::consts::*;
46
47 pub fn close(fd: c_int) -> c_int {
48     unsafe {
49         libc::close(fd)
50     }
51 }
52
53 pub mod rustrt {
54     use libc::{c_char, c_int};
55     use libc;
56
57     pub extern {
58         unsafe fn rust_get_argc() -> c_int;
59         unsafe fn rust_get_argv() -> **c_char;
60         unsafe fn rust_path_is_dir(path: *libc::c_char) -> c_int;
61         unsafe fn rust_path_exists(path: *libc::c_char) -> c_int;
62         unsafe fn rust_set_exit_status(code: libc::intptr_t);
63     }
64 }
65
66 pub static TMPBUF_SZ : uint = 1000u;
67 static BUF_BYTES : uint = 2048u;
68
69 pub fn getcwd() -> Path {
70     let buf = [0 as libc::c_char, ..BUF_BYTES];
71     unsafe {
72         if(0 as *libc::c_char == libc::getcwd(
73             &buf[0],
74             BUF_BYTES as libc::size_t)) {
75             fail!();
76         }
77         Path(str::raw::from_c_str(&buf[0]))
78     }
79 }
80
81 // FIXME: move these to str perhaps? #2620
82
83 pub fn as_c_charp<T>(s: &str, f: &fn(*c_char) -> T) -> T {
84     str::as_c_str(s, |b| f(b as *c_char))
85 }
86
87 pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool)
88     -> Option<~str> {
89     let mut buf = vec::from_elem(TMPBUF_SZ, 0u8 as c_char);
90     do vec::as_mut_buf(buf) |b, sz| {
91         if f(b, sz as size_t) {
92             unsafe {
93                 Some(str::raw::from_buf(b as *u8))
94             }
95         } else {
96             None
97         }
98     }
99 }
100
101 #[cfg(windows)]
102 pub mod win32 {
103     use libc;
104     use vec;
105     use str;
106     use option::{None, Option};
107     use option;
108     use os::TMPBUF_SZ;
109     use libc::types::os::arch::extra::DWORD;
110
111     pub fn fill_utf16_buf_and_decode(f: &fn(*mut u16, DWORD) -> DWORD)
112         -> Option<~str> {
113         unsafe {
114             let mut n = TMPBUF_SZ as DWORD;
115             let mut res = None;
116             let mut done = false;
117             while !done {
118                 let mut k: DWORD = 0;
119                 let mut buf = vec::from_elem(n as uint, 0u16);
120                 do vec::as_mut_buf(buf) |b, _sz| {
121                     k = f(b, TMPBUF_SZ as DWORD);
122                     if k == (0 as DWORD) {
123                         done = true;
124                     } else if (k == n &&
125                                libc::GetLastError() ==
126                                libc::ERROR_INSUFFICIENT_BUFFER as DWORD) {
127                         n *= (2 as DWORD);
128                     } else {
129                         done = true;
130                     }
131                 }
132                 if k != 0 && done {
133                     let sub = vec::slice(buf, 0u, k as uint);
134                     res = option::Some(str::from_utf16(sub));
135                 }
136             }
137             return res;
138         }
139     }
140
141     pub fn as_utf16_p<T>(s: &str, f: &fn(*u16) -> T) -> T {
142         let mut t = str::to_utf16(s);
143         // Null terminate before passing on.
144         t += ~[0u16];
145         vec::as_imm_buf(t, |buf, _len| f(buf))
146     }
147 }
148
149 /*
150 Accessing environment variables is not generally threadsafe.
151 This uses a per-runtime lock to serialize access.
152 FIXME #4726: It would probably be appropriate to make this a real global
153 */
154 fn with_env_lock<T>(f: &fn() -> T) -> T {
155     use unstable::global::global_data_clone_create;
156     use unstable::{Exclusive, exclusive};
157
158     struct SharedValue(());
159     type ValueMutex = Exclusive<SharedValue>;
160     fn key(_: ValueMutex) { }
161
162     unsafe {
163         let lock: ValueMutex = global_data_clone_create(key, || {
164             ~exclusive(SharedValue(()))
165         });
166
167         lock.with_imm(|_| f() )
168     }
169 }
170
171 pub fn env() -> ~[(~str,~str)] {
172     unsafe {
173         #[cfg(windows)]
174         unsafe fn get_env_pairs() -> ~[~str] {
175             use libc::types::os::arch::extra::LPTCH;
176             use libc::funcs::extra::kernel32::{
177                 GetEnvironmentStringsA,
178                 FreeEnvironmentStringsA
179             };
180             let ch = GetEnvironmentStringsA();
181             if (ch as uint == 0) {
182                 fail!(fmt!("os::env() failure getting env string from OS: %s",
183                            os::last_os_error()));
184             }
185             let mut curr_ptr: uint = ch as uint;
186             let mut result = ~[];
187             while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) {
188                 let env_pair = str::raw::from_c_str(
189                     curr_ptr as *libc::c_char);
190                 result.push(env_pair);
191                 curr_ptr +=
192                     libc::strlen(curr_ptr as *libc::c_char) as uint
193                     + 1;
194             }
195             FreeEnvironmentStringsA(ch);
196             result
197         }
198         #[cfg(unix)]
199         unsafe fn get_env_pairs() -> ~[~str] {
200             extern mod rustrt {
201                 unsafe fn rust_env_pairs() -> **libc::c_char;
202             }
203             let environ = rustrt::rust_env_pairs();
204             if (environ as uint == 0) {
205                 fail!(fmt!("os::env() failure getting env string from OS: %s",
206                            os::last_os_error()));
207             }
208             let mut result = ~[];
209             ptr::array_each(environ, |e| {
210                 let env_pair = str::raw::from_c_str(e);
211                 debug!("get_env_pairs: %s",
212                        env_pair);
213                 result.push(env_pair);
214             });
215             result
216         }
217
218         fn env_convert(input: ~[~str]) -> ~[(~str, ~str)] {
219             let mut pairs = ~[];
220             for input.each |p| {
221                 let mut vs = ~[];
222                 for str::each_splitn_char(*p, '=', 1) |s| { vs.push(s.to_owned()) }
223                 debug!("splitting: len: %u",
224                     vs.len());
225                 assert!(vs.len() == 2);
226                 pairs.push((copy vs[0], copy vs[1]));
227             }
228             pairs
229         }
230         do with_env_lock {
231             let unparsed_environ = get_env_pairs();
232             env_convert(unparsed_environ)
233         }
234     }
235 }
236
237 #[cfg(unix)]
238 pub fn getenv(n: &str) -> Option<~str> {
239     unsafe {
240         do with_env_lock {
241             let s = str::as_c_str(n, |s| libc::getenv(s));
242             if ptr::null::<u8>() == cast::transmute(s) {
243                 option::None::<~str>
244             } else {
245                 let s = cast::transmute(s);
246                 option::Some::<~str>(str::raw::from_buf(s))
247             }
248         }
249     }
250 }
251
252 #[cfg(windows)]
253 pub fn getenv(n: &str) -> Option<~str> {
254     unsafe {
255         do with_env_lock {
256             use os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
257             do as_utf16_p(n) |u| {
258                 do fill_utf16_buf_and_decode() |buf, sz| {
259                     libc::GetEnvironmentVariableW(u, buf, sz)
260                 }
261             }
262         }
263     }
264 }
265
266
267 #[cfg(unix)]
268 pub fn setenv(n: &str, v: &str) {
269     unsafe {
270         do with_env_lock {
271             do str::as_c_str(n) |nbuf| {
272                 do str::as_c_str(v) |vbuf| {
273                     libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
274                 }
275             }
276         }
277     }
278 }
279
280
281 #[cfg(windows)]
282 pub fn setenv(n: &str, v: &str) {
283     unsafe {
284         do with_env_lock {
285             use os::win32::as_utf16_p;
286             do as_utf16_p(n) |nbuf| {
287                 do as_utf16_p(v) |vbuf| {
288                     libc::SetEnvironmentVariableW(nbuf, vbuf);
289                 }
290             }
291         }
292     }
293 }
294
295 pub fn fdopen(fd: c_int) -> *FILE {
296     unsafe {
297         return do as_c_charp("r") |modebuf| {
298             libc::fdopen(fd, modebuf)
299         };
300     }
301 }
302
303
304 // fsync related
305
306 #[cfg(windows)]
307 pub fn fsync_fd(fd: c_int, _level: io::fsync::Level) -> c_int {
308     unsafe {
309         use libc::funcs::extra::msvcrt::*;
310         return commit(fd);
311     }
312 }
313
314 #[cfg(target_os = "linux")]
315 #[cfg(target_os = "android")]
316 pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
317     unsafe {
318         use libc::funcs::posix01::unistd::*;
319         match level {
320           io::fsync::FSync
321           | io::fsync::FullFSync => return fsync(fd),
322           io::fsync::FDataSync => return fdatasync(fd)
323         }
324     }
325 }
326
327 #[cfg(target_os = "macos")]
328 pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
329     unsafe {
330         use libc::consts::os::extra::*;
331         use libc::funcs::posix88::fcntl::*;
332         use libc::funcs::posix01::unistd::*;
333         match level {
334           io::fsync::FSync => return fsync(fd),
335           _ => {
336             // According to man fnctl, the ok retval is only specified to be
337             // !=-1
338             if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int)
339                 { return -1 as c_int; }
340             else
341                 { return 0 as c_int; }
342           }
343         }
344     }
345 }
346
347 #[cfg(target_os = "freebsd")]
348 pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
349     unsafe {
350         use libc::funcs::posix01::unistd::*;
351         return fsync(fd);
352     }
353 }
354
355 pub struct Pipe { mut in: c_int, mut out: c_int }
356
357 #[cfg(unix)]
358 pub fn pipe() -> Pipe {
359     unsafe {
360         let mut fds = Pipe {in: 0 as c_int,
361                         out: 0 as c_int };
362         assert!((libc::pipe(&mut fds.in) == (0 as c_int)));
363         return Pipe {in: fds.in, out: fds.out};
364     }
365 }
366
367
368
369 #[cfg(windows)]
370 pub fn pipe() -> Pipe {
371     unsafe {
372         // Windows pipes work subtly differently than unix pipes, and their
373         // inheritance has to be handled in a different way that I do not
374         // fully understand. Here we explicitly make the pipe non-inheritable,
375         // which means to pass it to a subprocess they need to be duplicated
376         // first, as in core::run.
377         let mut fds = Pipe {in: 0 as c_int,
378                     out: 0 as c_int };
379         let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint,
380                              (libc::O_BINARY | libc::O_NOINHERIT) as c_int);
381         assert!((res == 0 as c_int));
382         assert!((fds.in != -1 as c_int && fds.in != 0 as c_int));
383         assert!((fds.out != -1 as c_int && fds.in != 0 as c_int));
384         return Pipe {in: fds.in, out: fds.out};
385     }
386 }
387
388 fn dup2(src: c_int, dst: c_int) -> c_int {
389     unsafe {
390         libc::dup2(src, dst)
391     }
392 }
393
394
395 pub fn dll_filename(base: &str) -> ~str {
396     return str::from_slice(DLL_PREFIX) + str::from_slice(base) +
397            str::from_slice(DLL_SUFFIX)
398 }
399
400
401 pub fn self_exe_path() -> Option<Path> {
402
403     #[cfg(target_os = "freebsd")]
404     fn load_self() -> Option<~str> {
405         unsafe {
406             use libc::funcs::bsd44::*;
407             use libc::consts::os::extra::*;
408             do fill_charp_buf() |buf, sz| {
409                 let mib = ~[CTL_KERN as c_int,
410                            KERN_PROC as c_int,
411                            KERN_PROC_PATHNAME as c_int, -1 as c_int];
412                 let mut sz = sz;
413                 sysctl(vec::raw::to_ptr(mib), vec::len(mib) as ::libc::c_uint,
414                        buf as *mut c_void, &mut sz, ptr::null(),
415                        0u as size_t) == (0 as c_int)
416             }
417         }
418     }
419
420     #[cfg(target_os = "linux")]
421     #[cfg(target_os = "android")]
422     fn load_self() -> Option<~str> {
423         unsafe {
424             use libc::funcs::posix01::unistd::readlink;
425
426             let mut path_str = str::with_capacity(TMPBUF_SZ);
427             let len = do str::as_c_str(path_str) |buf| {
428                 let buf = buf as *mut c_char;
429                 do as_c_charp("/proc/self/exe") |proc_self_buf| {
430                     readlink(proc_self_buf, buf, TMPBUF_SZ as size_t)
431                 }
432             };
433             if len == -1 {
434                 None
435             } else {
436                 str::raw::set_len(&mut path_str, len as uint);
437                 Some(path_str)
438             }
439         }
440     }
441
442     #[cfg(target_os = "macos")]
443     fn load_self() -> Option<~str> {
444         unsafe {
445             do fill_charp_buf() |buf, sz| {
446                 let mut sz = sz as u32;
447                 libc::funcs::extra::_NSGetExecutablePath(
448                     buf, &mut sz) == (0 as c_int)
449             }
450         }
451     }
452
453     #[cfg(windows)]
454     fn load_self() -> Option<~str> {
455         unsafe {
456             use os::win32::fill_utf16_buf_and_decode;
457             do fill_utf16_buf_and_decode() |buf, sz| {
458                 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
459             }
460         }
461     }
462
463     do load_self().map |pth| {
464         Path(*pth).dir_path()
465     }
466 }
467
468
469 /**
470  * Returns the path to the user's home directory, if known.
471  *
472  * On Unix, returns the value of the 'HOME' environment variable if it is set
473  * and not equal to the empty string.
474  *
475  * On Windows, returns the value of the 'HOME' environment variable if it is
476  * set and not equal to the empty string. Otherwise, returns the value of the
477  * 'USERPROFILE' environment variable if it is set and not equal to the empty
478  * string.
479  *
480  * Otherwise, homedir returns option::none.
481  */
482 pub fn homedir() -> Option<Path> {
483     return match getenv(~"HOME") {
484         Some(ref p) => if !str::is_empty(*p) {
485           Some(Path(*p))
486         } else {
487           secondary()
488         },
489         None => secondary()
490     };
491
492     #[cfg(unix)]
493     fn secondary() -> Option<Path> {
494         None
495     }
496
497     #[cfg(windows)]
498     fn secondary() -> Option<Path> {
499         do getenv(~"USERPROFILE").chain |p| {
500             if !str::is_empty(p) {
501                 Some(Path(p))
502             } else {
503                 None
504             }
505         }
506     }
507 }
508
509 /**
510  * Returns the path to a temporary directory.
511  *
512  * On Unix, returns the value of the 'TMPDIR' environment variable if it is
513  * set and non-empty and '/tmp' otherwise.
514  *
515  * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
516  * 'USERPROFILE' environment variable  if any are set and not the empty
517  * string. Otherwise, tmpdir returns the path to the Windows directory.
518  */
519 pub fn tmpdir() -> Path {
520     return lookup();
521
522     fn getenv_nonempty(v: &str) -> Option<Path> {
523         match getenv(v) {
524             Some(x) =>
525                 if str::is_empty(x) {
526                     None
527                 } else {
528                     Some(Path(x))
529                 },
530             _ => None
531         }
532     }
533
534     #[cfg(unix)]
535     #[allow(non_implicitly_copyable_typarams)]
536     fn lookup() -> Path {
537         getenv_nonempty("TMPDIR").get_or_default(Path("/tmp"))
538     }
539
540     #[cfg(windows)]
541     #[allow(non_implicitly_copyable_typarams)]
542     fn lookup() -> Path {
543         getenv_nonempty("TMP").or(
544             getenv_nonempty("TEMP").or(
545                 getenv_nonempty("USERPROFILE").or(
546                    getenv_nonempty("WINDIR")))).get_or_default(Path("C:\\Windows"))
547     }
548 }
549 /// Recursively walk a directory structure
550 pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) {
551
552     walk_dir_(p, f);
553
554     fn walk_dir_(p: &Path, f: &fn(&Path) -> bool) -> bool {
555         let mut keepgoing = true;
556         do list_dir(p).each |q| {
557             let path = &p.push(*q);
558             if !f(path) {
559                 keepgoing = false;
560                 false
561             } else {
562                 if path_is_dir(path) {
563                     if !walk_dir_(path, f) {
564                         keepgoing = false;
565                         false
566                     } else {
567                         true
568                     }
569                 } else {
570                     true
571                 }
572             }
573         }
574         return keepgoing;
575     }
576 }
577
578 /// Indicates whether a path represents a directory
579 pub fn path_is_dir(p: &Path) -> bool {
580     unsafe {
581         do str::as_c_str(p.to_str()) |buf| {
582             rustrt::rust_path_is_dir(buf) != 0 as c_int
583         }
584     }
585 }
586
587 /// Indicates whether a path exists
588 pub fn path_exists(p: &Path) -> bool {
589     unsafe {
590         do str::as_c_str(p.to_str()) |buf| {
591             rustrt::rust_path_exists(buf) != 0 as c_int
592         }
593     }
594 }
595
596 /**
597  * Convert a relative path to an absolute path
598  *
599  * If the given path is relative, return it prepended with the current working
600  * directory. If the given path is already an absolute path, return it
601  * as is.
602  */
603 // NB: this is here rather than in path because it is a form of environment
604 // querying; what it does depends on the process working directory, not just
605 // the input paths.
606 pub fn make_absolute(p: &Path) -> Path {
607     if p.is_absolute {
608         copy *p
609     } else {
610         getcwd().push_many(p.components)
611     }
612 }
613
614
615 /// Creates a directory at the specified path
616 pub fn make_dir(p: &Path, mode: c_int) -> bool {
617     return mkdir(p, mode);
618
619     #[cfg(windows)]
620     fn mkdir(p: &Path, _mode: c_int) -> bool {
621         unsafe {
622             use os::win32::as_utf16_p;
623             // FIXME: turn mode into something useful? #2623
624             do as_utf16_p(p.to_str()) |buf| {
625                 libc::CreateDirectoryW(buf, unsafe {
626                     cast::transmute(0)
627                 })
628                     != (0 as libc::BOOL)
629             }
630         }
631     }
632
633     #[cfg(unix)]
634     fn mkdir(p: &Path, mode: c_int) -> bool {
635         unsafe {
636             do as_c_charp(p.to_str()) |c| {
637                 libc::mkdir(c, mode as mode_t) == (0 as c_int)
638             }
639         }
640     }
641 }
642
643 /// Creates a directory with a given mode.
644 /// Returns true iff creation
645 /// succeeded. Also creates all intermediate subdirectories
646 /// if they don't already exist, giving all of them the same mode.
647
648 // tjc: if directory exists but with different permissions,
649 // should we return false?
650 pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool {
651     if path_is_dir(p) {
652         return true;
653     }
654     else if p.components.is_empty() {
655         return false;
656     }
657     else if p.components.len() == 1 {
658         // No parent directories to create
659         path_is_dir(p) || make_dir(p, mode)
660     }
661     else {
662         mkdir_recursive(&p.pop(), mode) && make_dir(p, mode)
663     }
664 }
665
666 /// Lists the contents of a directory
667 #[allow(non_implicitly_copyable_typarams)]
668 pub fn list_dir(p: &Path) -> ~[~str] {
669     unsafe {
670         #[cfg(target_os = "linux")]
671         #[cfg(target_os = "android")]
672         #[cfg(target_os = "freebsd")]
673         #[cfg(target_os = "macos")]
674         unsafe fn get_list(p: &Path) -> ~[~str] {
675             use libc::{dirent_t};
676             use libc::{opendir, readdir, closedir};
677             extern mod rustrt {
678                 unsafe fn rust_list_dir_val(ptr: *dirent_t)
679                     -> *libc::c_char;
680             }
681             let input = p.to_str();
682             let mut strings = ~[];
683             let input_ptr = ::cast::transmute(&input[0]);
684             debug!("os::list_dir -- BEFORE OPENDIR");
685             let dir_ptr = opendir(input_ptr);
686             if (dir_ptr as uint != 0) {
687         debug!("os::list_dir -- opendir() SUCCESS");
688                 let mut entry_ptr = readdir(dir_ptr);
689                 while (entry_ptr as uint != 0) {
690                     strings.push(
691                         str::raw::from_c_str(
692                             rustrt::rust_list_dir_val(
693                                 entry_ptr)));
694                     entry_ptr = readdir(dir_ptr);
695                 }
696                 closedir(dir_ptr);
697             }
698             else {
699         debug!("os::list_dir -- opendir() FAILURE");
700             }
701             debug!(
702                 "os::list_dir -- AFTER -- #: %?",
703                      strings.len());
704             strings
705         }
706         #[cfg(windows)]
707         unsafe fn get_list(p: &Path) -> ~[~str] {
708             use libc::types::os::arch::extra::{LPCTSTR, HANDLE, BOOL};
709             use libc::consts::os::extra::INVALID_HANDLE_VALUE;
710             use libc::wcslen;
711             use libc::funcs::extra::kernel32::{
712                 FindFirstFileW,
713                 FindNextFileW,
714                 FindClose,
715             };
716             use os::win32::{
717                 as_utf16_p
718             };
719             use unstable::exchange_alloc::{malloc_raw, free_raw};
720             #[nolink]
721             extern mod rustrt {
722                 unsafe fn rust_list_dir_wfd_size() -> libc::size_t;
723                 unsafe fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void)
724                     -> *u16;
725             }
726             fn star(p: &Path) -> Path { p.push("*") }
727             do as_utf16_p(star(p).to_str()) |path_ptr| {
728                 let mut strings = ~[];
729                 let wfd_ptr = malloc_raw(
730                     rustrt::rust_list_dir_wfd_size() as uint);
731                 let find_handle =
732                     FindFirstFileW(
733                         path_ptr,
734                         ::cast::transmute(wfd_ptr));
735                 if find_handle as int != INVALID_HANDLE_VALUE {
736                     let mut more_files = 1 as libc::c_int;
737                     while more_files != 0 {
738                         let fp_buf = rustrt::rust_list_dir_wfd_fp_buf(
739                             wfd_ptr);
740                         if fp_buf as uint == 0 {
741                             fail!(~"os::list_dir() failure:"+
742                                   ~" got null ptr from wfd");
743                         }
744                         else {
745                             let fp_vec = vec::from_buf(
746                                 fp_buf, wcslen(fp_buf) as uint);
747                             let fp_str = str::from_utf16(fp_vec);
748                             strings.push(fp_str);
749                         }
750                         more_files = FindNextFileW(
751                             find_handle,
752                             ::cast::transmute(wfd_ptr));
753                     }
754                     FindClose(find_handle);
755                     free_raw(wfd_ptr);
756                 }
757                 strings
758             }
759         }
760         do get_list(p).filtered |filename| {
761             *filename != ~"." && *filename != ~".."
762         }
763     }
764 }
765
766 /**
767  * Lists the contents of a directory
768  *
769  * This version prepends each entry with the directory.
770  */
771 pub fn list_dir_path(p: &Path) -> ~[~Path] {
772     list_dir(p).map(|f| ~p.push(*f))
773 }
774
775 /// Removes a directory at the specified path
776 pub fn remove_dir(p: &Path) -> bool {
777    return rmdir(p);
778
779     #[cfg(windows)]
780     fn rmdir(p: &Path) -> bool {
781         unsafe {
782             use os::win32::as_utf16_p;
783             return do as_utf16_p(p.to_str()) |buf| {
784                 libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
785             };
786         }
787     }
788
789     #[cfg(unix)]
790     fn rmdir(p: &Path) -> bool {
791         unsafe {
792             return do as_c_charp(p.to_str()) |buf| {
793                 libc::rmdir(buf) == (0 as c_int)
794             };
795         }
796     }
797 }
798
799 pub fn change_dir(p: &Path) -> bool {
800     return chdir(p);
801
802     #[cfg(windows)]
803     fn chdir(p: &Path) -> bool {
804         unsafe {
805             use os::win32::as_utf16_p;
806             return do as_utf16_p(p.to_str()) |buf| {
807                 libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
808             };
809         }
810     }
811
812     #[cfg(unix)]
813     fn chdir(p: &Path) -> bool {
814         unsafe {
815             return do as_c_charp(p.to_str()) |buf| {
816                 libc::chdir(buf) == (0 as c_int)
817             };
818         }
819     }
820 }
821
822 /// Changes the current working directory to the specified
823 /// path while acquiring a global lock, then calls `action`.
824 /// If the change is successful, releases the lock and restores the
825 /// CWD to what it was before, returning true.
826 /// Returns false if the directory doesn't exist or if the directory change
827 /// is otherwise unsuccessful.
828 pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
829     use unstable::global::global_data_clone_create;
830     use unstable::{Exclusive, exclusive};
831
832     fn key(_: Exclusive<()>) { }
833
834     let result = unsafe {
835         global_data_clone_create(key, || {
836             ~exclusive(())
837         })
838     };
839
840     do result.with_imm() |_| {
841         let old_dir = os::getcwd();
842         if change_dir(p) {
843             action();
844             change_dir(&old_dir)
845         }
846         else {
847             false
848         }
849     }
850 }
851
852 /// Copies a file from one location to another
853 pub fn copy_file(from: &Path, to: &Path) -> bool {
854     return do_copy_file(from, to);
855
856     #[cfg(windows)]
857     fn do_copy_file(from: &Path, to: &Path) -> bool {
858         unsafe {
859             use os::win32::as_utf16_p;
860             return do as_utf16_p(from.to_str()) |fromp| {
861                 do as_utf16_p(to.to_str()) |top| {
862                     libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
863                         (0 as libc::BOOL)
864                 }
865             }
866         }
867     }
868
869     #[cfg(unix)]
870     fn do_copy_file(from: &Path, to: &Path) -> bool {
871         unsafe {
872             let istream = do as_c_charp(from.to_str()) |fromp| {
873                 do as_c_charp("rb") |modebuf| {
874                     libc::fopen(fromp, modebuf)
875                 }
876             };
877             if istream as uint == 0u {
878                 return false;
879             }
880             let ostream = do as_c_charp(to.to_str()) |top| {
881                 do as_c_charp("w+b") |modebuf| {
882                     libc::fopen(top, modebuf)
883                 }
884             };
885             if ostream as uint == 0u {
886                 fclose(istream);
887                 return false;
888             }
889             let bufsize = 8192u;
890             let mut buf = vec::with_capacity::<u8>(bufsize);
891             let mut done = false;
892             let mut ok = true;
893             while !done {
894                 do vec::as_mut_buf(buf) |b, _sz| {
895                   let nread = libc::fread(b as *mut c_void, 1u as size_t,
896                                           bufsize as size_t,
897                                           istream);
898                   if nread > 0 as size_t {
899                       if libc::fwrite(b as *c_void, 1u as size_t, nread,
900                                       ostream) != nread {
901                           ok = false;
902                           done = true;
903                       }
904                   } else {
905                       done = true;
906                   }
907               }
908             }
909             fclose(istream);
910             fclose(ostream);
911             return ok;
912         }
913     }
914 }
915
916 /// Deletes an existing file
917 pub fn remove_file(p: &Path) -> bool {
918     return unlink(p);
919
920     #[cfg(windows)]
921     fn unlink(p: &Path) -> bool {
922         unsafe {
923             use os::win32::as_utf16_p;
924             return do as_utf16_p(p.to_str()) |buf| {
925                 libc::DeleteFileW(buf) != (0 as libc::BOOL)
926             };
927         }
928     }
929
930     #[cfg(unix)]
931     fn unlink(p: &Path) -> bool {
932         unsafe {
933             return do as_c_charp(p.to_str()) |buf| {
934                 libc::unlink(buf) == (0 as c_int)
935             };
936         }
937     }
938 }
939
940 #[cfg(unix)]
941 pub fn errno() -> int {
942     #[cfg(target_os = "macos")]
943     #[cfg(target_os = "freebsd")]
944     fn errno_location() -> *c_int {
945         #[nolink]
946         extern {
947             unsafe fn __error() -> *c_int;
948         }
949         unsafe {
950             __error()
951         }
952     }
953
954     #[cfg(target_os = "linux")]
955     #[cfg(target_os = "android")]
956     fn errno_location() -> *c_int {
957         #[nolink]
958         extern {
959             unsafe fn __errno_location() -> *c_int;
960         }
961         unsafe {
962             __errno_location()
963         }
964     }
965
966     unsafe {
967         (*errno_location()) as int
968     }
969 }
970
971 #[cfg(windows)]
972 pub fn errno() -> uint {
973     use libc::types::os::arch::extra::DWORD;
974
975     #[link_name = "kernel32"]
976     #[abi = "stdcall"]
977     extern "stdcall" {
978         unsafe fn GetLastError() -> DWORD;
979     }
980
981     unsafe {
982         GetLastError() as uint
983     }
984 }
985
986 /// Get a string representing the platform-dependent last error
987 pub fn last_os_error() -> ~str {
988     #[cfg(unix)]
989     fn strerror() -> ~str {
990         #[cfg(target_os = "macos")]
991         #[cfg(target_os = "android")]
992         #[cfg(target_os = "freebsd")]
993         fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int {
994             #[nolink]
995             extern {
996                 unsafe fn strerror_r(errnum: c_int, buf: *c_char,
997                                      buflen: size_t) -> c_int;
998             }
999             unsafe {
1000                 strerror_r(errnum, buf, buflen)
1001             }
1002         }
1003
1004         // GNU libc provides a non-compliant version of strerror_r by default
1005         // and requires macros to instead use the POSIX compliant variant.
1006         // So we just use __xpg_strerror_r which is always POSIX compliant
1007         #[cfg(target_os = "linux")]
1008         fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int {
1009             #[nolink]
1010             extern {
1011                 unsafe fn __xpg_strerror_r(errnum: c_int, buf: *c_char,
1012                                            buflen: size_t) -> c_int;
1013             }
1014             unsafe {
1015                 __xpg_strerror_r(errnum, buf, buflen)
1016             }
1017         }
1018
1019         let mut buf = [0 as c_char, ..TMPBUF_SZ];
1020         unsafe {
1021             let err = strerror_r(errno() as c_int, &buf[0],
1022                                  TMPBUF_SZ as size_t);
1023             if err < 0 {
1024                 fail!(~"strerror_r failure");
1025             }
1026
1027             str::raw::from_c_str(&buf[0])
1028         }
1029     }
1030
1031     #[cfg(windows)]
1032     fn strerror() -> ~str {
1033         use libc::types::os::arch::extra::DWORD;
1034         use libc::types::os::arch::extra::LPSTR;
1035         use libc::types::os::arch::extra::LPVOID;
1036
1037         #[link_name = "kernel32"]
1038         #[abi = "stdcall"]
1039         extern "stdcall" {
1040             unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
1041                                      msgId: DWORD, langId: DWORD,
1042                                      buf: LPSTR, nsize: DWORD,
1043                                      args: *c_void) -> DWORD;
1044         }
1045
1046         static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
1047         static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
1048
1049         let mut buf = [0 as c_char, ..TMPBUF_SZ];
1050
1051         // This value is calculated from the macro
1052         // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
1053         let langId = 0x0800 as DWORD;
1054         let err = errno() as DWORD;
1055         unsafe {
1056             let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
1057                                      FORMAT_MESSAGE_IGNORE_INSERTS,
1058                                      ptr::mut_null(), err, langId,
1059                                      &mut buf[0], TMPBUF_SZ as DWORD,
1060                                      ptr::null());
1061             if res == 0 {
1062                 fail!(fmt!("[%?] FormatMessage failure", errno()));
1063             }
1064
1065             str::raw::from_c_str(&buf[0])
1066         }
1067     }
1068
1069     strerror()
1070 }
1071
1072 /**
1073  * Sets the process exit code
1074  *
1075  * Sets the exit code returned by the process if all supervised tasks
1076  * terminate successfully (without failing). If the current root task fails
1077  * and is supervised by the scheduler then any user-specified exit status is
1078  * ignored and the process exits with the default failure status
1079  */
1080 pub fn set_exit_status(code: int) {
1081     unsafe {
1082         rustrt::rust_set_exit_status(code as libc::intptr_t);
1083     }
1084 }
1085
1086 unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
1087     let mut args = ~[];
1088     for uint::range(0, argc as uint) |i| {
1089         vec::push(&mut args, str::raw::from_c_str(*argv.offset(i)));
1090     }
1091     args
1092 }
1093
1094 /**
1095  * Returns the command line arguments
1096  *
1097  * Returns a list of the command line arguments.
1098  */
1099 #[cfg(target_os = "macos")]
1100 pub fn real_args() -> ~[~str] {
1101     unsafe {
1102         let (argc, argv) = (*_NSGetArgc() as c_int,
1103                             *_NSGetArgv() as **c_char);
1104         load_argc_and_argv(argc, argv)
1105     }
1106 }
1107
1108 #[cfg(target_os = "linux")]
1109 #[cfg(target_os = "android")]
1110 #[cfg(target_os = "freebsd")]
1111 pub fn real_args() -> ~[~str] {
1112     unsafe {
1113         let argc = rustrt::rust_get_argc();
1114         let argv = rustrt::rust_get_argv();
1115         load_argc_and_argv(argc, argv)
1116     }
1117 }
1118
1119 #[cfg(windows)]
1120 pub fn real_args() -> ~[~str] {
1121     let mut nArgs: c_int = 0;
1122     let lpArgCount = ptr::to_mut_unsafe_ptr(&mut nArgs);
1123     let lpCmdLine = unsafe { GetCommandLineW() };
1124     let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1125
1126     let mut args = ~[];
1127     for uint::range(0, nArgs as uint) |i| {
1128         unsafe {
1129             // Determine the length of this argument.
1130             let ptr = *szArgList.offset(i);
1131             let mut len = 0;
1132             while *ptr.offset(len) != 0 { len += 1; }
1133
1134             // Push it onto the list.
1135             vec::push(&mut args,
1136                       vec::raw::buf_as_slice(ptr, len,
1137                                              str::from_utf16));
1138         }
1139     }
1140
1141     unsafe {
1142         LocalFree(cast::transmute(szArgList));
1143     }
1144
1145     return args;
1146 }
1147
1148 type LPCWSTR = *u16;
1149
1150 #[cfg(windows)]
1151 #[link_name="kernel32"]
1152 #[abi="stdcall"]
1153 extern "stdcall" {
1154     fn GetCommandLineW() -> LPCWSTR;
1155     fn LocalFree(ptr: *c_void);
1156 }
1157
1158 #[cfg(windows)]
1159 #[link_name="shell32"]
1160 #[abi="stdcall"]
1161 extern "stdcall" {
1162     fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
1163 }
1164
1165 struct OverriddenArgs {
1166     val: ~[~str]
1167 }
1168
1169 fn overridden_arg_key(_v: @OverriddenArgs) {}
1170
1171 pub fn args() -> ~[~str] {
1172     unsafe {
1173         match task::local_data::local_data_get(overridden_arg_key) {
1174             None => real_args(),
1175             Some(args) => copy args.val
1176         }
1177     }
1178 }
1179
1180 pub fn set_args(new_args: ~[~str]) {
1181     unsafe {
1182         let overridden_args = @OverriddenArgs { val: copy new_args };
1183         task::local_data::local_data_set(overridden_arg_key, overridden_args);
1184     }
1185 }
1186
1187 // FIXME #6100 we should really use an internal implementation of this - using
1188 // the POSIX glob functions isn't portable to windows, probably has slight
1189 // inconsistencies even where it is implemented, and makes extending
1190 // functionality a lot more difficult
1191 // FIXME #6101 also provide a non-allocating version - each_glob or so?
1192 /// Returns a vector of Path objects that match the given glob pattern
1193 #[cfg(target_os = "linux")]
1194 #[cfg(target_os = "android")]
1195 #[cfg(target_os = "freebsd")]
1196 #[cfg(target_os = "macos")]
1197 pub fn glob(pattern: &str) -> ~[Path] {
1198     #[cfg(target_os = "linux")]
1199     #[cfg(target_os = "android")]
1200     fn default_glob_t () -> libc::glob_t {
1201         libc::glob_t {
1202             gl_pathc: 0,
1203             gl_pathv: ptr::null(),
1204             gl_offs: 0,
1205             __unused1: ptr::null(),
1206             __unused2: ptr::null(),
1207             __unused3: ptr::null(),
1208             __unused4: ptr::null(),
1209             __unused5: ptr::null(),
1210         }
1211     }
1212
1213     #[cfg(target_os = "freebsd")]
1214     fn default_glob_t () -> libc::glob_t {
1215         libc::glob_t {
1216             gl_pathc: 0,
1217             __unused1: 0,
1218             gl_offs: 0,
1219             __unused2: 0,
1220             gl_pathv: ptr::null(),
1221             __unused3: ptr::null(),
1222             __unused4: ptr::null(),
1223             __unused5: ptr::null(),
1224             __unused6: ptr::null(),
1225             __unused7: ptr::null(),
1226             __unused8: ptr::null(),
1227         }
1228     }
1229
1230     #[cfg(target_os = "macos")]
1231     fn default_glob_t () -> libc::glob_t {
1232         libc::glob_t {
1233             gl_pathc: 0,
1234             __unused1: 0,
1235             gl_offs: 0,
1236             __unused2: 0,
1237             gl_pathv: ptr::null(),
1238             __unused3: ptr::null(),
1239             __unused4: ptr::null(),
1240             __unused5: ptr::null(),
1241             __unused6: ptr::null(),
1242             __unused7: ptr::null(),
1243             __unused8: ptr::null(),
1244         }
1245     }
1246
1247     let mut g = default_glob_t();
1248     do str::as_c_str(pattern) |c_pattern| {
1249         unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) }
1250     };
1251     do(|| {
1252         let paths = unsafe {
1253             vec::raw::from_buf_raw(g.gl_pathv, g.gl_pathc as uint)
1254         };
1255         do paths.map |&c_str| {
1256             Path(unsafe { str::raw::from_c_str(c_str) })
1257         }
1258     }).finally {
1259         unsafe { libc::globfree(&mut g) };
1260     }
1261 }
1262
1263 /// Returns a vector of Path objects that match the given glob pattern
1264 #[cfg(target_os = "win32")]
1265 pub fn glob(pattern: &str) -> ~[Path] {
1266     fail!(~"glob() is unimplemented on Windows")
1267 }
1268
1269 #[cfg(target_os = "macos")]
1270 extern {
1271     // These functions are in crt_externs.h.
1272     pub fn _NSGetArgc() -> *c_int;
1273     pub fn _NSGetArgv() -> ***c_char;
1274 }
1275
1276 pub mod consts {
1277
1278     #[cfg(unix)]
1279     pub use os::consts::unix::*;
1280
1281     #[cfg(windows)]
1282     pub use os::consts::windows::*;
1283
1284     #[cfg(target_os = "macos")]
1285     pub use os::consts::macos::*;
1286
1287     #[cfg(target_os = "freebsd")]
1288     pub use os::consts::freebsd::*;
1289
1290     #[cfg(target_os = "linux")]
1291     pub use os::consts::linux::*;
1292
1293     #[cfg(target_os = "android")]
1294     pub use os::consts::android::*;
1295
1296     #[cfg(target_os = "win32")]
1297     pub use os::consts::win32::*;
1298
1299     #[cfg(target_arch = "x86")]
1300     pub use os::consts::x86::*;
1301
1302     #[cfg(target_arch = "x86_64")]
1303     pub use os::consts::x86_64::*;
1304
1305     #[cfg(target_arch = "arm")]
1306     pub use os::consts::arm::*;
1307
1308     #[cfg(target_arch = "mips")]
1309     use os::consts::mips::*;
1310
1311     pub mod unix {
1312         pub static FAMILY: &'static str = "unix";
1313     }
1314
1315     pub mod windows {
1316         pub static FAMILY: &'static str = "windows";
1317     }
1318
1319     pub mod macos {
1320         pub static SYSNAME: &'static str = "macos";
1321         pub static DLL_PREFIX: &'static str = "lib";
1322         pub static DLL_SUFFIX: &'static str = ".dylib";
1323         pub static EXE_SUFFIX: &'static str = "";
1324     }
1325
1326     pub mod freebsd {
1327         pub static SYSNAME: &'static str = "freebsd";
1328         pub static DLL_PREFIX: &'static str = "lib";
1329         pub static DLL_SUFFIX: &'static str = ".so";
1330         pub static EXE_SUFFIX: &'static str = "";
1331     }
1332
1333     pub mod linux {
1334         pub static SYSNAME: &'static str = "linux";
1335         pub static DLL_PREFIX: &'static str = "lib";
1336         pub static DLL_SUFFIX: &'static str = ".so";
1337         pub static EXE_SUFFIX: &'static str = "";
1338     }
1339
1340     pub mod android {
1341         pub static SYSNAME: &'static str = "android";
1342         pub static DLL_PREFIX: &'static str = "lib";
1343         pub static DLL_SUFFIX: &'static str = ".so";
1344         pub static EXE_SUFFIX: &'static str = "";
1345     }
1346
1347     pub mod win32 {
1348         pub static SYSNAME: &'static str = "win32";
1349         pub static DLL_PREFIX: &'static str = "";
1350         pub static DLL_SUFFIX: &'static str = ".dll";
1351         pub static EXE_SUFFIX: &'static str = ".exe";
1352     }
1353
1354
1355     pub mod x86 {
1356         pub static ARCH: &'static str = "x86";
1357     }
1358     pub mod x86_64 {
1359         pub static ARCH: &'static str = "x86_64";
1360     }
1361     pub mod arm {
1362         pub static ARCH: &'static str = "arm";
1363     }
1364     pub mod mips {
1365         pub static ARCH: &'static str = "mips";
1366     }
1367 }
1368
1369 #[cfg(test)]
1370 #[allow(non_implicitly_copyable_typarams)]
1371 mod tests {
1372     use libc::{c_int, c_void, size_t};
1373     use libc;
1374     use option::Some;
1375     use option;
1376     use os::{as_c_charp, env, getcwd, getenv, make_absolute, real_args};
1377     use os::{remove_file, setenv};
1378     use os;
1379     use path::Path;
1380     use rand::RngUtil;
1381     use rand;
1382     use run;
1383     use str;
1384     use vec;
1385     use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
1386
1387
1388     #[test]
1389     pub fn last_os_error() {
1390         debug!(os::last_os_error());
1391     }
1392
1393     #[test]
1394     pub fn test_args() {
1395         let a = real_args();
1396         assert!(a.len() >= 1);
1397     }
1398
1399     fn make_rand_name() -> ~str {
1400         let rng = rand::rng();
1401         let n = ~"TEST" + rng.gen_str(10u);
1402         assert!(getenv(n).is_none());
1403         n
1404     }
1405
1406     #[test]
1407     fn test_setenv() {
1408         let n = make_rand_name();
1409         setenv(n, ~"VALUE");
1410         assert!(getenv(n) == option::Some(~"VALUE"));
1411     }
1412
1413     #[test]
1414     #[ignore(cfg(windows))]
1415     #[ignore]
1416     fn test_setenv_overwrite() {
1417         let n = make_rand_name();
1418         setenv(n, ~"1");
1419         setenv(n, ~"2");
1420         assert!(getenv(n) == option::Some(~"2"));
1421         setenv(n, ~"");
1422         assert!(getenv(n) == option::Some(~""));
1423     }
1424
1425     // Windows GetEnvironmentVariable requires some extra work to make sure
1426     // the buffer the variable is copied into is the right size
1427     #[test]
1428     #[ignore(cfg(windows))]
1429     #[ignore]
1430     fn test_getenv_big() {
1431         let mut s = ~"";
1432         let mut i = 0;
1433         while i < 100 { s += ~"aaaaaaaaaa"; i += 1; }
1434         let n = make_rand_name();
1435         setenv(n, s);
1436         debug!(copy s);
1437         assert!(getenv(n) == option::Some(s));
1438     }
1439
1440     #[test]
1441     fn test_self_exe_path() {
1442         let path = os::self_exe_path();
1443         assert!(path.is_some());
1444         let path = path.get();
1445         debug!(copy path);
1446
1447         // Hard to test this function
1448         assert!(path.is_absolute);
1449     }
1450
1451     #[test]
1452     #[ignore]
1453     fn test_env_getenv() {
1454         let e = env();
1455         assert!(vec::len(e) > 0u);
1456         for vec::each(e) |p| {
1457             let (n, v) = copy *p;
1458             debug!(copy n);
1459             let v2 = getenv(n);
1460             // MingW seems to set some funky environment variables like
1461             // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
1462             // from env() but not visible from getenv().
1463             assert!(v2.is_none() || v2 == option::Some(v));
1464         }
1465     }
1466
1467     #[test]
1468     fn test_env_setenv() {
1469         let n = make_rand_name();
1470
1471         let mut e = env();
1472         setenv(n, ~"VALUE");
1473         assert!(!vec::contains(e, &(copy n, ~"VALUE")));
1474
1475         e = env();
1476         assert!(vec::contains(e, &(n, ~"VALUE")));
1477     }
1478
1479     #[test]
1480     fn test() {
1481         assert!((!Path("test-path").is_absolute));
1482
1483         debug!(~"Current working directory: " + getcwd().to_str());
1484
1485         debug!(make_absolute(&Path("test-path")));
1486         debug!(make_absolute(&Path("/usr/bin")));
1487     }
1488
1489     #[test]
1490     #[cfg(unix)]
1491     fn homedir() {
1492         let oldhome = getenv(~"HOME");
1493
1494         setenv(~"HOME", ~"/home/MountainView");
1495         assert!(os::homedir() == Some(Path("/home/MountainView")));
1496
1497         setenv(~"HOME", ~"");
1498         assert!(os::homedir().is_none());
1499
1500         for oldhome.each |s| { setenv(~"HOME", *s) }
1501     }
1502
1503     #[test]
1504     #[cfg(windows)]
1505     fn homedir() {
1506
1507         let oldhome = getenv(~"HOME");
1508         let olduserprofile = getenv(~"USERPROFILE");
1509
1510         setenv(~"HOME", ~"");
1511         setenv(~"USERPROFILE", ~"");
1512
1513         assert!(os::homedir().is_none());
1514
1515         setenv(~"HOME", ~"/home/MountainView");
1516         assert!(os::homedir() == Some(Path("/home/MountainView")));
1517
1518         setenv(~"HOME", ~"");
1519
1520         setenv(~"USERPROFILE", ~"/home/MountainView");
1521         assert!(os::homedir() == Some(Path("/home/MountainView")));
1522
1523         setenv(~"HOME", ~"/home/MountainView");
1524         setenv(~"USERPROFILE", ~"/home/PaloAlto");
1525         assert!(os::homedir() == Some(Path("/home/MountainView")));
1526
1527         oldhome.each(|s| {setenv(~"HOME", *s);true});
1528         olduserprofile.each(|s| {setenv(~"USERPROFILE", *s);true});
1529     }
1530
1531     #[test]
1532     fn tmpdir() {
1533         assert!(!str::is_empty(os::tmpdir().to_str()));
1534     }
1535
1536     // Issue #712
1537     #[test]
1538     fn test_list_dir_no_invalid_memory_access() {
1539         os::list_dir(&Path("."));
1540     }
1541
1542     #[test]
1543     fn list_dir() {
1544         let dirs = os::list_dir(&Path("."));
1545         // Just assuming that we've got some contents in the current directory
1546         assert!((vec::len(dirs) > 0u));
1547
1548         for vec::each(dirs) |dir| {
1549             debug!(copy *dir);
1550         }
1551     }
1552
1553     #[test]
1554     fn path_is_dir() {
1555         assert!((os::path_is_dir(&Path("."))));
1556         assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
1557     }
1558
1559     #[test]
1560     fn path_exists() {
1561         assert!((os::path_exists(&Path("."))));
1562         assert!((!os::path_exists(&Path(
1563                      "test/nonexistent-bogus-path"))));
1564     }
1565
1566     #[test]
1567     fn copy_file_does_not_exist() {
1568       assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
1569                             &Path("test/other-bogus-path")));
1570       assert!(!os::path_exists(&Path("test/other-bogus-path")));
1571     }
1572
1573     #[test]
1574     fn copy_file_ok() {
1575         unsafe {
1576           let tempdir = getcwd(); // would like to use $TMPDIR,
1577                                   // doesn't seem to work on Linux
1578           assert!((str::len(tempdir.to_str()) > 0u));
1579           let in = tempdir.push("in.txt");
1580           let out = tempdir.push("out.txt");
1581
1582           /* Write the temp input file */
1583             let ostream = do as_c_charp(in.to_str()) |fromp| {
1584                 do as_c_charp("w+b") |modebuf| {
1585                     libc::fopen(fromp, modebuf)
1586                 }
1587           };
1588           assert!((ostream as uint != 0u));
1589           let s = ~"hello";
1590           let mut buf = str::to_bytes(s) + ~[0 as u8];
1591           do vec::as_mut_buf(buf) |b, _len| {
1592               assert!((libc::fwrite(b as *c_void, 1u as size_t,
1593                                    (str::len(s) + 1u) as size_t, ostream)
1594                       == buf.len() as size_t))
1595           }
1596           assert!((libc::fclose(ostream) == (0u as c_int)));
1597           let rs = os::copy_file(&in, &out);
1598           if (!os::path_exists(&in)) {
1599             fail!(fmt!("%s doesn't exist", in.to_str()));
1600           }
1601           assert!((rs));
1602           let rslt = run::run_program(~"diff", ~[in.to_str(), out.to_str()]);
1603           assert!((rslt == 0));
1604           assert!((remove_file(&in)));
1605           assert!((remove_file(&out)));
1606         }
1607     }
1608
1609     #[test]
1610     fn recursive_mkdir_slash() {
1611         let path = Path("/");
1612         assert!(os::mkdir_recursive(&path,  (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1613     }
1614
1615     #[test]
1616     fn recursive_mkdir_empty() {
1617         let path = Path("");
1618         assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1619     }
1620
1621     // More recursive_mkdir tests are in std::tempfile
1622 }