os.rs 58.4 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 91
pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool) -> Option<~str> {
    let mut buf = [0 as c_char, .. TMPBUF_SZ];
92
    do buf.as_mut_buf |b, sz| {
93 94 95 96
        if f(b, sz as size_t) {
            unsafe {
                Some(str::raw::from_buf(b as *u8))
            }
97
        } else {
B
Brian Anderson 已提交
98
            None
99 100 101 102
        }
    }
}

103
#[cfg(windows)]
104
pub mod win32 {
105 106 107
    use libc;
    use vec;
    use str;
108
    use option::{None, Option};
109
    use option;
110
    use os::TMPBUF_SZ;
111
    use libc::types::os::arch::extra::DWORD;
112

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

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

151 152
/*
Accessing environment variables is not generally threadsafe.
153
Serialize access through a global lock.
154 155
*/
fn with_env_lock<T>(f: &fn() -> T) -> T {
156
    use unstable::finally::Finally;
157

158
    unsafe {
159 160 161 162 163 164 165
        return do (|| {
            rust_take_env_lock();
            f()
        }).finally {
            rust_drop_env_lock();
        };
    }
166

167 168 169 170 171
    extern {
        #[fast_ffi]
        fn rust_take_env_lock();
        #[fast_ffi]
        fn rust_drop_env_lock();
172
    }
B
Ben Blum 已提交
173 174
}

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

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

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

255
#[cfg(windows)]
256 257
/// Fetches the environment variable `n` from the current process, returning
/// None if the variable isn't set.
258 259 260 261 262 263 264
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)
265 266 267
                }
            }
        }
268 269
    }
}
270 271


272
#[cfg(unix)]
273 274
/// Sets the environment variable `n` to the value `v` for the currently running
/// process
275 276 277
pub fn setenv(n: &str, v: &str) {
    unsafe {
        do with_env_lock {
278 279
            do n.to_str().as_c_str |nbuf| {
                do v.to_str().as_c_str |vbuf| {
280
                    libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
281 282 283
                }
            }
        }
284 285
    }
}
286 287


288
#[cfg(windows)]
289 290
/// Sets the environment variable `n` to the value `v` for the currently running
/// process
291 292 293 294 295 296 297
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);
298 299 300 301 302 303
                }
            }
        }
    }
}

C
Corey Richardson 已提交
304 305 306 307 308 309
/// Remove a variable from the environment entirely
pub fn unsetenv(n: &str) {
    #[cfg(unix)]
    fn _unsetenv(n: &str) {
        unsafe {
            do with_env_lock {
310
                do n.to_str().as_c_str |nbuf| {
C
Corey Richardson 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
                    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);
}

331
pub fn fdopen(fd: c_int) -> *FILE {
E
Erick Tryzelaar 已提交
332 333
    do "r".as_c_str |modebuf| {
        unsafe {
334
            libc::fdopen(fd, modebuf)
E
Erick Tryzelaar 已提交
335
        }
336
    }
337 338 339
}


340 341
// fsync related

342
#[cfg(windows)]
343
pub fn fsync_fd(fd: c_int, _level: io::fsync::Level) -> c_int {
344 345 346 347
    unsafe {
        use libc::funcs::extra::msvcrt::*;
        return commit(fd);
    }
348 349 350
}

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

#[cfg(target_os = "macos")]
364
pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    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; }
          }
        }
380 381 382 383
    }
}

#[cfg(target_os = "freebsd")]
384
pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
385 386 387 388
    unsafe {
        use libc::funcs::posix01::unistd::*;
        return fsync(fd);
    }
389 390
}

391 392 393 394
pub struct Pipe {
    in: c_int,
    out: c_int
}
395

396
#[cfg(unix)]
397
pub fn pipe() -> Pipe {
398
    unsafe {
399
        let mut fds = Pipe {in: 0 as c_int,
400
                            out: 0 as c_int };
401
        assert_eq!(libc::pipe(&mut fds.in), (0 as c_int));
402
        return Pipe {in: fds.in, out: fds.out};
403
    }
404 405 406 407
}



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

P
Patrick Walton 已提交
427
fn dup2(src: c_int, dst: c_int) -> c_int {
428 429 430
    unsafe {
        libc::dup2(src, dst)
    }
P
Patrick Walton 已提交
431 432
}

