os.rs 44.3 KB
Newer Older
1
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*!
 * Higher-level interfaces to libc::* functions and operating system services.
 *
 * In general these take and return rust types, use rust idioms (enums,
 * closures, vectors) rather than C idioms, and do more extensive safety
 * checks.
 *
 * This module is not meant to only contain 1:1 mappings to libc entries; any
 * os-interface code that is reasonably useful and broadly applicable can go
 * here. Including utility routines that merely build on other os code.
 *
 * We assume the general case is that users do not care, and do not want to
 * be made to care, which operating system they are on. While they may want
 * to special case various special cases -- and so we will not _hide_ the
 * facts of which OS the user is on -- they should be given the opportunity
 * to write OS-ignorant code by default.
 */
28

29 30
#[allow(missing_doc)];

31 32
#[cfg(unix)]
use c_str::CString;
33
use clone::Clone;
34
use container::Container;
35
use iter::range;
36
use libc;
A
Alex Crichton 已提交
37
use libc::{c_char, c_void, c_int, size_t};
P
Patrick Walton 已提交
38
use option::{Some, None};
39
use os;
40
use prelude::*;
41 42
use ptr;
use str;
43
use to_str;
44
use unstable::finally::Finally;
45

46
pub use os::consts::*;
47

48
/// Delegates to the libc close() function, returning the same return value.
49 50 51 52 53 54
pub fn close(fd: c_int) -> c_int {
    unsafe {
        libc::close(fd)
    }
}

55 56
pub static TMPBUF_SZ : uint = 1000u;
static BUF_BYTES : uint = 2048u;
57

58
#[cfg(unix)]
59
pub fn getcwd() -> Path {
60
    let mut buf = [0 as libc::c_char, ..BUF_BYTES];
61
    buf.as_mut_buf(|buf, len| {
62 63
        unsafe {
            if libc::getcwd(buf, len as size_t).is_null() {
64
                fail!()
65 66
            }

67
            Path::new(CString::new(buf as *c_char, false))
68
        }
69
    })
70 71
}

72 73 74 75 76
#[cfg(windows)]
pub fn getcwd() -> Path {
    use libc::DWORD;
    use libc::GetCurrentDirectoryW;
    let mut buf = [0 as u16, ..BUF_BYTES];
77
    buf.as_mut_buf(|buf, len| {
78 79
        unsafe {
            if libc::GetCurrentDirectoryW(len as DWORD, buf) == 0 as DWORD {
80
                fail!();
81 82
            }
        }
83
    });
84
    Path::new(str::from_utf16(buf))
85 86
}

87
#[cfg(windows)]
88
pub mod win32 {
89 90 91
    use libc;
    use vec;
    use str;
92
    use option::{None, Option};
93
    use option;
94
    use os::TMPBUF_SZ;
95
    use libc::types::os::arch::extra::DWORD;
96

97
    pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD)
B
Brian Anderson 已提交
98
        -> Option<~str> {
99

100
        unsafe {
101
            let mut n = TMPBUF_SZ as DWORD;
102 103 104
            let mut res = None;
            let mut done = false;
            while !done {
105
                let mut k: DWORD = 0;
106
                let mut buf = vec::from_elem(n as uint, 0u16);
107
                buf.as_mut_buf(|b, _sz| {
108
                    k = f(b, TMPBUF_SZ as DWORD);
109 110 111 112 113 114 115 116 117
                    if k == (0 as DWORD) {
                        done = true;
                    } else if (k == n &&
                               libc::GetLastError() ==
                               libc::ERROR_INSUFFICIENT_BUFFER as DWORD) {
                        n *= (2 as DWORD);
                    } else {
                        done = true;
                    }
118
                });
119
                if k != 0 && done {
120
                    let sub = buf.slice(0, k as uint);
121 122
                    res = option::Some(str::from_utf16(sub));
                }
123
            }
124
            return res;
125 126 127
        }
    }

128
    pub fn as_utf16_p<T>(s: &str, f: |*u16| -> T) -> T {
129
        let mut t = s.to_utf16();
130
        // Null terminate before passing on.
131
        t.push(0u16);
132
        t.as_imm_buf(|buf, _len| f(buf))
133
    }
134 135
}

136 137
/*
Accessing environment variables is not generally threadsafe.
138
Serialize access through a global lock.
139
*/
140
fn with_env_lock<T>(f: || -> T) -> T {
141
    use unstable::mutex::{Mutex, MUTEX_INIT};
142
    use unstable::finally::Finally;
143

144 145
    static mut lock: Mutex = MUTEX_INIT;

146
    unsafe {
147
        return (|| {
148
            lock.lock();
149
            f()
150
        }).finally(|| lock.unlock());
151
    }
B
Ben Blum 已提交
152 153
}

154 155
/// Returns a vector of (variable, value) pairs for all the environment
/// variables of the current process.
156 157
pub fn env() -> ~[(~str,~str)] {
    unsafe {
158 159
        #[cfg(windows)]
        unsafe fn get_env_pairs() -> ~[~str] {
A
Alex Crichton 已提交
160 161
            use c_str;
            use str::StrSlice;
162

163 164 165 166 167 168
            use libc::funcs::extra::kernel32::{
                GetEnvironmentStringsA,
                FreeEnvironmentStringsA
            };
            let ch = GetEnvironmentStringsA();
            if (ch as uint == 0) {
169
                fail!("os::env() failure getting env string from OS: {}",
A
Alex Crichton 已提交
170
                       os::last_os_error());
171
            }
A
Alex Crichton 已提交
172
            let mut result = ~[];
173
            c_str::from_c_multistring(ch as *libc::c_char, None, |cstr| {
A
Alex Crichton 已提交
174
                result.push(cstr.as_str().unwrap().to_owned());
175
            });
176 177 178 179 180
            FreeEnvironmentStringsA(ch);
            result
        }
        #[cfg(unix)]
        unsafe fn get_env_pairs() -> ~[~str] {
181
            extern {
182
                fn rust_env_pairs() -> **libc::c_char;
183
            }
184
            let environ = rust_env_pairs();
185
            if (environ as uint == 0) {
186
                fail!("os::env() failure getting env string from OS: {}",
A
Alex Crichton 已提交
187
                       os::last_os_error());
188 189 190 191
            }
            let mut result = ~[];
            ptr::array_each(environ, |e| {
                let env_pair = str::raw::from_c_str(e);
192
                debug!("get_env_pairs: {}", env_pair);
193 194 195 196 197 198
                result.push(env_pair);
            });
            result
        }

        fn env_convert(input: ~[~str]) -> ~[(~str, ~str)] {
199
            let mut pairs = ~[];
D
Daniel Micay 已提交
200
            for p in input.iter() {
201
                let vs: ~[&str] = p.splitn('=', 1).collect();
202
                debug!("splitting: len: {}", vs.len());
203
                assert_eq!(vs.len(), 2);
204
                pairs.push((vs[0].to_owned(), vs[1].to_owned()));
205
            }
L
Luqman Aden 已提交
206
            pairs
207
        }
208
        with_env_lock(|| {
209 210
            let unparsed_environ = get_env_pairs();
            env_convert(unparsed_environ)
211
        })
212
    }
213
}
214

