os.rs 59.5 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 cast;
32
use clone::Clone;
33
use container::Container;
34
use io;
35
use iterator::IteratorUtil;
36
use libc;
A
Alex Crichton 已提交
37
use libc::{c_char, c_void, c_int, size_t};
38
use libc::FILE;
39
use local_data;
P
Patrick Walton 已提交
40
use option::{Some, None};
41
use os;
42
use prelude::*;
43 44
use ptr;
use str;
45
use to_str;
46
use uint;
47
use unstable::finally::Finally;
48
use vec;
49

50
pub use libc::fclose;
51
pub use os::consts::*;
52

53
/// Delegates to the libc close() function, returning the same return value.
54 55 56 57 58 59
pub fn close(fd: c_int) -> c_int {
    unsafe {
        libc::close(fd)
    }
}

60 61 62 63
pub mod rustrt {
    use libc::{c_char, c_int};
    use libc;

64 65 66 67 68 69
    extern {
        pub unsafe fn rust_get_argc() -> c_int;
        pub unsafe fn rust_get_argv() -> **c_char;
        pub unsafe fn rust_path_is_dir(path: *libc::c_char) -> c_int;
        pub unsafe fn rust_path_exists(path: *libc::c_char) -> c_int;
        pub unsafe fn rust_set_exit_status(code: libc::intptr_t);
70
    }
71 72
}

73 74
pub static TMPBUF_SZ : uint = 1000u;
static BUF_BYTES : uint = 2048u;
75

76
pub fn getcwd() -> Path {
77
    let buf = [0 as libc::c_char, ..BUF_BYTES];
78
    unsafe {
79 80 81 82 83 84
        if(0 as *libc::c_char == libc::getcwd(
            &buf[0],
            BUF_BYTES as libc::size_t)) {
            fail!();
        }
        Path(str::raw::from_c_str(&buf[0]))
85
    }
86 87
}

88 89
// FIXME: move these to str perhaps? #2620

90
pub fn as_c_charp<T>(s: &str, f: &fn(*c_char) -> T) -> T {
B
Brian Anderson 已提交
91
    str::as_c_str(s, |b| f(b as *c_char))
92 93
}

94
pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool)
B
Brian Anderson 已提交
95
    -> Option<~str> {
B
Ben Striegel 已提交
96
    let mut buf = vec::from_elem(TMPBUF_SZ, 0u8 as c_char);
97
    do buf.as_mut_buf |b, sz| {
98 99 100 101
        if f(b, sz as size_t) {
            unsafe {
                Some(str::raw::from_buf(b as *u8))
            }
102
        } else {
B
Brian Anderson 已提交
103
            None
104 105 106 107
        }
    }
}

108
#[cfg(windows)]
109
pub mod win32 {
110 111 112
    use libc;
    use vec;
    use str;
113
    use option::{None, Option};
114
    use option;
115
    use os::TMPBUF_SZ;
116
    use libc::types::os::arch::extra::DWORD;
117

118
    pub fn fill_utf16_buf_and_decode(f: &fn(*mut u16, DWORD) -> DWORD)
B
Brian Anderson 已提交
119
        -> Option<~str> {
120
        unsafe {
121
            let mut n = TMPBUF_SZ as DWORD;
122 123 124
            let mut res = None;
            let mut done = false;
            while !done {
125
                let mut k: DWORD = 0;
126
                let mut buf = vec::from_elem(n as uint, 0u16);
127
                do buf.as_mut_buf |b, _sz| {
128
                    k = f(b, TMPBUF_SZ as DWORD);
129 130 131 132 133 134 135 136 137
                    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;
                    }
138
                }
139
                if k != 0 && done {
140
                    let sub = buf.slice(0, k as uint);
141 142
                    res = option::Some(str::from_utf16(sub));
                }
143
            }
144
            return res;
145 146 147
        }
    }

148
    pub fn as_utf16_p<T>(s: &str, f: &fn(*u16) -> T) -> T {
149
        let mut t = s.to_utf16();
150
        // Null terminate before passing on.
151
        t.push(0u16);
152
        t.as_imm_buf(|buf, _len| f(buf))
153
    }
154 155
}

156 157
/*
Accessing environment variables is not generally threadsafe.
158
Serialize access through a global lock.
159 160
*/
fn with_env_lock<T>(f: &fn() -> T) -> T {
161
    use unstable::finally::Finally;
162

163
    unsafe {
164 165 166 167 168 169 170
        return do (|| {
            rust_take_env_lock();
            f()
        }).finally {
            rust_drop_env_lock();
        };
    }
171

172 173 174 175 176
    extern {
        #[fast_ffi]
        fn rust_take_env_lock();
        #[fast_ffi]
        fn rust_drop_env_lock();
177
    }
B
Ben Blum 已提交
178 179
}

180 181
/// Returns a vector of (variable, value) pairs for all the environment
/// variables of the current process.
182 183
pub fn env() -> ~[(~str,~str)] {
    unsafe {
184 185 186 187 188 189 190 191
        #[cfg(windows)]
        unsafe fn get_env_pairs() -> ~[~str] {
            use libc::funcs::extra::kernel32::{
                GetEnvironmentStringsA,
                FreeEnvironmentStringsA
            };
            let ch = GetEnvironmentStringsA();
            if (ch as uint == 0) {
M
Marvin Löbel 已提交
192
                fail!("os::env() failure getting env string from OS: %s", os::last_os_error());
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
            }
            let mut curr_ptr: uint = ch as uint;
            let mut result = ~[];
            while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) {
                let env_pair = str::raw::from_c_str(
                    curr_ptr as *libc::c_char);
                result.push(env_pair);
                curr_ptr +=
                    libc::strlen(curr_ptr as *libc::c_char) as uint
                    + 1;
            }
            FreeEnvironmentStringsA(ch);
            result
        }
        #[cfg(unix)]
        unsafe fn get_env_pairs() -> ~[~str] {
209
            extern {
210 211
                unsafe fn rust_env_pairs() -> **libc::c_char;
            }
212
            let environ = rust_env_pairs();
213
            if (environ as uint == 0) {
M
Marvin Löbel 已提交
214
                fail!("os::env() failure getting env string from OS: %s", os::last_os_error());
215 216 217 218
            }
            let mut result = ~[];
            ptr::array_each(environ, |e| {
                let env_pair = str::raw::from_c_str(e);
B
Brian Anderson 已提交
219 220
                debug!("get_env_pairs: %s",
                       env_pair);
221 222 223 224 225 226
                result.push(env_pair);
            });
            result
        }

        fn env_convert(input: ~[~str]) -> ~[(~str, ~str)] {
227
            let mut pairs = ~[];
228
            for input.iter().advance |p| {
229
                let vs: ~[&str] = p.splitn_iter('=', 1).collect();
B
Brian Anderson 已提交
230 231
                debug!("splitting: len: %u",
                    vs.len());
232
                assert_eq!(vs.len(), 2);
233
                pairs.push((vs[0].to_owned(), vs[1].to_owned()));
234
            }
L
Luqman Aden 已提交
235
            pairs
236
        }
237 238 239 240
        do with_env_lock {
            let unparsed_environ = get_env_pairs();
            env_convert(unparsed_environ)
        }
241
    }
242
}
243

244
#[cfg(unix)]
245 246
/// Fetches the environment variable `n` from the current process, returning
/// None if the variable isn't set.
247 248 249 250
pub fn getenv(n: &str) -> Option<~str> {
    unsafe {
        do with_env_lock {
            let s = str::as_c_str(n, |s| libc::getenv(s));
251
            if ptr::null::<u8>() == cast::transmute(s) {
252
                None::<~str>
253
            } else {
254
                let s = cast::transmute(s);
255
                Some::<~str>(str::raw::from_buf(s))
256
            }
257
        }
258 259
    }
}
260