433
/// Returns the proper dll filename for the given basename of a file.
434
pub fn dll_filename(base: &str) -> ~str {
435
    fmt!("%s%s%s", DLL_PREFIX, base, DLL_SUFFIX)
436 437
}

438 439
/// Optionally returns the filesystem path to the current executable which is
/// running. If any failure occurs, None is returned.
440
pub fn self_exe_path() -> Option<Path> {
441 442

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

    #[cfg(target_os = "linux")]
K
kyeongwoon 已提交
460
    #[cfg(target_os = "android")]
B
Brian Anderson 已提交
461
    fn load_self() -> Option<~str> {
462 463
        unsafe {
            use libc::funcs::posix01::unistd::readlink;
464

465
            let mut path_str = str::with_capacity(TMPBUF_SZ);
466
            let len = do path_str.as_c_str |buf| {
467
                let buf = buf as *mut c_char;
E
Erick Tryzelaar 已提交
468
                do "/proc/self/exe".as_c_str |proc_self_buf| {
469
                    readlink(proc_self_buf, buf, TMPBUF_SZ as size_t)
470
                }
471 472 473 474 475 476
            };
            if len == -1 {
                None
            } else {
                str::raw::set_len(&mut path_str, len as uint);
                Some(path_str)
477
            }
478 479 480
        }
    }

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

492
    #[cfg(windows)]
B
Brian Anderson 已提交
493
    fn load_self() -> Option<~str> {
494 495 496 497 498
        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)
            }
499
        }
500 501
    }

B
Brian Anderson 已提交
502
    do load_self().map |pth| {
503
        Path(*pth).dir_path()
504
    }
505 506 507
}


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

531
    #[cfg(unix)]
B
Brian Anderson 已提交
532 533
    fn secondary() -> Option<Path> {
        None
534 535
    }

536
    #[cfg(windows)]
B
Brian Anderson 已提交
537
    fn secondary() -> Option<Path> {
538
        do getenv("USERPROFILE").chain |p| {
539
            if !p.is_empty() {
B
Brian Anderson 已提交
540
                Some(Path(p))
541
            } else {
B
Brian Anderson 已提交
542
                None
543 544 545 546 547
            }
        }
    }
}

548
/**
549
 * Returns the path to a temporary directory.
550 551 552 553 554
 *
 * 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',
555 556
 * 'USERPROFILE' environment variable  if any are set and not the empty
 * string. Otherwise, tmpdir returns the path to the Windows directory.
557
 */
558
pub fn tmpdir() -> Path {
559 560
    return lookup();

B
Brian Anderson 已提交
561
    fn getenv_nonempty(v: &str) -> Option<Path> {
562
        match getenv(v) {
L
Luqman Aden 已提交
563
            Some(x) =>
564
                if x.is_empty() {
B
Brian Anderson 已提交
565
                    None
566
                } else {
B
Brian Anderson 已提交
567
                    Some(Path(x))
568
                },
B
Brian Anderson 已提交
569
            _ => None
570 571 572 573
        }
    }

    #[cfg(unix)]
574
    fn lookup() -> Path {
575
        getenv_nonempty("TMPDIR").get_or_default(Path("/tmp"))
576 577 578
    }

    #[cfg(windows)]
579
    fn lookup() -> Path {
580 581 582 583
        getenv_nonempty("TMP").or(
            getenv_nonempty("TEMP").or(
                getenv_nonempty("USERPROFILE").or(
                   getenv_nonempty("WINDIR")))).get_or_default(Path("C:\\Windows"))
584 585
    }
}
B
Brian Anderson 已提交
586

587
/// Recursively walk a directory structure
A
Alex Crichton 已提交
588
pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool {
589 590
    let r = list_dir(p);
    r.iter().advance(|q| {
A
Alex Crichton 已提交
591
        let path = &p.push(*q);
592
        f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p)))
A
Alex Crichton 已提交
593 594
    })
}
595

596
/// Indicates whether a path represents a directory
597
pub fn path_is_dir(p: &Path) -> bool {
598
    unsafe {
599
        do p.to_str().as_c_str |buf| {
600 601
            rustrt::rust_path_is_dir(buf) != 0 as c_int
        }
602
    }
603 604
}

605
/// Indicates whether a path exists
606
pub fn path_exists(p: &Path) -> bool {
607
    unsafe {
608
        do p.to_str().as_c_str |buf| {
609 610
            rustrt::rust_path_exists(buf) != 0 as c_int
        }
611
    }
612 613
}