215
#[cfg(unix)]
216 217
/// Fetches the environment variable `n` from the current process, returning
/// None if the variable isn't set.
218 219
pub fn getenv(n: &str) -> Option<~str> {
    unsafe {
220 221
        with_env_lock(|| {
            let s = n.with_c_str(|buf| libc::getenv(buf));
222
            if s.is_null() {
223
                None
224
            } else {
225
                Some(str::raw::from_c_str(s))
226
            }
227
        })
228 229
    }
}
230

231
#[cfg(windows)]
232 233
/// Fetches the environment variable `n` from the current process, returning
/// None if the variable isn't set.
234 235
pub fn getenv(n: &str) -> Option<~str> {
    unsafe {
236
        with_env_lock(|| {
237
            use os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
238 239
            as_utf16_p(n, |u| {
                fill_utf16_buf_and_decode(|buf, sz| {
240
                    libc::GetEnvironmentVariableW(u, buf, sz)
241 242 243
                })
            })
        })
244 245
    }
}
246 247


248
#[cfg(unix)]
249 250
/// Sets the environment variable `n` to the value `v` for the currently running
/// process
251 252
pub fn setenv(n: &str, v: &str) {
    unsafe {
253 254 255
        with_env_lock(|| {
            n.with_c_str(|nbuf| {
                v.with_c_str(|vbuf| {
256
                    libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
257 258 259
                })
            })
        })
260 261
    }
}
262 263


264
#[cfg(windows)]
265 266
/// Sets the environment variable `n` to the value `v` for the currently running
/// process
267 268
pub fn setenv(n: &str, v: &str) {
    unsafe {
269
        with_env_lock(|| {
270
            use os::win32::as_utf16_p;
271 272
            as_utf16_p(n, |nbuf| {
                as_utf16_p(v, |vbuf| {
273
                    libc::SetEnvironmentVariableW(nbuf, vbuf);
274 275 276
                })
            })
        })
277 278 279
    }
}

C
Corey Richardson 已提交
280 281 282 283 284
/// Remove a variable from the environment entirely
pub fn unsetenv(n: &str) {
    #[cfg(unix)]
    fn _unsetenv(n: &str) {
        unsafe {
285 286
            with_env_lock(|| {
                n.with_c_str(|nbuf| {
C
Corey Richardson 已提交
287
                    libc::funcs::posix01::unistd::unsetenv(nbuf);
288 289
                })
            })
C
Corey Richardson 已提交
290 291 292 293 294
        }
    }
    #[cfg(windows)]
    fn _unsetenv(n: &str) {
        unsafe {
295
            with_env_lock(|| {
C
Corey Richardson 已提交
296
                use os::win32::as_utf16_p;
297
                as_utf16_p(n, |nbuf| {
C
Corey Richardson 已提交
298
                    libc::SetEnvironmentVariableW(nbuf, ptr::null());
299 300
                })
            })
C
Corey Richardson 已提交
301 302 303 304 305 306
        }
    }

    _unsetenv(n);
}

307
pub struct Pipe {
308
    input: c_int,
309 310
    out: c_int
}
311

312
#[cfg(unix)]
313
pub fn pipe() -> Pipe {
314
    unsafe {
315
        let mut fds = Pipe {input: 0 as c_int,
316
                            out: 0 as c_int };
317 318
        assert_eq!(libc::pipe(&mut fds.input), (0 as c_int));
        return Pipe {input: fds.input, out: fds.out};
319
    }
320 321
}

322
#[cfg(windows)]
323
pub fn pipe() -> Pipe {
324 325 326 327 328
    unsafe {
        // Windows pipes work subtly differently than unix pipes, and their
        // inheritance has to be handled in a different way that I do not
        // fully understand. Here we explicitly make the pipe non-inheritable,
        // which means to pass it to a subprocess they need to be duplicated
329
        // first, as in std::run.
330
        let mut fds = Pipe {input: 0 as c_int,
331
                    out: 0 as c_int };
332
        let res = libc::pipe(&mut fds.input, 1024 as ::libc::c_uint,
333
                             (libc::O_BINARY | libc::O_NOINHERIT) as c_int);
334
        assert_eq!(res, 0 as c_int);
335 336 337
        assert!((fds.input != -1 as c_int && fds.input != 0 as c_int));
        assert!((fds.out != -1 as c_int && fds.input != 0 as c_int));
        return Pipe {input: fds.input, out: fds.out};
338
    }
339 340
}

P
Patrick Walton 已提交
341
fn dup2(src: c_int, dst: c_int) -> c_int {
342 343 344
    unsafe {
        libc::dup2(src, dst)
    }
P
Patrick Walton 已提交
345 346
}

347
/// Returns the proper dll filename for the given basename of a file.
348
pub fn dll_filename(base: &str) -> ~str {
A
Alex Crichton 已提交
349
    format!("{}{}{}", DLL_PREFIX, base, DLL_SUFFIX)
350 351
}

352 353
/// Optionally returns the filesystem path to the current executable which is
/// running. If any failure occurs, None is returned.
354
pub fn self_exe_path() -> Option<Path> {
355 356

    #[cfg(target_os = "freebsd")]
357
    fn load_self() -> Option<~[u8]> {
358
        unsafe {
359 360
            use libc::funcs::bsd44::*;
            use libc::consts::os::extra::*;
361
            use vec;
362 363 364 365 366 367 368 369 370
            let mib = ~[CTL_KERN as c_int,
                        KERN_PROC as c_int,
                        KERN_PROC_PATHNAME as c_int, -1 as c_int];
            let mut sz: size_t = 0;
            let err = sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint,
                             ptr::mut_null(), &mut sz, ptr::null(), 0u as size_t);
            if err != 0 { return None; }
            if sz == 0 { return None; }
            let mut v: ~[u8] = vec::with_capacity(sz as uint);
371
            let err = v.as_mut_buf(|buf,_| {
Y
Youngmin Yoo 已提交
372
                sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint,
373
                       buf as *mut c_void, &mut sz, ptr::null(), 0u as size_t)
374
            });
375 376 377 378
            if err != 0 { return None; }
            if sz == 0 { return None; }
            vec::raw::set_len(&mut v, sz as uint - 1); // chop off trailing NUL
            Some(v)
379
        }
380 381 382
    }

    #[cfg(target_os = "linux")]
K
kyeongwoon 已提交
383
    #[cfg(target_os = "android")]
384
    fn load_self() -> Option<~[u8]> {
A
Alex Crichton 已提交
385
        use std::io;
386

387
        match io::result(|| io::fs::readlink(&Path::new("/proc/self/exe"))) {
388
            Ok(Some(path)) => Some(path.as_vec().to_owned()),
A
Alex Crichton 已提交
389
            Ok(None) | Err(..) => None
390 391 392
        }
    }

393
    #[cfg(target_os = "macos")]
