diff --git a/include/bits/stdio.h b/include/bits/stdio.h index 428cded594..97c6c1036b 100644 --- a/include/bits/stdio.h +++ b/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; diff --git a/src/header/stdio/constants.rs b/src/header/stdio/constants.rs index 2adea19182..eef8f3fe0e 100644 --- a/src/header/stdio/constants.rs +++ b/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; diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index bda7802055..6bc751c105 100644 --- a/src/header/stdio/mod.rs +++ b/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), @@ -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 diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index 658695ec17..c49dad8325 100644 --- a/src/header/stdio/printf.rs +++ b/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::()), - (FmtKind::Unsigned, IntKind::Short) | - (FmtKind::Signed, IntKind::Short) => VaArg::c_short(ap.arg::()), - (FmtKind::Unsigned, IntKind::Int) | - (FmtKind::Signed, IntKind::Int) => VaArg::c_int(ap.arg::()), - (FmtKind::Unsigned, IntKind::Long) | - (FmtKind::Signed, IntKind::Long) => VaArg::c_long(ap.arg::()), - (FmtKind::Unsigned, IntKind::LongLong) | - (FmtKind::Signed, IntKind::LongLong) => VaArg::c_longlong(ap.arg::()), - (FmtKind::Unsigned, IntKind::IntMax) | - (FmtKind::Signed, IntKind::IntMax) => VaArg::intmax_t(ap.arg::()), - (FmtKind::Unsigned, IntKind::PtrDiff) | - (FmtKind::Signed, IntKind::PtrDiff) => VaArg::ptrdiff_t(ap.arg::()), - (FmtKind::Unsigned, IntKind::Size) | - (FmtKind::Signed, IntKind::Size) => VaArg::ssize_t(ap.arg::()), + (FmtKind::Char, _) + | (FmtKind::Unsigned, IntKind::Byte) + | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::()), + (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => { + VaArg::c_short(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => { + VaArg::c_int(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => { + VaArg::c_long(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => { + VaArg::c_longlong(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => { + VaArg::intmax_t(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => { + VaArg::ptrdiff_t(ap.arg::()) + } + (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => { + VaArg::ssize_t(ap.arg::()) + } - (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) - => VaArg::c_double(ap.arg::()), + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => { + VaArg::c_double(ap.arg::()) + } - (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) - => VaArg::pointer(ap.arg::<*const c_void>()), + (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::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::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => { + VaArg::c_double(untyped.c_double) + } - (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) - => VaArg::pointer(untyped.pointer), + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => { + VaArg::pointer(untyped.pointer) + } } } } #[derive(Default)] struct VaListCache { args: Vec, - 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::()) + None => VaArg::c_int(ap.arg::()), }); self.args[i] } @@ -224,7 +241,6 @@ impl VaListCache { // |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_) // |_| - unsafe fn pop_int_raw(format: &mut *const u8) -> Option { 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 { unsafe fn pop_int(format: &mut *const u8) -> Option { 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( #[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; @@ -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: W, format: *const c_char, mut ap: VaList) -> io::Result { 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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, } } } diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 0712589627..e8002abe69 100644 --- a/src/header/stdlib/mod.rs +++ b/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; 32] = [None; 32]; static mut RNG: Option = None; +lazy_static! { + static ref RNG_SAMPLER: Uniform = 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::()]); + + let mut rng = XorShiftRng::from_seed(seed_arr); + let ret = RNG_SAMPLER.sample(&mut rng); + + *seed = ret as _; + + ret + } } // #[no_mangle] diff --git a/src/header/string/mod.rs b/src/header/string/mod.rs index c402707f36..e58e5beb69 100644 --- a/src/header/string/mod.rs +++ b/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) diff --git a/tests/Makefile b/tests/Makefile index 7b1f946fbc..2f967511f9 100644 --- a/tests/Makefile +++ b/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 \ diff --git a/tests/expected/stdlib/rand.stdout b/tests/expected/stdlib/rand.stdout index 60210f3e9d..2a50461db2 100644 --- a/tests/expected/stdlib/rand.stdout +++ b/tests/expected/stdlib/rand.stdout @@ -2,3 +2,7 @@ 201425341 201425341 67141780 +264204 +271585844 +264204 +271585844 diff --git a/tests/expected/string/strlen.stderr b/tests/expected/string/strlen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/string/strlen.stdout b/tests/expected/string/strlen.stdout new file mode 100644 index 0000000000..9ce90c8a65 --- /dev/null +++ b/tests/expected/string/strlen.stdout @@ -0,0 +1,9 @@ +12 +0 +12 +12 +0 +12 +6 +12 +0 diff --git a/tests/stdio/tempnam.c b/tests/stdio/tempnam.c new file mode 100644 index 0000000000..8fd7edf6d0 --- /dev/null +++ b/tests/stdio/tempnam.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#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); +} + diff --git a/tests/stdio/tmpnam.c b/tests/stdio/tmpnam.c new file mode 100644 index 0000000000..02c07d6724 --- /dev/null +++ b/tests/stdio/tmpnam.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#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; +} diff --git a/tests/stdlib/rand.c b/tests/stdlib/rand.c index 52908fae70..29791888d8 100644 --- a/tests/stdlib/rand.c +++ b/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; } diff --git a/tests/string/strlen.c b/tests/string/strlen.c new file mode 100644 index 0000000000..f7fd46f5b5 --- /dev/null +++ b/tests/string/strlen.c @@ -0,0 +1,73 @@ +#include +#include + +#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; +}