614 615 616 617 618
/**
 * 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
619
 * as is.
620
 */
621 622 623
// 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.
624
pub fn make_absolute(p: &Path) -> Path {
625
    if p.is_absolute {
626
        (*p).clone()
627 628 629
    } else {
        getcwd().push_many(p.components)
    }
630 631 632
}


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

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

649
    #[cfg(unix)]
650
    fn mkdir(p: &Path, mode: c_int) -> bool {
E
Erick Tryzelaar 已提交
651 652 653
        do p.to_str().as_c_str |buf| {
            unsafe {
                libc::mkdir(buf, mode as libc::mode_t) == (0 as c_int)
654
            }
655
        }
656 657 658
    }
}

659 660 661 662
/// 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.
663 664 665

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

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

737
            #[nolink]
738
            extern {
739 740 741 742 743 744 745
                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 = ~[];
746
                let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
747 748 749 750
                let find_handle =
                    FindFirstFileW(
                        path_ptr,
                        ::cast::transmute(wfd_ptr));
751
                if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
752 753
                    let mut more_files = 1 as libc::c_int;
                    while more_files != 0 {
754
                        let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr);
755
                        if fp_buf as uint == 0 {
756
                            fail!("os::list_dir() failure: got null ptr from wfd");
757 758 759 760 761 762 763 764 765 766 767 768
                        }
                        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 已提交
769
                    free(wfd_ptr)
770 771 772 773
                }
                strings
            }
        }
774 775 776
        do get_list(p).consume_iter().filter |filename| {
            "." != *filename && ".." != *filename
        }.collect()
777 778 779
    }
}

780 781 782 783 784
/**
 * Lists the contents of a directory
 *
 * This version prepends each entry with the directory.
 */
785 786
pub fn list_dir_path(p: &Path) -> ~[Path] {
    list_dir(p).map(|f| p.push(*f))
787 788
}

789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
/// 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)
}

811
/// Removes a directory at the specified path
812
pub fn remove_dir(p: &Path) -> bool {
B
Brian Anderson 已提交
813
   return rmdir(p);
814

815
    #[cfg(windows)]
816
    fn rmdir(p: &Path) -> bool {
817 818 819 820 821 822
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(p.to_str()) |buf| {
                libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
            };
        }
823 824
    }

825
    #[cfg(unix)]
826
    fn rmdir(p: &Path) -> bool {
E
Erick Tryzelaar 已提交
827 828
        do p.to_str().as_c_str |buf| {
            unsafe {
829
                libc::rmdir(buf) == (0 as c_int)
E
Erick Tryzelaar 已提交
830
            }
831
        }
832 833 834
    }
}

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

840
    #[cfg(windows)]
841
    fn chdir(p: &Path) -> bool {
842 843 844 845 846 847
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(p.to_str()) |buf| {
                libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
            };
        }
848 849
    }

850
    #[cfg(unix)]
851
    fn chdir(p: &Path) -> bool {
E
Erick Tryzelaar 已提交
852 853
        do p.to_str().as_c_str |buf| {
            unsafe {
854
                libc::chdir(buf) == (0 as c_int)
E
Erick Tryzelaar 已提交
855
            }
856
        }
857 858 859
    }
}

860
/// Copies a file from one location to another
861
pub fn copy_file(from: &Path, to: &Path) -> bool {
B
Brian Anderson 已提交
862
    return do_copy_file(from, to);
863

864
    #[cfg(windows)]
865
    fn do_copy_file(from: &Path, to: &Path) -> bool {
866 867 868 869 870 871 872
        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)
                }
873 874 875 876
            }
        }
    }

877
    #[cfg(unix)]
