Преглед на файлове

Merge branch 'master' into 'master'

Implement rand_r(), strnlen_s(), tempnam(), tmpnam()

See merge request redox-os/relibc!210
Jeremy Soller преди 6 години
родител
ревизия
be765f879d

+ 3 - 0
include/bits/stdio.h

@@ -1,6 +1,9 @@
 #ifndef _BITS_STDIO_H
 #define _BITS_STDIO_H
 
+// XXX: this is only here because cbindgen can't handle string constants
+#define P_tmpdir "/tmp"
+
 #define EOF (-1)
 
 typedef struct FILE FILE;

+ 9 - 0
src/header/stdio/constants.rs

@@ -24,5 +24,14 @@ pub const _IOFBF: c_int = 0;
 pub const _IOLBF: c_int = 1;
 pub const _IONBF: c_int = 2;
 
+// form of name is /XXXXXX, so 7
+pub const L_tmpnam: c_int = 7;
+// 36^6 (26 letters + 10 digits) is larger than i32::MAX, so just set to that
+// for now
+pub const TMP_MAX: int32_t = 2_147_483_647;
+// XXX: defined manually in bits/stdio.h as well because cbindgen can't handle
+//      string constants in any form AFAICT
+pub const P_tmpdir: &'static [u8; 5] = b"/tmp\0";
+
 #[allow(non_camel_case_types)]
 pub type fpos_t = off_t;

+ 71 - 7
src/header/stdio/mod.rs

@@ -11,7 +11,7 @@ use core::{fmt, mem, ptr, slice, str};
 use c_str::CStr;
 use fs::File;
 use header::errno::{self, STR_ERROR};
-use header::string::strlen;
+use header::string::{self, strlen};
 use header::{fcntl, stdlib, unistd};
 use io::{self, BufRead, LineWriter, Read, Write};
 use mutex::Mutex;
@@ -34,6 +34,8 @@ mod helpers;
 mod printf;
 mod scanf;
 
+static mut TMPNAM_BUF: [c_char; L_tmpnam as usize + 1] = [0; L_tmpnam as usize + 1];
+
 enum Buffer<'a> {
     Borrowed(&'a mut [u8]),
     Owned(Vec<u8>),
@@ -873,9 +875,47 @@ pub unsafe extern "C" fn setvbuf(
     0
 }
 
-// #[no_mangle]
-pub extern "C" fn tempnam(_dir: *const c_char, _pfx: *const c_char) -> *mut c_char {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char {
+    unsafe fn is_appropriate(pos_dir: *const c_char) -> bool {
+        !pos_dir.is_null() && unistd::access(pos_dir, unistd::W_OK) == 0
+    }
+
+    // directory search order is env!(TMPDIR), dir, P_tmpdir, "/tmp"
+    let dirname = {
+        let tmpdir = stdlib::getenv(b"TMPDIR\0".as_ptr() as _);
+        [tmpdir, dir, P_tmpdir.as_ptr() as _]
+            .into_iter()
+            .map(|&d| d)
+            .skip_while(|&d| !is_appropriate(d))
+            .next()
+            .unwrap_or(b"/tmp\0".as_ptr() as _)
+    };
+    let dirname_len = string::strlen(dirname);
+
+    let prefix_len = string::strnlen_s(pfx, 5);
+
+    // allocate enough for dirname "/" prefix "XXXXXX\0"
+    let mut out_buf =
+        platform::alloc(dirname_len + 1 + prefix_len + L_tmpnam as usize + 1) as *mut c_char;
+
+    if !out_buf.is_null() {
+        // copy the directory name and prefix into the allocated buffer
+        out_buf.copy_from_nonoverlapping(dirname, dirname_len);
+        *out_buf.add(dirname_len) = b'/' as _;
+        out_buf
+            .add(dirname_len + 1)
+            .copy_from_nonoverlapping(pfx, prefix_len);
+
+        // use the same mechanism as tmpnam to get the file name
+        if tmpnam_inner(out_buf, dirname_len + 1 + prefix_len).is_null() {
+            // failed to find a valid file name, so we need to free the buffer
+            platform::free(out_buf as _);
+            out_buf = ptr::null_mut();
+        }
+    }
+
+    out_buf
 }
 
 #[no_mangle]
@@ -901,9 +941,33 @@ pub unsafe extern "C" fn tmpfile() -> *mut FILE {
     fp
 }
 
-// #[no_mangle]
-pub extern "C" fn tmpnam(_s: *mut c_char) -> *mut c_char {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char {
+    let buf = if s.is_null() {
+        TMPNAM_BUF.as_mut_ptr()
+    } else {
+        s
+    };
+
+    *buf = b'/' as _;
+    tmpnam_inner(buf, 1)
+}
+
+unsafe extern "C" fn tmpnam_inner(buf: *mut c_char, offset: usize) -> *mut c_char {
+    const TEMPLATE: &[u8] = b"XXXXXX\0";
+
+    buf.add(offset)
+        .copy_from_nonoverlapping(TEMPLATE.as_ptr() as _, TEMPLATE.len());
+
+    let err = platform::errno;
+    stdlib::mktemp(buf);
+    platform::errno = err;
+
+    if *buf == 0 {
+        ptr::null_mut()
+    } else {
+        buf
+    }
 }
 
 /// Push character `c` back onto `stream` so it'll be read next

+ 117 - 102
src/header/stdio/printf.rs

@@ -1,6 +1,6 @@
+use alloc::collections::BTreeMap;
 use alloc::string::String;
 use alloc::string::ToString;
-use alloc::collections::BTreeMap;
 use alloc::vec::Vec;
 use core::ffi::VaList;
 use core::ops::Range;
@@ -17,7 +17,6 @@ use platform::types::*;
 // |____/ \___/|_|_|\___|_|  | .__/|_|\__,_|\__\___(_)
 //                           |_|
 
-
 #[derive(Clone, Copy, PartialEq, Eq)]
 enum IntKind {
     Byte,
@@ -43,19 +42,19 @@ enum FmtKind {
     String,
     Char,
     Pointer,
-    GetWritten
+    GetWritten,
 }
 #[derive(Clone, Copy, Debug)]
 enum Number {
     Static(usize),
     Index(usize),
-    Next
+    Next,
 }
 impl Number {
     unsafe fn resolve(&self, varargs: &mut VaListCache, ap: &mut VaList) -> usize {
         let arg = match *self {
             Number::Static(num) => return num,
-            Number::Index(i) => varargs.get(i-1, ap, None),
+            Number::Index(i) => varargs.get(i - 1, ap, None),
             Number::Next => {
                 let i = varargs.i;
                 varargs.i += 1;
@@ -72,7 +71,7 @@ impl Number {
             VaArg::intmax_t(i) => i as usize,
             VaArg::pointer(i) => i as usize,
             VaArg::ptrdiff_t(i) => i as usize,
-            VaArg::ssize_t(i) => i as usize
+            VaArg::ssize_t(i) => i as usize,
         }
     }
 }
@@ -87,7 +86,7 @@ enum VaArg {
     intmax_t(intmax_t),
     pointer(*const c_void),
     ptrdiff_t(ptrdiff_t),
-    ssize_t(ssize_t)
+    ssize_t(ssize_t),
 }
 impl VaArg {
     unsafe fn arg_from(arg: &PrintfArg, ap: &mut VaList) -> VaArg {
@@ -101,29 +100,38 @@ impl VaArg {
         match (arg.fmtkind, arg.intkind) {
             (FmtKind::Percent, _) => panic!("Can't call arg_from on %"),
 
-            (FmtKind::Char, _) |
-            (FmtKind::Unsigned, IntKind::Byte) |
-            (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::<c_char>()),
-            (FmtKind::Unsigned, IntKind::Short) |
-            (FmtKind::Signed, IntKind::Short) => VaArg::c_short(ap.arg::<c_short>()),
-            (FmtKind::Unsigned, IntKind::Int) |
-            (FmtKind::Signed, IntKind::Int) => VaArg::c_int(ap.arg::<c_int>()),
-            (FmtKind::Unsigned, IntKind::Long) |
-            (FmtKind::Signed, IntKind::Long) => VaArg::c_long(ap.arg::<c_long>()),
-            (FmtKind::Unsigned, IntKind::LongLong) |
-            (FmtKind::Signed, IntKind::LongLong) => VaArg::c_longlong(ap.arg::<c_longlong>()),
-            (FmtKind::Unsigned, IntKind::IntMax) |
-            (FmtKind::Signed, IntKind::IntMax) => VaArg::intmax_t(ap.arg::<intmax_t>()),
-            (FmtKind::Unsigned, IntKind::PtrDiff) |
-            (FmtKind::Signed, IntKind::PtrDiff) => VaArg::ptrdiff_t(ap.arg::<ptrdiff_t>()),
-            (FmtKind::Unsigned, IntKind::Size) |
-            (FmtKind::Signed, IntKind::Size) => VaArg::ssize_t(ap.arg::<ssize_t>()),
-
-            (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _)
-                => VaArg::c_double(ap.arg::<c_double>()),
-
-            (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _)
-                => VaArg::pointer(ap.arg::<*const c_void>()),
+            (FmtKind::Char, _)
+            | (FmtKind::Unsigned, IntKind::Byte)
+            | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::<c_char>()),
+            (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => {
+                VaArg::c_short(ap.arg::<c_short>())
+            }
+            (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => {
+                VaArg::c_int(ap.arg::<c_int>())
+            }
+            (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => {
+                VaArg::c_long(ap.arg::<c_long>())
+            }
+            (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => {
+                VaArg::c_longlong(ap.arg::<c_longlong>())
+            }
+            (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => {
+                VaArg::intmax_t(ap.arg::<intmax_t>())
+            }
+            (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => {
+                VaArg::ptrdiff_t(ap.arg::<ptrdiff_t>())
+            }
+            (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => {
+                VaArg::ssize_t(ap.arg::<ssize_t>())
+            }
+
+            (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
+                VaArg::c_double(ap.arg::<c_double>())
+            }
+
+            (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => {
+                VaArg::pointer(ap.arg::<*const c_void>())
+            }
         }
     }
     unsafe fn transmute(&self, arg: &PrintfArg) -> VaArg {
@@ -145,7 +153,7 @@ impl VaArg {
             intmax_t: intmax_t,
             pointer: *const c_void,
             ptrdiff_t: ptrdiff_t,
-            ssize_t: ssize_t
+            ssize_t: ssize_t,
         }
         let untyped = match *self {
             VaArg::c_char(i) => Untyped { c_char: i },
@@ -157,41 +165,50 @@ impl VaArg {
             VaArg::intmax_t(i) => Untyped { intmax_t: i },
             VaArg::pointer(i) => Untyped { pointer: i },
             VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i },
-            VaArg::ssize_t(i) => Untyped { ssize_t: i }
+            VaArg::ssize_t(i) => Untyped { ssize_t: i },
         };
         match (arg.fmtkind, arg.intkind) {
             (FmtKind::Percent, _) => panic!("Can't call transmute on %"),
 
-            (FmtKind::Char, _) |
-            (FmtKind::Unsigned, IntKind::Byte) |
-            (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(untyped.c_char),
-            (FmtKind::Unsigned, IntKind::Short) |
-            (FmtKind::Signed, IntKind::Short) => VaArg::c_short(untyped.c_short),
-            (FmtKind::Unsigned, IntKind::Int) |
-            (FmtKind::Signed, IntKind::Int) => VaArg::c_int(untyped.c_int),
-            (FmtKind::Unsigned, IntKind::Long) |
-            (FmtKind::Signed, IntKind::Long) => VaArg::c_long(untyped.c_long),
-            (FmtKind::Unsigned, IntKind::LongLong) |
-            (FmtKind::Signed, IntKind::LongLong) => VaArg::c_longlong(untyped.c_longlong),
-            (FmtKind::Unsigned, IntKind::IntMax) |
-            (FmtKind::Signed, IntKind::IntMax) => VaArg::intmax_t(untyped.intmax_t),
-            (FmtKind::Unsigned, IntKind::PtrDiff) |
-            (FmtKind::Signed, IntKind::PtrDiff) => VaArg::ptrdiff_t(untyped.ptrdiff_t),
-            (FmtKind::Unsigned, IntKind::Size) |
-            (FmtKind::Signed, IntKind::Size) => VaArg::ssize_t(untyped.ssize_t),
-
-            (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _)
-                => VaArg::c_double(untyped.c_double),
-
-            (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _)
-                => VaArg::pointer(untyped.pointer),
+            (FmtKind::Char, _)
+            | (FmtKind::Unsigned, IntKind::Byte)
+            | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(untyped.c_char),
+            (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => {
+                VaArg::c_short(untyped.c_short)
+            }
+            (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => {
+                VaArg::c_int(untyped.c_int)
+            }
+            (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => {
+                VaArg::c_long(untyped.c_long)
+            }
+            (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => {
+                VaArg::c_longlong(untyped.c_longlong)
+            }
+            (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => {
+                VaArg::intmax_t(untyped.intmax_t)
+            }
+            (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => {
+                VaArg::ptrdiff_t(untyped.ptrdiff_t)
+            }
+            (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => {
+                VaArg::ssize_t(untyped.ssize_t)
+            }
+
+            (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
+                VaArg::c_double(untyped.c_double)
+            }
+
+            (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => {
+                VaArg::pointer(untyped.pointer)
+            }
         }
     }
 }
 #[derive(Default)]
 struct VaListCache {
     args: Vec<VaArg>,
-    i: usize
+    i: usize,
 }
 impl VaListCache {
     unsafe fn get(&mut self, i: usize, ap: &mut VaList, default: Option<&PrintfArg>) -> VaArg {
@@ -211,7 +228,7 @@ impl VaListCache {
         }
         self.args.push(match default {
             Some(default) => VaArg::arg_from(default, ap),
-            None => VaArg::c_int(ap.arg::<c_int>())
+            None => VaArg::c_int(ap.arg::<c_int>()),
         });
         self.args[i]
     }
@@ -224,7 +241,6 @@ impl VaListCache {
 // |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_)
 //               |_|
 
-
 unsafe fn pop_int_raw(format: &mut *const u8) -> Option<usize> {
     let mut int = None;
     while let Some(digit) = (**format as char).to_digit(10) {
@@ -251,11 +267,7 @@ unsafe fn pop_index(format: &mut *const u8) -> Option<usize> {
 unsafe fn pop_int(format: &mut *const u8) -> Option<Number> {
     if **format == b'*' {
         *format = format.add(1);
-        Some(
-            pop_index(format)
-                .map(Number::Index)
-                .unwrap_or(Number::Next)
-        )
+        Some(pop_index(format).map(Number::Index).unwrap_or(Number::Next))
     } else {
         pop_int_raw(format).map(Number::Static)
     }
@@ -270,7 +282,10 @@ where
         b'u' => i.to_string(),
         b'x' => format!("{:x}", i),
         b'X' => format!("{:X}", i),
-        _ => panic!("fmt_int should never be called with the fmt {:?}", fmt as char),
+        _ => panic!(
+            "fmt_int should never be called with the fmt {:?}",
+            fmt as char
+        ),
     }
 }
 
@@ -384,7 +399,7 @@ fn fmt_float_normal<W: Write>(
 
 #[derive(Clone, Copy)]
 struct PrintfIter {
-    format: *const u8
+    format: *const u8,
 }
 #[derive(Clone, Copy)]
 struct PrintfArg {
@@ -400,11 +415,11 @@ struct PrintfArg {
     pad_zero: Number,
     intkind: IntKind,
     fmt: u8,
-    fmtkind: FmtKind
+    fmtkind: FmtKind,
 }
 enum PrintfFmt {
     Plain(&'static [u8]),
-    Arg(PrintfArg)
+    Arg(PrintfArg),
 }
 impl Iterator for PrintfIter {
     type Item = Result<PrintfFmt, ()>;
@@ -429,11 +444,10 @@ impl Iterator for PrintfIter {
             self.format = self.format.add(1);
 
             let mut peekahead = self.format;
-            let index = pop_index(&mut peekahead)
-                .map(|i| {
-                    self.format = peekahead;
-                    i
-                });
+            let index = pop_index(&mut peekahead).map(|i| {
+                self.format = peekahead;
+                i
+            });
 
             // Flags:
             let mut alternate = false;
@@ -525,18 +539,17 @@ impl Iterator for PrintfIter {
                 pad_zero,
                 intkind,
                 fmt,
-                fmtkind
+                fmtkind,
             })))
         }
     }
 }
 
-
 unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) -> io::Result<c_int> {
     let w = &mut platform::CountingWriter::new(w);
 
     let iterator = PrintfIter {
-        format: format as *const u8
+        format: format as *const u8,
     };
 
     // Pre-fetch vararg types
@@ -548,13 +561,13 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
         let arg = match section {
             Ok(PrintfFmt::Plain(text)) => continue,
             Ok(PrintfFmt::Arg(arg)) => arg,
-            Err(()) => return Ok(-1)
+            Err(()) => return Ok(-1),
         };
         if arg.fmtkind == FmtKind::Percent {
             continue;
         }
         if let Some(i) = arg.index {
-            positional.insert(i-1, arg);
+            positional.insert(i - 1, arg);
         } else {
             varargs.args.push(VaArg::arg_from(&arg, &mut ap));
         }
@@ -570,9 +583,9 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
             Ok(PrintfFmt::Plain(text)) => {
                 w.write_all(text)?;
                 continue;
-            },
+            }
             Ok(PrintfFmt::Arg(arg)) => arg,
-            Err(()) => return Ok(-1)
+            Err(()) => return Ok(-1),
         };
         let alternate = arg.alternate;
         let zero = arg.zero;
@@ -587,15 +600,15 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
         let fmt = arg.fmt;
         let fmtkind = arg.fmtkind;
 
-        let index = arg.index
-            .map(|i| i-1)
-            .unwrap_or_else(|| if fmtkind == FmtKind::Percent {
+        let index = arg.index.map(|i| i - 1).unwrap_or_else(|| {
+            if fmtkind == FmtKind::Percent {
                 0
             } else {
                 let i = varargs.i;
                 varargs.i += 1;
                 i
-            });
+            }
+        });
 
         match fmtkind {
             FmtKind::Percent => w.write_all(&[b'%'])?,
@@ -610,7 +623,7 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     VaArg::intmax_t(i) => i.to_string(),
                     VaArg::pointer(i) => (i as usize).to_string(),
                     VaArg::ptrdiff_t(i) => i.to_string(),
-                    VaArg::ssize_t(i) => i.to_string()
+                    VaArg::ssize_t(i) => i.to_string(),
                 };
                 let positive = !string.starts_with('-');
                 let zero = precision == Some(0) && string == "0";
@@ -645,7 +658,7 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                 }
 
                 pad(w, left, b' ', final_len..pad_space)?;
-            },
+            }
             FmtKind::Unsigned => {
                 let string = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::c_char(i) => fmt_int(fmt, i as c_uchar),
@@ -657,7 +670,7 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     VaArg::intmax_t(i) => fmt_int(fmt, i as uintmax_t),
                     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::ssize_t(i) => fmt_int(fmt, i as size_t),
                 };
                 let zero = precision == Some(0) && string == "0";
 
@@ -699,29 +712,31 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                 }
 
                 pad(w, left, b' ', final_len..pad_space)?;
-            },
+            }
             FmtKind::Scientific => {
                 let mut float = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::c_double(i) => i,
-                    _ => panic!("this should not be possible")
+                    _ => panic!("this should not be possible"),
                 };
                 let precision = precision.unwrap_or(6);
 
-                fmt_float_exp(w, fmt, None, false, precision, float, left, pad_space, pad_zero)?;
-            },
+                fmt_float_exp(
+                    w, fmt, None, false, precision, float, left, pad_space, pad_zero,
+                )?;
+            }
             FmtKind::Decimal => {
                 let mut float = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::c_double(i) => i,
-                    _ => panic!("this should not be possible")
+                    _ => panic!("this should not be possible"),
                 };
                 let precision = precision.unwrap_or(6);
 
                 fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?;
-            },
+            }
             FmtKind::AnyNotation => {
                 let mut float = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::c_double(i) => i,
-                    _ => panic!("this should not be possible")
+                    _ => panic!("this should not be possible"),
                 };
                 let exp_fmt = b'E' | (fmt & 32);
                 let precision = precision.unwrap_or(6);
@@ -739,13 +754,13 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                 )? {
                     fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?;
                 }
-            },
+            }
             FmtKind::String => {
                 // if intkind == IntKind::Long || intkind == IntKind::LongLong, handle *const wchar_t
 
                 let mut ptr = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::pointer(p) => p,
-                    _ => panic!("this should not be possible")
+                    _ => panic!("this should not be possible"),
                 } as *const c_char;
 
                 if ptr.is_null() {
@@ -761,23 +776,23 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     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)) {
                     VaArg::c_char(c) => c,
-                    _ => panic!("this should not be possible")
+                    _ => 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)?;
-            },
+            }
             FmtKind::Pointer => {
                 let mut ptr = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::pointer(p) => p,
-                    _ => panic!("this should not be possible")
+                    _ => panic!("this should not be possible"),
                 };
 
                 let mut len = 1;
@@ -798,11 +813,11 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     write!(w, "0x{:x}", ptr as usize)?;
                 }
                 pad(w, left, b' ', len..pad_space)?;
-            },
+            }
             FmtKind::GetWritten => {
                 let mut ptr = match varargs.get(index, &mut ap, Some(&arg)) {
                     VaArg::pointer(p) => p,
-                    _ => panic!("this should not be possible")
+                    _ => panic!("this should not be possible"),
                 };
 
                 match intkind {
@@ -813,7 +828,7 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) ->
                     IntKind::LongLong => *(ptr as *mut c_longlong) = w.written as c_longlong,
                     IntKind::IntMax => *(ptr as *mut intmax_t) = w.written as intmax_t,
                     IntKind::PtrDiff => *(ptr as *mut ptrdiff_t) = w.written as ptrdiff_t,
-                    IntKind::Size => *(ptr as *mut size_t) = w.written as size_t
+                    IntKind::Size => *(ptr as *mut size_t) = w.written as size_t,
                 }
             }
         }

+ 25 - 11
src/header/stdlib/mod.rs

@@ -1,7 +1,7 @@
 //! stdlib implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdlib.h.html
 
 use core::{intrinsics, iter, mem, ptr, slice};
-use rand::distributions::Alphanumeric;
+use rand::distributions::{Alphanumeric, Distribution, Uniform};
 use rand::prng::XorShiftRng;
 use rand::rngs::JitterRng;
 use rand::{Rng, SeedableRng};
@@ -34,6 +34,10 @@ pub const MB_LEN_MAX: c_int = 4;
 static mut ATEXIT_FUNCS: [Option<extern "C" fn()>; 32] = [None; 32];
 static mut RNG: Option<XorShiftRng> = None;
 
+lazy_static! {
+    static ref RNG_SAMPLER: Uniform<c_int> = Uniform::new_inclusive(0, RAND_MAX);
+}
+
 #[no_mangle]
 pub extern "C" fn _Exit(status: c_int) {
     unistd::_exit(status);
@@ -82,8 +86,7 @@ pub unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut
         /* The size-is-multiple-of-alignment requirement is the only
          * difference between aligned_alloc() and memalign(). */
         memalign(alignment, size)
-    }
-    else {
+    } else {
         platform::errno = errno::EINVAL;
         ptr::null_mut()
     }
@@ -201,7 +204,7 @@ pub unsafe extern "C" fn calloc(nelem: size_t, elsize: size_t) -> *mut c_void {
                 intrinsics::write_bytes(ptr as *mut u8, 0, size);
             }
             ptr
-        },
+        }
         None => {
             // For overflowing multiplication, we have to set errno here
             platform::errno = errno::ENOMEM;
@@ -431,8 +434,7 @@ pub unsafe extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_voi
             platform::errno = errno::ENOMEM;
         }
         ptr
-    }
-    else {
+    } else {
         platform::errno = errno::EINVAL;
         ptr::null_mut()
     }
@@ -618,19 +620,31 @@ pub extern "C" fn qsort(
 #[no_mangle]
 pub unsafe extern "C" fn rand() -> c_int {
     match RNG {
-        Some(ref mut rng) => rng.gen_range(0, RAND_MAX),
+        Some(ref mut rng) => RNG_SAMPLER.sample(rng),
         None => {
             let mut rng = XorShiftRng::from_seed([1; 16]);
-            let ret = rng.gen_range(0, RAND_MAX);
+            let ret = RNG_SAMPLER.sample(&mut rng);
             RNG = Some(rng);
             ret
         }
     }
 }
 
-// #[no_mangle]
-pub extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
+    if seed.is_null() {
+        errno::EINVAL
+    } else {
+        // set the type explicitly so this will fail if the array size for XorShiftRng changes
+        let seed_arr: [u8; 16] = mem::transmute([*seed; 16 / mem::size_of::<c_uint>()]);
+
+        let mut rng = XorShiftRng::from_seed(seed_arr);
+        let ret = RNG_SAMPLER.sample(&mut rng);
+
+        *seed = ret as _;
+
+        ret
+    }
 }
 
 // #[no_mangle]

+ 9 - 0
src/header/string/mod.rs

@@ -263,6 +263,15 @@ pub unsafe extern "C" fn strnlen(s: *const c_char, size: size_t) -> size_t {
     i as size_t
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn strnlen_s(s: *const c_char, size: size_t) -> size_t {
+    if s.is_null() {
+        0
+    } else {
+        strnlen(s, size)
+    }
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn strcat(s1: *mut c_char, s2: *const c_char) -> *mut c_char {
     strncat(s1, s2, usize::MAX)

+ 3 - 0
tests/Makefile

@@ -52,6 +52,7 @@ EXPECT_BINS=\
 	string/strchr \
 	string/strcpy \
 	string/strcspn \
+	string/strlen \
 	string/strncmp \
 	string/strpbrk \
 	string/strrchr \
@@ -96,6 +97,8 @@ BINS=\
 	$(EXPECT_BINS) \
 	dirent/main \
 	pwd \
+	stdio/tempnam \
+	stdio/tmpnam \
 	stdlib/alloc \
 	stdlib/bsearch \
 	stdlib/mktemp \

+ 4 - 0
tests/expected/stdlib/rand.stdout

@@ -2,3 +2,7 @@
 201425341
 201425341
 67141780
+264204
+271585844
+264204
+271585844

+ 0 - 0
tests/expected/string/strlen.stderr


+ 9 - 0
tests/expected/string/strlen.stdout

@@ -0,0 +1,9 @@
+12
+0
+12
+12
+0
+12
+6
+12
+0

+ 127 - 0
tests/stdio/tempnam.c

@@ -0,0 +1,127 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test_helpers.h"
+
+static void test_prefix(const char *prefix);
+static void test_dir(const char *dir);
+static void test_dir_and_prefix(const char *dir, const char *prefix);
+
+int main(void) {
+    char *first_null = tempnam(NULL, NULL);
+    if(first_null == NULL) {
+	// NOTE: assuming that we can at least get one file name
+	puts("tempnam(NULL, NULL) returned NULL on first try");
+	exit(EXIT_FAILURE);
+    }
+    printf("%s\n", first_null);
+
+    char *second_null = tempnam(NULL, NULL);
+    if(second_null == NULL) {
+	// NOTE: assuming that we can at least get one file name
+	puts("tempnam(NULL, NULL) returned NULL on second try");
+	free(first_null);
+	exit(EXIT_FAILURE);
+    }
+    printf("%s\n", second_null);
+
+    free(first_null);
+    free(second_null);
+
+    if(first_null == second_null) {
+	puts("tempnam(NULL, NULL) returns the same address");
+	exit(EXIT_FAILURE);
+    }
+
+    // Ensure the "prefix" argument works
+    test_prefix("this_is_a_test_prefix");
+    test_prefix("exact");
+    test_prefix("hi");
+    test_prefix("");
+
+    // Ensure the "dir" argument works
+
+    // NOTE: needed because TMPDIR is the first directory checked
+    unsetenv("TMPDIR");
+
+    test_dir("/tmp");
+    test_dir("");
+    // NOTE: assumes /root is NOT writable
+    test_dir("/root");
+
+    // Ensure "prefix" and "dir" work together
+    test_dir_and_prefix("/tmp", "this_is_a_prefix");
+    test_dir_and_prefix("/tmp", "exact");
+    test_dir_and_prefix("/root", "exact");
+    test_dir_and_prefix("/root", "long_prefix");
+    test_dir_and_prefix("", "prefix");
+    test_dir_and_prefix("/tmp", "test");
+
+    return 0;
+}
+
+static void test_prefix(const char *prefix) {
+    test_dir_and_prefix(NULL, prefix);
+}
+
+static void test_dir(const char *dir) {
+    test_dir_and_prefix(dir, NULL);
+}
+
+static void test_dir_and_prefix(const char *dir, const char *prefix) {
+    char funcbuf[256];
+    if(dir && prefix) {
+        snprintf(funcbuf, sizeof(funcbuf), "tempnam(\"%s\", \"%s\")", dir, prefix);
+    } else if(dir) {
+        snprintf(funcbuf, sizeof(funcbuf), "tempnam(\"%s\", NULL)", dir);
+    } else if(prefix) {
+        snprintf(funcbuf, sizeof(funcbuf), "tempnam(NULL, \"%s\")", prefix);
+    } else {
+        strcpy(funcbuf, "tempnam(NULL, NULL)");
+    }
+
+    char *result = tempnam(dir, prefix);
+    if(!result) {
+        printf("%s failed\n", funcbuf);
+        exit(EXIT_FAILURE);
+    }
+    printf("%s\n", result);
+
+    if(prefix) {
+        char buf[7] = { '/' };
+        strncpy(&buf[1], prefix, sizeof(buf) - 2);
+        buf[6] = 0;
+
+        char *prev = NULL;
+        char *substr = result;
+        do {
+            prev = substr;
+            substr = strstr(&substr[1], buf);
+        } while(substr);
+        substr = prev;
+
+        if(!substr) {
+            printf("%s did not add the full (5 bytes at most) prefix\n", funcbuf);
+            free(result);
+            exit(EXIT_FAILURE);
+        } else if(strlen(substr) != strlen(&buf[1]) + L_tmpnam) {
+            printf("%s has the wrong length\n", funcbuf);
+            free(result);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    if(dir) {
+        char *substr = strstr(result, dir);
+        char *other_substr = strstr(result, P_tmpdir);
+        if(!substr && !other_substr) {
+            printf("%s is in an unexpected directory\n", funcbuf);
+            free(result);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    free(result);
+}
+

+ 41 - 0
tests/stdio/tmpnam.c

@@ -0,0 +1,41 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test_helpers.h"
+
+int main(void) {
+    char *first_null = tmpnam(NULL);
+    if(first_null == NULL) {
+	// NOTE: assuming that we can at least get one file name
+	puts("tmpnam(NULL) returned NULL on first try");
+	exit(EXIT_FAILURE);
+    }
+    printf("%s\n", first_null);
+
+    char *second_null = tmpnam(NULL);
+    if(second_null == NULL) {
+	// NOTE: assuming that we can at least get one file name
+	puts("tmpnam(NULL) returned NULL on second try");
+	exit(EXIT_FAILURE);
+    }
+    printf("%s\n", second_null);
+
+    if(first_null != second_null) {
+	puts("tmpnam(NULL) returns different addresses");
+	exit(EXIT_FAILURE);
+    }
+
+    char buffer[L_tmpnam + 1];
+    char *buf_result = tmpnam(buffer);
+    if(buf_result == NULL) {
+	puts("tmpnam(buffer) failed");
+	exit(EXIT_FAILURE);
+    } else if(buf_result != buffer) {
+	puts("tmpnam(buffer) did not return buffer's address");
+	exit(EXIT_FAILURE);
+    }
+    printf("%s\n", buffer);
+
+    return 0;
+}

+ 36 - 0
tests/stdlib/rand.c

@@ -31,4 +31,40 @@ int main(void) {
         puts("srand(1) doesn't work");
         exit(EXIT_FAILURE);
     }
+
+    // Ensure rand_r() fails with NULL input
+    if (rand_r(NULL) != EINVAL) {
+        puts("rand_r(NULL) doesn't return EINVAL");
+        exit(EXIT_FAILURE);
+    }
+
+    // Ensure rand_r() produces unique values
+    int seed = 259;
+    int rand_r_seed259_1 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_1);
+
+    int rand_r_seed259_2 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_2);
+
+    if (rand_r_seed259_1 == rand_r_seed259_2) {
+        puts("rand_r() fails to produce unique values");
+        exit(EXIT_FAILURE);
+    }
+
+    // Ensure rand_r() returns reproducible values
+    seed = 259;
+    int rand_r_seed259_1_2 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_1_2);
+
+    int rand_r_seed259_2_2 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_2_2);
+
+    if (rand_r_seed259_1 != rand_r_seed259_1_2
+        || rand_r_seed259_2 != rand_r_seed259_2_2)
+    {
+        puts("rand_r() is not reproducible");
+	exit(EXIT_FAILURE);
+    }
+
+    return 0;
 }

+ 73 - 0
tests/string/strlen.c

@@ -0,0 +1,73 @@
+#include <string.h>
+#include <stdio.h>
+
+#include "test_helpers.h"
+
+int main(void) {
+    char dest1[13] = "hello world!";
+    int dest1_len = strlen(dest1);
+    printf("%d\n", dest1_len);
+    if(dest1_len != 12) {
+        puts("strlen(\"hello world!\") failed");
+	exit(EXIT_FAILURE);
+    }
+
+    char empty[1] = { 0 };
+    int empty_len = strlen(empty);
+    printf("%d\n", empty_len);
+    if(empty_len != 0) {
+        puts("strlen(\"\") failed");
+        exit(EXIT_FAILURE);
+    }
+
+    dest1_len = strnlen(dest1, sizeof(dest1));
+    printf("%d\n", dest1_len);
+    if(dest1_len != 12) {
+        puts("strnlen(\"hello world!\", 13) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    dest1_len = strnlen(dest1, sizeof(dest1) - 1);
+    printf("%d\n", dest1_len);
+    if(dest1_len != 12) {
+        puts("strnlen(\"hello world!\", 12) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    dest1_len = strnlen(dest1, 0);
+    printf("%d\n", dest1_len);
+    if(dest1_len != 0) {
+        puts("strnlen(\"hello world!\", 0) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    dest1_len = strnlen(dest1, 300);
+    printf("%d\n", dest1_len);
+    if(dest1_len != 12) {
+        puts("strnlen(\"hello world!\", 300) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    dest1_len = strnlen_s(dest1, 6);
+    printf("%d\n", dest1_len);
+    if(dest1_len != 6) {
+        puts("strnlen_s(\"hello world!\", 6) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    dest1_len = strnlen_s(dest1, 20);
+    printf("%d\n", dest1_len);
+    if(dest1_len != 12) {
+        puts("strnlen_s(\"hello world!\", 20) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    int null_len = strnlen_s(NULL, 100);
+    printf("%d\n", null_len);
+    if(null_len != 0) {
+        puts("strnlen_s(NULL, 100) failed");
+        exit(EXIT_FAILURE);
+    }
+
+    return 0;
+}