1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 * Higher-level interfaces to libc::* functions and operating system services.
14 * In general these take and return rust types, use rust idioms (enums,
15 * closures, vectors) rather than C idioms, and do more extensive safety
18 * This module is not meant to only contain 1:1 mappings to libc entries; any
19 * os-interface code that is reasonably useful and broadly applicable can go
20 * here. Including utility routines that merely build on other os code.
22 * We assume the general case is that users do not care, and do not want to
23 * be made to care, which operating system they are on. While they may want
24 * to special case various special cases -- and so we will not _hide_ the
25 * facts of which OS the user is on -- they should be given the opportunity
26 * to write OS-ignorant code by default.
32 use libc::{c_char, c_void, c_int, size_t};
33 use libc::{mode_t, FILE};
35 use option::{Some, None};
41 use unstable::finally::Finally;
45 pub use os::consts::*;
47 pub fn close(fd: c_int) -> c_int {
54 use libc::{c_char, c_int};
58 unsafe fn rust_get_argc() -> c_int;
59 unsafe fn rust_get_argv() -> **c_char;
60 unsafe fn rust_path_is_dir(path: *libc::c_char) -> c_int;
61 unsafe fn rust_path_exists(path: *libc::c_char) -> c_int;
62 unsafe fn rust_set_exit_status(code: libc::intptr_t);
66 pub static TMPBUF_SZ : uint = 1000u;
67 static BUF_BYTES : uint = 2048u;
69 pub fn getcwd() -> Path {
70 let buf = [0 as libc::c_char, ..BUF_BYTES];
72 if(0 as *libc::c_char == libc::getcwd(
74 BUF_BYTES as libc::size_t)) {
77 Path(str::raw::from_c_str(&buf[0]))
81 // FIXME: move these to str perhaps? #2620
83 pub fn as_c_charp<T>(s: &str, f: &fn(*c_char) -> T) -> T {
84 str::as_c_str(s, |b| f(b as *c_char))
87 pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool)
89 let mut buf = vec::from_elem(TMPBUF_SZ, 0u8 as c_char);
90 do vec::as_mut_buf(buf) |b, sz| {
91 if f(b, sz as size_t) {
93 Some(str::raw::from_buf(b as *u8))
106 use option::{None, Option};
109 use libc::types::os::arch::extra::DWORD;
111 pub fn fill_utf16_buf_and_decode(f: &fn(*mut u16, DWORD) -> DWORD)
114 let mut n = TMPBUF_SZ as DWORD;
116 let mut done = false;
118 let mut k: DWORD = 0;
119 let mut buf = vec::from_elem(n as uint, 0u16);
120 do vec::as_mut_buf(buf) |b, _sz| {
121 k = f(b, TMPBUF_SZ as DWORD);
122 if k == (0 as DWORD) {
125 libc::GetLastError() ==
126 libc::ERROR_INSUFFICIENT_BUFFER as DWORD) {
133 let sub = vec::slice(buf, 0u, k as uint);
134 res = option::Some(str::from_utf16(sub));
141 pub fn as_utf16_p<T>(s: &str, f: &fn(*u16) -> T) -> T {
142 let mut t = str::to_utf16(s);
143 // Null terminate before passing on.
145 vec::as_imm_buf(t, |buf, _len| f(buf))
150 Accessing environment variables is not generally threadsafe.
151 This uses a per-runtime lock to serialize access.
152 FIXME #4726: It would probably be appropriate to make this a real global
154 fn with_env_lock<T>(f: &fn() -> T) -> T {
155 use unstable::global::global_data_clone_create;
156 use unstable::{Exclusive, exclusive};
158 struct SharedValue(());
159 type ValueMutex = Exclusive<SharedValue>;
160 fn key(_: ValueMutex) { }
163 let lock: ValueMutex = global_data_clone_create(key, || {
164 ~exclusive(SharedValue(()))
167 lock.with_imm(|_| f() )
171 pub fn env() -> ~[(~str,~str)] {
174 unsafe fn get_env_pairs() -> ~[~str] {
175 use libc::types::os::arch::extra::LPTCH;
176 use libc::funcs::extra::kernel32::{
177 GetEnvironmentStringsA,
178 FreeEnvironmentStringsA
180 let ch = GetEnvironmentStringsA();
181 if (ch as uint == 0) {
182 fail!(fmt!("os::env() failure getting env string from OS: %s",
183 os::last_os_error()));
185 let mut curr_ptr: uint = ch as uint;
186 let mut result = ~[];
187 while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) {
188 let env_pair = str::raw::from_c_str(
189 curr_ptr as *libc::c_char);
190 result.push(env_pair);
192 libc::strlen(curr_ptr as *libc::c_char) as uint
195 FreeEnvironmentStringsA(ch);
199 unsafe fn get_env_pairs() -> ~[~str] {
201 unsafe fn rust_env_pairs() -> **libc::c_char;
203 let environ = rustrt::rust_env_pairs();
204 if (environ as uint == 0) {
205 fail!(fmt!("os::env() failure getting env string from OS: %s",
206 os::last_os_error()));
208 let mut result = ~[];
209 ptr::array_each(environ, |e| {
210 let env_pair = str::raw::from_c_str(e);
211 debug!("get_env_pairs: %s",
213 result.push(env_pair);
218 fn env_convert(input: ~[~str]) -> ~[(~str, ~str)] {
222 for str::each_splitn_char(*p, '=', 1) |s| { vs.push(s.to_owned()) }
223 debug!("splitting: len: %u",
225 assert!(vs.len() == 2);
226 pairs.push((copy vs[0], copy vs[1]));
231 let unparsed_environ = get_env_pairs();
232 env_convert(unparsed_environ)
238 pub fn getenv(n: &str) -> Option<~str> {
241 let s = str::as_c_str(n, |s| libc::getenv(s));
242 if ptr::null::<u8>() == cast::transmute(s) {
245 let s = cast::transmute(s);
246 option::Some::<~str>(str::raw::from_buf(s))
253 pub fn getenv(n: &str) -> Option<~str> {
256 use os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
257 do as_utf16_p(n) |u| {
258 do fill_utf16_buf_and_decode() |buf, sz| {
259 libc::GetEnvironmentVariableW(u, buf, sz)
268 pub fn setenv(n: &str, v: &str) {
271 do str::as_c_str(n) |nbuf| {
272 do str::as_c_str(v) |vbuf| {
273 libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1);
282 pub fn setenv(n: &str, v: &str) {
285 use os::win32::as_utf16_p;
286 do as_utf16_p(n) |nbuf| {
287 do as_utf16_p(v) |vbuf| {
288 libc::SetEnvironmentVariableW(nbuf, vbuf);
295 pub fn fdopen(fd: c_int) -> *FILE {
297 return do as_c_charp("r") |modebuf| {
298 libc::fdopen(fd, modebuf)
307 pub fn fsync_fd(fd: c_int, _level: io::fsync::Level) -> c_int {
309 use libc::funcs::extra::msvcrt::*;
314 #[cfg(target_os = "linux")]
315 #[cfg(target_os = "android")]
316 pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
318 use libc::funcs::posix01::unistd::*;
321 | io::fsync::FullFSync => return fsync(fd),
322 io::fsync::FDataSync => return fdatasync(fd)
327 #[cfg(target_os = "macos")]
328 pub fn fsync_fd(fd: c_int, level: io::fsync::Level) -> c_int {
330 use libc::consts::os::extra::*;
331 use libc::funcs::posix88::fcntl::*;
332 use libc::funcs::posix01::unistd::*;
334 io::fsync::FSync => return fsync(fd),
336 // According to man fnctl, the ok retval is only specified to be
338 if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int)
339 { return -1 as c_int; }
341 { return 0 as c_int; }
347 #[cfg(target_os = "freebsd")]
348 pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
350 use libc::funcs::posix01::unistd::*;
355 pub struct Pipe { mut in: c_int, mut out: c_int }
358 pub fn pipe() -> Pipe {
360 let mut fds = Pipe {in: 0 as c_int,
362 assert!((libc::pipe(&mut fds.in) == (0 as c_int)));
363 return Pipe {in: fds.in, out: fds.out};
370 pub fn pipe() -> Pipe {
372 // Windows pipes work subtly differently than unix pipes, and their
373 // inheritance has to be handled in a different way that I do not
374 // fully understand. Here we explicitly make the pipe non-inheritable,
375 // which means to pass it to a subprocess they need to be duplicated
376 // first, as in core::run.
377 let mut fds = Pipe {in: 0 as c_int,
379 let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint,
380 (libc::O_BINARY | libc::O_NOINHERIT) as c_int);
381 assert!((res == 0 as c_int));
382 assert!((fds.in != -1 as c_int && fds.in != 0 as c_int));
383 assert!((fds.out != -1 as c_int && fds.in != 0 as c_int));
384 return Pipe {in: fds.in, out: fds.out};
388 fn dup2(src: c_int, dst: c_int) -> c_int {
395 pub fn dll_filename(base: &str) -> ~str {
396 return str::from_slice(DLL_PREFIX) + str::from_slice(base) +
397 str::from_slice(DLL_SUFFIX)
401 pub fn self_exe_path() -> Option<Path> {
403 #[cfg(target_os = "freebsd")]
404 fn load_self() -> Option<~str> {
406 use libc::funcs::bsd44::*;
407 use libc::consts::os::extra::*;
408 do fill_charp_buf() |buf, sz| {
409 let mib = ~[CTL_KERN as c_int,
411 KERN_PROC_PATHNAME as c_int, -1 as c_int];
413 sysctl(vec::raw::to_ptr(mib), vec::len(mib) as ::libc::c_uint,
414 buf as *mut c_void, &mut sz, ptr::null(),
415 0u as size_t) == (0 as c_int)
420 #[cfg(target_os = "linux")]
421 #[cfg(target_os = "android")]
422 fn load_self() -> Option<~str> {
424 use libc::funcs::posix01::unistd::readlink;
426 let mut path_str = str::with_capacity(TMPBUF_SZ);
427 let len = do str::as_c_str(path_str) |buf| {
428 let buf = buf as *mut c_char;
429 do as_c_charp("/proc/self/exe") |proc_self_buf| {
430 readlink(proc_self_buf, buf, TMPBUF_SZ as size_t)
436 str::raw::set_len(&mut path_str, len as uint);
442 #[cfg(target_os = "macos")]
443 fn load_self() -> Option<~str> {
445 do fill_charp_buf() |buf, sz| {
446 let mut sz = sz as u32;
447 libc::funcs::extra::_NSGetExecutablePath(
448 buf, &mut sz) == (0 as c_int)
454 fn load_self() -> Option<~str> {
456 use os::win32::fill_utf16_buf_and_decode;
457 do fill_utf16_buf_and_decode() |buf, sz| {
458 libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
463 do load_self().map |pth| {
464 Path(*pth).dir_path()
470 * Returns the path to the user's home directory, if known.
472 * On Unix, returns the value of the 'HOME' environment variable if it is set
473 * and not equal to the empty string.
475 * On Windows, returns the value of the 'HOME' environment variable if it is
476 * set and not equal to the empty string. Otherwise, returns the value of the
477 * 'USERPROFILE' environment variable if it is set and not equal to the empty
480 * Otherwise, homedir returns option::none.
482 pub fn homedir() -> Option<Path> {
483 return match getenv(~"HOME") {
484 Some(ref p) => if !str::is_empty(*p) {
493 fn secondary() -> Option<Path> {
498 fn secondary() -> Option<Path> {
499 do getenv(~"USERPROFILE").chain |p| {
500 if !str::is_empty(p) {
510 * Returns the path to a temporary directory.
512 * On Unix, returns the value of the 'TMPDIR' environment variable if it is
513 * set and non-empty and '/tmp' otherwise.
515 * On Windows, returns the value of, in order, the 'TMP', 'TEMP',
516 * 'USERPROFILE' environment variable if any are set and not the empty
517 * string. Otherwise, tmpdir returns the path to the Windows directory.
519 pub fn tmpdir() -> Path {
522 fn getenv_nonempty(v: &str) -> Option<Path> {
525 if str::is_empty(x) {
535 #[allow(non_implicitly_copyable_typarams)]
536 fn lookup() -> Path {
537 getenv_nonempty("TMPDIR").get_or_default(Path("/tmp"))
541 #[allow(non_implicitly_copyable_typarams)]
542 fn lookup() -> Path {
543 getenv_nonempty("TMP").or(
544 getenv_nonempty("TEMP").or(
545 getenv_nonempty("USERPROFILE").or(
546 getenv_nonempty("WINDIR")))).get_or_default(Path("C:\\Windows"))
549 /// Recursively walk a directory structure
550 pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) {
554 fn walk_dir_(p: &Path, f: &fn(&Path) -> bool) -> bool {
555 let mut keepgoing = true;
556 do list_dir(p).each |q| {
557 let path = &p.push(*q);
562 if path_is_dir(path) {
563 if !walk_dir_(path, f) {
578 /// Indicates whether a path represents a directory
579 pub fn path_is_dir(p: &Path) -> bool {
581 do str::as_c_str(p.to_str()) |buf| {
582 rustrt::rust_path_is_dir(buf) != 0 as c_int
587 /// Indicates whether a path exists
588 pub fn path_exists(p: &Path) -> bool {
590 do str::as_c_str(p.to_str()) |buf| {
591 rustrt::rust_path_exists(buf) != 0 as c_int
597 * Convert a relative path to an absolute path
599 * If the given path is relative, return it prepended with the current working
600 * directory. If the given path is already an absolute path, return it
603 // NB: this is here rather than in path because it is a form of environment
604 // querying; what it does depends on the process working directory, not just
606 pub fn make_absolute(p: &Path) -> Path {
610 getcwd().push_many(p.components)
615 /// Creates a directory at the specified path
616 pub fn make_dir(p: &Path, mode: c_int) -> bool {
617 return mkdir(p, mode);
620 fn mkdir(p: &Path, _mode: c_int) -> bool {
622 use os::win32::as_utf16_p;
623 // FIXME: turn mode into something useful? #2623
624 do as_utf16_p(p.to_str()) |buf| {
625 libc::CreateDirectoryW(buf, unsafe {
634 fn mkdir(p: &Path, mode: c_int) -> bool {
636 do as_c_charp(p.to_str()) |c| {
637 libc::mkdir(c, mode as mode_t) == (0 as c_int)
643 /// Creates a directory with a given mode.
644 /// Returns true iff creation
645 /// succeeded. Also creates all intermediate subdirectories
646 /// if they don't already exist, giving all of them the same mode.
648 // tjc: if directory exists but with different permissions,
649 // should we return false?
650 pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool {
654 else if p.components.is_empty() {
657 else if p.components.len() == 1 {
658 // No parent directories to create
659 path_is_dir(p) || make_dir(p, mode)
662 mkdir_recursive(&p.pop(), mode) && make_dir(p, mode)
666 /// Lists the contents of a directory
667 #[allow(non_implicitly_copyable_typarams)]
668 pub fn list_dir(p: &Path) -> ~[~str] {
670 #[cfg(target_os = "linux")]
671 #[cfg(target_os = "android")]
672 #[cfg(target_os = "freebsd")]
673 #[cfg(target_os = "macos")]
674 unsafe fn get_list(p: &Path) -> ~[~str] {
675 use libc::{dirent_t};
676 use libc::{opendir, readdir, closedir};
678 unsafe fn rust_list_dir_val(ptr: *dirent_t)
681 let input = p.to_str();
682 let mut strings = ~[];
683 let input_ptr = ::cast::transmute(&input[0]);
684 debug!("os::list_dir -- BEFORE OPENDIR");
685 let dir_ptr = opendir(input_ptr);
686 if (dir_ptr as uint != 0) {
687 debug!("os::list_dir -- opendir() SUCCESS");
688 let mut entry_ptr = readdir(dir_ptr);
689 while (entry_ptr as uint != 0) {
691 str::raw::from_c_str(
692 rustrt::rust_list_dir_val(
694 entry_ptr = readdir(dir_ptr);
699 debug!("os::list_dir -- opendir() FAILURE");
702 "os::list_dir -- AFTER -- #: %?",
707 unsafe fn get_list(p: &Path) -> ~[~str] {
708 use libc::types::os::arch::extra::{LPCTSTR, HANDLE, BOOL};
709 use libc::consts::os::extra::INVALID_HANDLE_VALUE;
711 use libc::funcs::extra::kernel32::{
719 use unstable::exchange_alloc::{malloc_raw, free_raw};
722 unsafe fn rust_list_dir_wfd_size() -> libc::size_t;
723 unsafe fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void)
726 fn star(p: &Path) -> Path { p.push("*") }
727 do as_utf16_p(star(p).to_str()) |path_ptr| {
728 let mut strings = ~[];
729 let wfd_ptr = malloc_raw(
730 rustrt::rust_list_dir_wfd_size() as uint);
734 ::cast::transmute(wfd_ptr));
735 if find_handle as int != INVALID_HANDLE_VALUE {
736 let mut more_files = 1 as libc::c_int;
737 while more_files != 0 {
738 let fp_buf = rustrt::rust_list_dir_wfd_fp_buf(
740 if fp_buf as uint == 0 {
741 fail!(~"os::list_dir() failure:"+
742 ~" got null ptr from wfd");
745 let fp_vec = vec::from_buf(
746 fp_buf, wcslen(fp_buf) as uint);
747 let fp_str = str::from_utf16(fp_vec);
748 strings.push(fp_str);
750 more_files = FindNextFileW(
752 ::cast::transmute(wfd_ptr));
754 FindClose(find_handle);
760 do get_list(p).filtered |filename| {
761 *filename != ~"." && *filename != ~".."
767 * Lists the contents of a directory
769 * This version prepends each entry with the directory.
771 pub fn list_dir_path(p: &Path) -> ~[~Path] {
772 list_dir(p).map(|f| ~p.push(*f))
775 /// Removes a directory at the specified path
776 pub fn remove_dir(p: &Path) -> bool {
780 fn rmdir(p: &Path) -> bool {
782 use os::win32::as_utf16_p;
783 return do as_utf16_p(p.to_str()) |buf| {
784 libc::RemoveDirectoryW(buf) != (0 as libc::BOOL)
790 fn rmdir(p: &Path) -> bool {
792 return do as_c_charp(p.to_str()) |buf| {
793 libc::rmdir(buf) == (0 as c_int)
799 pub fn change_dir(p: &Path) -> bool {
803 fn chdir(p: &Path) -> bool {
805 use os::win32::as_utf16_p;
806 return do as_utf16_p(p.to_str()) |buf| {
807 libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL)
813 fn chdir(p: &Path) -> bool {
815 return do as_c_charp(p.to_str()) |buf| {
816 libc::chdir(buf) == (0 as c_int)
822 /// Changes the current working directory to the specified
823 /// path while acquiring a global lock, then calls `action`.
824 /// If the change is successful, releases the lock and restores the
825 /// CWD to what it was before, returning true.
826 /// Returns false if the directory doesn't exist or if the directory change
827 /// is otherwise unsuccessful.
828 pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
829 use unstable::global::global_data_clone_create;
830 use unstable::{Exclusive, exclusive};
832 fn key(_: Exclusive<()>) { }
834 let result = unsafe {
835 global_data_clone_create(key, || {
840 do result.with_imm() |_| {
841 let old_dir = os::getcwd();
852 /// Copies a file from one location to another
853 pub fn copy_file(from: &Path, to: &Path) -> bool {
854 return do_copy_file(from, to);
857 fn do_copy_file(from: &Path, to: &Path) -> bool {
859 use os::win32::as_utf16_p;
860 return do as_utf16_p(from.to_str()) |fromp| {
861 do as_utf16_p(to.to_str()) |top| {
862 libc::CopyFileW(fromp, top, (0 as libc::BOOL)) !=
870 fn do_copy_file(from: &Path, to: &Path) -> bool {
872 let istream = do as_c_charp(from.to_str()) |fromp| {
873 do as_c_charp("rb") |modebuf| {
874 libc::fopen(fromp, modebuf)
877 if istream as uint == 0u {
880 let ostream = do as_c_charp(to.to_str()) |top| {
881 do as_c_charp("w+b") |modebuf| {
882 libc::fopen(top, modebuf)
885 if ostream as uint == 0u {
890 let mut buf = vec::with_capacity::<u8>(bufsize);
891 let mut done = false;
894 do vec::as_mut_buf(buf) |b, _sz| {
895 let nread = libc::fread(b as *mut c_void, 1u as size_t,
898 if nread > 0 as size_t {
899 if libc::fwrite(b as *c_void, 1u as size_t, nread,
916 /// Deletes an existing file
917 pub fn remove_file(p: &Path) -> bool {
921 fn unlink(p: &Path) -> bool {
923 use os::win32::as_utf16_p;
924 return do as_utf16_p(p.to_str()) |buf| {
925 libc::DeleteFileW(buf) != (0 as libc::BOOL)
931 fn unlink(p: &Path) -> bool {
933 return do as_c_charp(p.to_str()) |buf| {
934 libc::unlink(buf) == (0 as c_int)
941 pub fn errno() -> int {
942 #[cfg(target_os = "macos")]
943 #[cfg(target_os = "freebsd")]
944 fn errno_location() -> *c_int {
947 unsafe fn __error() -> *c_int;
954 #[cfg(target_os = "linux")]
955 #[cfg(target_os = "android")]
956 fn errno_location() -> *c_int {
959 unsafe fn __errno_location() -> *c_int;
967 (*errno_location()) as int
972 pub fn errno() -> uint {
973 use libc::types::os::arch::extra::DWORD;
975 #[link_name = "kernel32"]
978 unsafe fn GetLastError() -> DWORD;
982 GetLastError() as uint
986 /// Get a string representing the platform-dependent last error
987 pub fn last_os_error() -> ~str {
989 fn strerror() -> ~str {
990 #[cfg(target_os = "macos")]
991 #[cfg(target_os = "android")]
992 #[cfg(target_os = "freebsd")]
993 fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int {
996 unsafe fn strerror_r(errnum: c_int, buf: *c_char,
997 buflen: size_t) -> c_int;
1000 strerror_r(errnum, buf, buflen)
1004 // GNU libc provides a non-compliant version of strerror_r by default
1005 // and requires macros to instead use the POSIX compliant variant.
1006 // So we just use __xpg_strerror_r which is always POSIX compliant
1007 #[cfg(target_os = "linux")]
1008 fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int {
1011 unsafe fn __xpg_strerror_r(errnum: c_int, buf: *c_char,
1012 buflen: size_t) -> c_int;
1015 __xpg_strerror_r(errnum, buf, buflen)
1019 let mut buf = [0 as c_char, ..TMPBUF_SZ];
1021 let err = strerror_r(errno() as c_int, &buf[0],
1022 TMPBUF_SZ as size_t);
1024 fail!(~"strerror_r failure");
1027 str::raw::from_c_str(&buf[0])
1032 fn strerror() -> ~str {
1033 use libc::types::os::arch::extra::DWORD;
1034 use libc::types::os::arch::extra::LPSTR;
1035 use libc::types::os::arch::extra::LPVOID;
1037 #[link_name = "kernel32"]
1040 unsafe fn FormatMessageA(flags: DWORD, lpSrc: LPVOID,
1041 msgId: DWORD, langId: DWORD,
1042 buf: LPSTR, nsize: DWORD,
1043 args: *c_void) -> DWORD;
1046 static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
1047 static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
1049 let mut buf = [0 as c_char, ..TMPBUF_SZ];
1051 // This value is calculated from the macro
1052 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
1053 let langId = 0x0800 as DWORD;
1054 let err = errno() as DWORD;
1056 let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
1057 FORMAT_MESSAGE_IGNORE_INSERTS,
1058 ptr::mut_null(), err, langId,
1059 &mut buf[0], TMPBUF_SZ as DWORD,
1062 fail!(fmt!("[%?] FormatMessage failure", errno()));
1065 str::raw::from_c_str(&buf[0])
1073 * Sets the process exit code
1075 * Sets the exit code returned by the process if all supervised tasks
1076 * terminate successfully (without failing). If the current root task fails
1077 * and is supervised by the scheduler then any user-specified exit status is
1078 * ignored and the process exits with the default failure status
1080 pub fn set_exit_status(code: int) {
1082 rustrt::rust_set_exit_status(code as libc::intptr_t);
1086 unsafe fn load_argc_and_argv(argc: c_int, argv: **c_char) -> ~[~str] {
1088 for uint::range(0, argc as uint) |i| {
1089 vec::push(&mut args, str::raw::from_c_str(*argv.offset(i)));
1095 * Returns the command line arguments
1097 * Returns a list of the command line arguments.
1099 #[cfg(target_os = "macos")]
1100 pub fn real_args() -> ~[~str] {
1102 let (argc, argv) = (*_NSGetArgc() as c_int,
1103 *_NSGetArgv() as **c_char);
1104 load_argc_and_argv(argc, argv)
1108 #[cfg(target_os = "linux")]
1109 #[cfg(target_os = "android")]
1110 #[cfg(target_os = "freebsd")]
1111 pub fn real_args() -> ~[~str] {
1113 let argc = rustrt::rust_get_argc();
1114 let argv = rustrt::rust_get_argv();
1115 load_argc_and_argv(argc, argv)
1120 pub fn real_args() -> ~[~str] {
1121 let mut nArgs: c_int = 0;
1122 let lpArgCount = ptr::to_mut_unsafe_ptr(&mut nArgs);
1123 let lpCmdLine = unsafe { GetCommandLineW() };
1124 let szArgList = unsafe { CommandLineToArgvW(lpCmdLine, lpArgCount) };
1127 for uint::range(0, nArgs as uint) |i| {
1129 // Determine the length of this argument.
1130 let ptr = *szArgList.offset(i);
1132 while *ptr.offset(len) != 0 { len += 1; }
1134 // Push it onto the list.
1135 vec::push(&mut args,
1136 vec::raw::buf_as_slice(ptr, len,
1142 LocalFree(cast::transmute(szArgList));
1148 type LPCWSTR = *u16;
1151 #[link_name="kernel32"]
1154 fn GetCommandLineW() -> LPCWSTR;
1155 fn LocalFree(ptr: *c_void);
1159 #[link_name="shell32"]
1162 fn CommandLineToArgvW(lpCmdLine: LPCWSTR, pNumArgs: *mut c_int) -> **u16;
1165 struct OverriddenArgs {
1169 fn overridden_arg_key(_v: @OverriddenArgs) {}
1171 pub fn args() -> ~[~str] {
1173 match task::local_data::local_data_get(overridden_arg_key) {
1174 None => real_args(),
1175 Some(args) => copy args.val
1180 pub fn set_args(new_args: ~[~str]) {
1182 let overridden_args = @OverriddenArgs { val: copy new_args };
1183 task::local_data::local_data_set(overridden_arg_key, overridden_args);
1187 // FIXME #6100 we should really use an internal implementation of this - using
1188 // the POSIX glob functions isn't portable to windows, probably has slight
1189 // inconsistencies even where it is implemented, and makes extending
1190 // functionality a lot more difficult
1191 // FIXME #6101 also provide a non-allocating version - each_glob or so?
1192 /// Returns a vector of Path objects that match the given glob pattern
1193 #[cfg(target_os = "linux")]
1194 #[cfg(target_os = "android")]
1195 #[cfg(target_os = "freebsd")]
1196 #[cfg(target_os = "macos")]
1197 pub fn glob(pattern: &str) -> ~[Path] {
1198 #[cfg(target_os = "linux")]
1199 #[cfg(target_os = "android")]
1200 fn default_glob_t () -> libc::glob_t {
1203 gl_pathv: ptr::null(),
1205 __unused1: ptr::null(),
1206 __unused2: ptr::null(),
1207 __unused3: ptr::null(),
1208 __unused4: ptr::null(),
1209 __unused5: ptr::null(),
1213 #[cfg(target_os = "freebsd")]
1214 fn default_glob_t () -> libc::glob_t {
1220 gl_pathv: ptr::null(),
1221 __unused3: ptr::null(),
1222 __unused4: ptr::null(),
1223 __unused5: ptr::null(),
1224 __unused6: ptr::null(),
1225 __unused7: ptr::null(),
1226 __unused8: ptr::null(),
1230 #[cfg(target_os = "macos")]
1231 fn default_glob_t () -> libc::glob_t {
1237 gl_pathv: ptr::null(),
1238 __unused3: ptr::null(),
1239 __unused4: ptr::null(),
1240 __unused5: ptr::null(),
1241 __unused6: ptr::null(),
1242 __unused7: ptr::null(),
1243 __unused8: ptr::null(),
1247 let mut g = default_glob_t();
1248 do str::as_c_str(pattern) |c_pattern| {
1249 unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) }
1252 let paths = unsafe {
1253 vec::raw::from_buf_raw(g.gl_pathv, g.gl_pathc as uint)
1255 do paths.map |&c_str| {
1256 Path(unsafe { str::raw::from_c_str(c_str) })
1259 unsafe { libc::globfree(&mut g) };
1263 /// Returns a vector of Path objects that match the given glob pattern
1264 #[cfg(target_os = "win32")]
1265 pub fn glob(pattern: &str) -> ~[Path] {
1266 fail!(~"glob() is unimplemented on Windows")
1269 #[cfg(target_os = "macos")]
1271 // These functions are in crt_externs.h.
1272 pub fn _NSGetArgc() -> *c_int;
1273 pub fn _NSGetArgv() -> ***c_char;
1279 pub use os::consts::unix::*;
1282 pub use os::consts::windows::*;
1284 #[cfg(target_os = "macos")]
1285 pub use os::consts::macos::*;
1287 #[cfg(target_os = "freebsd")]
1288 pub use os::consts::freebsd::*;
1290 #[cfg(target_os = "linux")]
1291 pub use os::consts::linux::*;
1293 #[cfg(target_os = "android")]
1294 pub use os::consts::android::*;
1296 #[cfg(target_os = "win32")]
1297 pub use os::consts::win32::*;
1299 #[cfg(target_arch = "x86")]
1300 pub use os::consts::x86::*;
1302 #[cfg(target_arch = "x86_64")]
1303 pub use os::consts::x86_64::*;
1305 #[cfg(target_arch = "arm")]
1306 pub use os::consts::arm::*;
1308 #[cfg(target_arch = "mips")]
1309 use os::consts::mips::*;
1312 pub static FAMILY: &'static str = "unix";
1316 pub static FAMILY: &'static str = "windows";
1320 pub static SYSNAME: &'static str = "macos";
1321 pub static DLL_PREFIX: &'static str = "lib";
1322 pub static DLL_SUFFIX: &'static str = ".dylib";
1323 pub static EXE_SUFFIX: &'static str = "";
1327 pub static SYSNAME: &'static str = "freebsd";
1328 pub static DLL_PREFIX: &'static str = "lib";
1329 pub static DLL_SUFFIX: &'static str = ".so";
1330 pub static EXE_SUFFIX: &'static str = "";
1334 pub static SYSNAME: &'static str = "linux";
1335 pub static DLL_PREFIX: &'static str = "lib";
1336 pub static DLL_SUFFIX: &'static str = ".so";
1337 pub static EXE_SUFFIX: &'static str = "";
1341 pub static SYSNAME: &'static str = "android";
1342 pub static DLL_PREFIX: &'static str = "lib";
1343 pub static DLL_SUFFIX: &'static str = ".so";
1344 pub static EXE_SUFFIX: &'static str = "";
1348 pub static SYSNAME: &'static str = "win32";
1349 pub static DLL_PREFIX: &'static str = "";
1350 pub static DLL_SUFFIX: &'static str = ".dll";
1351 pub static EXE_SUFFIX: &'static str = ".exe";
1356 pub static ARCH: &'static str = "x86";
1359 pub static ARCH: &'static str = "x86_64";
1362 pub static ARCH: &'static str = "arm";
1365 pub static ARCH: &'static str = "mips";
1370 #[allow(non_implicitly_copyable_typarams)]
1372 use libc::{c_int, c_void, size_t};
1376 use os::{as_c_charp, env, getcwd, getenv, make_absolute, real_args};
1377 use os::{remove_file, setenv};
1385 use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
1389 pub fn last_os_error() {
1390 debug!(os::last_os_error());
1394 pub fn test_args() {
1395 let a = real_args();
1396 assert!(a.len() >= 1);
1399 fn make_rand_name() -> ~str {
1400 let rng = rand::rng();
1401 let n = ~"TEST" + rng.gen_str(10u);
1402 assert!(getenv(n).is_none());
1408 let n = make_rand_name();
1409 setenv(n, ~"VALUE");
1410 assert!(getenv(n) == option::Some(~"VALUE"));
1414 #[ignore(cfg(windows))]
1416 fn test_setenv_overwrite() {
1417 let n = make_rand_name();
1420 assert!(getenv(n) == option::Some(~"2"));
1422 assert!(getenv(n) == option::Some(~""));
1425 // Windows GetEnvironmentVariable requires some extra work to make sure
1426 // the buffer the variable is copied into is the right size
1428 #[ignore(cfg(windows))]
1430 fn test_getenv_big() {
1433 while i < 100 { s += ~"aaaaaaaaaa"; i += 1; }
1434 let n = make_rand_name();
1437 assert!(getenv(n) == option::Some(s));
1441 fn test_self_exe_path() {
1442 let path = os::self_exe_path();
1443 assert!(path.is_some());
1444 let path = path.get();
1447 // Hard to test this function
1448 assert!(path.is_absolute);
1453 fn test_env_getenv() {
1455 assert!(vec::len(e) > 0u);
1456 for vec::each(e) |p| {
1457 let (n, v) = copy *p;
1460 // MingW seems to set some funky environment variables like
1461 // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned
1462 // from env() but not visible from getenv().
1463 assert!(v2.is_none() || v2 == option::Some(v));
1468 fn test_env_setenv() {
1469 let n = make_rand_name();
1472 setenv(n, ~"VALUE");
1473 assert!(!vec::contains(e, &(copy n, ~"VALUE")));
1476 assert!(vec::contains(e, &(n, ~"VALUE")));
1481 assert!((!Path("test-path").is_absolute));
1483 debug!(~"Current working directory: " + getcwd().to_str());
1485 debug!(make_absolute(&Path("test-path")));
1486 debug!(make_absolute(&Path("/usr/bin")));
1492 let oldhome = getenv(~"HOME");
1494 setenv(~"HOME", ~"/home/MountainView");
1495 assert!(os::homedir() == Some(Path("/home/MountainView")));
1497 setenv(~"HOME", ~"");
1498 assert!(os::homedir().is_none());
1500 for oldhome.each |s| { setenv(~"HOME", *s) }
1507 let oldhome = getenv(~"HOME");
1508 let olduserprofile = getenv(~"USERPROFILE");
1510 setenv(~"HOME", ~"");
1511 setenv(~"USERPROFILE", ~"");
1513 assert!(os::homedir().is_none());
1515 setenv(~"HOME", ~"/home/MountainView");
1516 assert!(os::homedir() == Some(Path("/home/MountainView")));
1518 setenv(~"HOME", ~"");
1520 setenv(~"USERPROFILE", ~"/home/MountainView");
1521 assert!(os::homedir() == Some(Path("/home/MountainView")));
1523 setenv(~"HOME", ~"/home/MountainView");
1524 setenv(~"USERPROFILE", ~"/home/PaloAlto");
1525 assert!(os::homedir() == Some(Path("/home/MountainView")));
1527 oldhome.each(|s| {setenv(~"HOME", *s);true});
1528 olduserprofile.each(|s| {setenv(~"USERPROFILE", *s);true});
1533 assert!(!str::is_empty(os::tmpdir().to_str()));
1538 fn test_list_dir_no_invalid_memory_access() {
1539 os::list_dir(&Path("."));
1544 let dirs = os::list_dir(&Path("."));
1545 // Just assuming that we've got some contents in the current directory
1546 assert!((vec::len(dirs) > 0u));
1548 for vec::each(dirs) |dir| {
1555 assert!((os::path_is_dir(&Path("."))));
1556 assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs"))));
1561 assert!((os::path_exists(&Path("."))));
1562 assert!((!os::path_exists(&Path(
1563 "test/nonexistent-bogus-path"))));
1567 fn copy_file_does_not_exist() {
1568 assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"),
1569 &Path("test/other-bogus-path")));
1570 assert!(!os::path_exists(&Path("test/other-bogus-path")));
1576 let tempdir = getcwd(); // would like to use $TMPDIR,
1577 // doesn't seem to work on Linux
1578 assert!((str::len(tempdir.to_str()) > 0u));
1579 let in = tempdir.push("in.txt");
1580 let out = tempdir.push("out.txt");
1582 /* Write the temp input file */
1583 let ostream = do as_c_charp(in.to_str()) |fromp| {
1584 do as_c_charp("w+b") |modebuf| {
1585 libc::fopen(fromp, modebuf)
1588 assert!((ostream as uint != 0u));
1590 let mut buf = str::to_bytes(s) + ~[0 as u8];
1591 do vec::as_mut_buf(buf) |b, _len| {
1592 assert!((libc::fwrite(b as *c_void, 1u as size_t,
1593 (str::len(s) + 1u) as size_t, ostream)
1594 == buf.len() as size_t))
1596 assert!((libc::fclose(ostream) == (0u as c_int)));
1597 let rs = os::copy_file(&in, &out);
1598 if (!os::path_exists(&in)) {
1599 fail!(fmt!("%s doesn't exist", in.to_str()));
1602 let rslt = run::run_program(~"diff", ~[in.to_str(), out.to_str()]);
1603 assert!((rslt == 0));
1604 assert!((remove_file(&in)));
1605 assert!((remove_file(&out)));
1610 fn recursive_mkdir_slash() {
1611 let path = Path("/");
1612 assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1616 fn recursive_mkdir_empty() {
1617 let path = Path("");
1618 assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32));
1621 // More recursive_mkdir tests are in std::tempfile