261
#[cfg(windows)]
262 263
/// Fetches the environment variable `n` from the current process, returning
/// None if the variable isn't set.
264 265 266 267 268 269 270
pub fn getenv(n: &str) -> Option<~str> {
    unsafe {
        do with_env_lock {
            use os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
            do as_utf16_p(n) |u| {
                do fill_utf16_buf_and_decode() |buf, sz| {
                    libc::GetEnvironmentVariableW(u, buf, sz)
271 272 273
                }
            }
        }
274 275
    }
}
276 277


278
#[cfg(unix)]
279 280
/// Sets the environment variable `n` to the value `v` for the currently running
/// process
281 282 283 284 285 286
pub fn setenv(n: &str, v: &str) {
    unsafe {
        do with_env_lock {
            do str::as_c_str(n) |nbuf| {
                do str::as_c_str(v) |vbuf| {
                    libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
287 288 289
                }
            }
        }
290 291
    }
}
292 293


294
#[cfg(windows)]
295 296
/// Sets the environment variable `n` to the value `v` for the currently running
/// process
297 298 299 300 301 302 303
pub fn setenv(n: &str, v: &str) {
    unsafe {
        do with_env_lock {
            use os::win32::as_utf16_p;
            do as_utf16_p(n) |nbuf| {
                do as_utf16_p(v) |vbuf| {
                    libc::SetEnvironmentVariableW(nbuf, vbuf);
304 305 306 307 308 309
                }
            }
        }
    }
}

C
Corey Richardson 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
/// Remove a variable from the environment entirely
pub fn unsetenv(n: &str) {
    #[cfg(unix)]
    fn _unsetenv(n: &str) {
        unsafe {
            do with_env_lock {
                do str::as_c_str(n) |nbuf| {
                    libc::funcs::posix01::unistd::unsetenv(nbuf);
                }
            }
        }
    }
    #[cfg(windows)]
    fn _unsetenv(n: &str) {
        unsafe {
            do with_env_lock {
                use os::win32::as_utf16_p;
                do as_utf16_p(n) |nbuf| {
                    libc::SetEnvironmentVariableW(nbuf, ptr::null());
                }
            }
        }
    }

    _unsetenv(n);
}

337
pub fn fdopen(fd: c_int) -> *FILE {
338 339 340 341 342
    unsafe {
        return do as_c_charp("r") |modebuf| {
            libc::fdopen(fd, modebuf)
        };
    }
343 344 345
}


346 347
// fsync related

348
#[cfg(windows)]
349
pub fn fsync_fd(fd: c_int, _level: io::fsync::Level) -> c_int {
350 351 352 353
    unsafe {
        use libc::funcs::extra::msvcrt::*;
        return commit(fd);
    }
354 355 356
}

#[cfg(target_os = "linux")]
K
kyeongwoon 已提交
357
#[cfg(target_os = "android")]
358
pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
359 360 361 362 363 364 365
    unsafe {
        use libc::funcs::posix01::unistd::*;
        match level {
          io::fsync::FSync
          | io::fsync::FullFSync => return fsync(fd),
          io::fsync::FDataSync => return fdatasync(fd)
        }
366 367 368 369
    }
}

#[cfg(target_os = "macos")]
370
pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    unsafe {
        use libc::consts::os::extra::*;
        use libc::funcs::posix88::fcntl::*;
        use libc::funcs::posix01::unistd::*;
        match level {
          io::fsync::FSync => return fsync(fd),
          _ => {
            // According to man fnctl, the ok retval is only specified to be
            // !=-1
            if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int)
                { return -1 as c_int; }
            else
                { return 0 as c_int; }
          }
        }
386 387 388 389
    }
}

#[cfg(target_os = "freebsd")]
390
pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
391 392 393 394
    unsafe {
        use libc::funcs::posix01::unistd::*;
        return fsync(fd);
    }
395 396
}

397 398 399 400
pub struct Pipe {
    in: c_int,
    out: c_int
}
401

402
#[cfg(unix)]
403
pub fn pipe() -> Pipe {
404
    unsafe {
405
        let mut fds = Pipe {in: 0 as c_int,
406
                            out: 0 as c_int };
407
        assert_eq!(libc::pipe(&mut fds.in), (0 as c_int));
408
        return Pipe {in: fds.in, out: fds.out};
409
    }
410 411 412 413
}



414
#[cfg(windows)]
415
pub fn pipe() -> Pipe {
416 417 418 419 420
    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
421
        // first, as in core::run.
422 423
        let mut fds = Pipe {in: 0 as c_int,
                    out: 0 as c_int };
A
Alex Crichton 已提交
424
        let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint,
425
                             (libc::O_BINARY | libc::O_NOINHERIT) as c_int);
426
        assert_eq!(res, 0 as c_int);
P
Patrick Walton 已提交
427 428
        assert!((fds.in != -1 as c_int && fds.in != 0 as c_int));
        assert!((fds.out != -1 as c_int && fds.in != 0 as c_int));
429
        return Pipe {in: fds.in, out: fds.out};
430
    }
431 432
}

P
Patrick Walton 已提交
433
fn dup2(src: c_int, dst: c_int) -> c_int {
434 435 436
    unsafe {
        libc::dup2(src, dst)
    }
P
Patrick Walton 已提交
437 438
}

439
/// Returns the proper dll filename for the given basename of a file.
440
pub fn dll_filename(base: &str) -> ~str {
441
    fmt!("%s%s%s", DLL_PREFIX, base, DLL_SUFFIX)
442 443
}

444 445
/// Optionally returns the filesystem path to the current executable which is
/// running. If any failure occurs, None is returned.
446
pub fn self_exe_path() -> Option<Path> {
447 448

    #[cfg(target_os = "freebsd")]
B
Brian Anderson 已提交
449
    fn load_self() -> Option<~str> {
450
        unsafe {
451 452
            use libc::funcs::bsd44::*;
            use libc::consts::os::extra::*;
B
Brian Anderson 已提交
453
            do fill_charp_buf() |buf, sz| {
454
                let mib = ~[CTL_KERN as c_int,
455
                           KERN_PROC as c_int,
456
                           KERN_PROC_PATHNAME as c_int, -1 as c_int];
T
Tim Chevalier 已提交
457
                let mut sz = sz;
Y
Youngmin Yoo 已提交
458
                sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint,
G
Graydon Hoare 已提交
459
                       buf as *mut c_void, &mut sz, ptr::null(),
460
                       0u as size_t) == (0 as c_int)
461
            }
462
        }
463 464 465
    }

    #[cfg(target_os = "linux")]
K
kyeongwoon 已提交
466
    #[cfg(target_os = "android")]
B
Brian Anderson 已提交
467
    fn load_self() -> Option<~str> {
468 469
        unsafe {
            use libc::funcs::posix01::unistd::readlink;
470

471
            let mut path_str = str::with_capacity(TMPBUF_SZ);
472 473
            let len = do str::as_c_str(path_str) |buf| {
                let buf = buf as *mut c_char;
474
                do as_c_charp("/proc/self/exe") |proc_self_buf| {
475
                    readlink(proc_self_buf, buf, TMPBUF_SZ as size_t)
476
                }
477 478 479 480 481 482
            };
            if len == -1 {
                None
            } else {
                str::raw::set_len(&mut path_str, len as uint);
                Some(path_str)
483
            }
484 485 486
        }
    }

487
    #[cfg(target_os = "macos")]
B
Brian Anderson 已提交
488
    fn load_self() -> Option<~str> {
489 490
        unsafe {
            do fill_charp_buf() |buf, sz| {
491
                let mut sz = sz as u32;
492
                libc::funcs::extra::_NSGetExecutablePath(
493
                    buf, &mut sz) == (0 as c_int)
494
            }
495
        }
496 497
    }