878
    fn do_copy_file(from: &Path, to: &Path) -> bool {
879
        unsafe {
E
Erick Tryzelaar 已提交
880 881
            let istream = do from.to_str().as_c_str |fromp| {
                do "rb".as_c_str |modebuf| {
882 883 884 885 886
                    libc::fopen(fromp, modebuf)
                }
            };
            if istream as uint == 0u {
                return false;
887
            }
888 889 890 891
            // Preserve permissions
            let from_mode = from.get_mode().expect("copy_file: couldn't get permissions \
                                                    for source file");

E
Erick Tryzelaar 已提交
892 893
            let ostream = do to.to_str().as_c_str |top| {
                do "w+b".as_c_str |modebuf| {
894 895 896 897 898 899
                    libc::fopen(top, modebuf)
                }
            };
            if ostream as uint == 0u {
                fclose(istream);
                return false;
900
            }
901 902 903 904 905
            let bufsize = 8192u;
            let mut buf = vec::with_capacity::<u8>(bufsize);
            let mut done = false;
            let mut ok = true;
            while !done {
906
                do buf.as_mut_buf |b, _sz| {
907 908 909 910 911 912 913 914 915 916
                  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 {
917 918
                      done = true;
                  }
919
              }
920 921 922
            }
            fclose(istream);
            fclose(ostream);
923 924

            // Give the new file the old file's permissions
925
            if do to.to_str().as_c_str |to_buf| {
926
                libc::chmod(to_buf, from_mode as libc::mode_t)
J
James Miller 已提交
927 928
            } != 0 {
                return false; // should be a condition...
929
            }
930
            return ok;
931 932 933 934
        }
    }
}

935
/// Deletes an existing file
936
pub fn remove_file(p: &Path) -> bool {
B
Brian Anderson 已提交
937
    return unlink(p);
938

939
    #[cfg(windows)]
940
    fn unlink(p: &Path) -> bool {
941 942 943 944 945 946
        unsafe {
            use os::win32::as_utf16_p;
            return do as_utf16_p(p.to_str()) |buf| {
                libc::DeleteFileW(buf) != (0 as libc::BOOL)
            };
        }
947 948
    }

949
    #[cfg(unix)]
950
    fn unlink(p: &Path) -> bool {
951
        unsafe {
E
Erick Tryzelaar 已提交
952
            do p.to_str().as_c_str |buf| {
953
                libc::unlink(buf) == (0 as c_int)
E
Erick Tryzelaar 已提交
954
            }
955
        }
956 957 958
    }
}

959
#[cfg(unix)]
960
/// Returns the platform-specific value of errno
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
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)]
992
/// Returns the platform-specific value of errno
993 994 995 996 997
pub fn errno() -> uint {
    use libc::types::os::arch::extra::DWORD;

    #[link_name = "kernel32"]
    #[abi = "stdcall"]
998
    extern "stdcall" {
999 1000 1001 1002
        unsafe fn GetLastError() -> DWORD;
    }

    unsafe {
1003
        GetLastError() as uint
1004 1005 1006
    }
}

1007
/// Get a string representing the platform-dependent last error
1008
pub fn last_os_error() -> ~str {
1009 1010 1011 1012 1013
    #[cfg(unix)]
    fn strerror() -> ~str {
        #[cfg(target_os = "macos")]
        #[cfg(target_os = "android")]
        #[cfg(target_os = "freebsd")]
1014
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1015 1016
            #[nolink]
            extern {
1017
                unsafe fn strerror_r(errnum: c_int, buf: *mut c_char,
1018 1019 1020 1021 1022 1023 1024 1025 1026
                                     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 已提交
1027
        // So we just use __xpg_strerror_r which is always POSIX compliant
1028
        #[cfg(target_os = "linux")]
1029
        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
1030 1031
            #[nolink]
            extern {
1032
                unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char,
1033 1034 1035 1036 1037 1038 1039 1040 1041
                                           buflen: size_t) -> c_int;
            }
            unsafe {
                __xpg_strerror_r(errnum, buf, buflen)
            }
        }

        let mut buf = [0 as c_char, ..TMPBUF_SZ];
        unsafe {
1042
            let err = strerror_r(errno() as c_int, &mut buf[0],
1043 1044
                                 TMPBUF_SZ as size_t);
            if err < 0 {
1045
                fail!("strerror_r failure");
1046
            }
1047

1048 1049
            str::raw::from_c_str(&buf[0])
        }
1050
    }
1051 1052 1053

    #[cfg(windows)]
    fn strerror() -> ~str {
1054 1055 1056 1057 1058 1059
        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"]
1060
        extern "stdcall" {
1061 1062 1063 1064 1065 1066
            unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
                                     msgId: DWORD, langId: DWORD,
                                     buf: LPSTR, nsize: DWORD,
                                     args: *c_void) -> DWORD;
        }

1067 1068
        static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
        static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
1069 1070 1071 1072 1073 1074 1075

        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;
1076
        unsafe {
1077 1078 1079
            let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
                                     FORMAT_MESSAGE_IGNORE_INSERTS,
                                     ptr::mut_null(), err, langId,
L
Luqman Aden 已提交
1080 1081
                                     &mut buf[0], TMPBUF_SZ as DWORD,
                                     ptr::null());
1082
            if res == 0 {
1083
                fail!("[%?] FormatMessage failure", errno());
1084 1085 1086
            }

            str::raw::from_c_str(&buf[0])
1087 1088 1089 1090
        }
    }

    strerror()
