about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2012-03-06 18:49:08 -0800
committerGraydon Hoare <graydon@mozilla.com>2012-03-06 18:49:08 -0800
commit04e7bd675881bc091db2154e84caebb255b0ff8c (patch)
tree6420ef3a15db3d250bbb27bca6e4bd6070a7b9ca /src/libcore
parentcf8f5b7606b87c26901abc0f6e271f48828bcf5b (diff)
More UTF-16 wrapping on win32. Close #1927.
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/libc.rs17
-rw-r--r--src/libcore/os.rs191
2 files changed, 122 insertions, 86 deletions
diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs
index 09af26336e0..4e8389d34fc 100644
--- a/src/libcore/libc.rs
+++ b/src/libcore/libc.rs
@@ -493,6 +493,9 @@ mod consts {
             const O_TEXT : int = 16384;
             const O_BINARY : int = 32768;
             const O_NOINHERIT: int = 128;
+
+            const ERROR_SUCCESS : int = 0;
+            const ERROR_INSUFFICIENT_BUFFER : int = 122;
         }
     }
 
@@ -1105,15 +1108,17 @@ mod funcs {
                                        nsize: DWORD) -> DWORD;
             fn SetEnvironmentVariableW(n: LPCWSTR, v: LPCWSTR) -> BOOL;
 