498
    #[cfg(windows)]
B
Brian Anderson 已提交
499
    fn load_self() -> Option<~str> {
500 501 502 503 504
        unsafe {
            use os::win32::fill_utf16_buf_and_decode;
            do fill_utf16_buf_and_decode() |buf, sz| {
                libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
            }
505
        }
506 507
    }

B
Brian Anderson 已提交
508
    do load_self().map |pth| {
509
        Path(*pth).dir_path()
510
    }
511 512 513
}


514 515 516 517 518 519 520 521 522 523 524 525 526
/**
 * 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.
 */
527
pub fn homedir() -> Option<Path> {
528
    return match getenv("HOME") {
529
        Some(ref p) => if !p.is_empty() {
B
Brian Anderson 已提交
530
          Some(Path(*p))
B
Brian Anderson 已提交
531 532
        } else {
          secondary()
533
        },
B
Brian Anderson 已提交
534
        None => secondary()
535 536
    };

537
    #[cfg(unix)]
B
Brian Anderson 已提交
538 539
    fn secondary() -> Option<Path> {
        None
540 541
    }

542
    #[cfg(windows)]
B
Brian Anderson 已提交
543
    fn secondary() -> Option<Path> {
544
        do getenv("USERPROFILE").chain |p| {
545
            if !p.is_empty() {
B
Brian Anderson 已提交
546
                Some(Path(p))
547
            } else {
B
Brian Anderson 已提交
548
                None
549 550 551 552 553
            }
        }
    }
}

554
/**
555
 * Returns the path to a temporary directory.
556 557 558 559 560
 *
 * On Unix, returns the value of the 'TMPDIR' environment variable if it is
 * set and non-empty and '/tmp' otherwise.
 *
 * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
561 562
 * 'USERPROFILE' environment variable  if any are set and not the empty
 * string. Otherwise, tmpdir returns the path to the Windows directory.
563
 */
564
pub fn tmpdir() -> Path {
565 566
    return lookup();

B
Brian Anderson 已提交
567
    fn getenv_nonempty(v: &str) -> Option<Path> {
568
        match getenv(v) {
L
Luqman Aden 已提交
569
            Some(x) =>
570
                if x.is_empty() {
B
Brian Anderson 已提交
571
                    None
572
                } else {
B
Brian Anderson 已提交
573
                    Some(Path(x))
574
                },
B
Brian Anderson 已提交
575
            _ => None
576 577 578 579
        }
    }

    #[cfg(unix)]
580
    fn lookup() -> Path {
581
        getenv_nonempty("TMPDIR").get_or_default(Path("/tmp"))
582 583 584
    }

    #[cfg(windows)]
585
    fn lookup() -> Path {
586 587 588 589
        getenv_nonempty("TMP").or(
            getenv_nonempty("TEMP").or(
                getenv_nonempty("USERPROFILE").or(
                   getenv_nonempty("WINDIR")))).get_or_default(Path("C:\\Windows"))
590 591
    }
}
B
Brian Anderson 已提交
592

593
/// Recursively walk a directory structure
A
Alex Crichton 已提交
594
pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool {
595 596
    let r = list_dir(p);
    r.iter().advance(|q| {
A
Alex Crichton 已提交
597
        let path = &p.push(*q);
598
        f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p)))
A
Alex Crichton 已提交
599 600
    })
}
601

602
/// Indicates whether a path represents a directory
603
pub fn path_is_dir(p: &Path) -> bool {
604 605 606 607
    unsafe {
        do str::as_c_str(p.to_str()) |buf| {
            rustrt::rust_path_is_dir(buf) != 0 as c_int
        }
608
    }
609 610
}

611
/// Indicates whether a path exists
612
pub fn path_exists(p: &Path) -> bool {
613 614 615 616
    unsafe {
        do str::as_c_str(p.to_str()) |buf| {
            rustrt::rust_path_exists(buf) != 0 as c_int
        }
617
    }
618 619
}

620 621 622 623 624
/**
 * 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
625
 * as is.
626
 */
627 628 629
// 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.
630
pub fn make_absolute(p: &Path) -> Path {
631
    if p.is_absolute {
632
        (*p).clone()
633 634 635
    } else {
        getcwd().push_many(p.components)
    }
636 637 638
}


639
/// Creates a directory at the specified path
640
pub fn make_dir(p: &Path, mode: c_int) -> bool {
B
Brian Anderson 已提交
641
    return mkdir(p, mode);
642

643
    #[cfg(windows)]
644
    fn mkdir(p: &Path, _mode: c_int) -> bool {
645 646 647 648
        unsafe {
            use os::win32::as_utf16_p;
            // FIXME: turn mode into something useful? #2623
            do as_utf16_p(p.to_str()) |buf| {
649
                libc::CreateDirectoryW(buf, cast::transmute(0))
650 651
                    != (0 as libc::BOOL)
            }
652
        }
653 654
    }

655
    #[cfg(unix)]
656
    fn mkdir(p: &Path, mode: c_int) -> bool {
657 658
        unsafe {
            do as_c_charp(p.to_str()) |c| {
659
                libc::mkdir(c, mode as libc::mode_t) == (0 as c_int)
660
            }
661
        }
662 663 664
    }
}

665 666 667 668
/// Creates a directory with a given mode.
/// Returns true iff creation
/// succeeded. Also creates all intermediate subdirectories
/// if they don't already exist, giving all of them the same mode.
669 670 671

// tjc: if directory exists but with different permissions,
// should we return false?
672 673 674 675
pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool {
    if path_is_dir(p) {
        return true;
    }
676 677 678 679
    else if p.components.is_empty() {
        return false;
    }
    else if p.components.len() == 1 {
680
        // No parent directories to create
681
        path_is_dir(p) || make_dir(p, mode)
682 683
    }
    else {
684
        mkdir_recursive(&p.pop(), mode) && make_dir(p, mode)
685 686 687
    }
}