1091
}
1092

1093 1094 1095 1096 1097 1098 1099 1100
/**
 * 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
 */
1101
pub fn set_exit_status(code: int) {
1102 1103 1104 1105 1106 1107 1108 1109 1110
    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);
1111
    }
1112
}
1113

1114 1115 1116
unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
    let mut args = ~[];
    for uint::range(0, argc as uint) |i| {
1117
        args.push(str::raw::from_c_str(*argv.offset(i as int)));
1118
    }
L
Luqman Aden 已提交
1119
    args
1120 1121
}

1122 1123 1124 1125 1126 1127
/**
 * Returns the command line arguments
 *
 * Returns a list of the command line arguments.
 */
#[cfg(target_os = "macos")]
1128
pub fn real_args() -> ~[~str] {
1129
    unsafe {
1130 1131 1132
        let (argc, argv) = (*_NSGetArgc() as c_int,
                            *_NSGetArgv() as **c_char);
        load_argc_and_argv(argc, argv)
1133 1134 1135
    }
}

1136
#[cfg(target_os = "linux")]
K
kyeongwoon 已提交
1137
#[cfg(target_os = "android")]
1138
#[cfg(target_os = "freebsd")]
1139
pub fn real_args() -> ~[~str] {
1140 1141 1142
    use rt;
    use rt::TaskContext;

B
Brian Anderson 已提交
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
    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)
        }
1154
    }
1155 1156
}

1157
#[cfg(windows)]
1158
pub fn real_args() -> ~[~str] {
1159
    let mut nArgs: c_int = 0;
D
Daniel Micay 已提交
1160
    let lpArgCount: *mut c_int = &mut nArgs;
T
Tim Chevalier 已提交
1161 1162
    let lpCmdLine = unsafe { GetCommandLineW() };
    let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1163 1164 1165 1166 1167

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

            // Push it onto the list.
1173
            args.push(vec::raw::buf_as_slice(ptr, len,
1174
                                             str::from_utf16));
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
        }
    }

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

    return args;
}

type LPCWSTR = *u16;

#[cfg(windows)]
#[link_name="kernel32"]
#[abi="stdcall"]
1190
extern "stdcall" {
1191 1192 1193 1194 1195 1196 1197
    fn GetCommandLineW() -> LPCWSTR;
    fn LocalFree(ptr: *c_void);
}

#[cfg(windows)]
#[link_name="shell32"]
#[abi="stdcall"]
1198
extern "stdcall" {
1199
    fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
1200 1201 1202 1203 1204 1205
}

struct OverriddenArgs {
    val: ~[~str]
}

1206
static overridden_arg_key: local_data::Key<@OverriddenArgs> = &local_data::Key;
1207

1208 1209 1210 1211 1212
/// 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.
1213
pub fn args() -> ~[~str] {
1214 1215
    match local_data::get(overridden_arg_key, |k| k.map(|&k| *k)) {
        None => real_args(),
1216
        Some(args) => args.val.clone()
1217 1218 1219
    }
}

1220 1221 1222
/// 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 已提交
1223
pub fn set_args(new_args: ~[~str]) {
1224 1225 1226
    let overridden_args = @OverriddenArgs {
        val: new_args.clone()
    };
1227
    local_data::set(overridden_arg_key, overridden_args);
1228 1229
}

1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 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
// 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();
1291
    do pattern.as_c_str |c_pattern| {
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
        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")]
1308
pub fn glob(_pattern: &str) -> ~[Path] {
1309
    fail!("glob() is unimplemented on Windows")
1310 1311
}

1312 1313 1314 1315 1316 1317 1318
#[cfg(target_os = "macos")]
extern {
    // These functions are in crt_externs.h.
    pub fn _NSGetArgc() -> *c_int;
    pub fn _NSGetArgv() -> ***c_char;
}

1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 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
// 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()));
                    }
                }
            }
        }
    }
}