394
    fn load_self() -> Option<~[u8]> {
395
        unsafe {
396
            use libc::funcs::extra::_NSGetExecutablePath;
397
            use vec;
398 399 400 401
            let mut sz: u32 = 0;
            _NSGetExecutablePath(ptr::mut_null(), &mut sz);
            if sz == 0 { return None; }
            let mut v: ~[u8] = vec::with_capacity(sz as uint);
402
            let err = v.as_mut_buf(|buf, _| {
403
                _NSGetExecutablePath(buf as *mut i8, &mut sz)
404
            });
405 406 407
            if err != 0 { return None; }
            vec::raw::set_len(&mut v, sz as uint - 1); // chop off trailing NUL
            Some(v)
408
        }
409 410
    }

411
    #[cfg(windows)]
412
    fn load_self() -> Option<~[u8]> {
413 414
        unsafe {
            use os::win32::fill_utf16_buf_and_decode;
415
            fill_utf16_buf_and_decode(|buf, sz| {
416
                libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
417
            }).map(|s| s.into_bytes())
418
        }
419 420
    }

421
    load_self().and_then(|path| Path::new_opt(path).map(|mut p| { p.pop(); p }))
422 423
}

424 425 426 427 428 429 430 431 432 433 434 435 436
/**
 * Returns the path to the user's home directory, if known.
 *
 * On Unix, returns the value of the 'HOME' environment variable if it is set
 * and not equal to the empty string.
 *
 * On Windows, returns the value of the 'HOME' environment variable if it is
 * set and not equal to the empty string. Otherwise, returns the value of the
 * 'USERPROFILE' environment variable if it is set and not equal to the empty
 * string.
 *
 * Otherwise, homedir returns option::none.
 */
437
pub fn homedir() -> Option<Path> {
438
    // FIXME (#7188): getenv needs a ~[u8] variant
439
    return match getenv("HOME") {
440
        Some(ref p) if !p.is_empty() => Path::new_opt(p.as_slice()),
441
        _ => secondary()
442 443
    };

444
    #[cfg(unix)]
B
Brian Anderson 已提交
445 446
    fn secondary() -> Option<Path> {
        None
447 448
    }

449
    #[cfg(windows)]
B
Brian Anderson 已提交
450
    fn secondary() -> Option<Path> {
451
        getenv("USERPROFILE").and_then(|p| {
452
            if !p.is_empty() {
453
                Path::new_opt(p)
454
            } else {
B
Brian Anderson 已提交
455
                None
456
            }
457
        })
458 459 460
    }
}

461
/**
462
 * Returns the path to a temporary directory.
463 464 465
 *
 * On Unix, returns the value of the 'TMPDIR' environment variable if it is
 * set and non-empty and '/tmp' otherwise.
466 467
 * On Android, there is no global temporary folder (it is usually allocated
 * per-app), hence returns '/data/tmp' which is commonly used.
468 469
 *
 * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
470 471
 * 'USERPROFILE' environment variable  if any are set and not the empty
 * string. Otherwise, tmpdir returns the path to the Windows directory.
472
 */
473
pub fn tmpdir() -> Path {
474 475
    return lookup();

B
Brian Anderson 已提交
476
    fn getenv_nonempty(v: &str) -> Option<Path> {
477
        match getenv(v) {
L
Luqman Aden 已提交
478
            Some(x) =>
479
                if x.is_empty() {
B
Brian Anderson 已提交
480
                    None
481
                } else {
482
                    Path::new_opt(x)
483
                },
B
Brian Anderson 已提交
484
            _ => None
485 486 487 488
        }
    }

    #[cfg(unix)]
489
    fn lookup() -> Path {
490
        if cfg!(target_os = "android") {
491
            Path::new("/data/tmp")
492
        } else {
493
            getenv_nonempty("TMPDIR").unwrap_or(Path::new("/tmp"))
494
        }
495 496 497
    }

    #[cfg(windows)]
498
    fn lookup() -> Path {
499 500 501
        getenv_nonempty("TMP").or(
            getenv_nonempty("TEMP").or(
                getenv_nonempty("USERPROFILE").or(
502
                   getenv_nonempty("WINDIR")))).unwrap_or(Path::new("C:\\Windows"))
503 504
    }
}
B
Brian Anderson 已提交
505

506 507 508 509 510
/**
 * Convert a relative path to an absolute path
 *
 * If the given path is relative, return it prepended with the current working
 * directory. If the given path is already an absolute path, return it
511
 * as is.
512
 */
513 514 515
// NB: this is here rather than in path because it is a form of environment
// querying; what it does depends on the process working directory, not just
// the input paths.
516
pub fn make_absolute(p: &Path) -> Path {
517 518
    if p.is_absolute() {
        p.clone()
519
    } else {
520
        let mut ret = getcwd();
521
        ret.push(p);
522
        ret
523
    }
524 525
}

526 527
/// Changes the current working directory to the specified path, returning
/// whether the change was completed successfully or not.
528
pub fn change_dir(p: &Path) -> bool {
B
Brian Anderson 已提交
529
    return chdir(p);
530

531
    #[cfg(windows)]
532
    fn chdir(p: &Path) -> bool {
533 534
        unsafe {
            use os::win32::as_utf16_p;
535
            return as_utf16_p(p.as_str().unwrap(), |buf| {
536
                libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
537
            });
538
        }
539 540
    }

541
    #[cfg(unix)]
542
    fn chdir(p: &Path) -> bool {
543
        p.with_c_str(|buf| {
E
Erick Tryzelaar 已提交
544
            unsafe {
545
                libc::chdir(buf) == (0 as c_int)
E
Erick Tryzelaar 已提交
546
            }
547
        })
548 549 550
    }
}

551
#[cfg(unix)]
552
/// Returns the platform-specific value of errno
553 554 555 556 557 558
pub fn errno() -> int {
    #[cfg(target_os = "macos")]
    #[cfg(target_os = "freebsd")]
    fn errno_location() -> *c_int {
        #[nolink]
        extern {
559
            fn __error() -> *c_int;
560 561 562 563 564 565 566 567 568 569 570
        }
        unsafe {
            __error()
        }
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
    fn errno_location() -> *c_int {
        #[nolink]
        extern {
571
            fn __errno_location() -> *c_int;
572 573 574 575 576 577 578 579 580 581 582 583
        }
        unsafe {
            __errno_location()
        }
    }

    unsafe {
        (*errno_location()) as int
    }
}

#[cfg(windows)]
584
/// Returns the platform-specific value of errno
585 586 587 588
pub fn errno() -> uint {
    use libc::types::os::arch::extra::DWORD;

    #[link_name = "kernel32"]
A
Alex Crichton 已提交
589
    extern "system" {
K
klutzy 已提交
590 591 592
        fn GetLastError() -> DWORD;
    }

593
    unsafe {
594
        GetLastError() as uint
595 596 597
    }
}

598
/// Get a string representing the platform-dependent last error
599
pub fn last_os_error() -> ~str {
600 601 602 603 604
    #[cfg(unix)]
    fn strerror() -> ~str {
        #[cfg(target_os = "macos")]
        #[cfg(target_os = "android")]
        #[cfg(target_os = "freebsd")]
605 606
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t)
                      -> c_int {
607 608
            #[nolink]
            extern {
609 610
                fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t)
                              -> c_int;
611 612 613 614 615 616 617 618
            }
            unsafe {
                strerror_r(errnum, buf, buflen)
            }
        }

        // GNU libc provides a non-compliant version of strerror_r by default
        // and requires macros to instead use the POSIX compliant variant.
L
Luqman Aden 已提交
619
        // So we just use __xpg_strerror_r which is always POSIX compliant
620
        #[cfg(target_os = "linux")]
621
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
622 623
            #[nolink]
            extern {
624 625 626 627
                fn __xpg_strerror_r(errnum: c_int,
                                    buf: *mut c_char,
                                    buflen: size_t)
                                    -> c_int;
628 629 630 631 632 633 634
            }
            unsafe {
                __xpg_strerror_r(errnum, buf, buflen)
            }
        }

        let mut buf = [0 as c_char, ..TMPBUF_SZ];
