os.rs 44.0 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
use clone::Clone;
32
use container::Container;
K
Kiet Tran 已提交
33
#[cfg(target_os = "macos")]
34
use iter::range;
35
use libc;
A
Alex Crichton 已提交
36
use libc::{c_char, c_void, c_int, size_t};
P
Patrick Walton 已提交
37
use option::{Some, None};
38
use os;
39
use prelude::*;
40 41
use ptr;
use str;
42
use to_str;
43
use unstable::finally::Finally;
44
use sync::atomics::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
45

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

53 54
pub static TMPBUF_SZ : uint = 1000u;
static BUF_BYTES : uint = 2048u;
55

56
#[cfg(unix)]
57
pub fn getcwd() -> Path {
58 59
    use c_str::CString;

60
    let mut buf = [0 as c_char, ..BUF_BYTES];
61 62 63
    unsafe {
        if libc::getcwd(buf.as_mut_ptr(), buf.len() as size_t).is_null() {
            fail!()
64
        }
65 66
        Path::new(CString::new(buf.as_ptr(), false))
    }
67 68
}

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

82
#[cfg(windows)]
83
pub mod win32 {
84
    use libc::types::os::arch::extra::DWORD;
85
    use libc;
86
    use option::{None, Option};
87
    use option;
88
    use os::TMPBUF_SZ;
89 90 91 92
    use str::StrSlice;
    use str;
    use vec::{MutableVector, ImmutableVector, OwnedVector};
    use vec;
93

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

97
        unsafe {
98
            let mut n = TMPBUF_SZ as DWORD;
99 100 101
            let mut res = None;
            let mut done = false;
            while !done {
102
                let mut buf = vec::from_elem(n as uint, 0u16);
103 104 105
                let k = f(buf.as_mut_ptr(), TMPBUF_SZ as DWORD);
                if k == (0 as DWORD) {
                    done = true;
H
Huon Wilson 已提交
106 107 108
                } else if k == n &&
                          libc::GetLastError() ==
                          libc::ERROR_INSUFFICIENT_BUFFER as DWORD {
109 110 111 112
                    n *= (2 as DWORD);
                } else {
                    done = true;
                }
113
                if k != 0 && done {
114
                    let sub = buf.slice(0, k as uint);
115 116
                    res = option::Some(str::from_utf16(sub));
                }
117
            }
118
            return res;
119 120 121
        }
    }

122
    pub fn as_utf16_p<T>(s: &str, f: |*u16| -> T) -> T {
123
        let mut t = s.to_utf16();
124
        // Null terminate before passing on.
125
        t.push(0u16);
126
        f(t.as_ptr())
127
    }
128 129
}

130 131
/*
Accessing environment variables is not generally threadsafe.
132
Serialize access through a global lock.
133
*/
134
fn with_env_lock<T>(f: || -> T) -> T {
135
    use unstable::mutex::{Mutex, MUTEX_INIT};
136
    use unstable::finally::Finally;
137

138 139
    static mut lock: Mutex = MUTEX_INIT;

140
    unsafe {
141
        return (|| {
142
            lock.lock();
143
            f()
144
        }).finally(|| lock.unlock());
145
    }
B
Ben Blum 已提交
146 147
}

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

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

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

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

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


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


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

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

    _unsetenv(n);
}

301
pub struct Pipe {
302
    input: c_int,
303 304
    out: c_int
}
305

306
#[cfg(unix)]
307
pub fn pipe() -> Pipe {
308
    unsafe {
309 310 311
        let mut fds = Pipe {input: 0,
                            out: 0};
        assert_eq!(libc::pipe(&mut fds.input), 0);
312
        return Pipe {input: fds.input, out: fds.out};
313
    }
314 315
}

316
#[cfg(windows)]
317
pub fn pipe() -> Pipe {
318 319 320 321 322
    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
323
        // first, as in std::run.
324 325
        let mut fds = Pipe {input: 0,
                    out: 0};
326
        let res = libc::pipe(&mut fds.input, 1024 as ::libc::c_uint,
327
                             (libc::O_BINARY | libc::O_NOINHERIT) as c_int);
328 329 330
        assert_eq!(res, 0);
        assert!((fds.input != -1 && fds.input != 0 ));
        assert!((fds.out != -1 && fds.input != 0));
331
        return Pipe {input: fds.input, out: fds.out};
332
    }
333 334
}

335
/// Returns the proper dll filename for the given basename of a file.
336
pub fn dll_filename(base: &str) -> ~str {
337
    format!("{}{}{}", consts::DLL_PREFIX, base, consts::DLL_SUFFIX)
338 339
}