-            fn GetModuleFileNameA(hModule: HMODULE,
-                                  lpFilename: LPSTR,
+            fn GetModuleFileNameW(hModule: HMODULE,
+                                  lpFilename: LPWSTR,
                                   nSize: DWORD) -> DWORD;
-            fn CreateDirectoryA(lpPathName: LPCSTR,
+            fn CreateDirectoryW(lpPathName: LPCWSTR,
                                 lpSecurityAttributes:
                                 LPSECURITY_ATTRIBUTES) -> BOOL;
-            fn DeleteFileA(lpPathName: LPCSTR) -> BOOL;
-            fn RemoveDirectoryA(lpPathName: LPCSTR) -> BOOL;
-            fn SetCurrentDirectoryA(lpPathName: LPCSTR) -> BOOL;
+            fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL;
+            fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL;
+            fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL;
+
+            fn GetLastError() -> DWORD;
         }
 
         #[abi = "cdecl"]
diff --git a/src/libcore/os.rs b/src/libcore/os.rs
index ad18993c762..0258c7b55f6 100644
--- a/src/libcore/os.rs
+++ b/src/libcore/os.rs
@@ -14,9 +14,13 @@
 // facts of which OS the user is on -- they should be given the opportunity
 // to write OS-ignorant code by default.
 
-import libc::{c_char, c_void, c_int, c_uint, size_t, mode_t, pid_t, FILE};
+import libc::{c_char, c_void, c_int, c_uint, size_t, ssize_t,
+              mode_t, pid_t, FILE};
 import libc::{close, fclose};
 
+import option::{some, none};
+import option = option::t;
+
 import getcwd = rustrt::rust_getcwd;
 import consts::*;
 
@@ -46,15 +50,48 @@ fn env() -> [(str,str)] {
     ret pairs;
 }
 
+const tmpbuf_sz : uint = 1000u;
+
 fn as_c_charp<T>(s: str, f: fn(*c_char) -> T) -> T {
     str::as_buf(s) {|b| f(b as *c_char) }
 }
 
-fn as_utf16_p<T>(s: str, f: fn(*u16) -> T) -> T {
-    let t = str::to_utf16(s);
-    // "null terminate"
-    t += [0u16];
-    vec::as_buf(t, f)
+fn fill_charp_buf(f: fn(*mutable c_char, size_t) -> bool)
+    -> option<str> {
+    let buf = vec::to_mut(vec::init_elt(tmpbuf_sz, 0u8 as c_char));
+    vec::as_mut_buf(buf) { |b|
+        if f(b, tmpbuf_sz as size_t) {
+            some(str::from_cstr(b as str::sbuf))
+        } else {
+            none
+        }
+    }
+}
+
+#[cfg(target_os = "win32")]
+mod win32 {
+    import dword = libc::types::os::arch::extra::DWORD;
+
+    fn fill_utf16_buf_and_decode(f: fn(*mutable u16, dword) -> dword)
+        -> option<str> {
+        let buf = vec::to_mut(vec::init_elt(tmpbuf_sz, 0u16));
+        vec::as_mut_buf(buf) {|b|
+            let k : dword = f(b, tmpbuf_sz as dword);
+            if k == (0 as dword) {
+                none
+            } else {
+                let sub = vec::slice(buf, 0u, k as uint);
+                option::some::<str>(str::from_utf16(sub))
+            }
+        }
+    }
+
+    fn as_utf16_p<T>(s: str, f: fn(*u16) -> T) -> T {
+        let t = str::to_utf16(s);
+        // Null terminate before passing on.
+        t += [0u16];
+        vec::as_buf(t, f)
+    }
 }
 
 
@@ -74,19 +111,11 @@ fn getenv(n: str) -> option<str> unsafe {
 #[cfg(target_os = "win32")]
 fn getenv(n: str) -> option<str> unsafe {
     import libc::types::os::arch::extra::*;
-    import libc::funcs::extra::kernel32;
+    import libc::funcs::extra::kernel32::*;
+    import win32::*;
     as_utf16_p(n) {|u|
-        let bufsize = 1023u;
-        let buf = vec::to_mut(vec::init_elt(bufsize, 0u16));
-        vec::as_mut_buf(buf) {|b|
-            let k = kernel32::GetEnvironmentVariableW(u, b,
-                                                      bufsize as DWORD);
-            if k != (0 as DWORD) {
-                let sub = vec::slice(buf, 0u, k as uint);
-                option::some::<str>(str::from_utf16(sub))
-            } else {
-                option::none::<str>
-            }
+        fill_utf16_buf_and_decode() {|buf, sz|
+            GetEnvironmentVariableW(u, buf, sz)
         }
     }
 }
@@ -99,7 +128,6 @@ fn setenv(n: str, v: str) {
 
     // FIXME: remove this when export globs work properly.
     import libc::funcs::posix01::unistd::setenv;
-
     as_c_charp(n) {|nbuf|
         as_c_charp(v) {|vbuf|
             setenv(nbuf, vbuf, 1i32);
@@ -111,10 +139,11 @@ fn setenv(n: str, v: str) {
 #[cfg(target_os = "win32")]
 fn setenv(n: str, v: str) {
     // FIXME: remove imports when export globs work properly.
-    import libc::funcs::extra::kernel32;
+    import libc::funcs::extra::kernel32::*;
+    import win32::*;
     as_utf16_p(n) {|nbuf|
         as_utf16_p(v) {|vbuf|
-            kernel32::SetEnvironmentVariableW(nbuf, vbuf);
+            SetEnvironmentVariableW(nbuf, vbuf);
         }
     }
 }
@@ -244,59 +273,57 @@ fn dll_filename(base: str) -> str {
     fn pre() -> str { "" }
 }
 
-fn self_exe_path() -> option<path> unsafe {
-    let bufsize = 1023u;
-    let buf = vec::to_mut(vec::init_elt(bufsize, 0u8 as c_char));
-    // FIXME: This does not handle the case where the buffer is too small
-    ret vec::as_mut_buf(buf) {|pbuf|
-        if load_self(pbuf as *mutable c_char, bufsize as c_uint) {
-            let path = str::from_cstr(pbuf as str::sbuf);
-            option::some(path::dirname(path) + path::path_sep())
-        } else {
-            option::none
-        }
-    };
+
+fn self_exe_path() -> option<path> {
 
     #[cfg(target_os = "freebsd")]
-    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
-        // FIXME: remove imports when export globs work properly.
+    fn load_self() -> option<path> unsafe {
         import libc::funcs::bsd44::*;
         import libc::consts::os::extra::*;
-        let mib = [CTL_KERN as c_int,
-                   KERN_PROC as c_int,
-                   KERN_PROC_PATHNAME as c_int, -1 as c_int];
-        ret sysctl(vec::unsafe::to_ptr(mib), vec::len(mib) as c_uint,
-                   pth as *mutable c_void, ptr::mut_addr_of(plen as size_t),
-                   ptr::null(), 0u as size_t)
-            == (0 as c_int);
+        fill_charp_buf() {|buf, sz|
+            let mib = [CTL_KERN as c_int,
+                       KERN_PROC as c_int,
+                       KERN_PROC_PATHNAME as c_int, -1 as c_int];
+            sysctl(vec::unsafe::to_ptr(mib), vec::len(mib) as c_uint,
+                   buf as *mutable c_void, ptr::mut_addr_of(sz),
+                   ptr::null(), 0u as size_t) != (0 as c_int)
+        }
     }
 
     #[cfg(target_os = "linux")]
-    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
-        // FIXME: remove imports when export globs work properly.
+    fn load_self() -> option<path> unsafe {
         import libc::funcs::posix01::unistd::readlink;
-        as_c_charp("/proc/self/exe") { |proc_self_buf|
-            ret readlink(proc_self_buf, pth, plen as size_t) != -1;
+        fill_charp_buf() {|buf, sz|
+            as_c_charp("/proc/self/exe") { |proc_self_buf|
+                readlink(proc_self_buf, buf, sz) != (-1 as ssize_t)
+            }
         }
     }
 
-    #[cfg(target_os = "win32")]
-    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
+    #[cfg(target_os = "macos")]
+    fn load_self() -> option<path> unsafe {
         // FIXME: remove imports when export globs work properly.
-        import libc::types::os::arch::extra::*;
-        import libc::funcs::extra::kernel32;
-        ret kernel32::GetModuleFileNameA(0u, pth, plen) != (0 as DWORD);
+        import libc::funcs::extra::*;
+        fill_charp_buf() {|buf, sz|
+            _NSGetExecutablePath(buf, ptr::mut_addr_of(sz as u32))
+                == (0 as c_int)
+        }
     }
 
-    #[cfg(target_os = "macos")]
-    unsafe fn load_self(pth: *mutable c_char, plen: c_uint) -> bool {
+    #[cfg(target_os = "win32")]
+    fn load_self() -> option<path> unsafe {
         // FIXME: remove imports when export globs work properly.
-        import libc::funcs::extra::*;
-        let mplen = plen;
-        ret _NSGetExecutablePath(pth, ptr::mut_addr_of(mplen))
-            == (0 as c_int);
+        import libc::types::os::arch::extra::*;
+        import libc::funcs::extra::kernel32::*;
+        import win32::*;
+        fill_utf16_buf_and_decode() {|buf, sz|
+            GetModuleFileNameW(0u, buf, sz)
+        }
     }
 
+    option::map(load_self()) {|pth|
+        path::dirname(pth) + path::path_sep()
+    }
 }
 
 
@@ -356,9 +383,9 @@ Function: path_is_dir
 Indicates whether a path represents a directory.
 */
 fn path_is_dir(p: path) -> bool {
-    ret str::as_buf(p, {|buf|
+    str::as_buf(p) {|buf|
         rustrt::rust_path_is_dir(buf) != 0 as c_int
-    });
+    }
 }
 
 /*
@@ -367,9 +394,9 @@ Function: path_exists
 Indicates whether a path exists.
 */
 fn path_exists(p: path) -> bool {
-    ret str::as_buf(p, {|buf|
+    str::as_buf(p) {|buf|
         rustrt::rust_path_exists(buf) != 0 as c_int
-    });
+    }
 }
 
 // FIXME: under Windows, we should prepend the current drive letter to paths
@@ -404,24 +431,25 @@ fn make_dir(p: path, mode: c_int) -> bool {
     ret mkdir(p, mode);
 
     #[cfg(target_os = "win32")]
-    fn mkdir(_p: path, _mode: c_int) -> bool unsafe {
+    fn mkdir(p: path, _mode: c_int) -> bool unsafe {
+        // FIXME: remove imports when export globs work properly.
+        import libc::types::os::arch::extra::*;
+        import libc::funcs::extra::kernel32::*;
+        import win32::*;
         // FIXME: turn mode into something useful?
-        ret as_c_charp(_p, {|buf|
-            // FIXME: remove imports when export globs work properly.
-            import libc::types::os::arch::extra::*;
-            import libc::funcs::extra::kernel32;
-            kernel32::CreateDirectoryA(
-                buf, unsafe::reinterpret_cast(0)) != (0 as BOOL)
-        });
+        as_utf16_p(p) {|buf|
+            CreateDirectoryW(buf, unsafe::reinterpret_cast(0))
+                != (0 as BOOL)
+        }
     }
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
     #[cfg(target_os = "freebsd")]
     fn mkdir(p: path, mode: c_int) -> bool {
-        ret as_c_charp(p) {|c|
+        as_c_charp(p) {|c|
             libc::mkdir(c, mode as mode_t) == (0 as c_int)
-        };
+        }
     }
 }
 
@@ -468,10 +496,11 @@ fn remove_dir(p: path) -> bool {
     #[cfg(target_os = "win32")]
     fn rmdir(p: path) -> bool {
         // FIXME: remove imports when export globs work properly.
-        import libc::funcs::extra::kernel32;
+        import libc::funcs::extra::kernel32::*;
         import libc::types::os::arch::extra::*;
-        ret as_c_charp(p) {|buf|
-            kernel32::RemoveDirectoryA(buf) != (0 as BOOL)
+        import win32::*;
+        ret as_utf16_p(p) {|buf|
+            RemoveDirectoryW(buf) != (0 as BOOL)
         };
     }
 
@@ -491,10 +520,11 @@ fn change_dir(p: path) -> bool {
     #[cfg(target_os = "win32")]
     fn chdir(p: path) -> bool {
         // FIXME: remove imports when export globs work properly.
-        import libc::funcs::extra::kernel32;
+        import libc::funcs::extra::kernel32::*;
         import libc::types::os::arch::extra::*;
-        ret as_c_charp(p) {|buf|
-            kernel32::SetCurrentDirectoryA(buf) != (0 as BOOL)
+        import win32::*;
+        ret as_utf16_p(p) {|buf|
+            SetCurrentDirectoryW(buf) != (0 as BOOL)
         };
     }
 
@@ -519,10 +549,11 @@ fn remove_file(p: path) -> bool {
     #[cfg(target_os = "win32")]
     fn unlink(p: path) -> bool {
         // FIXME: remove imports when export globs work properly.
-        import libc::funcs::extra::kernel32;
+        import libc::funcs::extra::kernel32::*;
         import libc::types::os::arch::extra::*;
-        ret as_c_charp(p) {|buf|
-            kernel32::DeleteFileA(buf) != (0 as BOOL)
+        import win32::*;
+        ret as_utf16_p(p) {|buf|
+            DeleteFileW(buf) != (0 as BOOL)
         };
     }