635

636
        buf.as_mut_buf(|buf, len| {
637 638
            unsafe {
                if strerror_r(errno() as c_int, buf, len as size_t) < 0 {
639
                    fail!("strerror_r failure");
640 641 642 643
                }

                str::raw::from_c_str(buf as *c_char)
            }
644
        })
645
    }
646 647 648

    #[cfg(windows)]
    fn strerror() -> ~str {
649
        use libc::types::os::arch::extra::DWORD;
650
        use libc::types::os::arch::extra::LPWSTR;
651
        use libc::types::os::arch::extra::LPVOID;
652
        use libc::types::os::arch::extra::WCHAR;
653 654

        #[link_name = "kernel32"]
A
Alex Crichton 已提交
655
        extern "system" {
656
            fn FormatMessageW(flags: DWORD,
K
klutzy 已提交
657 658 659
                              lpSrc: LPVOID,
                              msgId: DWORD,
                              langId: DWORD,
660
                              buf: LPWSTR,
K
klutzy 已提交
661 662 663 664 665
                              nsize: DWORD,
                              args: *c_void)
                              -> DWORD;
        }

666 667
        static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
        static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
668 669 670 671 672 673

        // This value is calculated from the macro
        // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
        let langId = 0x0800 as DWORD;
        let err = errno() as DWORD;

674
        let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
675

676
        unsafe {
677
            buf.as_mut_buf(|buf, len| {
678
                let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
679 680 681 682 683 684 685 686
                                         FORMAT_MESSAGE_IGNORE_INSERTS,
                                         ptr::mut_null(),
                                         err,
                                         langId,
                                         buf,
                                         len as DWORD,
                                         ptr::null());
                if res == 0 {
687
                    fail!("[{}] FormatMessage failure", errno());
688
                }
689
            });
690

691
            str::from_utf16(buf)
692 693 694 695
        }
    }

    strerror()
696
}
697

698 699 700 701 702 703 704 705
/**
 * Sets the process exit code
 *
 * Sets the exit code returned by the process if all supervised tasks
 * terminate successfully (without failing). If the current root task fails
 * and is supervised by the scheduler then any user-specified exit status is
 * ignored and the process exits with the default failure status
 */
706
pub fn set_exit_status(code: int) {
707
    use rt;
708
    rt::set_exit_status(code);
709
}
710

711 712
unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
    let mut args = ~[];
D
Daniel Micay 已提交
713
    for i in range(0u, argc as uint) {
714
        args.push(str::raw::from_c_str(*argv.offset(i as int)));
715
    }
L
Luqman Aden 已提交
716
    args
717 718
}

719 720 721 722 723 724
/**
 * Returns the command line arguments
 *
 * Returns a list of the command line arguments.
 */
#[cfg(target_os = "macos")]
725
fn real_args() -> ~[~str] {
726
    unsafe {
727 728 729
        let (argc, argv) = (*_NSGetArgc() as c_int,
                            *_NSGetArgv() as **c_char);
        load_argc_and_argv(argc, argv)
730 731 732
    }
}

733
#[cfg(target_os = "linux")]
K
kyeongwoon 已提交
734
#[cfg(target_os = "android")]
735
#[cfg(target_os = "freebsd")]
736
fn real_args() -> ~[~str] {
737 738
    use rt;

739 740
    match rt::args::clone() {
        Some(args) => args,
741
        None => fail!("process arguments not initialized")
742
    }
743 744
}

745
#[cfg(windows)]
746
fn real_args() -> ~[~str] {
747
    use vec;
748

749
    let mut nArgs: c_int = 0;
D
Daniel Micay 已提交
750
    let lpArgCount: *mut c_int = &mut nArgs;
T
Tim Chevalier 已提交
751 752
    let lpCmdLine = unsafe { GetCommandLineW() };
    let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
753 754

    let mut args = ~[];
D
Daniel Micay 已提交
755
    for i in range(0u, nArgs as uint) {
756 757
        unsafe {
            // Determine the length of this argument.
758
            let ptr = *szArgList.offset(i as int);
759
            let mut len = 0;
760
            while *ptr.offset(len as int) != 0 { len += 1; }
761 762

            // Push it onto the list.
763
            args.push(vec::raw::buf_as_slice(ptr, len,
764
                                             str::from_utf16));
765 766 767 768
        }
    }

    unsafe {
769
        LocalFree(szArgList as *c_void);
770 771 772 773 774 775 776
    }

    return args;
}

type LPCWSTR = *u16;

A
Alex Crichton 已提交
777
#[cfg(windows)]
K
klutzy 已提交
778
#[link_name="kernel32"]
A
Alex Crichton 已提交
779
extern "system" {
K
klutzy 已提交
780 781 782 783
    fn GetCommandLineW() -> LPCWSTR;
    fn LocalFree(ptr: *c_void);
}

A
Alex Crichton 已提交
784
#[cfg(windows)]
K
klutzy 已提交
785
#[link_name="shell32"]
A
Alex Crichton 已提交
786
extern "system" {
K
klutzy 已提交
787 788 789
    fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
}

790 791 792 793
struct OverriddenArgs {
    val: ~[~str]
}

794 795
/// Returns the arguments which this program was started with (normally passed
/// via the command line).
796
pub fn args() -> ~[~str] {
797
    real_args()
798 799
}

800 801 802 803 804 805 806
#[cfg(target_os = "macos")]
extern {
    // These functions are in crt_externs.h.
    pub fn _NSGetArgc() -> *c_int;
    pub fn _NSGetArgv() -> ***c_char;
}

807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
// Round up `from` to be divisible by `to`
fn round_up(from: uint, to: uint) -> uint {
    let r = if from % to == 0 {
        from
    } else {
        from + to - (from % to)
    };
    if r == 0 {
        to
    } else {
        r
    }
}

#[cfg(unix)]
pub fn page_size() -> uint {
    unsafe {
        libc::sysconf(libc::_SC_PAGESIZE) as uint
    }
}