340 341
/// Optionally returns the filesystem path to the current executable which is
/// running. If any failure occurs, None is returned.
342
pub fn self_exe_path() -> Option<Path> {
343 344

    #[cfg(target_os = "freebsd")]
345
    fn load_self() -> Option<~[u8]> {
346
        unsafe {
347 348
            use libc::funcs::bsd44::*;
            use libc::consts::os::extra::*;
349
            use vec;
350 351 352 353
            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;
354
            let err = sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint,
355 356 357 358
                             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);
359 360
            let err = sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint,
                             v.as_mut_ptr() as *mut c_void, &mut sz, ptr::null(), 0u as size_t);
361 362
            if err != 0 { return None; }
            if sz == 0 { return None; }
363
            v.set_len(sz as uint - 1); // chop off trailing NUL
364
            Some(v)
365
        }
366 367 368
    }

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

373
        match io::result(|| io::fs::readlink(&Path::new("/proc/self/exe"))) {
374
            Ok(Some(path)) => Some(path.as_vec().to_owned()),
A
Alex Crichton 已提交
375
            Ok(None) | Err(..) => None
376 377 378
        }
    }

379
    #[cfg(target_os = "macos")]
380
    fn load_self() -> Option<~[u8]> {
381
        unsafe {
382
            use libc::funcs::extra::_NSGetExecutablePath;
383
            use vec;
384 385 386 387
            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);
388
            let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
389
            if err != 0 { return None; }
390
            v.set_len(sz as uint - 1); // chop off trailing NUL
391
            Some(v)
392
        }
393 394
    }

395
    #[cfg(windows)]
396
    fn load_self() -> Option<~[u8]> {
397 398
        unsafe {
            use os::win32::fill_utf16_buf_and_decode;
399
            fill_utf16_buf_and_decode(|buf, sz| {
400
                libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
401
            }).map(|s| s.into_bytes())
402
        }
403 404
    }

405
    load_self().and_then(|path| Path::new_opt(path).map(|mut p| { p.pop(); p }))
406 407
}

408 409 410 411 412 413 414 415 416 417 418 419 420
/**
 * 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.
 */
421
pub fn homedir() -> Option<Path> {
422
    // FIXME (#7188): getenv needs a ~[u8] variant
423
    return match getenv("HOME") {
424
        Some(ref p) if !p.is_empty() => Path::new_opt(p.as_slice()),
425
        _ => secondary()
426 427
    };

428
    #[cfg(unix)]
B
Brian Anderson 已提交
429 430
    fn secondary() -> Option<Path> {
        None
431 432
    }

433
    #[cfg(windows)]
B
Brian Anderson 已提交
434
    fn secondary() -> Option<Path> {
435
        getenv("USERPROFILE").and_then(|p| {
436
            if !p.is_empty() {
437
                Path::new_opt(p)
438
            } else {
B
Brian Anderson 已提交
439
                None
440
            }
441
        })
442 443 444
    }
}

445
/**
446
 * Returns the path to a temporary directory.
447 448 449
 *
 * On Unix, returns the value of the 'TMPDIR' environment variable if it is
 * set and non-empty and '/tmp' otherwise.
450 451
 * On Android, there is no global temporary folder (it is usually allocated
 * per-app), hence returns '/data/tmp' which is commonly used.
452 453
 *
 * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
454 455
 * 'USERPROFILE' environment variable  if any are set and not the empty
 * string. Otherwise, tmpdir returns the path to the Windows directory.
456
 */
457
pub fn tmpdir() -> Path {
458 459
    return lookup();

B
Brian Anderson 已提交
460
    fn getenv_nonempty(v: &str) -> Option<Path> {
461
        match getenv(v) {
L
Luqman Aden 已提交
462
            Some(x) =>
463
                if x.is_empty() {
B
Brian Anderson 已提交
464
                    None
465
                } else {
466
                    Path::new_opt(x)
467
                },
B
Brian Anderson 已提交
468
            _ => None
469 470 471 472
        }
    }

    #[cfg(unix)]
473
    fn lookup() -> Path {
474
        if cfg!(target_os = "android") {
475
            Path::new("/data/tmp")
476
        } else {
477
            getenv_nonempty("TMPDIR").unwrap_or(Path::new("/tmp"))
478
        }
479 480 481
    }

    #[cfg(windows)]
482
    fn lookup() -> Path {
483 484 485
        getenv_nonempty("TMP").or(
            getenv_nonempty("TEMP").or(
                getenv_nonempty("USERPROFILE").or(
486
                   getenv_nonempty("WINDIR")))).unwrap_or(Path::new("C:\\Windows"))
487 488
    }
}
B
Brian Anderson 已提交
489

490 491 492 493 494
/**
 * 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
495
 * as is.
496
 */
497 498 499
// 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.
500
pub fn make_absolute(p: &Path) -> Path {
501 502
    if p.is_absolute() {
        p.clone()
503
    } else {
504
        let mut ret = getcwd();
505
        ret.push(p);
506
        ret
507
    }
508 509
}

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

515
    #[cfg(windows)]
516
    fn chdir(p: &Path) -> bool {
517 518
        unsafe {
            use os::win32::as_utf16_p;
519
            return as_utf16_p(p.as_str().unwrap(), |buf| {
520
                libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
521
            });
522
        }
523 524
    }

525
    #[cfg(unix)]