688
/// Lists the contents of a directory
689
pub fn list_dir(p: &Path) -> ~[~str] {
690
    if p.components.is_empty() && !p.is_absolute() {
691 692 693 694
        // Not sure what the right behavior is here, but this
        // prevents a bounds check failure later
        return ~[];
    }
695
    unsafe {
696 697 698 699 700
        #[cfg(target_os = "linux")]
        #[cfg(target_os = "android")]
        #[cfg(target_os = "freebsd")]
        #[cfg(target_os = "macos")]
        unsafe fn get_list(p: &Path) -> ~[~str] {
A
Alex Crichton 已提交
701
            use libc::{dirent_t};
702
            use libc::{opendir, readdir, closedir};
703 704
            extern {
                unsafe fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char;
705 706 707 708
            }
            let input = p.to_str();
            let mut strings = ~[];
            let input_ptr = ::cast::transmute(&input[0]);
B
Brian Anderson 已提交
709
            debug!("os::list_dir -- BEFORE OPENDIR");
710 711
            let dir_ptr = opendir(input_ptr);
            if (dir_ptr as uint != 0) {
B
Brian Anderson 已提交
712
        debug!("os::list_dir -- opendir() SUCCESS");
713 714
                let mut entry_ptr = readdir(dir_ptr);
                while (entry_ptr as uint != 0) {
715 716
                    strings.push(str::raw::from_c_str(rust_list_dir_val(
                        entry_ptr)));
717 718 719 720 721
                    entry_ptr = readdir(dir_ptr);
                }
                closedir(dir_ptr);
            }
            else {
B
Brian Anderson 已提交
722
        debug!("os::list_dir -- opendir() FAILURE");
723
            }
B
Brian Anderson 已提交
724 725 726
            debug!(
                "os::list_dir -- AFTER -- #: %?",
                     strings.len());
727 728
            strings
        }
729
        #[cfg(windows)]
730 731
        unsafe fn get_list(p: &Path) -> ~[~str] {
            use libc::consts::os::extra::INVALID_HANDLE_VALUE;
D
Daniel Micay 已提交
732
            use libc::{wcslen, free};
733 734 735 736 737 738 739 740
            use libc::funcs::extra::kernel32::{
                FindFirstFileW,
                FindNextFileW,
                FindClose,
            };
            use os::win32::{
                as_utf16_p
            };
D
Daniel Micay 已提交
741
            use rt::global_heap::malloc_raw;
742

743
            #[nolink]
744
            extern {
745 746 747 748 749 750 751
                unsafe fn rust_list_dir_wfd_size() -> libc::size_t;
                unsafe fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void)
                    -> *u16;
            }
            fn star(p: &Path) -> Path { p.push("*") }
            do as_utf16_p(star(p).to_str()) |path_ptr| {
                let mut strings = ~[];
752
                let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
753 754 755 756
                let find_handle =
                    FindFirstFileW(
                        path_ptr,
                        ::cast::transmute(wfd_ptr));
757
                if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
758 759
                    let mut more_files = 1 as libc::c_int;
                    while more_files != 0 {
760
                        let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr);
761
                        if fp_buf as uint == 0 {
762
                            fail!("os::list_dir() failure: got null ptr from wfd");
763 764 765 766 767 768 769 770 771 772 773 774
                        }
                        else {
                            let fp_vec = vec::from_buf(
                                fp_buf, wcslen(fp_buf) as uint);
                            let fp_str = str::from_utf16(fp_vec);
                            strings.push(fp_str);
                        }
                        more_files = FindNextFileW(
                            find_handle,
                            ::cast::transmute(wfd_ptr));
                    }
                    FindClose(find_handle);
D
Daniel Micay 已提交
775
                    free(wfd_ptr)
776 777 778 779
                }
                strings
            }
        }
780 781 782
        do get_list(p).consume_iter().filter |filename| {
            "." != *filename && ".." != *filename
        }.collect()
783 784 785
    }
}

786 787 788 789 790
/**
 * Lists the contents of a directory
 *
 * This version prepends each entry with the directory.
 */
791
pub fn list_dir_path(p: &Path) -> ~[~Path] {
E
Erick Tryzelaar 已提交
792
    list_dir(p).map(|f| ~p.push(*f))
793 794
}

795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
/// Removes a directory at the specified path, after removing
/// all its contents. Use carefully!
pub fn remove_dir_recursive(p: &Path) -> bool {
    let mut error_happened = false;
    for walk_dir(p) |inner| {
        if !error_happened {
            if path_is_dir(inner) {
                if !remove_dir_recursive(inner) {
                    error_happened = true;
                }
            }
            else {
                if !remove_file(inner) {
                    error_happened = true;
                }
            }
        }
    };
    // Directory should now be empty
    !error_happened && remove_dir(p)
}

817
/// Removes a directory at the specified path
818
pub fn remove_dir(p: &Path) -> bool {
B
Brian Anderson 已提交
819
   return rmdir(p);
820

821
    #[cfg(windows)]
822
    fn rmdir(p: &Path) -> bool {
823 824 825 826 827 828
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(p.to_str()) |buf| {
                libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
            };
        }
829 830
    }

831
    #[cfg(unix)]
832
    fn rmdir(p: &Path) -> bool {
833 834 835 836 837
        unsafe {
            return do as_c_charp(p.to_str()) |buf| {
                libc::rmdir(buf) == (0 as c_int)
            };
        }
838 839 840
    }
}

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

846
    #[cfg(windows)]
847
    fn chdir(p: &Path) -> bool {
848 849 850 851 852 853
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(p.to_str()) |buf| {
                libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
            };
        }
854 855
    }

856
    #[cfg(unix)]
857
    fn chdir(p: &Path) -> bool {
858 859 860 861 862
        unsafe {
            return do as_c_charp(p.to_str()) |buf| {
                libc::chdir(buf) == (0 as c_int)
            };
        }
863 864 865
    }
}

866 867 868 869 870 871 872 873
/// Changes the current working directory to the specified
/// path while acquiring a global lock, then calls `action`.
/// If the change is successful, releases the lock and restores the
/// CWD to what it was before, returning true.
/// Returns false if the directory doesn't exist or if the directory change
/// is otherwise unsuccessful.
pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
    use unstable::global::global_data_clone_create;
874
    use unstable::sync::{Exclusive, exclusive};
875 876 877

    fn key(_: Exclusive<()>) { }

878 879
    unsafe {
        let result = global_data_clone_create(key, || { ~exclusive(()) });
880

881 882 883 884 885 886 887 888 889
        do result.with_imm() |_| {
            let old_dir = os::getcwd();
            if change_dir(p) {
                action();
                change_dir(&old_dir)
            }
            else {
                false
            }
890 891 892 893
        }
    }
}

894
/// Copies a file from one location to another
895
pub fn copy_file(from: &Path, to: &Path) -> bool {
B
Brian Anderson 已提交
896
    return do_copy_file(from, to);
897

898
    #[cfg(windows)]
899
    fn do_copy_file(from: &Path, to: &Path) -> bool {
900 901 902 903 904 905 906
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(from.to_str()) |fromp| {
                do as_utf16_p(to.to_str()) |top| {
                    libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
                        (0 as libc::BOOL)
                }
907 908 909 910
            }
        }
    }

911
    #[cfg(unix)]
912
    fn do_copy_file(from: &Path, to: &Path) -> bool {
913 914 915 916 917 918 919 920
        unsafe {
            let istream = do as_c_charp(from.to_str()) |fromp| {
                do as_c_charp("rb") |modebuf| {
                    libc::fopen(fromp, modebuf)
                }
            };
            if istream as uint == 0u {
                return false;
921
            }
922 923 924 925
            // Preserve permissions
            let from_mode = from.get_mode().expect("copy_file: couldn't get permissions \
                                                    for source file");

926 927 928 929 930 931 932 933
            let ostream = do as_c_charp(to.to_str()) |top| {
                do as_c_charp("w+b") |modebuf| {
                    libc::fopen(top, modebuf)
                }
            };
            if ostream as uint == 0u {
                fclose(istream);
                return false;
934
            }
935 936 937 938 939
            let bufsize = 8192u;
            let mut buf = vec::with_capacity::<u8>(bufsize);
            let mut done = false;
            let mut ok = true;
            while !done {
940
                do buf.as_mut_buf |b, _sz| {
941 942 943 944 945 946 947 948 949 950
                  let nread = libc::fread(b as *mut c_void, 1u as size_t,
                                          bufsize as size_t,
                                          istream);
                  if nread > 0 as size_t {
                      if libc::fwrite(b as *c_void, 1u as size_t, nread,
                                      ostream) != nread {
                          ok = false;
                          done = true;
                      }
                  } else {
951 952
                      done = true;
                  }
953
              }
954 955 956
            }
            fclose(istream);
            fclose(ostream);
957 958

            // Give the new file the old file's permissions
J
James Miller 已提交
959
            if do str::as_c_str(to.to_str()) |to_buf| {
960
                libc::chmod(to_buf, from_mode as libc::mode_t)
J
James Miller 已提交
961 962
            } != 0 {
                return false; // should be a condition...
963
            }
964
            return ok;
965 966 967 968
        }
    }
}

969
/// Deletes an existing file
970
pub fn remove_file(p: &Path) -> bool {
B
Brian Anderson 已提交
971
    return unlink(p);
972

973
    #[cfg(windows)]
974
    fn unlink(p: &Path) -> bool {
975 976 977 978 979 980
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(p.to_str()) |buf| {
                libc::DeleteFileW(buf) != (0 as libc::BOOL)
            };
        }
981 982
    }