#[cfg(windows)]
pub fn page_size() -> uint {
V
Vadim Chugunov 已提交
830 831 832
    unsafe {
        let mut info = libc::SYSTEM_INFO::new();
        libc::GetSystemInfo(&mut info);
833

V
Vadim Chugunov 已提交
834 835
        return info.dwPageSize as uint;
    }
836 837
}

C
Corey Richardson 已提交
838 839 840 841 842 843 844
/// A memory mapped file or chunk of memory. This is a very system-specific interface to the OS's
/// memory mapping facilities (`mmap` on POSIX, `VirtualAlloc`/`CreateFileMapping` on win32). It
/// makes no attempt at abstracting platform differences, besides in error values returned. Consider
/// yourself warned.
///
/// The memory map is released (unmapped) when the destructor is run, so don't let it leave scope by
/// accident if you want it to stick around.
845
pub struct MemoryMap {
C
Corey Richardson 已提交
846
    /// Pointer to the memory created or modified by this map.
847
    data: *mut u8,
C
Corey Richardson 已提交
848
    /// Number of bytes this map applies to
849
    len: size_t,
C
Corey Richardson 已提交
850
    /// Type of mapping
851 852 853
    kind: MemoryMapKind
}

C
Corey Richardson 已提交
854
/// Type of memory map
855
pub enum MemoryMapKind {
C
Corey Richardson 已提交
856 857
    /// Memory-mapped file. On Windows, the inner pointer is a handle to the mapping, and
    /// corresponds to `CreateFileMapping`. Elsewhere, it is null.
858
    MapFile(*c_void),
C
Corey Richardson 已提交
859 860
    /// Virtual memory map. Usually used to change the permissions of a given chunk of memory.
    /// Corresponds to `VirtualAlloc` on Windows.
861 862 863
    MapVirtual
}

C
Corey Richardson 已提交
864
/// Options the memory map is created with
865
pub enum MapOption {
C
Corey Richardson 已提交
866
    /// The memory should be readable
867
    MapReadable,
C
Corey Richardson 已提交
868
    /// The memory should be writable
869
    MapWritable,
C
Corey Richardson 已提交
870
    /// The memory should be executable
871
    MapExecutable,
C
Corey Richardson 已提交
872
    /// Create a map for a specific address range. Corresponds to `MAP_FIXED` on POSIX.
873
    MapAddr(*c_void),
C
Corey Richardson 已提交
874
    /// Create a memory mapping for a file with a given fd.
875
    MapFd(c_int),
C
Corey Richardson 已提交
876
    /// When using `MapFd`, the start of the map is `uint` bytes from the start of the file.
877 878 879
    MapOffset(uint)
}

C
Corey Richardson 已提交
880
/// Possible errors when creating a map.
881
pub enum MapError {
C
Corey Richardson 已提交
882 883 884
    /// ## The following are POSIX-specific
    ///
    /// fd was not open for reading or, if using `MapWritable`, was not open for writing.
885
    ErrFdNotAvail,
C
Corey Richardson 已提交
886
    /// fd was not valid
887
    ErrInvalidFd,
C
Corey Richardson 已提交
888 889
    /// Either the address given by `MapAddr` or offset given by `MapOffset` was not a multiple of
    /// `MemoryMap::granularity` (unaligned to page size).
890
    ErrUnaligned,
C
Corey Richardson 已提交
891
    /// With `MapFd`, the fd does not support mapping.
892
    ErrNoMapSupport,
C
Corey Richardson 已提交
893 894
    /// If using `MapAddr`, the address + `min_len` was outside of the process's address space. If
    /// using `MapFd`, the target of the fd didn't have enough resources to fulfill the request.
895
    ErrNoMem,
C
Corey Richardson 已提交
896
    /// Unrecognized error. The inner value is the unrecognized errno.
897
    ErrUnknown(libc::c_int),
C
Corey Richardson 已提交
898 899 900
    /// ## The following are win32-specific
    ///
    /// Unsupported combination of protection flags (`MapReadable`/`MapWritable`/`MapExecutable`).
901
    ErrUnsupProt,
C
Corey Richardson 已提交
902
    /// When using `MapFd`, `MapOffset` was given (Windows does not support this at all)
903
    ErrUnsupOffset,
C
Corey Richardson 已提交
904
    /// When using `MapFd`, there was already a mapping to the file.
905
    ErrAlreadyExists,
C
Corey Richardson 已提交
906
    /// Unrecognized error from `VirtualAlloc`. The inner value is the return value of GetLastError.
907
    ErrVirtualAlloc(uint),
C
Corey Richardson 已提交
908 909
    /// Unrecognized error from `CreateFileMapping`. The inner value is the return value of
    /// `GetLastError`.
910
    ErrCreateFileMappingW(uint),
C
Corey Richardson 已提交
911 912
    /// Unrecognized error from `MapViewOfFile`. The inner value is the return value of
    /// `GetLastError`.
913 914 915 916 917 918 919 920 921 922 923 924
    ErrMapViewOfFile(uint)
}

impl to_str::ToStr for MapError {
    fn to_str(&self) -> ~str {
        match *self {
            ErrFdNotAvail => ~"fd not available for reading or writing",
            ErrInvalidFd => ~"Invalid fd",
            ErrUnaligned => ~"Unaligned address, invalid flags, \
                              negative length or unaligned offset",
            ErrNoMapSupport=> ~"File doesn't support mapping",
            ErrNoMem => ~"Invalid address, or not enough available memory",
A
Alex Crichton 已提交
925
            ErrUnknown(code) => format!("Unknown error={}", code),
926 927 928
            ErrUnsupProt => ~"Protection mode unsupported",
            ErrUnsupOffset => ~"Offset in virtual memory mode is unsupported",
            ErrAlreadyExists => ~"File mapping for specified file already exists",
A
Alex Crichton 已提交
929 930 931
            ErrVirtualAlloc(code) => format!("VirtualAlloc failure={}", code),
            ErrCreateFileMappingW(code) => format!("CreateFileMappingW failure={}", code),
            ErrMapViewOfFile(code) => format!("MapViewOfFile failure={}", code)
932 933 934 935 936 937
        }
    }
}

#[cfg(unix)]
impl MemoryMap {
C
Corey Richardson 已提交
938
    /// Create a new mapping with the given `options`, at least `min_len` bytes long.
939
    pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
940 941 942 943 944 945 946 947 948
        use libc::off_t;

        let mut addr: *c_void = ptr::null();
        let mut prot: c_int = 0;
        let mut flags: c_int = libc::MAP_PRIVATE;
        let mut fd: c_int = -1;
        let mut offset: off_t = 0;
        let len = round_up(min_len, page_size()) as size_t;

D
Daniel Micay 已提交
949
        for &o in options.iter() {
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
            match o {
                MapReadable => { prot |= libc::PROT_READ; },
                MapWritable => { prot |= libc::PROT_WRITE; },
                MapExecutable => { prot |= libc::PROT_EXEC; },
                MapAddr(addr_) => {
                    flags |= libc::MAP_FIXED;
                    addr = addr_;
                },
                MapFd(fd_) => {
                    flags |= libc::MAP_FILE;
                    fd = fd_;
                },
                MapOffset(offset_) => { offset = offset_ as off_t; }
            }
        }
        if fd == -1 { flags |= libc::MAP_ANON; }