1601
pub mod consts {
1602

I
ILyoan 已提交
1603
    #[cfg(unix)]
1604
    pub use os::consts::unix::*;
1605

I
ILyoan 已提交
1606
    #[cfg(windows)]
1607
    pub use os::consts::windows::*;
1608

I
ILyoan 已提交
1609
    #[cfg(target_os = "macos")]
1610
    pub use os::consts::macos::*;
I
ILyoan 已提交
1611 1612

    #[cfg(target_os = "freebsd")]
1613
    pub use os::consts::freebsd::*;
I
ILyoan 已提交
1614 1615

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

K
kyeongwoon 已提交
1618
    #[cfg(target_os = "android")]
1619
    pub use os::consts::android::*;
K
kyeongwoon 已提交
1620

I
ILyoan 已提交
1621
    #[cfg(target_os = "win32")]
1622
    pub use os::consts::win32::*;
1623

1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643
    #[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 已提交
1644
    pub mod macos {
1645 1646 1647 1648
        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 已提交
1649 1650 1651
    }

    pub mod freebsd {
1652 1653 1654 1655
        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 已提交
1656 1657 1658
    }

    pub mod linux {
1659 1660 1661 1662
        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 已提交
1663
    }
K
kyeongwoon 已提交
1664 1665

    pub mod android {
1666 1667 1668 1669
        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 已提交
1670
    }
1671

I
ILyoan 已提交
1672
    pub mod win32 {
1673 1674 1675 1676
        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 已提交
1677 1678 1679 1680
    }


    pub mod x86 {
1681
        pub static ARCH: &'static str = "x86";
I
ILyoan 已提交
1682 1683
    }
    pub mod x86_64 {
1684
        pub static ARCH: &'static str = "x86_64";
I
ILyoan 已提交
1685 1686
    }
    pub mod arm {
1687
        pub static ARCH: &'static str = "arm";
I
ILyoan 已提交
1688
    }
J
Jyun-Yan You 已提交
1689
    pub mod mips {
1690
        pub static ARCH: &'static str = "mips";
J
Jyun-Yan You 已提交
1691
    }
I
ILyoan 已提交
1692
}
1693 1694 1695

#[cfg(test)]
mod tests {
1696
    use libc::{c_int, c_void, size_t};
1697
    use libc;
A
Alex Crichton 已提交
1698
    use option::Some;
1699
    use option;
E
Erick Tryzelaar 已提交
1700
    use os::{env, getcwd, getenv, make_absolute, real_args};
C
Corey Richardson 已提交
1701
    use os::{remove_file, setenv, unsetenv};
1702
    use os;
1703
    use path::Path;
1704
    use rand::RngUtil;
1705 1706
    use rand;
    use run;
1707
    use str::StrSlice;
1708
    use vec::CopyableVector;
1709 1710
    use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};

1711

1712
    #[test]
1713
    pub fn last_os_error() {
B
Brian Anderson 已提交
1714
        debug!(os::last_os_error());
1715
    }
1716

1717 1718 1719
    #[test]
    pub fn test_args() {
        let a = real_args();
P
Patrick Walton 已提交
1720
        assert!(a.len() >= 1);
1721 1722
    }

1723
    fn make_rand_name() -> ~str {
P
Patrick Walton 已提交
1724
        let mut rng = rand::rng();
1725
        let n = ~"TEST" + rng.gen_str(10u);
P
Patrick Walton 已提交
1726
        assert!(getenv(n).is_none());
L
Luqman Aden 已提交
1727
        n
1728 1729 1730 1731 1732
    }

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

C
Corey Richardson 已提交
1737 1738 1739
    #[test]
    fn test_unsetenv() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1740
        setenv(n, "VALUE");
C
Corey Richardson 已提交
1741
        unsetenv(n);
1742
        assert_eq!(getenv(n), option::None);
C
Corey Richardson 已提交
1743 1744
    }

1745
    #[test]
1746 1747
    #[ignore(cfg(windows))]
    #[ignore]