983
    #[cfg(unix)]
984
    fn unlink(p: &Path) -> bool {
985 986 987 988 989
        unsafe {
            return do as_c_charp(p.to_str()) |buf| {
                libc::unlink(buf) == (0 as c_int)
            };
        }
990 991 992
    }
}

993
#[cfg(unix)]
994
/// Returns the platform-specific value of errno
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
pub fn errno() -> int {
    #[cfg(target_os = "macos")]
    #[cfg(target_os = "freebsd")]
    fn errno_location() -> *c_int {
        #[nolink]
        extern {
            unsafe fn __error() -> *c_int;
        }
        unsafe {
            __error()
        }
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
    fn errno_location() -> *c_int {
        #[nolink]
        extern {
            unsafe fn __errno_location() -> *c_int;
        }
        unsafe {
            __errno_location()
        }
    }

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

#[cfg(windows)]
1026
/// Returns the platform-specific value of errno
1027 1028 1029 1030 1031
pub fn errno() -> uint {
    use libc::types::os::arch::extra::DWORD;

    #[link_name = "kernel32"]
    #[abi = "stdcall"]
1032
    extern "stdcall" {
1033 1034 1035 1036
        unsafe fn GetLastError() -> DWORD;
    }

    unsafe {
1037
        GetLastError() as uint
1038 1039 1040
    }
}

1041
/// Get a string representing the platform-dependent last error
1042
pub fn last_os_error() -> ~str {
1043 1044 1045 1046 1047
    #[cfg(unix)]
    fn strerror() -> ~str {
        #[cfg(target_os = "macos")]
        #[cfg(target_os = "android")]
        #[cfg(target_os = "freebsd")]
1048
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1049 1050
            #[nolink]
            extern {
1051
                unsafe fn strerror_r(errnum: c_int, buf: *mut c_char,
1052 1053 1054 1055 1056 1057 1058 1059 1060
                                     buflen: size_t) -> c_int;
            }
            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 已提交
1061
        // So we just use __xpg_strerror_r which is always POSIX compliant
1062
        #[cfg(target_os = "linux")]
1063
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1064 1065
            #[nolink]
            extern {
1066
                unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char,
1067 1068 1069 1070 1071 1072 1073 1074 1075
                                           buflen: size_t) -> c_int;
            }
            unsafe {
                __xpg_strerror_r(errnum, buf, buflen)
            }
        }

        let mut buf = [0 as c_char, ..TMPBUF_SZ];
        unsafe {
1076
            let err = strerror_r(errno() as c_int, &mut buf[0],
1077 1078
                                 TMPBUF_SZ as size_t);
            if err < 0 {
1079
                fail!("strerror_r failure");
1080
            }
1081

1082 1083
            str::raw::from_c_str(&buf[0])
        }
1084
    }
1085 1086 1087

    #[cfg(windows)]
    fn strerror() -> ~str {
1088 1089 1090 1091 1092 1093
        use libc::types::os::arch::extra::DWORD;
        use libc::types::os::arch::extra::LPSTR;
        use libc::types::os::arch::extra::LPVOID;

        #[link_name = "kernel32"]
        #[abi = "stdcall"]
1094
        extern "stdcall" {
1095 1096 1097 1098 1099 1100
            unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
                                     msgId: DWORD, langId: DWORD,
                                     buf: LPSTR, nsize: DWORD,
                                     args: *c_void) -> DWORD;
        }

1101 1102
        static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
        static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
1103 1104 1105 1106 1107 1108 1109

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

        // This value is calculated from the macro
        // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
        let langId = 0x0800 as DWORD;
        let err = errno() as DWORD;
1110
        unsafe {
1111 1112 1113
            let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
                                     FORMAT_MESSAGE_IGNORE_INSERTS,
                                     ptr::mut_null(), err, langId,
L
Luqman Aden 已提交
1114 1115
                                     &mut buf[0], TMPBUF_SZ as DWORD,
                                     ptr::null());
1116
            if res == 0 {
1117
                fail!("[%?] FormatMessage failure", errno());
1118 1119 1120
            }

            str::raw::from_c_str(&buf[0])
1121 1122 1123 1124
        }
    }

    strerror()
1125
}
1126

1127 1128 1129 1130 1131 1132 1133 1134
/**
 * 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
 */
1135
pub fn set_exit_status(code: int) {
1136 1137 1138 1139 1140 1141 1142 1143 1144
    use rt;
    use rt::OldTaskContext;

    if rt::context() == OldTaskContext {
        unsafe {
            rustrt::rust_set_exit_status(code as libc::intptr_t);
        }
    } else {
        rt::util::set_exit_status(code);
1145
    }
1146
}
1147

1148 1149 1150
unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
    let mut args = ~[];
    for uint::range(0, argc as uint) |i| {
1151
        args.push(str::raw::from_c_str(*argv.offset(i)));
1152
    }
L
Luqman Aden 已提交
1153
    args
1154 1155
}

1156 1157 1158 1159 1160 1161
/**
 * Returns the command line arguments
 *
 * Returns a list of the command line arguments.
 */
#[cfg(target_os = "macos")]
1162
pub fn real_args() -> ~[~str] {
1163
    unsafe {
1164 1165 1166
        let (argc, argv) = (*_NSGetArgc() as c_int,
                            *_NSGetArgv() as **c_char);
        load_argc_and_argv(argc, argv)
1167 1168 1169
    }
}

1170
#[cfg(target_os = "linux")]
K
kyeongwoon 已提交
1171
#[cfg(target_os = "android")]
1172
#[cfg(target_os = "freebsd")]
1173
pub fn real_args() -> ~[~str] {
1174 1175 1176
    use rt;
    use rt::TaskContext;

B
Brian Anderson 已提交
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
    if rt::context() == TaskContext {
        match rt::args::clone() {
            Some(args) => args,
            None => fail!("process arguments not initialized")
        }
    } else {
        unsafe {
            let argc = rustrt::rust_get_argc();
            let argv = rustrt::rust_get_argv();
            load_argc_and_argv(argc, argv)
        }
1188
    }
1189 1190
}

1191
#[cfg(windows)]
1192
pub fn real_args() -> ~[~str] {
1193
    let mut nArgs: c_int = 0;
D
Daniel Micay 已提交
1194
    let lpArgCount: *mut c_int = &mut nArgs;
T
Tim Chevalier 已提交
1195 1196
    let lpCmdLine = unsafe { GetCommandLineW() };
    let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206

    let mut args = ~[];
    for uint::range(0, nArgs as uint) |i| {
        unsafe {
            // Determine the length of this argument.
            let ptr = *szArgList.offset(i);
            let mut len = 0;
            while *ptr.offset(len) != 0 { len += 1; }

            // Push it onto the list.
1207
            args.push(vec::raw::buf_as_slice(ptr, len,
1208
                                             str::from_utf16));
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
        }
    }

    unsafe {
        LocalFree(cast::transmute(szArgList));
    }

    return args;
}

type LPCWSTR = *u16;

#[cfg(windows)]
#[link_name="kernel32"]
#[abi="stdcall"]
1224
extern "stdcall" {
1225 1226 1227 1228 1229 1230 1231
    fn GetCommandLineW() -> LPCWSTR;
    fn LocalFree(ptr: *c_void);
}

#[cfg(windows)]
#[link_name="shell32"]
#[abi="stdcall"]
1232
extern "stdcall" {
1233
    fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
1234 1235 1236 1237 1238 1239
}

struct OverriddenArgs {
    val: ~[~str]
}

1240
static overridden_arg_key: local_data::Key<@OverriddenArgs> = &local_data::Key;
1241