526
    fn chdir(p: &Path) -> bool {
527
        p.with_c_str(|buf| {
E
Erick Tryzelaar 已提交
528
            unsafe {
529
                libc::chdir(buf) == (0 as c_int)
E
Erick Tryzelaar 已提交
530
            }
531
        })
532 533 534
    }
}

535
#[cfg(unix)]
536
/// Returns the platform-specific value of errno
537 538 539 540 541 542
pub fn errno() -> int {
    #[cfg(target_os = "macos")]
    #[cfg(target_os = "freebsd")]
    fn errno_location() -> *c_int {
        #[nolink]
        extern {
543
            fn __error() -> *c_int;
544 545 546 547 548 549 550 551 552 553 554
        }
        unsafe {
            __error()
        }
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
    fn errno_location() -> *c_int {
        #[nolink]
        extern {
555
            fn __errno_location() -> *c_int;
556 557 558 559 560 561 562 563 564 565 566 567
        }
        unsafe {
            __errno_location()
        }
    }

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

#[cfg(windows)]
568
/// Returns the platform-specific value of errno
569 570 571 572
pub fn errno() -> uint {
    use libc::types::os::arch::extra::DWORD;

    #[link_name = "kernel32"]
A
Alex Crichton 已提交
573
    extern "system" {
K
klutzy 已提交
574 575 576
        fn GetLastError() -> DWORD;
    }

577
    unsafe {
578
        GetLastError() as uint
579 580 581
    }
}

582
/// Get a string representing the platform-dependent last error
583
pub fn last_os_error() -> ~str {
584 585 586 587 588
    #[cfg(unix)]
    fn strerror() -> ~str {
        #[cfg(target_os = "macos")]
        #[cfg(target_os = "android")]
        #[cfg(target_os = "freebsd")]
589 590
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t)
                      -> c_int {
591 592
            #[nolink]
            extern {
593 594
                fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t)
                              -> c_int;
595 596 597 598 599 600 601 602
            }
            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 已提交
603
        // So we just use __xpg_strerror_r which is always POSIX compliant
604
        #[cfg(target_os = "linux")]
605
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
606 607
            #[nolink]
            extern {
608 609 610 611
                fn __xpg_strerror_r(errnum: c_int,
                                    buf: *mut c_char,
                                    buflen: size_t)
                                    -> c_int;
612 613 614 615 616 617 618
            }
            unsafe {
                __xpg_strerror_r(errnum, buf, buflen)
            }
        }

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

620 621 622 623
        let p = buf.as_mut_ptr();
        unsafe {
            if strerror_r(errno() as c_int, p, buf.len() as size_t) < 0 {
                fail!("strerror_r failure");
624
            }
625 626 627

            str::raw::from_c_str(p as *c_char)
        }
628
    }
629 630 631

    #[cfg(windows)]
    fn strerror() -> ~str {
632
        use libc::types::os::arch::extra::DWORD;
633
        use libc::types::os::arch::extra::LPWSTR;
634
        use libc::types::os::arch::extra::LPVOID;
635
        use libc::types::os::arch::extra::WCHAR;
636 637

        #[link_name = "kernel32"]
A
Alex Crichton 已提交
638
        extern "system" {
639
            fn FormatMessageW(flags: DWORD,
K
klutzy 已提交
640 641 642
                              lpSrc: LPVOID,
                              msgId: DWORD,
                              langId: DWORD,
643
                              buf: LPWSTR,
K
klutzy 已提交
644 645 646 647 648
                              nsize: DWORD,
                              args: *c_void)
                              -> DWORD;
        }

649 650
        static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
        static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
651 652 653 654 655 656

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

657
        let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
658

659
        unsafe {
660 661 662 663 664 665 666 667 668 669 670
            let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
                                     FORMAT_MESSAGE_IGNORE_INSERTS,
                                     ptr::mut_null(),
                                     err,
                                     langId,
                                     buf.as_mut_ptr(),
                                     buf.len() as DWORD,
                                     ptr::null());
            if res == 0 {
                fail!("[{}] FormatMessage failure", errno());
            }
671

672
            str::from_utf16(buf)
673 674 675 676
        }
    }

    strerror()
677
}
678

679 680
static mut EXIT_STATUS: AtomicInt = INIT_ATOMIC_INT;

681 682 683 684 685 686
/**
 * 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
687 688 689
 * ignored and the process exits with the default failure status.
 *
 * Note that this is not synchronized against modifications of other threads.
690
 */
691
pub fn set_exit_status(code: int) {
692 693 694 695 696 697 698
    unsafe { EXIT_STATUS.store(code, SeqCst) }
}

/// Fetches the process's current exit code. This defaults to 0 and can change
/// by calling `set_exit_status`.
pub fn get_exit_status() -> int {
    unsafe { EXIT_STATUS.load(SeqCst) }
699
}
700

K
Kiet Tran 已提交
701
#[cfg(target_os = "macos")]
702
unsafe fn load_argc_and_argv(argc: int, argv: **c_char) -> ~[~str] {
703
    let mut args = ~[];
D
Daniel Micay 已提交
704
    for i in range(0u, argc as uint) {
705
        args.push(str::raw::from_c_str(*argv.offset(i as int)));
706
    }
L
Luqman Aden 已提交
707
    args
708 709
}