1748 1749
    fn test_setenv_overwrite() {
        let n = make_rand_name();
E
Erick Tryzelaar 已提交
1750 1751
        setenv(n, "1");
        setenv(n, "2");
1752
        assert_eq!(getenv(n), option::Some(~"2"));
E
Erick Tryzelaar 已提交
1753
        setenv(n, "");
1754
        assert_eq!(getenv(n), option::Some(~""));
1755 1756 1757 1758 1759
    }

    // Windows GetEnvironmentVariable requires some extra work to make sure
    // the buffer the variable is copied into is the right size
    #[test]
1760 1761
    #[ignore(cfg(windows))]
    #[ignore]
1762
    fn test_getenv_big() {
1763
        let mut s = ~"";
1764
        let mut i = 0;
1765 1766 1767 1768
        while i < 100 {
            s = s + "aaaaaaaaaa";
            i += 1;
        }
1769 1770
        let n = make_rand_name();
        setenv(n, s);
1771
        debug!(s.clone());
1772
        assert_eq!(getenv(n), option::Some(s));
1773 1774 1775 1776 1777
    }

    #[test]
    fn test_self_exe_path() {
        let path = os::self_exe_path();
P
Patrick Walton 已提交
1778
        assert!(path.is_some());
B
Brian Anderson 已提交
1779
        let path = path.get();
1780
        debug!(path.clone());
1781 1782

        // Hard to test this function
P
Patrick Walton 已提交
1783
        assert!(path.is_absolute);
1784 1785 1786
    }

    #[test]
1787
    #[ignore]
1788 1789
    fn test_env_getenv() {
        let e = env();
Y
Youngmin Yoo 已提交
1790
        assert!(e.len() > 0u);
1791
        for e.iter().advance |p| {
1792 1793
            let (n, v) = (*p).clone();
            debug!(n.clone());
1794 1795 1796 1797
            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 已提交
1798
            assert!(v2.is_none() || v2 == option::Some(v));
1799 1800 1801 1802 1803 1804 1805
        }
    }

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

1806
        let mut e = env();
E
Erick Tryzelaar 已提交
1807
        setenv(n, "VALUE");
1808
        assert!(!e.contains(&(n.clone(), ~"VALUE")));
1809 1810

        e = env();
1811
        assert!(e.contains(&(n, ~"VALUE")));
1812 1813
    }

1814 1815
    #[test]
    fn test() {
P
Patrick Walton 已提交
1816
        assert!((!Path("test-path").is_absolute));
1817

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

B
Brian Anderson 已提交
1820 1821
        debug!(make_absolute(&Path("test-path")));
        debug!(make_absolute(&Path("/usr/bin")));
1822 1823 1824
    }

    #[test]
1825
    #[cfg(unix)]
1826
    fn homedir() {
E
Erick Tryzelaar 已提交
1827
        let oldhome = getenv("HOME");
1828

E
Erick Tryzelaar 已提交
1829
        setenv("HOME", "/home/MountainView");
1830
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1831

E
Erick Tryzelaar 已提交
1832
        setenv("HOME", "");
P
Patrick Walton 已提交
1833
        assert!(os::homedir().is_none());
1834

1835
        for oldhome.iter().advance |s| { setenv("HOME", *s) }
1836 1837 1838
    }

    #[test]
1839
    #[cfg(windows)]
1840 1841
    fn homedir() {

E
Erick Tryzelaar 已提交
1842 1843
        let oldhome = getenv("HOME");
        let olduserprofile = getenv("USERPROFILE");
1844

E
Erick Tryzelaar 已提交
1845 1846
        setenv("HOME", "");
        setenv("USERPROFILE", "");
1847

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

E
Erick Tryzelaar 已提交
1850
        setenv("HOME", "/home/MountainView");
1851
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1852

E
Erick Tryzelaar 已提交
1853
        setenv("HOME", "");
1854

E
Erick Tryzelaar 已提交
1855
        setenv("USERPROFILE", "/home/MountainView");
1856
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1857

E
Erick Tryzelaar 已提交
1858 1859
        setenv("HOME", "/home/MountainView");
        setenv("USERPROFILE", "/home/PaloAlto");
1860
        assert_eq!(os::homedir(), Some(Path("/home/MountainView")));
1861

1862 1863
        oldhome.iter().advance(|s| { setenv("HOME", *s); true });
        olduserprofile.iter().advance(|s| { setenv("USERPROFILE", *s); true });
1864 1865
    }

1866 1867
    #[test]
    fn tmpdir() {
1868
        assert!(!os::tmpdir().to_str().is_empty());
1869 1870
    }

1871 1872
    // Issue #712
    #[test]