1242 1243 1244 1245 1246
/// Returns the arguments which this program was started with (normally passed
/// via the command line).
///
/// The return value of the function can be changed by invoking the
/// `os::set_args` function.
1247
pub fn args() -> ~[~str] {
1248 1249
    match local_data::get(overridden_arg_key, |k| k.map(|&k| *k)) {
        None => real_args(),
1250
        Some(args) => args.val.clone()
1251 1252 1253
    }
}

1254 1255 1256
/// For the current task, overrides the task-local cache of the arguments this
/// program had when it started. These new arguments are only available to the
/// current task via the `os::args` method.
T
Tim Chevalier 已提交
1257
pub fn set_args(new_args: ~[~str]) {
1258 1259 1260
    let overridden_args = @OverriddenArgs {
        val: new_args.clone()
    };
1261
    local_data::set(overridden_arg_key, overridden_args);
1262 1263
}

1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
// FIXME #6100 we should really use an internal implementation of this - using
// the POSIX glob functions isn't portable to windows, probably has slight
// inconsistencies even where it is implemented, and makes extending
// functionality a lot more difficult
// FIXME #6101 also provide a non-allocating version - each_glob or so?
/// Returns a vector of Path objects that match the given glob pattern
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[cfg(target_os = "freebsd")]
#[cfg(target_os = "macos")]
pub fn glob(pattern: &str) -> ~[Path] {
    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
    fn default_glob_t () -> libc::glob_t {
        libc::glob_t {
            gl_pathc: 0,
            gl_pathv: ptr::null(),
            gl_offs: 0,
            __unused1: ptr::null(),
            __unused2: ptr::null(),
            __unused3: ptr::null(),
            __unused4: ptr::null(),
            __unused5: ptr::null(),
        }
    }

    #[cfg(target_os = "freebsd")]
    fn default_glob_t () -> libc::glob_t {
        libc::glob_t {
            gl_pathc: 0,
            __unused1: 0,
            gl_offs: 0,
            __unused2: 0,
            gl_pathv: ptr::null(),
            __unused3: ptr::null(),
            __unused4: ptr::null(),
            __unused5: ptr::null(),
            __unused6: ptr::null(),
            __unused7: ptr::null(),
            __unused8: ptr::null(),
        }
    }

    #[cfg(target_os = "macos")]
    fn default_glob_t () -> libc::glob_t {
        libc::glob_t {
            gl_pathc: 0,
            __unused1: 0,
            gl_offs: 0,
            __unused2: 0,
            gl_pathv: ptr::null(),
            __unused3: ptr::null(),
            __unused4: ptr::null(),
            __unused5: ptr::null(),
            __unused6: ptr::null(),
            __unused7: ptr::null(),
            __unused8: ptr::null(),
        }
    }

    let mut g = default_glob_t();
    do str::as_c_str(pattern) |c_pattern| {
        unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) }
    };
    do(|| {
        let paths = unsafe {
            vec::raw::from_buf_raw(g.gl_pathv, g.gl_pathc as uint)
        };
        do paths.map |&c_str| {
            Path(unsafe { str::raw::from_c_str(c_str) })
        }
    }).finally {
        unsafe { libc::globfree(&mut g) };
    }
}

/// Returns a vector of Path objects that match the given glob pattern
#[cfg(target_os = "win32")]
1342
pub fn glob(_pattern: &str) -> ~[Path] {
1343
    fail!("glob() is unimplemented on Windows")
1344 1345
}

1346 1347 1348 1349 1350 1351 1352
#[cfg(target_os = "macos")]
extern {
    // These functions are in crt_externs.h.
    pub fn _NSGetArgc() -> *c_int;
    pub fn _NSGetArgv() -> ***c_char;
}

1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
// 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 {
  unsafe {
    let mut info = libc::SYSTEM_INFO::new();
    libc::GetSystemInfo(&mut info);

    return info.dwPageSize as uint;
  }
}

pub struct MemoryMap {
    data: *mut u8,
    len: size_t,
    kind: MemoryMapKind
}

pub enum MemoryMapKind {
    MapFile(*c_void),
    MapVirtual
}

pub enum MapOption {
    MapReadable,
    MapWritable,
    MapExecutable,
    MapAddr(*c_void),
    MapFd(c_int),
    MapOffset(uint)
}

pub enum MapError {
    // Linux-specific errors
    ErrFdNotAvail,
    ErrInvalidFd,
    ErrUnaligned,
    ErrNoMapSupport,
    ErrNoMem,
    ErrUnknown(libc::c_int),

    // Windows-specific errors
    ErrUnsupProt,
    ErrUnsupOffset,
    ErrNeedRW,
    ErrAlreadyExists,
    ErrVirtualAlloc(uint),
    ErrCreateFileMappingW(uint),
    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",
            ErrUnknown(code) => fmt!("Unknown error=%?", code),
            ErrUnsupProt => ~"Protection mode unsupported",
            ErrUnsupOffset => ~"Offset in virtual memory mode is unsupported",
            ErrNeedRW => ~"File mapping should be at least readable/writable",
            ErrAlreadyExists => ~"File mapping for specified file already exists",
            ErrVirtualAlloc(code) => fmt!("VirtualAlloc failure=%?", code),
            ErrCreateFileMappingW(code) => fmt!("CreateFileMappingW failure=%?", code),
            ErrMapViewOfFile(code) => fmt!("MapViewOfFile failure=%?", code)
        }
    }
}

#[cfg(unix)]
impl MemoryMap {
    pub fn new(min_len: uint, options: ~[MapOption]) -> Result<~MemoryMap, MapError> {
        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;

        for options.iter().advance |&o| {
            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)
        };
        if r == libc::MAP_FAILED {
            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 {
            Ok(~MemoryMap {
               data: r as *mut u8,
               len: len,
               kind: if fd == -1 {
                   MapVirtual
               } else {
                   MapFile(ptr::null())
               }
            })
        }
    }
}

#[cfg(unix)]
impl Drop for MemoryMap {
    fn drop(&self) {
        unsafe {
            match libc::munmap(self.data as *c_void, self.len) {
                0 => (),
                -1 => error!(match errno() as c_int {
                    libc::EINVAL => ~"invalid addr or len",
                    e => fmt!("unknown errno=%?", e)
                }),
                r => error!(fmt!("Unexpected result %?", r))
            }
        }
    }
}

#[cfg(windows)]
impl MemoryMap {
    pub fn new(min_len: uint, options: ~[MapOption]) -> Result<~MemoryMap, MapError> {
        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;

        for options.iter().advance |&o| {
            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())),
                _ => Ok(~MemoryMap {
                   data: r as *mut u8,
                   len: len,
                   kind: MapVirtual
                })
            }
        } else {
            let dwDesiredAccess = match (readable, writable) {
                (true, true) => libc::FILE_MAP_ALL_ACCESS,
                (true, false) => libc::FILE_MAP_READ,
                (false, true) => libc::FILE_MAP_WRITE,
                _ => {
                    return Err(ErrNeedRW);
                }
            };
            unsafe {
                let hFile = libc::get_osfhandle(fd) as HANDLE;
                let mapping = libc::CreateFileMappingW(hFile,
                                                       ptr::mut_null(),
                                                       flProtect,
                                                       (len >> 32) as DWORD,
                                                       (len & 0xffff_ffff) as DWORD,
                                                       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,
                                            (offset >> 32) as DWORD,
                                            (offset & 0xffff_ffff) as DWORD,
                                            0);
                match r as uint {
                    0 => Err(ErrMapViewOfFile(errno())),
                    _ => Ok(~MemoryMap {
                       data: r as *mut u8,
                       len: len,
                       kind: MapFile(mapping as *c_void)
                    })
                }
            }
        }
    }
}