710 711 712 713 714 715
/**
 * Returns the command line arguments
 *
 * Returns a list of the command line arguments.
 */
#[cfg(target_os = "macos")]
716
fn real_args() -> ~[~str] {
717
    unsafe {
718
        let (argc, argv) = (*_NSGetArgc() as int,
719 720
                            *_NSGetArgv() as **c_char);
        load_argc_and_argv(argc, argv)
721 722 723
    }
}

724
#[cfg(target_os = "linux")]
K
kyeongwoon 已提交
725
#[cfg(target_os = "android")]
726
#[cfg(target_os = "freebsd")]
727
fn real_args() -> ~[~str] {
728 729
    use rt;

730 731
    match rt::args::clone() {
        Some(args) => args,
732
        None => fail!("process arguments not initialized")
733
    }
734 735
}

736
#[cfg(windows)]
737
fn real_args() -> ~[~str] {
738
    use vec;
739

740
    let mut nArgs: c_int = 0;
D
Daniel Micay 已提交
741
    let lpArgCount: *mut c_int = &mut nArgs;
T
Tim Chevalier 已提交
742 743
    let lpCmdLine = unsafe { GetCommandLineW() };
    let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
744 745

    let mut args = ~[];
D
Daniel Micay 已提交
746
    for i in range(0u, nArgs as uint) {
747 748
        unsafe {
            // Determine the length of this argument.
749
            let ptr = *szArgList.offset(i as int);
750
            let mut len = 0;
751
            while *ptr.offset(len as int) != 0 { len += 1; }
752 753

            // Push it onto the list.
754
            args.push(vec::raw::buf_as_slice(ptr, len,
755
                                             str::from_utf16));
756 757 758 759
        }
    }

    unsafe {
760
        LocalFree(szArgList as *c_void);
761 762 763 764 765 766 767
    }

    return args;
}

type LPCWSTR = *u16;

A
Alex Crichton 已提交
768
#[cfg(windows)]
K
klutzy 已提交
769
#[link_name="kernel32"]
A
Alex Crichton 已提交
770
extern "system" {
K
klutzy 已提交
771 772 773 774
    fn GetCommandLineW() -> LPCWSTR;
    fn LocalFree(ptr: *c_void);
}

A
Alex Crichton 已提交
775
#[cfg(windows)]
K
klutzy 已提交
776
#[link_name="shell32"]
A
Alex Crichton 已提交
777
extern "system" {
K
klutzy 已提交
778 779 780
    fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
}

781 782
/// Returns the arguments which this program was started with (normally passed
/// via the command line).
783
pub fn args() -> ~[~str] {
784
    real_args()
785 786
}

787 788 789 790 791 792 793
#[cfg(target_os = "macos")]
extern {
    // These functions are in crt_externs.h.
    pub fn _NSGetArgc() -> *c_int;
    pub fn _NSGetArgv() -> ***c_char;
}

794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
// 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 已提交
817 818 819
    unsafe {
        let mut info = libc::SYSTEM_INFO::new();
        libc::GetSystemInfo(&mut info);
820

V
Vadim Chugunov 已提交
821 822
        return info.dwPageSize as uint;
    }
823 824
}

C
Corey Richardson 已提交
825 826 827 828 829 830 831
/// 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.
832
pub struct MemoryMap {
C
Corey Richardson 已提交
833
    /// Pointer to the memory created or modified by this map.
834
    data: *mut u8,
C
Corey Richardson 已提交
835
    /// Number of bytes this map applies to
836
    len: uint,
C
Corey Richardson 已提交
837
    /// Type of mapping
838 839 840
    kind: MemoryMapKind
}

C
Corey Richardson 已提交
841
/// Type of memory map
842
pub enum MemoryMapKind {
C
Corey Richardson 已提交
843 844
    /// Memory-mapped file. On Windows, the inner pointer is a handle to the mapping, and
    /// corresponds to `CreateFileMapping`. Elsewhere, it is null.
845
    MapFile(*u8),
C
Corey Richardson 已提交
846 847
    /// Virtual memory map. Usually used to change the permissions of a given chunk of memory.
    /// Corresponds to `VirtualAlloc` on Windows.
848 849 850
    MapVirtual
}

C
Corey Richardson 已提交
851
/// Options the memory map is created with
852
pub enum MapOption {
C
Corey Richardson 已提交
853
    /// The memory should be readable
854
    MapReadable,
C
Corey Richardson 已提交
855
    /// The memory should be writable
856
    MapWritable,
C
Corey Richardson 已提交
857
    /// The memory should be executable
858
    MapExecutable,
C
Corey Richardson 已提交
859
    /// Create a map for a specific address range. Corresponds to `MAP_FIXED` on POSIX.
860
    MapAddr(*u8),
C
Corey Richardson 已提交
861
    /// Create a memory mapping for a file with a given fd.
862
    MapFd(c_int),
C
Corey Richardson 已提交
863
    /// When using `MapFd`, the start of the map is `uint` bytes from the start of the file.
864 865 866
    MapOffset(uint)
}

