Commit be765f87 authored by Jeremy Soller's avatar Jeremy Soller

Merge branch 'master' into 'master'

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

See merge request !210
parents 6d857f8d 68d3c5f1
Pipeline #3999 failed with stages
in 6 minutes and 5 seconds
#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;
......
......@@ -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;
......@@ -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
......
This diff is collapsed.
//! 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]
......
......@@ -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)
......
......@@ -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 \
......
......@@ -2,3 +2,7 @@
201425341
201425341
67141780
264204
271585844
264204
271585844
#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);
}
#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;
}
......@@ -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;
}
#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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment