Browse Source

Implement wcstol, wcstod, and printf:ing wchars

jD91mZM2 5 years ago
parent
commit
a8280e8991

+ 6 - 3
src/c_vec.rs

@@ -107,7 +107,8 @@ impl<T> CVec<T> {
     pub fn truncate(&mut self, len: usize) {
         if len < self.len {
             unsafe {
-                self.drop_range(len, self.len);
+                let old_len = self.len;
+                self.drop_range(len, old_len);
             }
             self.len = len;
         }
@@ -115,7 +116,8 @@ impl<T> CVec<T> {
     pub fn shrink_to_fit(&mut self) -> Result<(), AllocError> {
         if self.len < self.cap {
             unsafe {
-                self.resize(self.len)?;
+                let new_cap = self.len;
+                self.resize(new_cap)?;
             }
         }
         Ok(())
@@ -155,7 +157,8 @@ impl<T> DerefMut for CVec<T> {
 impl<T> Drop for CVec<T> {
     fn drop(&mut self) {
         unsafe {
-            self.drop_range(0, self.len);
+            let len = self.len;
+            self.drop_range(0, len);
         }
     }
 }

+ 8 - 14
src/fs.rs

@@ -3,15 +3,9 @@ use core::ops::Deref;
 use header::fcntl::O_CREAT;
 use header::unistd::{SEEK_CUR, SEEK_END, SEEK_SET};
 use io;
-use platform;
 use platform::types::*;
 use platform::{Pal, Sys};
 
-fn last_os_error() -> io::Error {
-    let errno = unsafe { platform::errno };
-    io::Error::from_raw_os_error(errno)
-}
-
 pub struct File {
     pub fd: c_int,
     /// To avoid self referential FILE struct that needs both a reader and a writer,
@@ -29,35 +23,35 @@ impl File {
 
     pub fn open(path: &CStr, oflag: c_int) -> io::Result<Self> {
         match Sys::open(path, oflag, 0) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             ok => Ok(Self::new(ok)),
         }
     }
 
     pub fn create(path: &CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> {
         match Sys::open(path, oflag | O_CREAT, mode) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             ok => Ok(Self::new(ok)),
         }
     }
 
     pub fn sync_all(&self) -> io::Result<()> {
         match Sys::fsync(self.fd) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             _ok => Ok(()),
         }
     }
 
     pub fn set_len(&self, size: u64) -> io::Result<()> {
         match Sys::ftruncate(self.fd, size as off_t) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             _ok => Ok(()),
         }
     }
 
     pub fn try_clone(&self) -> io::Result<Self> {
         match Sys::dup(self.fd) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             ok => Ok(Self::new(ok)),
         }
     }
@@ -76,7 +70,7 @@ impl File {
 impl io::Read for File {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         match Sys::read(self.fd, buf) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             ok => Ok(ok as usize),
         }
     }
@@ -85,7 +79,7 @@ impl io::Read for File {
 impl io::Write for File {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         match Sys::write(self.fd, buf) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             ok => Ok(ok as usize),
         }
     }
@@ -104,7 +98,7 @@ impl io::Seek for File {
         };
 
         match Sys::lseek(self.fd, offset, whence) {
-            -1 => Err(last_os_error()),
+            -1 => Err(io::last_os_error()),
             ok => Ok(ok as u64),
         }
     }

+ 71 - 22
src/header/stdio/printf.rs

@@ -4,11 +4,12 @@ use alloc::string::ToString;
 use alloc::vec::Vec;
 use core::ffi::VaList;
 use core::ops::Range;
-use core::{cmp, f64, fmt, slice};
+use core::{char, cmp, f64, fmt, slice};
 use io::{self, Write};
 
-use platform;
-use platform::types::*;
+use crate::header::errno::EILSEQ;
+use crate::platform::types::*;
+use crate::platform;
 
 //  ____        _ _                 _       _
 // | __ )  ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _
@@ -72,6 +73,7 @@ impl Number {
             VaArg::pointer(i) => i as usize,
             VaArg::ptrdiff_t(i) => i as usize,
             VaArg::ssize_t(i) => i as usize,
+            VaArg::wint_t(i) => i as usize,
         }
     }
 }
@@ -87,6 +89,7 @@ enum VaArg {
     pointer(*const c_void),
     ptrdiff_t(ptrdiff_t),
     ssize_t(ssize_t),
+    wint_t(wint_t),
 }
 impl VaArg {
     unsafe fn arg_from(fmtkind: FmtKind, intkind: IntKind, ap: &mut VaList) -> VaArg {
@@ -100,6 +103,9 @@ impl VaArg {
         match (fmtkind, intkind) {
             (FmtKind::Percent, _) => panic!("Can't call arg_from on %"),
 
+            (FmtKind::Char, IntKind::Long)
+            | (FmtKind::Char, IntKind::LongLong) => VaArg::wint_t(ap.arg::<wint_t>()),
+
             (FmtKind::Char, _)
             | (FmtKind::Unsigned, IntKind::Byte)
             | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::<c_char>()),
@@ -154,6 +160,7 @@ impl VaArg {
             pointer: *const c_void,
             ptrdiff_t: ptrdiff_t,
             ssize_t: ssize_t,
+            wint_t: wint_t,
         }
         let untyped = match *self {
             VaArg::c_char(i) => Untyped { c_char: i },
@@ -166,10 +173,14 @@ impl VaArg {
             VaArg::pointer(i) => Untyped { pointer: i },
             VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i },
             VaArg::ssize_t(i) => Untyped { ssize_t: i },
+            VaArg::wint_t(i) => Untyped { wint_t: i },
         };
         match (fmtkind, intkind) {
             (FmtKind::Percent, _) => panic!("Can't call transmute on %"),
 
+            (FmtKind::Char, IntKind::Long)
+            | (FmtKind::Char, IntKind::LongLong) => VaArg::wint_t(untyped.wint_t),
+
             (FmtKind::Char, _)
             | (FmtKind::Unsigned, IntKind::Byte)
             | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(untyped.c_char),
@@ -646,6 +657,7 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     VaArg::pointer(i) => (i as usize).to_string(),
                     VaArg::ptrdiff_t(i) => i.to_string(),
                     VaArg::ssize_t(i) => i.to_string(),
+                    VaArg::wint_t(_) => unreachable!("this should not be possible"),
                 };
                 let positive = !string.starts_with('-');
                 let zero = precision == Some(0) && string == "0";
@@ -693,6 +705,7 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     VaArg::pointer(i) => fmt_int(fmt, i as usize),
                     VaArg::ptrdiff_t(i) => fmt_int(fmt, i as size_t),
                     VaArg::ssize_t(i) => fmt_int(fmt, i as size_t),
+                    VaArg::wint_t(_) => unreachable!("this should not be possible"),
                 };
                 let zero = precision == Some(0) && string == "0";
 
@@ -785,8 +798,6 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                 }
             }
             FmtKind::String => {
-                // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle *const wchar_t
-
                 let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
                     VaArg::pointer(p) => p,
                     _ => panic!("this should not be possible"),
@@ -796,27 +807,65 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     w.write_all(b"(null)")?;
                 } else {
                     let max = precision.unwrap_or(::core::usize::MAX);
-                    let mut len = 0;
-                    while *ptr.add(len) != 0 && len < max {
-                        len += 1;
-                    }
 
-                    pad(w, !left, b' ', len..pad_space)?;
-                    w.write_all(slice::from_raw_parts(ptr as *const u8, len))?;
-                    pad(w, left, b' ', len..pad_space)?;
+                    if intkind == IntKind::Long || intkind == IntKind::LongLong {
+                        // Handle wchar_t
+                        let mut ptr = ptr as *const wchar_t;
+                        let mut string = String::new();
+
+                        while *ptr != 0 {
+                            let c = match char::from_u32(*ptr as _) {
+                                Some(c) => c,
+                                None => {
+                                    platform::errno = EILSEQ;
+                                    return Err(io::last_os_error());
+                                }
+                            };
+                            if string.len() + c.len_utf8() >= max {
+                                break;
+                            }
+                            string.push(c);
+                            ptr = ptr.add(1);
+                        }
+
+                        pad(w, !left, b' ', string.len()..pad_space)?;
+                        w.write_all(string.as_bytes())?;
+                        pad(w, left, b' ', string.len()..pad_space)?;
+                    } else {
+                        let mut len = 0;
+                        while *ptr.add(len) != 0 && len < max {
+                            len += 1;
+                        }
+
+                        pad(w, !left, b' ', len..pad_space)?;
+                        w.write_all(slice::from_raw_parts(ptr as *const u8, len))?;
+                        pad(w, left, b' ', len..pad_space)?;
+                    }
                 }
             }
             FmtKind::Char => {
-                // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle wint_t
-
-                let c = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
-                    VaArg::c_char(c) => c,
-                    _ => panic!("this should not be possible"),
-                };
-
-                pad(w, !left, b' ', 1..pad_space)?;
-                w.write_all(&[c as u8])?;
-                pad(w, left, b' ', 1..pad_space)?;
+                match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
+                    VaArg::c_char(c) => {
+                        pad(w, !left, b' ', 1..pad_space)?;
+                        w.write_all(&[c as u8])?;
+                        pad(w, left, b' ', 1..pad_space)?;
+                    },
+                    VaArg::wint_t(c) => {
+                        let c = match char::from_u32(c as _) {
+                            Some(c) => c,
+                            None => {
+                                platform::errno = EILSEQ;
+                                return Err(io::last_os_error());
+                            }
+                        };
+                        let mut buf = [0; 4];
+
+                        pad(w, !left, b' ', 1..pad_space)?;
+                        w.write_all(c.encode_utf8(&mut buf).as_bytes())?;
+                        pad(w, left, b' ', 1..pad_space)?;
+                    },
+                    _ => unreachable!("this should not be possible"),
+                }
             }
             FmtKind::Pointer => {
                 let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {

+ 105 - 10
src/header/wchar/mod.rs

@@ -1,8 +1,10 @@
 //! wchar implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/wchar.h.html
 
 use core::ffi::VaList as va_list;
-use core::{mem, ptr, usize};
+use core::{char, mem, ptr, usize};
 
+use header::ctype::isspace;
+use header::errno::ERANGE;
 use header::stdio::*;
 use header::stdlib::MB_CUR_MAX;
 use header::string;
@@ -470,9 +472,52 @@ pub extern "C" fn wcsstr(ws1: *const wchar_t, ws2: *const wchar_t) -> *mut wchar
     unimplemented!();
 }
 
-// #[no_mangle]
-pub extern "C" fn wcstod(nptr: *const wchar_t, endptr: *mut *mut wchar_t) -> f64 {
-    unimplemented!();
+macro_rules! skipws {
+    ($ptr:expr) => {
+        while isspace(*$ptr) != 0 {
+            $ptr = $ptr.add(1);
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wcstod(mut ptr: *const wchar_t, end: *mut *mut wchar_t) -> c_double {
+    const RADIX: u32 = 10;
+
+    skipws!(ptr);
+    let negative = *ptr == '-' as wchar_t;
+    if negative {
+        ptr = ptr.add(1);
+    }
+
+    let mut result: c_double = 0.0;
+    while let Some(digit) = char::from_u32(*ptr as _).and_then(|c| c.to_digit(RADIX)) {
+        result *= 10.0;
+        if negative {
+            result -= digit as c_double;
+        } else {
+            result += digit as c_double;
+        }
+        ptr = ptr.add(1);
+    }
+    if *ptr == '.' as wchar_t {
+        ptr = ptr.add(1);
+
+        let mut scale = 1.0;
+        while let Some(digit) = char::from_u32(*ptr as _).and_then(|c| c.to_digit(RADIX)) {
+            scale /= 10.0;
+            if negative {
+                result -= digit as c_double * scale;
+            } else {
+                result += digit as c_double * scale;
+            }
+            ptr = ptr.add(1);
+        }
+    }
+    if !end.is_null() {
+        *end = ptr as *mut _;
+    }
+    result
 }
 
 #[no_mangle]
@@ -510,14 +555,64 @@ pub unsafe extern "C" fn wcstok(
     wcs
 }
 
-// #[no_mangle]
-pub extern "C" fn wcstol(nptr: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_long {
-    unimplemented!();
+macro_rules! strtou_impl {
+    ($type:ident, $ptr:expr, $base:expr) => {
+        strtou_impl!($type, $ptr, $base, false)
+    };
+    ($type:ident, $ptr:expr, $base:expr, $negative:expr) => {{
+        if $base == 16 && *$ptr == '0' as wchar_t && *$ptr.add(1) | 0x20 == 'x' as wchar_t {
+            $ptr = $ptr.add(2);
+        }
+
+        let mut result: $type = 0;
+        while let Some(digit) = char::from_u32(*$ptr as u32).and_then(|c| c.to_digit($base as u32)) {
+            let new = result.checked_mul($base as $type)
+                .and_then(|result| if $negative {
+                    result.checked_sub(digit as $type)
+                } else {
+                    result.checked_add(digit as $type)
+                });
+            result = match new {
+                Some(new) => new,
+                None => {
+                    platform::errno = ERANGE;
+                    return !0;
+                }
+            };
+
+            $ptr = $ptr.add(1);
+        }
+        result
+    }}
+}
+macro_rules! strto_impl {
+    ($type:ident, $ptr:expr, $base:expr) => {{
+        let negative = *$ptr == '-' as wchar_t;
+        if negative {
+            $ptr = $ptr.add(1);
+        }
+        strtou_impl!($type, $ptr, $base, negative)
+    }}
 }
 
-// #[no_mangle]
-pub extern "C" fn wcstoul(nptr: *const wchar_t, endptr: *mut *mut wchar_t, base: c_int) -> c_ulong {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn wcstol(mut ptr: *const wchar_t, end: *mut *mut wchar_t, base: c_int) -> c_long {
+    skipws!(ptr);
+    let result = strto_impl!(c_long, ptr, base);
+    if !end.is_null() {
+        *end = ptr as *mut _;
+    }
+    result
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wcstoul(mut ptr: *const wchar_t, end: *mut *mut wchar_t, base: c_int) -> c_ulong {
+    skipws!(ptr);
+    let result = strtou_impl!(c_ulong, ptr, base);
+    if !end.is_null() {
+        *end = ptr as *mut _;
+    }
+    result
 }
 
 // #[no_mangle]

+ 7 - 0
src/io.rs

@@ -1 +1,8 @@
 pub use core_io::*;
+
+use crate::platform;
+
+pub fn last_os_error() -> Error {
+    let errno = unsafe { platform::errno };
+    Error::from_raw_os_error(errno)
+}

+ 8 - 4
tests/Makefile

@@ -15,8 +15,6 @@ EXPECT_NAMES=\
 	libgen \
 	locale \
 	math \
-	netdb/netdb \
-	netdb/getaddrinfo \
 	regex \
 	select \
 	setjmp \
@@ -90,12 +88,18 @@ EXPECT_NAMES=\
 	waitpid \
 	wchar/mbrtowc \
 	wchar/mbsrtowcs \
+	wchar/printf-on-wchars \
 	wchar/putwchar \
 	wchar/wcrtomb \
 	wchar/wcscspn \
 	wchar/wcsrchr \
-	wchar/wcstok
-	# signal (TODO: Fix)
+	wchar/wcstod \
+	wchar/wcstok \
+	wchar/wcstol
+	# TODO: Fix these
+	# netdb/getaddrinfo \
+	# netdb/netdb \
+	# signal
 
 # Binaries that may generate varied output
 NAMES=\

+ 0 - 1
tests/expected/netdb/getaddrinfo.stdout

@@ -1 +0,0 @@
-IPv4 address: 23.21.162.66 (www.redox-os.org)

+ 0 - 1
tests/expected/netdb/netdb.stdout

@@ -1 +0,0 @@
-No visible errors occurred!

+ 0 - 0
tests/expected/netdb/getaddrinfo.stderr → tests/expected/wchar/printf-on-wchars.stderr


+ 4 - 0
tests/expected/wchar/printf-on-wchars.stdout

@@ -0,0 +1,4 @@
+This is a few one-byte chars: 1 2 a b
+Long one-byte string: Hello World
+This is a few multi-byte chars: ❤ R 😠 C
+Long multi-byte string: 👉😎👉 Zoop!

+ 0 - 0
tests/expected/netdb/netdb.stderr → tests/expected/wchar/wcstod.stderr


+ 6 - 0
tests/expected/wchar/wcstod.stdout

@@ -0,0 +1,6 @@
+strtod(1.2345wowzah) = (1.234500, wowzah)
+strtod(53) = (53.000000, )
+strtod(-254352.5...) = (-254352.500000, ...)
+strtod(   19.2 wat) = (19.200000,  wat)
+strtod(365.24 29.53) = (365.240000,  29.53)
+strtod( 29.53) = (29.530000, )

+ 0 - 0
tests/expected/wchar/wcstol.stderr


+ 1 - 0
tests/expected/wchar/wcstol.stdout

@@ -0,0 +1 @@
+The decimal equivalents are: 2001, 6340800, -3624224 and 0.

+ 22 - 0
tests/wchar/printf-on-wchars.c

@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <wchar.h>
+
+int main() {
+    wint_t a = L'1';
+    wint_t b = L'2';
+    wint_t c = L'a';
+    wint_t d = L'b';
+    printf("This is a few one-byte chars: %lc %lc %lc %lc\n", a, b, c, d);
+    wchar_t *s = L"Hello World";
+    printf("Long one-byte string: %ls\n", s);
+
+    a = L'❤';
+    b = L'R';
+    c = L'😠';
+    d = L'C';
+    printf("This is a few multi-byte chars: %lc %lc %lc %lc\n", a, b, c, d);
+
+    s = L"👉😎👉 Zoop!";
+    printf("Long multi-byte string: %ls\n", s);
+}

+ 16 - 0
tests/wchar/wcstod.c

@@ -0,0 +1,16 @@
+#include <sys/types.h>
+#include <wchar.h>
+
+void attempt(wchar_t *s) {
+    wchar_t *end;
+    double result = wcstod(s, &end);
+    printf("strtod(%lls) = (%f, %lls)\n", s, result, end);
+}
+int main() {
+    attempt(L"1.2345wowzah");
+    attempt(L"53");
+    attempt(L"-254352.5...");
+    attempt(L"   19.2 wat");
+    attempt(L"365.24 29.53");
+    attempt(L" 29.53");
+}

+ 17 - 0
tests/wchar/wcstol.c

@@ -0,0 +1,17 @@
+// from http://www.cplusplus.com/reference/cwchar/wcstol/
+#include <stdio.h>
+#include <wchar.h>
+
+int main () {
+  wchar_t wsNumbers[] = L"2001 60c0c0 -1101110100110100100000 0x6fffff";
+  wchar_t * pEnd;
+  long int li1, li2, li3, li4;
+
+  li1 = wcstol(wsNumbers,&pEnd,10);
+  li2 = wcstol(pEnd,&pEnd,16);
+  li3 = wcstol(pEnd,&pEnd,2);
+  li4 = wcstol(pEnd,NULL,0);
+
+  printf("The decimal equivalents are: %ld, %ld, %ld and %ld.\n", li1, li2, li3, li4);
+  return 0;
+}