C
Corey Richardson 已提交
867
/// Possible errors when creating a map.
868
pub enum MapError {
C
Corey Richardson 已提交
869 870 871
    /// ## The following are POSIX-specific
    ///
    /// fd was not open for reading or, if using `MapWritable`, was not open for writing.
872
    ErrFdNotAvail,
C
Corey Richardson 已提交
873
    /// fd was not valid
874
    ErrInvalidFd,
C
Corey Richardson 已提交
875 876
    /// Either the address given by `MapAddr` or offset given by `MapOffset` was not a multiple of
    /// `MemoryMap::granularity` (unaligned to page size).
877
    ErrUnaligned,
C
Corey Richardson 已提交
878
    /// With `MapFd`, the fd does not support mapping.
879
    ErrNoMapSupport,
C
Corey Richardson 已提交
880 881
    /// 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.
882
    ErrNoMem,
C
Corey Richardson 已提交
883
    /// Unrecognized error. The inner value is the unrecognized errno.
884
    ErrUnknown(int),
C
Corey Richardson 已提交
885 886 887
    /// ## The following are win32-specific
    ///
    /// Unsupported combination of protection flags (`MapReadable`/`MapWritable`/`MapExecutable`).
888
    ErrUnsupProt,
C
Corey Richardson 已提交
889
    /// When using `MapFd`, `MapOffset` was given (Windows does not support this at all)
890
    ErrUnsupOffset,
C
Corey Richardson 已提交
891
    /// When using `MapFd`, there was already a mapping to the file.
892
    ErrAlreadyExists,
C
Corey Richardson 已提交
893
    /// Unrecognized error from `VirtualAlloc`. The inner value is the return value of GetLastError.
894
    ErrVirtualAlloc(uint),
C
Corey Richardson 已提交
895 896
    /// Unrecognized error from `CreateFileMapping`. The inner value is the return value of
    /// `GetLastError`.
897
    ErrCreateFileMappingW(uint),
C
Corey Richardson 已提交
898 899
    /// Unrecognized error from `MapViewOfFile`. The inner value is the return value of
    /// `GetLastError`.
900 901 902 903 904 905 906 907 908 909 910 911
    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 已提交
912
            ErrUnknown(code) => format!("Unknown error={}", code),
913 914 915
            ErrUnsupProt => ~"Protection mode unsupported",
            ErrUnsupOffset => ~"Offset in virtual memory mode is unsupported",
            ErrAlreadyExists => ~"File mapping for specified file already exists",
A
Alex Crichton 已提交
916 917 918
            ErrVirtualAlloc(code) => format!("VirtualAlloc failure={}", code),
            ErrCreateFileMappingW(code) => format!("CreateFileMappingW failure={}", code),
            ErrMapViewOfFile(code) => format!("MapViewOfFile failure={}", code)
919 920 921 922 923 924
        }
    }
}

#[cfg(unix)]
impl MemoryMap {
C
Corey Richardson 已提交
925
    /// Create a new mapping with the given `options`, at least `min_len` bytes long.
926
    pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
927 928
        use libc::off_t;

929 930 931 932 933 934
        let mut addr: *u8 = ptr::null();
        let mut prot = 0;
        let mut flags = libc::MAP_PRIVATE;
        let mut fd = -1;
        let mut offset = 0;
        let len = round_up(min_len, page_size());
935

D
Daniel Micay 已提交
936
        for &o in options.iter() {
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
            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 {
955
            libc::mmap(addr as *c_void, len as size_t, prot, flags, fd, offset)
956
        };
957
        if r.equiv(&libc::MAP_FAILED) {
958 959 960 961 962 963
            Err(match errno() as c_int {
                libc::EACCES => ErrFdNotAvail,
                libc::EBADF => ErrInvalidFd,
                libc::EINVAL => ErrUnaligned,
                libc::ENODEV => ErrNoMapSupport,
                libc::ENOMEM => ErrNoMem,
964
                code => ErrUnknown(code as int)
965 966
            })
        } else {
967
            Ok(MemoryMap {
968 969 970 971 972 973 974 975 976 977
               data: r as *mut u8,
               len: len,
               kind: if fd == -1 {
                   MapVirtual
               } else {
                   MapFile(ptr::null())
               }
            })
        }
    }
V
Vadim Chugunov 已提交
978

C
Corey Richardson 已提交
979
    /// Granularity that the offset or address must be for `MapOffset` and `MapAddr` respectively.
V
Vadim Chugunov 已提交
980 981 982
    pub fn granularity() -> uint {
        page_size()
    }
983 984 985 986
}