1873 1874 1875
    fn test_list_dir_no_invalid_memory_access() {
        os::list_dir(&Path("."));
    }
1876 1877 1878

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

1883
        for dirs.iter().advance |dir| {
1884
            debug!((*dir).clone());
1885
        }
1886 1887
    }

1888 1889 1890 1891 1892 1893
    #[test]
    fn list_dir_empty_path() {
        let dirs = os::list_dir(&Path(""));
        assert!(dirs.is_empty());
    }

1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907
    #[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);
    }


1908 1909
    #[test]
    fn path_is_dir() {
P
Patrick Walton 已提交
1910 1911
        assert!((os::path_is_dir(&Path("."))));
        assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
1912 1913 1914 1915
    }

    #[test]
    fn path_exists() {
P
Patrick Walton 已提交
1916 1917
        assert!((os::path_exists(&Path("."))));
        assert!((!os::path_exists(&Path(
P
Patrick Walton 已提交
1918
                     "test/nonexistent-bogus-path"))));
1919 1920
    }

1921 1922
    #[test]
    fn copy_file_does_not_exist() {
P
Patrick Walton 已提交
1923
      assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
1924
                            &Path("test/other-bogus-path")));
P
Patrick Walton 已提交
1925
      assert!(!os::path_exists(&Path("test/other-bogus-path")));
1926 1927 1928 1929
    }

    #[test]
    fn copy_file_ok() {
1930 1931 1932
        unsafe {
          let tempdir = getcwd(); // would like to use $TMPDIR,
                                  // doesn't seem to work on Linux
1933
          assert!((tempdir.to_str().len() > 0u));
1934 1935 1936 1937
          let in = tempdir.push("in.txt");
          let out = tempdir.push("out.txt");

          /* Write the temp input file */
E
Erick Tryzelaar 已提交
1938 1939
            let ostream = do in.to_str().as_c_str |fromp| {
                do "w+b".as_c_str |modebuf| {
1940 1941 1942
                    libc::fopen(fromp, modebuf)
                }
          };
P
Patrick Walton 已提交
1943
          assert!((ostream as uint != 0u));
1944
          let s = ~"hello";
1945
          let mut buf = s.as_bytes_with_null().to_owned();
D
Daniel Micay 已提交
1946
          let len = buf.len();
1947
          do buf.as_mut_buf |b, _len| {
D
Daniel Micay 已提交
1948 1949 1950
              assert_eq!(libc::fwrite(b as *c_void, 1u as size_t,
                                      (s.len() + 1u) as size_t, ostream),
                         len as size_t)
1951
          }
1952
          assert_eq!(libc::fclose(ostream), (0u as c_int));
1953
          let in_mode = in.get_mode();
1954 1955
          let rs = os::copy_file(&in, &out);
          if (!os::path_exists(&in)) {
1956
            fail!("%s doesn't exist", in.to_str());
1957
          }
P
Patrick Walton 已提交
1958
          assert!((rs));
1959
          let rslt = run::process_status("diff", [in.to_str(), out.to_str()]);
1960 1961
          assert_eq!(rslt, 0);
          assert_eq!(out.get_mode(), in_mode);
P
Patrick Walton 已提交
1962 1963
          assert!((remove_file(&in)));
          assert!((remove_file(&out)));
1964
        }
1965
    }
1966 1967

    #[test]
1968 1969 1970 1971
    fn recursive_mkdir_slash() {
        let path = Path("/");
        assert!(os::mkdir_recursive(&path,  (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
    }
1972

1973 1974 1975 1976
    #[test]
    fn recursive_mkdir_empty() {
        let path = Path("");
        assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1977 1978
    }

1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
    #[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);
           }
        }

E
Erick Tryzelaar 已提交
2017
        let path = tmpdir().push("mmap_file.tmp");
2018
        let size = page_size() * 2;
E
Erick Tryzelaar 已提交
2019
        remove_file(&path);
2020 2021

        let fd = unsafe {
E
Erick Tryzelaar 已提交
2022
            let fd = do path.to_str().as_c_str |path| {
2023 2024 2025
                open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)
            };
            lseek_(fd, size);
E
Erick Tryzelaar 已提交
2026
            do "x".as_c_str |x| {
2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048
                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);
        }
    }

2049
    // More recursive_mkdir tests are in extra::tempfile
2050
}