diff --git a/rules/stdio/ir_unsafe.json b/rules/stdio/ir_unsafe.json index ea38ee60..43661b84 100644 --- a/rules/stdio/ir_unsafe.json +++ b/rules/stdio/ir_unsafe.json @@ -264,6 +264,305 @@ "is_unsafe_pointer": true } }, + "f11": { + "body": [ + { + "text": "match " + }, + { + "method_call": { + "receiver": [ + { + "text": "(*" + }, + { + "placeholder": { + "arg": 1, + "access": "write" + } + }, + { + "text": ")" + } + ], + "body": [ + { + "text": ".write_all(&[" + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " as u8])" + } + ] + } + }, + { + "text": " {\n Ok(()) => " + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " & 0xff,\n Err(_) => -1,\n }" + } + ], + "params": { + "a0": { + "type": "i32" + }, + "a1": { + "type": "*mut ::std::fs::File", + "is_unsafe_pointer": true + } + }, + "return_type": { + "type": "i32" + } + }, + "f12": { + "body": [ + { + "text": "let bytes = " + }, + { + "method_call": { + "receiver": [ + { + "text": "std::ffi::CStr::from_ptr(" + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " as *const i8)" + } + ], + "body": [ + { + "text": ".to_bytes()" + } + ] + } + }, + { + "text": ";\n match " + }, + { + "method_call": { + "receiver": [ + { + "text": "(*" + }, + { + "placeholder": { + "arg": 1, + "access": "write" + } + }, + { + "text": ")" + } + ], + "body": [ + { + "text": ".write_all(bytes)" + } + ] + } + }, + { + "text": " {\n Ok(()) => 0,\n Err(_) => -1,\n }" + } + ], + "multi_statement": true, + "params": { + "a0": { + "type": "*const u8", + "is_unsafe_pointer": true + }, + "a1": { + "type": "*mut ::std::fs::File", + "is_unsafe_pointer": true + } + }, + "return_type": { + "type": "i32" + } + }, + "f13": { + "body": [ + { + "text": "let bytes = " + }, + { + "method_call": { + "receiver": [ + { + "text": "std::ffi::CStr::from_ptr(" + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " as *const i8)" + } + ], + "body": [ + { + "text": ".to_bytes()" + } + ] + } + }, + { + "text": ";\n let stdout = libcc2rs::cout_unsafe();\n let r1 = " + }, + { + "method_call": { + "receiver": [ + { + "text": "(*stdout)" + } + ], + "body": [ + { + "text": ".write_all(bytes)" + } + ] + } + }, + { + "text": ";\n let r2 = " + }, + { + "method_call": { + "receiver": [ + { + "text": "(*stdout)" + } + ], + "body": [ + { + "text": ".write_all(b\"\\n\")" + } + ] + } + }, + { + "text": ";\n if " + }, + { + "method_call": { + "receiver": [ + { + "text": "r1" + } + ], + "body": [ + { + "text": ".is_ok()" + } + ] + } + }, + { + "text": " && " + }, + { + "method_call": { + "receiver": [ + { + "text": "r2" + } + ], + "body": [ + { + "text": ".is_ok()" + } + ] + } + }, + { + "text": " { 0 } else { -1 }" + } + ], + "multi_statement": true, + "params": { + "a0": { + "type": "*const u8", + "is_unsafe_pointer": true + } + }, + "return_type": { + "type": "i32" + } + }, + "f14": { + "body": [ + { + "text": "if " + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " == libcc2rs::cin_unsafe() {\n 0\n } else if " + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " == libcc2rs::cout_unsafe() {\n 1\n } else if " + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": " == libcc2rs::cerr_unsafe() {\n 2\n } else {\n ::std::os::fd::AsRawFd::as_raw_fd(&*" + }, + { + "placeholder": { + "arg": 0, + "access": "read" + } + }, + { + "text": ")\n }" + } + ], + "params": { + "a0": { + "type": "*mut ::std::fs::File", + "is_unsafe_pointer": true + } + }, + "return_type": { + "type": "i32" + } + }, "f2": { "body": [ { diff --git a/rules/stdio/src.cpp b/rules/stdio/src.cpp index 77450328..c1ecc453 100644 --- a/rules/stdio/src.cpp +++ b/rules/stdio/src.cpp @@ -32,3 +32,11 @@ FILE *f8() { return stdout; } FILE *f9() { return stderr; } FILE *f10() { return stdin; } + +int f11(int c, FILE *stream) { return fputc(c, stream); } + +int f12(const char *s, FILE *stream) { return fputs(s, stream); } + +int f13(const char *s) { return puts(s); } + +int f14(FILE *stream) { return fileno(stream); } diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index 0b76382b..5b717f57 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -105,3 +105,38 @@ unsafe fn f9() -> *mut ::std::fs::File { unsafe fn f10() -> *mut ::std::fs::File { libcc2rs::cin_unsafe() } + +unsafe fn f11(a0: i32, a1: *mut ::std::fs::File) -> i32 { + match (*a1).write_all(&[a0 as u8]) { + Ok(()) => a0 & 0xff, + Err(_) => -1, + } +} + +unsafe fn f12(a0: *const u8, a1: *mut ::std::fs::File) -> i32 { + let bytes = std::ffi::CStr::from_ptr(a0 as *const i8).to_bytes(); + match (*a1).write_all(bytes) { + Ok(()) => 0, + Err(_) => -1, + } +} + +unsafe fn f13(a0: *const u8) -> i32 { + let bytes = std::ffi::CStr::from_ptr(a0 as *const i8).to_bytes(); + let stdout = libcc2rs::cout_unsafe(); + let r1 = (*stdout).write_all(bytes); + let r2 = (*stdout).write_all(b"\n"); + if r1.is_ok() && r2.is_ok() { 0 } else { -1 } +} + +unsafe fn f14(a0: *mut ::std::fs::File) -> i32 { + if a0 == libcc2rs::cin_unsafe() { + 0 + } else if a0 == libcc2rs::cout_unsafe() { + 1 + } else if a0 == libcc2rs::cerr_unsafe() { + 2 + } else { + ::std::os::fd::AsRawFd::as_raw_fd(&*a0) + } +} diff --git a/tests/unit/out/unsafe/stdio.rs b/tests/unit/out/unsafe/stdio.rs new file mode 100644 index 00000000..484b7cd5 --- /dev/null +++ b/tests/unit/out/unsafe/stdio.rs @@ -0,0 +1,190 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn test_fputc_0() { + match (*libcc2rs::cout_unsafe()).write_all(&[('H' as i32) as u8]) { + Ok(()) => ('H' as i32) & 0xff, + Err(_) => -1, + }; + match (*libcc2rs::cout_unsafe()).write_all(&[('i' as i32) as u8]) { + Ok(()) => ('i' as i32) & 0xff, + Err(_) => -1, + }; + match (*libcc2rs::cout_unsafe()).write_all(&[('\n' as i32) as u8]) { + Ok(()) => ('\n' as i32) & 0xff, + Err(_) => -1, + }; +} +pub unsafe fn test_fputs_1() { + { + let bytes = + std::ffi::CStr::from_ptr((b"hello\0".as_ptr().cast_mut()).cast_const() as *const i8) + .to_bytes(); + match (*libcc2rs::cout_unsafe()).write_all(bytes) { + Ok(()) => 0, + Err(_) => -1, + } + }; + match (*libcc2rs::cout_unsafe()).write_all(&[('\n' as i32) as u8]) { + Ok(()) => ('\n' as i32) & 0xff, + Err(_) => -1, + }; + let mut s: *const u8 = (b"from variable\0".as_ptr().cast_mut()).cast_const(); + { + let bytes = std::ffi::CStr::from_ptr(s as *const i8).to_bytes(); + match (*libcc2rs::cout_unsafe()).write_all(bytes) { + Ok(()) => 0, + Err(_) => -1, + } + }; + match (*libcc2rs::cout_unsafe()).write_all(&[('\n' as i32) as u8]) { + Ok(()) => ('\n' as i32) & 0xff, + Err(_) => -1, + }; + let mut buf: [u8; 4] = [ + (('b' as i32) as u8), + (('u' as i32) as u8), + (('f' as i32) as u8), + (('\0' as i32) as u8), + ]; + { + let bytes = + std::ffi::CStr::from_ptr((buf.as_mut_ptr()).cast_const() as *const i8).to_bytes(); + match (*libcc2rs::cout_unsafe()).write_all(bytes) { + Ok(()) => 0, + Err(_) => -1, + } + }; + match (*libcc2rs::cout_unsafe()).write_all(&[('\n' as i32) as u8]) { + Ok(()) => ('\n' as i32) & 0xff, + Err(_) => -1, + }; +} +pub unsafe fn test_puts_2() { + { + let bytes = std::ffi::CStr::from_ptr( + (b"puts hello\0".as_ptr().cast_mut()).cast_const() as *const i8 + ) + .to_bytes(); + let stdout = libcc2rs::cout_unsafe(); + let r1 = (*stdout).write_all(bytes); + let r2 = (*stdout).write_all(b"\n"); + if r1.is_ok() && r2.is_ok() { + 0 + } else { + -1 + } + }; + let mut s: *const u8 = (b"puts variable\0".as_ptr().cast_mut()).cast_const(); + { + let bytes = std::ffi::CStr::from_ptr(s as *const i8).to_bytes(); + let stdout = libcc2rs::cout_unsafe(); + let r1 = (*stdout).write_all(bytes); + let r2 = (*stdout).write_all(b"\n"); + if r1.is_ok() && r2.is_ok() { + 0 + } else { + -1 + } + }; +} +pub unsafe fn test_fileno_3() { + assert!( + ((((if libcc2rs::cin_unsafe() == libcc2rs::cin_unsafe() { + 0 + } else if libcc2rs::cin_unsafe() == libcc2rs::cout_unsafe() { + 1 + } else if libcc2rs::cin_unsafe() == libcc2rs::cerr_unsafe() { + 2 + } else { + ::std::os::fd::AsRawFd::as_raw_fd(&*libcc2rs::cin_unsafe()) + }) == (0)) as i32) + != 0) + ); + assert!( + ((((if libcc2rs::cout_unsafe() == libcc2rs::cin_unsafe() { + 0 + } else if libcc2rs::cout_unsafe() == libcc2rs::cout_unsafe() { + 1 + } else if libcc2rs::cout_unsafe() == libcc2rs::cerr_unsafe() { + 2 + } else { + ::std::os::fd::AsRawFd::as_raw_fd(&*libcc2rs::cout_unsafe()) + }) == (1)) as i32) + != 0) + ); + assert!( + ((((if libcc2rs::cerr_unsafe() == libcc2rs::cin_unsafe() { + 0 + } else if libcc2rs::cerr_unsafe() == libcc2rs::cout_unsafe() { + 1 + } else if libcc2rs::cerr_unsafe() == libcc2rs::cerr_unsafe() { + 2 + } else { + ::std::os::fd::AsRawFd::as_raw_fd(&*libcc2rs::cerr_unsafe()) + }) == (2)) as i32) + != 0) + ); + let mut file: *const u8 = (b"/tmp/cpp2rust_fileno_test.tmp\0".as_ptr().cast_mut()).cast_const(); + let mut fp: *mut ::std::fs::File = + match std::ffi::CStr::from_ptr((b"wb\0".as_ptr().cast_mut()).cast_const() as *const i8) + .to_str() + .expect("invalid c-string") + { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open( + std::ffi::CStr::from_ptr(file as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open( + std::ffi::CStr::from_ptr(file as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + _ => panic!("unsupported mode"), + }; + assert!((((!((fp).is_null())) as i32) != 0)); + assert!( + ((((if fp == libcc2rs::cin_unsafe() { + 0 + } else if fp == libcc2rs::cout_unsafe() { + 1 + } else if fp == libcc2rs::cerr_unsafe() { + 2 + } else { + ::std::os::fd::AsRawFd::as_raw_fd(&*fp) + }) > (2)) as i32) + != 0) + ); + { + Box::from_raw(fp); + 0 + }; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + (unsafe { test_fputc_0() }); + (unsafe { test_fputs_1() }); + (unsafe { test_puts_2() }); + (unsafe { test_fileno_3() }); + return 0; +} diff --git a/tests/unit/stdio.c b/tests/unit/stdio.c new file mode 100644 index 00000000..63078e68 --- /dev/null +++ b/tests/unit/stdio.c @@ -0,0 +1,47 @@ +// no-compile: refcount +#include +#include + +static void test_fputc(void) { + fputc('H', stdout); + fputc('i', stdout); + fputc('\n', stdout); +} + +static void test_fputs(void) { + fputs("hello", stdout); + fputc('\n', stdout); + const char *s = "from variable"; + fputs(s, stdout); + fputc('\n', stdout); + char buf[] = {'b', 'u', 'f', '\0'}; + fputs(buf, stdout); + fputc('\n', stdout); +} + +static void test_puts(void) { + puts("puts hello"); + const char *s = "puts variable"; + puts(s); +} + +static void test_fileno(void) { + assert(fileno(stdin) == 0); + assert(fileno(stdout) == 1); + assert(fileno(stderr) == 2); + const char *file = "/tmp/cpp2rust_fileno_test.tmp"; + FILE *fp = fopen(file, "wb"); + assert(fp != NULL); + assert(fileno(fp) > 2); + fclose(fp); + // TODO: uncomment when unlink is available + // assert(unlink(file) == 0); +} + +int main(void) { + test_fputc(); + test_fputs(); + test_puts(); + test_fileno(); + return 0; +}