#[cfg(unix)]
impl Drop for MemoryMap {
C
Corey Richardson 已提交
987
    /// Unmap the mapping. Fails the task if `munmap` fails.
D
Daniel Micay 已提交
988
    fn drop(&mut self) {
989
        unsafe {
990
            match libc::munmap(self.data as *c_void, self.len as libc::size_t) {
991
                0 => (),
A
Alex Crichton 已提交
992
                -1 => match errno() as c_int {
993 994
                    libc::EINVAL => error!("invalid addr or len"),
                    e => error!("unknown errno={}", e)
A
Alex Crichton 已提交
995
                },
996
                r => error!("Unexpected result {}", r)
997 998 999 1000 1001 1002 1003
            }
        }
    }
}

#[cfg(windows)]
impl MemoryMap {
C
Corey Richardson 已提交
1004
    /// Create a new mapping with the given `options`, at least `min_len` bytes long.
1005
    pub fn new(min_len: uint, options: &[MapOption]) -> Result<MemoryMap, MapError> {
1006 1007 1008 1009 1010 1011 1012 1013
        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;
1014
        let len = round_up(min_len, page_size());
1015

D
Daniel Micay 已提交
1016
        for &o in options.iter() {
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
            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,
1043
                                   len as SIZE_T,
1044 1045 1046 1047 1048
                                   libc::MEM_COMMIT | libc::MEM_RESERVE,
                                   flProtect)
            };
            match r as uint {
                0 => Err(ErrVirtualAlloc(errno())),
1049
                _ => Ok(MemoryMap {
1050 1051 1052 1053 1054 1055
                   data: r as *mut u8,
                   len: len,
                   kind: MapVirtual
                })
            }
        } else {
V
Vadim Chugunov 已提交
1056 1057 1058 1059 1060 1061 1062
            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.
1063 1064 1065 1066 1067 1068
            };
            unsafe {
                let hFile = libc::get_osfhandle(fd) as HANDLE;
                let mapping = libc::CreateFileMappingW(hFile,
                                                       ptr::mut_null(),
                                                       flProtect,
V
Vadim Chugunov 已提交
1069 1070
                                                       0,
                                                       0,
1071 1072 1073 1074 1075 1076 1077 1078 1079
                                                       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 已提交
1080
                                            ((len as u64) >> 32) as DWORD,
1081 1082 1083 1084
                                            (offset & 0xffff_ffff) as DWORD,
                                            0);
                match r as uint {
                    0 => Err(ErrMapViewOfFile(errno())),
1085
                    _ => Ok(MemoryMap {
1086 1087
                       data: r as *mut u8,
                       len: len,
1088
                       kind: MapFile(mapping as *u8)
1089 1090 1091 1092 1093
                    })
                }
            }
        }
    }
V
Vadim Chugunov 已提交
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104

    /// 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;
        }
    }
1105 1106 1107 1108
}

#[cfg(windows)]
impl Drop for MemoryMap {
C
Corey Richardson 已提交
1109 1110
    /// Unmap the mapping. Fails the task if any of `VirtualFree`, `UnmapViewOfFile`, or
    /// `CloseHandle` fail.
D
Daniel Micay 已提交
1111
    fn drop(&mut self) {
1112
        use libc::types::os::arch::extra::{LPCVOID, HANDLE};
V
Vadim Chugunov 已提交
1113
        use libc::consts::os::extra::FALSE;
1114 1115 1116

        unsafe {
            match self.kind {
V
Vadim Chugunov 已提交
1117 1118
                MapVirtual => {
                    if libc::VirtualFree(self.data as *mut c_void,
1119
                                         self.len as size_t,
V
Vadim Chugunov 已提交
1120
                                         libc::MEM_RELEASE) == FALSE {
1121
                        error!("VirtualFree failed: {}", errno());
V
Vadim Chugunov 已提交
1122
                    }
1123 1124
                },
                MapFile(mapping) => {
V
Vadim Chugunov 已提交
1125
                    if libc::UnmapViewOfFile(self.data as LPCVOID) == FALSE {
1126
                        error!("UnmapViewOfFile failed: {}", errno());
1127
                    }
V
Vadim Chugunov 已提交
1128
                    if libc::CloseHandle(mapping as HANDLE) == FALSE {
1129
                        error!("CloseHandle failed: {}", errno());
1130 1131 1132 1133 1134 1135 1136
                    }
                }
            }
        }
    }
}

1137
pub mod consts {
1138

I
ILyoan 已提交
1139
    #[cfg(unix)]
1140
    pub use os::consts::unix::*;
1141

I
ILyoan 已提交
1142
    #[cfg(windows)]
1143
    pub use os::consts::windows::*;
1144

I
ILyoan 已提交
1145
    #[cfg(target_os = "macos")]
1146
    pub use os::consts::macos::*;
I
ILyoan 已提交
1147 1148