        let r = unsafe {
            libc::mmap(addr, len, prot, flags, fd, offset)
        };
970
        if r.equiv(&libc::MAP_FAILED) {
971 972 973 974 975 976 977 978 979
            Err(match errno() as c_int {
                libc::EACCES => ErrFdNotAvail,
                libc::EBADF => ErrInvalidFd,
                libc::EINVAL => ErrUnaligned,
                libc::ENODEV => ErrNoMapSupport,
                libc::ENOMEM => ErrNoMem,
                code => ErrUnknown(code)
            })
        } else {
980
            Ok(MemoryMap {
981 982 983 984 985 986 987 988 989 990
               data: r as *mut u8,
               len: len,
               kind: if fd == -1 {
                   MapVirtual
               } else {
                   MapFile(ptr::null())
               }
            })
        }
    }
V
Vadim Chugunov 已提交
991

C
Corey Richardson 已提交
992
    /// Granularity that the offset or address must be for `MapOffset` and `MapAddr` respectively.
V
Vadim Chugunov 已提交
993 994 995
    pub fn granularity() -> uint {
        page_size()
    }
996 997 998 999
}

#[cfg(unix)]
impl Drop for MemoryMap {
C
Corey Richardson 已提交
1000
    /// Unmap the mapping. Fails the task if `munmap` fails.
D
Daniel Micay 已提交
1001
    fn drop(&mut self) {
1002 1003 1004
        unsafe {
            match libc::munmap(self.data as *c_void, self.len) {
                0 => (),
A
Alex Crichton 已提交
1005
                -1 => match errno() as c_int {
1006 1007
                    libc::EINVAL => error!("invalid addr or len"),
                    e => error!("unknown errno={}", e)
A
Alex Crichton 已提交
1008
                },
1009
                r => error!("Unexpected result {}", r)
1010 1011 1012 1013 1014 1015 1016
            }
        }
    }
}

#[cfg(windows)]
impl MemoryMap {
C
Corey Richardson 已提交
1017
    /// Create a new mapping with the given `options`, at least `min_len` bytes long.
1018
    pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
        use libc::types::os::arch::extra::{LPVOID, DWORD, SIZE_T, HANDLE};

        let mut lpAddress: LPVOID = ptr::mut_null();
        let mut readable = false;
        let mut writable = false;
        let mut executable = false;
        let mut fd: c_int = -1;
        let mut offset: uint = 0;
        let len = round_up(min_len, page_size()) as SIZE_T;

D
Daniel Micay 已提交
1029
        for &o in options.iter() {
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
            match o {
                MapReadable => { readable = true; },
                MapWritable => { writable = true; },
                MapExecutable => { executable = true; }
                MapAddr(addr_) => { lpAddress = addr_ as LPVOID; },
                MapFd(fd_) => { fd = fd_; },
                MapOffset(offset_) => { offset = offset_; }
            }
        }

        let flProtect = match (executable, readable, writable) {
            (false, false, false) if fd == -1 => libc::PAGE_NOACCESS,
            (false, true, false) => libc::PAGE_READONLY,
            (false, true, true) => libc::PAGE_READWRITE,
            (true, false, false) if fd == -1 => libc::PAGE_EXECUTE,
            (true, true, false) => libc::PAGE_EXECUTE_READ,
            (true, true, true) => libc::PAGE_EXECUTE_READWRITE,
            _ => return Err(ErrUnsupProt)
        };

        if fd == -1 {
            if offset != 0 {
                return Err(ErrUnsupOffset);
            }
            let r = unsafe {
                libc::VirtualAlloc(lpAddress,
                                   len,
                                   libc::MEM_COMMIT | libc::MEM_RESERVE,
                                   flProtect)
            };
            match r as uint {
                0 => Err(ErrVirtualAlloc(errno())),
1062
                _ => Ok(MemoryMap {
1063 1064 1065 1066 1067 1068
                   data: r as *mut u8,
                   len: len,
                   kind: MapVirtual
                })
            }
        } else {
V
Vadim Chugunov 已提交
1069 1070 1071 1072 1073 1074 1075
            let dwDesiredAccess = match (executable, readable, writable) {
                (false, true, false) => libc::FILE_MAP_READ,
                (false, true, true) => libc::FILE_MAP_WRITE,
                (true, true, false) => libc::FILE_MAP_READ | libc::FILE_MAP_EXECUTE,
                (true, true, true) => libc::FILE_MAP_WRITE | libc::FILE_MAP_EXECUTE,
                _ => return Err(ErrUnsupProt) // Actually, because of the check above,
                                              // we should never get here.
1076 1077 1078 1079 1080 1081
            };
            unsafe {
                let hFile = libc::get_osfhandle(fd) as HANDLE;
                let mapping = libc::CreateFileMappingW(hFile,
                                                       ptr::mut_null(),
                                                       flProtect,
V
Vadim Chugunov 已提交
1082 1083
                                                       0,
                                                       0,
1084 1085 1086 1087 1088 1089 1090 1091 1092
                                                       ptr::null());
                if mapping == ptr::mut_null() {
                    return Err(ErrCreateFileMappingW(errno()));
                }
                if errno() as c_int == libc::ERROR_ALREADY_EXISTS {
                    return Err(ErrAlreadyExists);
                }
                let r = libc::MapViewOfFile(mapping,
                                            dwDesiredAccess,
V
Vadim Chugunov 已提交
1093
                                            ((len as u64) >> 32) as DWORD,
1094 1095 1096 1097
                                            (offset & 0xffff_ffff) as DWORD,
                                            0);
                match r as uint {
                    0 => Err(ErrMapViewOfFile(errno())),
1098
                    _ => Ok(MemoryMap {
1099 1100 1101 1102 1103 1104 1105 1106
                       data: r as *mut u8,
                       len: len,
                       kind: MapFile(mapping as *c_void)
                    })
                }
            }
        }
    }
V
Vadim Chugunov 已提交
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117

    /// Granularity of MapAddr() and MapOffset() parameter values.
    /// This may be greater than the value returned by page_size().
    pub fn granularity() -> uint {
        unsafe {
            let mut info = libc::SYSTEM_INFO::new();
            libc::GetSystemInfo(&mut info);

            return info.dwAllocationGranularity as uint;
        }
    }
1118 1119 1120 1121
}