#[cfg(windows)]
impl Drop for MemoryMap {
    fn drop(&self) {
        use libc::types::os::arch::extra::{LPCVOID, HANDLE};

        unsafe {
            match self.kind {
                MapVirtual => match libc::VirtualFree(self.data as *mut c_void,
                                                      self.len,
                                                      libc::MEM_RELEASE) {
                    0 => error!(fmt!("VirtualFree failed: %?", errno())),
                    _ => ()
                },
                MapFile(mapping) => {
                    if libc::UnmapViewOfFile(self.data as LPCVOID) != 0 {
                        error!(fmt!("UnmapViewOfFile failed: %?", errno()));
                    }
                    if libc::CloseHandle(mapping as HANDLE) != 0 {
                        error!(fmt!("CloseHandle failed: %?", errno()));
                    }
                }
            }
        }
    }
}

1635
pub mod consts {
1636

I
ILyoan 已提交
1637
    #[cfg(unix)]
1638
    pub use os::consts::unix::*;
1639

I
ILyoan 已提交
1640
    #[cfg(windows)]
1641
    pub use os::consts::windows::*;
1642

I
ILyoan 已提交
1643
    #[cfg(target_os = "macos")]
1644
    pub use os::consts::macos::*;
I
ILyoan 已提交
1645 1646

    #[cfg(target_os = "freebsd")]
1647
    pub use os::consts::freebsd::*;
I
ILyoan 已提交
1648 1649

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

K
kyeongwoon 已提交
1652
    #[cfg(target_os = "android")]
1653
    pub use os::consts::android::*;
K
kyeongwoon 已提交
1654

I
ILyoan 已提交
1655
    #[cfg(target_os = "win32")]
1656
    pub use os::consts::win32::*;
1657

1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677
    #[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")]
    use os::consts::mips::*;

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

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

I
ILyoan 已提交
1678
    pub mod macos {
1679 1680 1681 1682
        pub static SYSNAME: &'static str = "macos";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".dylib";
        pub static EXE_SUFFIX: &'static str = "";
I
ILyoan 已提交
1683 1684 1685
    }

    pub mod freebsd {
1686 1687 1688 1689
        pub static SYSNAME: &'static str = "freebsd";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
        pub static EXE_SUFFIX: &'static str = "";
I
ILyoan 已提交
1690 1691 1692
    }

    pub mod linux {
1693 1694 1695 1696
        pub static SYSNAME: &'static str = "linux";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
        pub static EXE_SUFFIX: &'static str = "";
I
ILyoan 已提交
1697
    }
K
kyeongwoon 已提交
1698 1699

    pub mod android {
1700 1701 1702 1703
        pub static SYSNAME: &'static str = "android";
        pub static DLL_PREFIX: &'static str = "lib";
        pub static DLL_SUFFIX: &'static str = ".so";
        pub static EXE_SUFFIX: &'static str = "";
K
kyeongwoon 已提交
1704
    }
1705

I
ILyoan 已提交
1706
    pub mod win32 {
1707 1708 1709 1710
        pub static SYSNAME: &'static str = "win32";
        pub static DLL_PREFIX: &'static str = "";
        pub static DLL_SUFFIX: &'static str = ".dll";
        pub static EXE_SUFFIX: &'static str = ".exe";
I
ILyoan 已提交
1711 1712 1713 1714
    }


    pub mod x86 {
1715
        pub static ARCH: &'static str = "x86";
I
ILyoan 已提交
1716 1717
    }
    pub mod x86_64 {
1718
        pub static ARCH: &'static str = "x86_64";
I
ILyoan 已提交
1719 1720
    }
    pub mod arm {
1721
        pub static ARCH: &'static str = "arm";
I
ILyoan 已提交
1722
    }
J
Jyun-Yan You 已提交
1723
    pub mod mips {
1724
        pub static ARCH: &'static str = "mips";
J
Jyun-Yan You 已提交
1725
    }
I
ILyoan 已提交
1726
}
1727 1728 1729

#[cfg(test)]
mod tests {
1730
    use libc::{c_int, c_void, size_t};
1731
    use libc;
A
Alex Crichton 已提交
1732
    use option::Some;
1733
    use option;
1734
    use os::{as_c_charp, env, getcwd, getenv, make_absolute, real_args};
C
Corey Richardson 已提交
1735
    use os::{remove_file, setenv, unsetenv};
1736
    use os;
1737
    use path::Path;
1738
    use rand::RngUtil;
1739 1740
    use rand;
    use run;
1741
    use str::StrSlice;
1742
    use vec::CopyableVector;
1743 1744
    use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};

1745

1746
    #[test]
1747
    pub fn last_os_error() {
B
Brian Anderson 已提交
1748
        debug!(os::last_os_error());
1749
    }
1750

1751 1752 1753
    #[test]
    pub fn test_args() {
        let a = real_args();
P
Patrick Walton 已提交
1754
        assert!(a.len() >= 1);
1755 1756
    }

1757
    fn make_rand_name() -> ~str {
P
Patrick Walton 已提交
1758
        let mut rng = rand::rng();
1759
        let n = ~"TEST" + rng.gen_str(10u);
P
Patrick Walton 已提交
1760
        assert!(getenv(n).is_none());
L
Luqman Aden 已提交
1761
        n
1762 1763 1764 1765 1766
    }

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

C
Corey Richardson 已提交
1771 1772 1773
    #[test]
    fn test_unsetenv() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1774
        setenv(n, "VALUE");
C
Corey Richardson 已提交
1775
        unsetenv(n);
1776
        assert_eq!(getenv(n), option::None);
C
Corey Richardson 已提交
1777 1778
    }

1779
    #[test]
1780 1781
    #[ignore(cfg(windows))]
    #[ignore]
1782 1783
    fn test_setenv_overwrite() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1784 1785
        setenv(n, "1");
        setenv(n, "2");
1786
        assert_eq!(getenv(n), option::Some(~"2"));
E
Erick Tryzelaar 已提交
1787
        setenv(n, "");
1788
        assert_eq!(getenv(n), option::Some(~""));
1789 1790 1791 1792 1793
    }

    // Windows GetEnvironmentVariable requires some extra work to make sure
    // the buffer the variable is copied into is the right size
    #[test]
1794 1795
    #[ignore(cfg(windows))]
    #[ignore]
1796
    fn test_getenv_big() {
1797
        let mut s = ~"";
1798
        let mut i = 0;
1799 1800 1801 1802
        while i < 100 {
            s = s + "aaaaaaaaaa";
            i += 1;
        }
1803 1804
        let n = make_rand_name();
        setenv(n, s);
1805
        debug!(s.clone());
1806
        assert_eq!(getenv(n), option::Some(s));
1807 1808 1809 1810 1811
    }

    #[test]
    fn test_self_exe_path() {
        let path = os::self_exe_path();
P
Patrick Walton 已提交
1812
        assert!(path.is_some());
B
Brian Anderson 已提交
1813
        let path = path.get();
1814
        debug!(path.clone());
1815 1816

        // Hard to test this function
P
Patrick Walton 已提交
1817
        assert!(path.is_absolute);
1818 1819 1820
    }

    #[test]
1821
    #[ignore]
1822 1823
    fn test_env_getenv() {
        let e = env();
Y
Youngmin Yoo 已提交
1824
        assert!(e.len() > 0u);
1825
        for e.iter().advance |p| {
1826 1827
            let (n, v) = (*p).clone();
            debug!(n.clone());
1828 1829 1830 1831
            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 已提交
1832
            assert!(v2.is_none() || v2 == option::Some(v));
1833 1834 1835 1836 1837 1838 1839
        }
    }

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

1840
        let mut e = env();
E
Erick Tryzelaar 已提交
1841
        setenv(n, "VALUE");