    #[cfg(target_os = "freebsd")]
1149
    pub use os::consts::freebsd::*;
I
ILyoan 已提交
1150 1151

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

K
kyeongwoon 已提交
1154
    #[cfg(target_os = "android")]
1155
    pub use os::consts::android::*;
K
kyeongwoon 已提交
1156

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

1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
    #[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")]
1170
    pub use os::consts::mips::*;
1171 1172 1173 1174 1175 1176 1177 1178 1179

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

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

I
ILyoan 已提交
1180
    pub mod macos {
1181 1182 1183
        pub static SYSNAME: &'static str = "macos";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".dylib";
1184
        pub static DLL_EXTENSION: &'static str = "dylib";
1185
        pub static EXE_SUFFIX: &'static str = "";
1186
        pub static EXE_EXTENSION: &'static str = "";
I
ILyoan 已提交
1187 1188 1189
    }

    pub mod freebsd {
1190 1191 1192
        pub static SYSNAME: &'static str = "freebsd";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
1193
        pub static DLL_EXTENSION: &'static str = "so";
1194
        pub static EXE_SUFFIX: &'static str = "";
1195
        pub static EXE_EXTENSION: &'static str = "";
I
ILyoan 已提交
1196 1197 1198
    }

    pub mod linux {
1199 1200 1201
        pub static SYSNAME: &'static str = "linux";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
1202
        pub static DLL_EXTENSION: &'static str = "so";
1203
        pub static EXE_SUFFIX: &'static str = "";
1204
        pub static EXE_EXTENSION: &'static str = "";
I
ILyoan 已提交
1205
    }
K
kyeongwoon 已提交
1206 1207

    pub mod android {
1208 1209 1210
        pub static SYSNAME: &'static str = "android";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
1211
        pub static DLL_EXTENSION: &'static str = "so";
1212
        pub static EXE_SUFFIX: &'static str = "";
1213
        pub static EXE_EXTENSION: &'static str = "";
K
kyeongwoon 已提交
1214
    }
1215

I
ILyoan 已提交
1216
    pub mod win32 {
1217 1218 1219
        pub static SYSNAME: &'static str = "win32";
        pub static DLL_PREFIX: &'static str = "";
        pub static DLL_SUFFIX: &'static str = ".dll";
1220
        pub static DLL_EXTENSION: &'static str = "dll";
1221
        pub static EXE_SUFFIX: &'static str = ".exe";
1222
        pub static EXE_EXTENSION: &'static str = "exe";
I
ILyoan 已提交
1223 1224 1225 1226
    }


    pub mod x86 {
1227
        pub static ARCH: &'static str = "x86";
I
ILyoan 已提交
1228 1229
    }
    pub mod x86_64 {
1230
        pub static ARCH: &'static str = "x86_64";
I
ILyoan 已提交
1231 1232
    }
    pub mod arm {
1233
        pub static ARCH: &'static str = "arm";
I
ILyoan 已提交
1234
    }
J
Jyun-Yan You 已提交
1235
    pub mod mips {
1236
        pub static ARCH: &'static str = "mips";
J
Jyun-Yan You 已提交
1237
    }
I
ILyoan 已提交
1238
}
1239 1240 1241

#[cfg(test)]
mod tests {
1242
    use prelude::*;
1243
    use c_str::ToCStr;
1244
    use option;
1245
    use os::{env, getcwd, getenv, make_absolute, args};
1246
    use os::{setenv, unsetenv};
1247
    use os;
1248
    use rand::Rng;
1249
    use rand;
1250

1251

1252
    #[test]
1253
    pub fn last_os_error() {
1254
        debug!("{}", os::last_os_error());
1255
    }
1256

1257 1258
    #[test]
    pub fn test_args() {
1259
        let a = args();
P
Patrick Walton 已提交
1260
        assert!(a.len() >= 1);
1261 1262
    }

1263
    fn make_rand_name() -> ~str {
P
Patrick Walton 已提交
1264
        let mut rng = rand::rng();
1265
        let n = ~"TEST" + rng.gen_ascii_str(10u);
P
Patrick Walton 已提交
1266
        assert!(getenv(n).is_none());
L
Luqman Aden 已提交
1267
        n
1268 1269 1270 1271 1272
    }

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

C
Corey Richardson 已提交
1277 1278 1279
    #[test]
    fn test_unsetenv() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1280
        setenv(n, "VALUE");
C
Corey Richardson 已提交
1281
        unsetenv(n);
1282
        assert_eq!(getenv(n), option::None);
C
Corey Richardson 已提交
1283 1284
    }

1285
    #[test]
1286
    #[ignore]
1287 1288
    fn test_setenv_overwrite() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1289 1290
        setenv(n, "1");
        setenv(n, "2");
1291
        assert_eq!(getenv(n), option::Some(~"2"));
E
Erick Tryzelaar 已提交
1292
        setenv(n, "");
1293
        assert_eq!(getenv(n), option::Some(~""));
1294 1295 1296 1297 1298
    }

    // Windows GetEnvironmentVariable requires some extra work to make sure
    // the buffer the variable is copied into is the right size
    #[test]
1299
    #[ignore]