#[cfg(windows)]
impl Drop for MemoryMap {
C
Corey Richardson 已提交
1122 1123
    /// Unmap the mapping. Fails the task if any of `VirtualFree`, `UnmapViewOfFile`, or
    /// `CloseHandle` fail.
D
Daniel Micay 已提交
1124
    fn drop(&mut self) {
1125
        use libc::types::os::arch::extra::{LPCVOID, HANDLE};
V
Vadim Chugunov 已提交
1126
        use libc::consts::os::extra::FALSE;
1127 1128 1129

        unsafe {
            match self.kind {
V
Vadim Chugunov 已提交
1130 1131 1132 1133
                MapVirtual => {
                    if libc::VirtualFree(self.data as *mut c_void,
                                         self.len,
                                         libc::MEM_RELEASE) == FALSE {
1134
                        error!("VirtualFree failed: {}", errno());
V
Vadim Chugunov 已提交
1135
                    }
1136 1137
                },
                MapFile(mapping) => {
V
Vadim Chugunov 已提交
1138
                    if libc::UnmapViewOfFile(self.data as LPCVOID) == FALSE {
1139
                        error!("UnmapViewOfFile failed: {}", errno());
1140
                    }
V
Vadim Chugunov 已提交
1141
                    if libc::CloseHandle(mapping as HANDLE) == FALSE {
1142
                        error!("CloseHandle failed: {}", errno());
1143 1144 1145 1146 1147 1148 1149
                    }
                }
            }
        }
    }
}

1150
pub mod consts {
1151

I
ILyoan 已提交
1152
    #[cfg(unix)]
1153
    pub use os::consts::unix::*;
1154

I
ILyoan 已提交
1155
    #[cfg(windows)]
1156
    pub use os::consts::windows::*;
1157

I
ILyoan 已提交
1158
    #[cfg(target_os = "macos")]
1159
    pub use os::consts::macos::*;
I
ILyoan 已提交
1160 1161

    #[cfg(target_os = "freebsd")]
1162
    pub use os::consts::freebsd::*;
I
ILyoan 已提交
1163 1164

    #[cfg(target_os = "linux")]
1165
    pub use os::consts::linux::*;
I
ILyoan 已提交
1166

K
kyeongwoon 已提交
1167
    #[cfg(target_os = "android")]
1168
    pub use os::consts::android::*;
K
kyeongwoon 已提交
1169

I
ILyoan 已提交
1170
    #[cfg(target_os = "win32")]
1171
    pub use os::consts::win32::*;
1172

1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
    #[cfg(target_arch = "x86")]
    pub use os::consts::x86::*;

    #[cfg(target_arch = "x86_64")]
    pub use os::consts::x86_64::*;

    #[cfg(target_arch = "arm")]
    pub use os::consts::arm::*;

    #[cfg(target_arch = "mips")]
1183
    pub use os::consts::mips::*;
1184 1185 1186 1187 1188 1189 1190 1191 1192

    pub mod unix {
        pub static FAMILY: &'static str = "unix";
    }

    pub mod windows {
        pub static FAMILY: &'static str = "windows";
    }

I
ILyoan 已提交
1193
    pub mod macos {
1194 1195 1196
        pub static SYSNAME: &'static str = "macos";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".dylib";
1197
        pub static DLL_EXTENSION: &'static str = "dylib";
1198
        pub static EXE_SUFFIX: &'static str = "";
1199
        pub static EXE_EXTENSION: &'static str = "";
I
ILyoan 已提交
1200 1201 1202
    }

    pub mod freebsd {
1203 1204 1205
        pub static SYSNAME: &'static str = "freebsd";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
1206
        pub static DLL_EXTENSION: &'static str = "so";
1207
        pub static EXE_SUFFIX: &'static str = "";
1208
        pub static EXE_EXTENSION: &'static str = "";
I
ILyoan 已提交
1209 1210 1211
    }

    pub mod linux {
1212 1213 1214
        pub static SYSNAME: &'static str = "linux";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
1215
        pub static DLL_EXTENSION: &'static str = "so";
1216
        pub static EXE_SUFFIX: &'static str = "";
1217
        pub static EXE_EXTENSION: &'static str = "";
I
ILyoan 已提交
1218
    }
K
kyeongwoon 已提交
1219 1220

    pub mod android {
1221 1222 1223
        pub static SYSNAME: &'static str = "android";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
1224
        pub static DLL_EXTENSION: &'static str = "so";
1225
        pub static EXE_SUFFIX: &'static str = "";
1226
        pub static EXE_EXTENSION: &'static str = "";
K
kyeongwoon 已提交
1227
    }
1228

I
ILyoan 已提交
1229
    pub mod win32 {
1230 1231 1232
        pub static SYSNAME: &'static str = "win32";
        pub static DLL_PREFIX: &'static str = "";
        pub static DLL_SUFFIX: &'static str = ".dll";
1233
        pub static DLL_EXTENSION: &'static str = "dll";
1234
        pub static EXE_SUFFIX: &'static str = ".exe";
1235
        pub static EXE_EXTENSION: &'static str = "exe";
I
ILyoan 已提交
1236 1237 1238 1239
    }


    pub mod x86 {
1240
        pub static ARCH: &'static str = "x86";
I
ILyoan 已提交
1241 1242
    }
    pub mod x86_64 {
1243
        pub static ARCH: &'static str = "x86_64";
I
ILyoan 已提交
1244 1245
    }
    pub mod arm {
1246
        pub static ARCH: &'static str = "arm";
I
ILyoan 已提交
1247
    }
J
Jyun-Yan You 已提交
1248
    pub mod mips {
1249
        pub static ARCH: &'static str = "mips";
J
Jyun-Yan You 已提交
1250
    }
I
ILyoan 已提交
1251
}
1252 1253 1254

#[cfg(test)]
mod tests {
1255
    use c_str::ToCStr;
A
Alex Crichton 已提交
1256
    use option::Some;
1257
    use option;
1258
    use os::{env, getcwd, getenv, make_absolute, args};
1259
    use os::{setenv, unsetenv};
1260
    use os;
1261
    use path::Path;
1262
    use rand::Rng;
1263
    use rand;
1264
    use str::StrSlice;
1265

1266

1267
    #[test]
1268
    pub fn last_os_error() {
1269
        debug!("{}", os::last_os_error());
1270
    }
1271

1272 1273
    #[test]
    pub fn test_args() {
1274
        let a = args();
P
Patrick Walton 已提交
1275
        assert!(a.len() >= 1);
1276 1277
    }

1278
    fn make_rand_name() -> ~str {
P
Patrick Walton 已提交
1279
        let mut rng = rand::rng();
1280
        let n = ~"TEST" + rng.gen_ascii_str(10u);
P
Patrick Walton 已提交
1281
        assert!(getenv(n).is_none());
L
Luqman Aden 已提交
1282
        n
1283 1284 1285 1286 1287
    }

    #[test]
    fn test_setenv() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1288
        setenv(n, "VALUE");
1289
        assert_eq!(getenv(n), option::Some(~"VALUE"));
1290 1291
    }

C
Corey Richardson 已提交
1292 1293 1294
    #[test]
    fn test_unsetenv() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1295
        setenv(n, "VALUE");
C
Corey Richardson 已提交
1296
        unsetenv(n);
1297
        assert_eq!(getenv(n), option::None);
C
Corey Richardson 已提交
1298 1299
    }

1300
    #[test]
1301
    #[ignore]
1302 1303
    fn test_setenv_overwrite() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1304 1305
        setenv(n, "1");
        setenv(n, "2");
1306
        assert_eq!(getenv(n), option::Some(~"2"));