1842
        assert!(!e.contains(&(n.clone(), ~"VALUE")));
1843 1844

        e = env();
1845
        assert!(e.contains(&(n, ~"VALUE")));
1846 1847
    }

1848 1849
    #[test]
    fn test() {
P
Patrick Walton 已提交
1850
        assert!((!Path("test-path").is_absolute));
1851

E
Erick Tryzelaar 已提交
1852
        debug!("Current working directory: %s", getcwd().to_str());
1853

B
Brian Anderson 已提交
1854 1855
        debug!(make_absolute(&Path("test-path")));
        debug!(make_absolute(&Path("/usr/bin")));
1856 1857 1858
    }

    #[test]
1859
    #[cfg(unix)]
1860
    fn homedir() {
E
Erick Tryzelaar 已提交
1861
        let oldhome = getenv("HOME");
1862

E
Erick Tryzelaar 已提交
1863
        setenv("HOME", "/home/MountainView");
1864
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1865

E
Erick Tryzelaar 已提交
1866
        setenv("HOME", "");
P
Patrick Walton 已提交
1867
        assert!(os::homedir().is_none());
1868

1869
        for oldhome.iter().advance |s| { setenv("HOME", *s) }
1870 1871 1872
    }

    #[test]
1873
    #[cfg(windows)]
1874 1875
    fn homedir() {

E
Erick Tryzelaar 已提交
1876 1877
        let oldhome = getenv("HOME");
        let olduserprofile = getenv("USERPROFILE");
1878

E
Erick Tryzelaar 已提交
1879 1880
        setenv("HOME", "");
        setenv("USERPROFILE", "");
1881

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

E
Erick Tryzelaar 已提交
1884
        setenv("HOME", "/home/MountainView");
1885
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1886

E
Erick Tryzelaar 已提交
1887
        setenv("HOME", "");
1888

E
Erick Tryzelaar 已提交
1889
        setenv("USERPROFILE", "/home/MountainView");
1890
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1891

E
Erick Tryzelaar 已提交
1892 1893
        setenv("HOME", "/home/MountainView");
        setenv("USERPROFILE", "/home/PaloAlto");
1894
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1895

1896 1897
        oldhome.iter().advance(|s| { setenv("HOME", *s); true });
        olduserprofile.iter().advance(|s| { setenv("USERPROFILE", *s); true });
1898 1899
    }

1900 1901
    #[test]
    fn tmpdir() {
1902
        assert!(!os::tmpdir().to_str().is_empty());
1903 1904
    }

1905 1906
    // Issue #712
    #[test]
1907 1908 1909
    fn test_list_dir_no_invalid_memory_access() {
        os::list_dir(&Path("."));
    }
1910 1911 1912

    #[test]
    fn list_dir() {
1913
        let dirs = os::list_dir(&Path("."));
1914
        // Just assuming that we've got some contents in the current directory
Y
Youngmin Yoo 已提交
1915
        assert!(dirs.len() > 0u);
1916

1917
        for dirs.iter().advance |dir| {
1918
            debug!((*dir).clone());
1919
        }
1920 1921
    }

1922 1923 1924 1925 1926 1927
    #[test]
    fn list_dir_empty_path() {
        let dirs = os::list_dir(&Path(""));
        assert!(dirs.is_empty());
    }

1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941
    #[test]
    #[cfg(not(windows))]
    fn list_dir_root() {
        let dirs = os::list_dir(&Path("/"));
        assert!(dirs.len() > 1);
    }
    #[test]
    #[cfg(windows)]
    fn list_dir_root() {
        let dirs = os::list_dir(&Path("C:\\"));
        assert!(dirs.len() > 1);
    }


1942 1943
    #[test]
    fn path_is_dir() {
P
Patrick Walton 已提交
1944 1945
        assert!((os::path_is_dir(&Path("."))));
        assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
1946 1947 1948 1949
    }

    #[test]
    fn path_exists() {
P
Patrick Walton 已提交
1950 1951
        assert!((os::path_exists(&Path("."))));
        assert!((!os::path_exists(&Path(
P
Patrick Walton 已提交
1952
                     "test/nonexistent-bogus-path"))));
1953 1954
    }

1955 1956
    #[test]
    fn copy_file_does_not_exist() {
P
Patrick Walton 已提交
1957
      assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
1958
                            &Path("test/other-bogus-path")));
P
Patrick Walton 已提交
1959
      assert!(!os::path_exists(&Path("test/other-bogus-path")));
1960 1961 1962 1963
    }

    #[test]
    fn copy_file_ok() {
1964 1965 1966
        unsafe {
          let tempdir = getcwd(); // would like to use $TMPDIR,
                                  // doesn't seem to work on Linux
1967
          assert!((tempdir.to_str().len() > 0u));
1968 1969 1970 1971 1972 1973 1974 1975 1976
          let in = tempdir.push("in.txt");
          let out = tempdir.push("out.txt");

          /* Write the temp input file */
            let ostream = do as_c_charp(in.to_str()) |fromp| {
                do as_c_charp("w+b") |modebuf| {
                    libc::fopen(fromp, modebuf)
                }
          };
P
Patrick Walton 已提交
1977
          assert!((ostream as uint != 0u));
1978
          let s = ~"hello";
1979
          let mut buf = s.as_bytes_with_null().to_owned();
D
Daniel Micay 已提交
1980
          let len = buf.len();
1981
          do buf.as_mut_buf |b, _len| {
D
Daniel Micay 已提交
1982 1983 1984
              assert_eq!(libc::fwrite(b as *c_void, 1u as size_t,
                                      (s.len() + 1u) as size_t, ostream),
                         len as size_t)
1985
          }
1986
          assert_eq!(libc::fclose(ostream), (0u as c_int));
1987
          let in_mode = in.get_mode();
1988 1989
          let rs = os::copy_file(&in, &out);
          if (!os::path_exists(&in)) {
1990
            fail!("%s doesn't exist", in.to_str());
1991
          }
P
Patrick Walton 已提交
1992
          assert!((rs));
1993
          let rslt = run::process_status("diff", [in.to_str(), out.to_str()]);
1994 1995
          assert_eq!(rslt, 0);
          assert_eq!(out.get_mode(), in_mode);
P
Patrick Walton 已提交
1996 1997
          assert!((remove_file(&in)));
          assert!((remove_file(&out)));
1998
        }
1999
    }
2000 2001

    #[test]
2002 2003 2004 2005
    fn recursive_mkdir_slash() {
        let path = Path("/");
        assert!(os::mkdir_recursive(&path,  (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
    }
2006

2007 2008 2009 2010
    #[test]
    fn recursive_mkdir_empty() {
        let path = Path("");
        assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
2011 2012
    }

2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082
    #[test]
    fn memory_map_rw() {
        use result::{Ok, Err};

        let chunk = match os::MemoryMap::new(16, ~[
            os::MapReadable,
            os::MapWritable
        ]) {
            Ok(chunk) => chunk,
            Err(msg) => fail!(msg.to_str())
        };
        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::*;

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

        let p = tmpdir().push("mmap_file.tmp");
        let size = page_size() * 2;
        remove_file(&p);

        let fd = unsafe {
            let fd = do as_c_charp(p.to_str()) |path| {
                open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
            };
            lseek_(fd, size);
            do as_c_charp("x") |x| {
                assert!(write(fd, x as *c_void, 1) == 1);
            }
            fd
        };
        let chunk = match MemoryMap::new(size / 2, ~[
            MapReadable,
            MapWritable,
            MapFd(fd),
            MapOffset(size / 2)
        ]) {
            Ok(chunk) => chunk,
            Err(msg) => fail!(msg.to_str())
        };
        assert!(chunk.len > 0);

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

2083
    // More recursive_mkdir tests are in extra::tempfile
2084
}