1300
    fn test_getenv_big() {
1301
        let mut s = ~"";
1302
        let mut i = 0;
1303 1304 1305 1306
        while i < 100 {
            s = s + "aaaaaaaaaa";
            i += 1;
        }
1307 1308
        let n = make_rand_name();
        setenv(n, s);
1309
        debug!("{}", s.clone());
1310
        assert_eq!(getenv(n), option::Some(s));
1311 1312 1313 1314 1315
    }

    #[test]
    fn test_self_exe_path() {
        let path = os::self_exe_path();
P
Patrick Walton 已提交
1316
        assert!(path.is_some());
1317
        let path = path.unwrap();
1318
        debug!("{:?}", path.clone());
1319 1320

        // Hard to test this function
1321
        assert!(path.is_absolute());
1322 1323 1324
    }

    #[test]
1325
    #[ignore]
1326 1327
    fn test_env_getenv() {
        let e = env();
Y
Youngmin Yoo 已提交
1328
        assert!(e.len() > 0u);
D
Daniel Micay 已提交
1329
        for p in e.iter() {
1330
            let (n, v) = (*p).clone();
1331
            debug!("{:?}", n.clone());
1332 1333 1334 1335
            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 已提交
1336
            assert!(v2.is_none() || v2 == option::Some(v));
1337 1338 1339 1340 1341 1342 1343
        }
    }

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

1344
        let mut e = env();
E
Erick Tryzelaar 已提交
1345
        setenv(n, "VALUE");
1346
        assert!(!e.contains(&(n.clone(), ~"VALUE")));
1347 1348

        e = env();
1349
        assert!(e.contains(&(n, ~"VALUE")));
1350 1351
    }

1352 1353
    #[test]
    fn test() {
1354
        assert!((!Path::new("test-path").is_absolute()));
1355

1356
        let cwd = getcwd();
1357
        debug!("Current working directory: {}", cwd.display());
1358

1359 1360
        debug!("{:?}", make_absolute(&Path::new("test-path")));
        debug!("{:?}", make_absolute(&Path::new("/usr/bin")));
1361 1362 1363
    }

    #[test]
1364
    #[cfg(unix)]
1365
    fn homedir() {
E
Erick Tryzelaar 已提交
1366
        let oldhome = getenv("HOME");
1367

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

E
Erick Tryzelaar 已提交
1371
        setenv("HOME", "");
P
Patrick Walton 已提交
1372
        assert!(os::homedir().is_none());
1373

D
Daniel Micay 已提交
1374
        for s in oldhome.iter() { setenv("HOME", *s) }
1375 1376 1377
    }

    #[test]
1378
    #[cfg(windows)]
1379 1380
    fn homedir() {

E
Erick Tryzelaar 已提交
1381 1382
        let oldhome = getenv("HOME");
        let olduserprofile = getenv("USERPROFILE");
1383

E
Erick Tryzelaar 已提交
1384 1385
        setenv("HOME", "");
        setenv("USERPROFILE", "");
1386

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

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

E
Erick Tryzelaar 已提交
1392
        setenv("HOME", "");
1393

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

E
Erick Tryzelaar 已提交
1397 1398
        setenv("HOME", "/home/MountainView");
        setenv("USERPROFILE", "/home/PaloAlto");
1399
        assert_eq!(os::homedir(), Some(Path::new("/home/MountainView")));
1400

1401 1402
        for s in oldhome.iter() { setenv("HOME", *s) }
        for s in olduserprofile.iter() { setenv("USERPROFILE", *s) }
1403 1404
    }

1405 1406 1407 1408
    #[test]
    fn memory_map_rw() {
        use result::{Ok, Err};

1409
        let chunk = match os::MemoryMap::new(16, [
1410 1411 1412 1413
            os::MapReadable,
            os::MapWritable
        ]) {
            Ok(chunk) => chunk,
1414
            Err(msg) => fail!(msg.to_str())
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
        };
        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 已提交
1429 1430
        use io;
        use io::fs;
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444

        #[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);
           }
        }

1445
        let mut path = tmpdir();
1446
        path.push("mmap_file.tmp");
V
Vadim Chugunov 已提交
1447
        let size = MemoryMap::granularity() * 2;
1448 1449

        let fd = unsafe {
1450
            let fd = path.with_c_str(|path| {
1451
                open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
1452
            });
1453
            lseek_(fd, size);
1454
            "x".with_c_str(|x| assert!(write(fd, x as *c_void, 1) == 1));
1455 1456
            fd
        };
1457
        let chunk = match MemoryMap::new(size / 2, [
1458 1459 1460 1461 1462 1463
            MapReadable,
            MapWritable,
            MapFd(fd),
            MapOffset(size / 2)
        ]) {
            Ok(chunk) => chunk,
1464
            Err(msg) => fail!(msg.to_str())
1465 1466 1467 1468 1469 1470 1471 1472
        };
        assert!(chunk.len > 0);

        unsafe {
            *chunk.data = 0xbe;
            assert!(*chunk.data == 0xbe);
            close(fd);
        }
1473 1474 1475

        let _guard = io::ignore_io_error();
        fs::unlink(&path);
1476 1477
    }

1478
    // More recursive_mkdir tests are in extra::tempfile
1479
}