E
Erick Tryzelaar 已提交
1307
        setenv(n, "");
1308
        assert_eq!(getenv(n), option::Some(~""));
1309 1310 1311 1312 1313
    }

    // Windows GetEnvironmentVariable requires some extra work to make sure
    // the buffer the variable is copied into is the right size
    #[test]
1314
    #[ignore]
1315
    fn test_getenv_big() {
1316
        let mut s = ~"";
1317
        let mut i = 0;
1318 1319 1320 1321
        while i < 100 {
            s = s + "aaaaaaaaaa";
            i += 1;
        }
1322 1323
        let n = make_rand_name();
        setenv(n, s);
1324
        debug!("{}", s.clone());
1325
        assert_eq!(getenv(n), option::Some(s));
1326 1327 1328 1329 1330
    }

    #[test]
    fn test_self_exe_path() {
        let path = os::self_exe_path();
P
Patrick Walton 已提交
1331
        assert!(path.is_some());
1332
        let path = path.unwrap();
1333
        debug!("{:?}", path.clone());
1334 1335

        // Hard to test this function
1336
        assert!(path.is_absolute());
1337 1338 1339
    }

    #[test]
1340
    #[ignore]
1341 1342
    fn test_env_getenv() {
        let e = env();
Y
Youngmin Yoo 已提交
1343
        assert!(e.len() > 0u);
D
Daniel Micay 已提交
1344
        for p in e.iter() {
1345
            let (n, v) = (*p).clone();
1346
            debug!("{:?}", n.clone());
1347 1348 1349 1350
            let v2 = getenv(n);
            // MingW seems to set some funky environment variables like
            // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
            // from env() but not visible from getenv().
P
Patrick Walton 已提交
1351
            assert!(v2.is_none() || v2 == option::Some(v));
1352 1353 1354 1355 1356 1357 1358
        }
    }

    #[test]
    fn test_env_setenv() {
        let n = make_rand_name();

1359
        let mut e = env();
E
Erick Tryzelaar 已提交
1360
        setenv(n, "VALUE");
1361
        assert!(!e.contains(&(n.clone(), ~"VALUE")));
1362 1363

        e = env();
1364
        assert!(e.contains(&(n, ~"VALUE")));
1365 1366
    }

1367 1368
    #[test]
    fn test() {
1369
        assert!((!Path::new("test-path").is_absolute()));
1370

1371
        let cwd = getcwd();
1372
        debug!("Current working directory: {}", cwd.display());
1373

1374 1375
        debug!("{:?}", make_absolute(&Path::new("test-path")));
        debug!("{:?}", make_absolute(&Path::new("/usr/bin")));
1376 1377 1378
    }

    #[test]
1379
    #[cfg(unix)]
1380
    fn homedir() {
E
Erick Tryzelaar 已提交
1381
        let oldhome = getenv("HOME");
1382

E
Erick Tryzelaar 已提交
1383
        setenv("HOME", "/home/MountainView");
1384
        assert_eq!(os::homedir(), Some(Path::new("/home/MountainView")));
1385

E
Erick Tryzelaar 已提交
1386
        setenv("HOME", "");
P
Patrick Walton 已提交
1387
        assert!(os::homedir().is_none());
1388

D
Daniel Micay 已提交
1389
        for s in oldhome.iter() { setenv("HOME", *s) }
1390 1391 1392
    }

    #[test]
1393
    #[cfg(windows)]
1394 1395
    fn homedir() {

E
Erick Tryzelaar 已提交
1396 1397
        let oldhome = getenv("HOME");
        let olduserprofile = getenv("USERPROFILE");
1398

E
Erick Tryzelaar 已提交
1399 1400
        setenv("HOME", "");
        setenv("USERPROFILE", "");
1401

P
Patrick Walton 已提交
1402
        assert!(os::homedir().is_none());
1403

E
Erick Tryzelaar 已提交
1404
        setenv("HOME", "/home/MountainView");
1405
        assert_eq!(os::homedir(), Some(Path::new("/home/MountainView")));
1406

E
Erick Tryzelaar 已提交
1407
        setenv("HOME", "");
1408

E
Erick Tryzelaar 已提交
1409
        setenv("USERPROFILE", "/home/MountainView");
1410
        assert_eq!(os::homedir(), Some(Path::new("/home/MountainView")));
1411

E
Erick Tryzelaar 已提交
1412 1413
        setenv("HOME", "/home/MountainView");
        setenv("USERPROFILE", "/home/PaloAlto");
1414
        assert_eq!(os::homedir(), Some(Path::new("/home/MountainView")));
1415

1416 1417
        for s in oldhome.iter() { setenv("HOME", *s) }
        for s in olduserprofile.iter() { setenv("USERPROFILE", *s) }
1418 1419
    }

1420 1421 1422 1423
    #[test]
    fn memory_map_rw() {
        use result::{Ok, Err};

1424
        let chunk = match os::MemoryMap::new(16, [
1425 1426 1427 1428
            os::MapReadable,
            os::MapWritable
        ]) {
            Ok(chunk) => chunk,
1429
            Err(msg) => fail!(msg.to_str())
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
        };
        assert!(chunk.len >= 16);

        unsafe {
            *chunk.data = 0xBE;
            assert!(*chunk.data == 0xBE);
        }
    }

    #[test]
    fn memory_map_file() {
        use result::{Ok, Err};
        use os::*;
        use libc::*;
A
Alex Crichton 已提交
1444 1445
        use io;
        use io::fs;
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459

        #[cfg(unix)]
        fn lseek_(fd: c_int, size: uint) {
            unsafe {
                assert!(lseek(fd, size as off_t, SEEK_SET) == size as off_t);
            }
        }
        #[cfg(windows)]
        fn lseek_(fd: c_int, size: uint) {
           unsafe {
               assert!(lseek(fd, size as c_long, SEEK_SET) == size as c_long);
           }
        }

1460
        let mut path = tmpdir();
1461
        path.push("mmap_file.tmp");
V
Vadim Chugunov 已提交
1462
        let size = MemoryMap::granularity() * 2;
1463 1464

        let fd = unsafe {
1465
            let fd = path.with_c_str(|path| {
1466
                open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
1467
            });
1468
            lseek_(fd, size);
1469
            "x".with_c_str(|x| assert!(write(fd, x as *c_void, 1) == 1));
1470 1471
            fd
        };
1472
        let chunk = match MemoryMap::new(size / 2, [
1473 1474 1475 1476 1477 1478
            MapReadable,
            MapWritable,
            MapFd(fd),
            MapOffset(size / 2)
        ]) {
            Ok(chunk) => chunk,
1479
            Err(msg) => fail!(msg.to_str())
1480 1481 1482 1483 1484 1485 1486 1487
        };
        assert!(chunk.len > 0);

        unsafe {
            *chunk.data = 0xbe;
            assert!(*chunk.data == 0xbe);
            close(fd);
        }
1488
        io::ignore_io_error(|| fs::unlink(&path));
1489 1490
    }

1491
    // More recursive_mkdir tests are in extra::tempfile
1492
}