Merge branch 'printf' into 'master'

Implement almost all of printf

See merge request redox-os/relibc!171
This commit is contained in:
Jeremy Soller
2018-10-13 14:14:27 +00:00
13 changed files with 713 additions and 185 deletions
+523 -107
View File
@@ -1,120 +1,536 @@
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::fmt::Write as CoreWrite;
use core::{ptr, slice, str};
use core::ops::Range;
use core::{fmt, ptr, slice, str};
use c_str::CStr;
use io::{self, Write};
use platform::types::*;
use platform::{self, WriteByte};
use va_list::VaList;
use va_list::{VaList, VaPrimitive};
pub unsafe fn printf<W: WriteByte>(w: W, format: *const c_char, mut ap: VaList) -> c_int {
let mut w = platform::CountingWriter::new(w);
#[derive(PartialEq, Eq)]
enum IntKind {
Byte,
Short,
Int,
Long,
LongLong,
IntMax,
PtrDiff,
Size,
}
let format = slice::from_raw_parts(format as *const u8, usize::max_value());
let mut found_percent = false;
for &b in format.iter() {
// check for NUL
if b == 0 {
break;
}
if found_percent {
if match b as char {
'%' => {
found_percent = false;
w.write_char('%')
}
'c' => {
let a = ap.get::<u32>();
found_percent = false;
w.write_u8(a as u8)
}
'd' | 'i' => {
let a = ap.get::<c_int>();
found_percent = false;
w.write_fmt(format_args!("{}", a))
}
'f' | 'F' => {
let a = ap.get::<f64>();
found_percent = false;
w.write_fmt(format_args!("{}", a))
}
'n' => {
let _a = ap.get::<c_int>();
found_percent = false;
Ok(())
}
'p' => {
let a = ap.get::<usize>();
found_percent = false;
w.write_fmt(format_args!("0x{:x}", a))
}
's' => {
let a = ap.get::<*const c_char>();
found_percent = false;
if a != ptr::null() {
let a_cstr = CStr::from_ptr(a);
w.write_str(str::from_utf8_unchecked(a_cstr.to_bytes()))
} else {
w.write_str("NULL")
}
}
'u' => {
let a = ap.get::<c_uint>();
found_percent = false;
w.write_fmt(format_args!("{}", a))
}
'x' => {
let a = ap.get::<c_uint>();
found_percent = false;
w.write_fmt(format_args!("{:x}", a))
}
'X' => {
let a = ap.get::<c_uint>();
found_percent = false;
w.write_fmt(format_args!("{:X}", a))
}
'o' => {
let a = ap.get::<c_uint>();
found_percent = false;
w.write_fmt(format_args!("{:o}", a))
}
'-' => Ok(()),
'+' => Ok(()),
' ' => Ok(()),
'#' => Ok(()),
'0'...'9' => Ok(()),
_ => Ok(()),
}.is_err()
{
return -1;
trait IntoUsize {
fn into_usize(self) -> usize;
fn from_usize(i: usize) -> Self;
}
macro_rules! impl_intousize {
($($kind:tt;)*) => {
$(impl IntoUsize for $kind {
fn into_usize(self) -> usize {
self as usize
}
} else if b == b'%' {
found_percent = true;
} else {
if w.write_u8(b).is_err() {
return -1;
fn from_usize(i: usize) -> Self {
i as Self
}
})*
}
}
impl_intousize! {
i32;
u32;
i64;
u64;
isize;
usize;
}
impl<T> IntoUsize for *const T {
fn into_usize(self) -> usize {
self as usize
}
fn from_usize(i: usize) -> Self {
i as Self
}
}
impl<T> IntoUsize for *mut T {
fn into_usize(self) -> usize {
self as usize
}
fn from_usize(i: usize) -> Self {
i as Self
}
}
impl IntoUsize for f32 {
fn into_usize(self) -> usize {
self.to_bits() as usize
}
fn from_usize(i: usize) -> Self {
Self::from_bits(i as u32)
}
}
impl IntoUsize for f64 {
fn into_usize(self) -> usize {
self.to_bits() as usize
}
fn from_usize(i: usize) -> Self {
Self::from_bits(i as u64)
}
}
struct BufferedVaList {
list: VaList,
buf: Vec<usize>,
i: usize
}
impl BufferedVaList {
fn new(list: VaList) -> Self {
Self {
list,
buf: Vec::new(),
i: 0
}
}
unsafe fn get<T: VaPrimitive + IntoUsize>(&mut self, i: Option<usize>) -> T {
match i {
None => self.next(),
Some(i) => self.index(i),
}
}
unsafe fn next<T: VaPrimitive + IntoUsize>(&mut self) -> T {
if self.i >= self.buf.len() {
self.buf.push(self.list.get::<T>().into_usize());
}
let arg = T::from_usize(self.buf[self.i]);
self.i += 1;
arg
}
unsafe fn index<T: VaPrimitive + IntoUsize>(&mut self, i: usize) -> T {
while self.buf.len() < i {
// Getting a usize here most definitely isn't sane, however,
// there's no way to know the type!
// Just take this for example:
//
// printf("%*4$d\n", "hi", 0, "hello", 10);
//
// This chooses the width 10. How does it know the type of 0 and "hello"?
// It clearly can't.
w.written as c_int
self.buf.push(self.list.get::<usize>());
}
T::from_usize(self.buf[i - 1])
}
}
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) {
*format = format.offset(1);
if int.is_none() {
int = Some(0);
}
*int.as_mut().unwrap() *= 10;
*int.as_mut().unwrap() += digit as usize;
}
int
}
unsafe fn pop_int(format: &mut *const u8, ap: &mut BufferedVaList) -> Option<usize> {
if **format == b'*' {
*format = format.offset(1);
// Peek ahead for a positional argument:
let mut format2 = *format;
if let Some(i) = pop_int_raw(&mut format2) {
if *format2 == b'$' {
*format = format2.offset(1);
return Some(ap.index::<usize>(i))
}
}
Some(ap.next::<usize>())
} else {
pop_int_raw(format)
}
}
unsafe fn fmt_int<I>(fmt: u8, i: I) -> String
where I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex
{
match fmt {
b'o' => format!("{:o}", i),
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)
}
}
fn pad<W: Write>(w: &mut W, current_side: bool, pad_char: u8, range: Range<usize>) -> io::Result<()> {
if current_side {
for _ in range {
w.write_all(&[pad_char])?;
}
}
Ok(())
}
fn float_string(float: c_double, precision: usize, trim: bool) -> String {
let mut string = format!("{:.p$}", float, p = precision);
if trim {
let truncate = {
let mut slice = string.trim_right_matches('0');
if slice.ends_with('.') {
slice.len() - 1
} else {
slice.len()
}
};
string.truncate(truncate);
}
string
}
fn fmt_float_exp<W: Write>(
w: &mut W,
exp_fmt: u8,
range: Option<(isize, isize)>,
trim: bool,
precision: usize,
mut float: c_double,
left: bool,
pad_space: usize,
pad_zero: usize,
) -> io::Result<bool> {
let mut exp: isize = 0;
while float >= 10.0 || float <= -10.0 {
float /= 10.0;
exp += 1;
}
while (float > 0.0 && float < 1.0) || (float > -1.0 && float < 0.0) {
float *= 10.0;
exp -= 1;
}
if range.map(|(start, end)| exp >= start && exp < end).unwrap_or(false) {
return Ok(false);
}
let mut exp2 = exp;
let mut exp_len = 1;
while exp2 >= 10 {
exp2 /= 10;
exp_len += 1;
}
let string = float_string(float, precision, trim);
let mut len = string.len() + 2 + 2.max(exp_len);
pad(w, !left, b' ', len..pad_space)?;
let bytes = if string.starts_with('-') {
w.write_all(&[b'-'])?;
&string.as_bytes()[1..]
} else {
string.as_bytes()
};
pad(w, !left, b'0', len..pad_zero)?;
w.write_all(bytes)?;
write!(w, "{}{:+03}", exp_fmt as char, exp)?;
pad(w, left, b' ', len..pad_space)?;
Ok(true)
}
fn fmt_float_normal<W: Write>(
w: &mut W,
trim: bool,
precision: usize,
float: c_double,
left: bool,
pad_space: usize,
pad_zero: usize,
) -> io::Result<usize> {
let string = float_string(float, precision, trim);
pad(w, !left, b' ', string.len()..pad_space)?;
let bytes = if string.starts_with('-') {
w.write_all(&[b'-'])?;
&string.as_bytes()[1..]
} else {
string.as_bytes()
};
pad(w, true, b'0', string.len()..pad_zero)?;
w.write_all(bytes)?;
pad(w, left, b' ', string.len()..pad_space)?;
Ok(string.len())
}
unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) -> io::Result<c_int> {
let mut w = &mut platform::CountingWriter::new(w);
let mut ap = BufferedVaList::new(ap);
let mut format = format as *const u8;
while *format != 0 {
if *format != b'%' {
w.write_all(&[*format])?;
} else {
format = format.offset(1);
// Peek ahead to maybe specify argument to fetch from
let mut index = None;
let mut format2 = format;
if let Some(i) = pop_int_raw(&mut format2) {
if *format2 == b'$' {
format = format2.offset(1);
index = Some(i);
}
}
// Flags:
let mut alternate = false;
let mut zero = false;
let mut left = false;
let mut sign_reserve = false;
let mut sign_always = false;
loop {
match *format {
b'#' => alternate = true,
b'0' => zero = true,
b'-' => left = true,
b' ' => sign_reserve = true,
b'+' => sign_always = true,
_ => break
}
format = format.offset(1);
}
// Width and precision:
let min_width = pop_int(&mut format, &mut ap).unwrap_or(0);
let precision = if *format == b'.' {
format = format.offset(1);
match pop_int(&mut format, &mut ap) {
int@Some(_) => int,
None => return Ok(-1)
}
} else {
None
};
let pad_space = if zero { 0 } else { min_width };
let pad_zero = if zero { min_width } else { 0 };
// Integer size:
let mut kind = IntKind::Int;
loop {
kind = match *format {
b'h' => if kind == IntKind::Short || kind == IntKind::Byte {
IntKind::Byte
} else {
IntKind::Short
},
b'j' => IntKind::IntMax,
b'l' => if kind == IntKind::Long || kind == IntKind::LongLong {
IntKind::LongLong
} else {
IntKind::Long
},
b'q' | b'L' => IntKind::LongLong,
b't' => IntKind::PtrDiff,
b'z' => IntKind::Size,
_ => break,
};
format = format.offset(1);
}
// Finally, type:
match *format {
b'%' => w.write_all(&[b'%'])?,
b'd' | b'i' => {
let string = match kind {
// VaList does not seem to support these two:
// IntKind::Byte => ap.get::<c_char>(index).to_string(),
// IntKind::Short => ap.get::<c_short>(index).to_string(),
IntKind::Byte => (ap.get::<c_int>(index) as c_char).to_string(),
IntKind::Short => (ap.get::<c_int>(index) as c_short).to_string(),
IntKind::Int => ap.get::<c_int>(index).to_string(),
IntKind::Long => ap.get::<c_long>(index).to_string(),
IntKind::LongLong => ap.get::<c_longlong>(index).to_string(),
IntKind::PtrDiff => ap.get::<ptrdiff_t>(index).to_string(),
IntKind::Size => ap.get::<ssize_t>(index).to_string(),
IntKind::IntMax => ap.get::<intmax_t>(index).to_string(),
};
let positive = !string.starts_with('-');
let zero = precision == Some(0) && string == "0";
let mut len = string.len();
let mut final_len = string.len().max(precision.unwrap_or(0));
if positive && (sign_reserve || sign_always) {
final_len += 1;
}
if zero {
len = 0;
final_len = 0;
}
pad(w, !left, b' ', final_len..pad_space)?;
let bytes = if positive {
if sign_reserve {
w.write_all(&[b' '])?;
} else if sign_always {
w.write_all(&[b'+'])?;
}
string.as_bytes()
} else {
w.write_all(&[b'-'])?;
&string.as_bytes()[1..]
};
pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?;
if !zero {
w.write_all(bytes)?;
}
pad(w, left, b' ', final_len..pad_space)?;
},
b'o' | b'u' | b'x' | b'X' => {
let fmt = *format;
let string = match kind {
// VaList does not seem to support these two:
// IntKind::Byte => fmt_int(kind ap.get::<c_char>(index)),
// IntKind::Short => fmt_int(kind ap.get::<c_short>(index)),
IntKind::Byte => fmt_int(fmt, ap.get::<c_uint>(index) as c_uchar),
IntKind::Short => fmt_int(fmt, ap.get::<c_uint>(index) as c_ushort),
IntKind::Int => fmt_int(fmt, ap.get::<c_uint>(index)),
IntKind::Long => fmt_int(fmt, ap.get::<c_ulong>(index)),
IntKind::LongLong => fmt_int(fmt, ap.get::<c_ulonglong>(index)),
IntKind::PtrDiff => fmt_int(fmt, ap.get::<ptrdiff_t>(index)),
IntKind::Size => fmt_int(fmt, ap.get::<size_t>(index)),
IntKind::IntMax => fmt_int(fmt, ap.get::<uintmax_t>(index)),
};
let zero = precision == Some(0) && string == "0";
// If this int is padded out to be larger than it is, don't
// add an extra zero if octal.
let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true);
let mut len = string.len();
let mut final_len = string.len().max(precision.unwrap_or(0)) +
if alternate && string != "0" {
match fmt {
b'o' if no_precision => 1,
b'x' |
b'X' => 2,
_ => 0
}
} else { 0 };
if zero {
len = 0;
final_len = 0;
}
pad(w, !left, b' ', final_len..pad_space)?;
if alternate && string != "0" {
match fmt {
b'o' if no_precision => w.write_all(&[b'0'])?,
b'x' => w.write_all(&[b'0', b'x'])?,
b'X' => w.write_all(&[b'0', b'X'])?,
_ => ()
}
}
pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?;
if !zero {
w.write_all(string.as_bytes())?;
}
pad(w, left, b' ', final_len..pad_space)?;
},
b'e' | b'E' => {
let exp_fmt = *format;
let mut float = ap.get::<c_double>(index);
let precision = precision.unwrap_or(6);
fmt_float_exp(w, exp_fmt, None, false, precision, float, left, pad_space, pad_zero)?;
},
b'f' | b'F' => {
let mut float = ap.get::<c_double>(index);
let precision = precision.unwrap_or(6);
fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?;
},
b'g' | b'G' => {
let exp_fmt = b'E' | (*format & 32);
let mut float = ap.get::<c_double>(index);
let precision = precision.unwrap_or(6);
if !fmt_float_exp(w, exp_fmt, Some((-4, precision as isize)), true,
precision, float, left, pad_space, pad_zero)? {
fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?;
}
}
b's' => {
// if kind == IntKind::Long || kind == IntKind::LongLong, handle *const wchar_t
let ptr = ap.get::<*const c_char>(index);
if ptr.is_null() {
w.write_all(b"(null)")?;
} else {
let mut len = 0;
while *ptr.offset(len) != 0 {
len += 1;
}
let len = (len as usize).min(precision.unwrap_or(::core::usize::MAX));
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)?;
}
},
b'c' => {
// if kind == IntKind::Long || kind == IntKind::LongLong, handle wint_t
let c = ap.get::<c_int>(index) as c_char;
pad(w, !left, b' ', 1..pad_space)?;
w.write_all(&[c as u8])?;
pad(w, left, b' ', 1..pad_space)?;
},
b'p' => {
let ptr = ap.get::<*const c_void>(index);
let mut len = 1;
if ptr.is_null() {
len = "(nil)".len();
} else {
let mut ptr = ptr as usize;
while ptr >= 10 {
ptr /= 10;
len += 1;
}
}
pad(w, !left, b' ', len..pad_space)?;
if ptr.is_null() {
write!(w, "(nil)")?;
} else {
write!(w, "0x{:x}", ptr as usize)?;
}
pad(w, left, b' ', len..pad_space)?;
},
b'n' => {
let ptr = ap.get::<*mut c_int>(index);
*ptr = w.written as c_int;
},
_ => return Ok(-1)
}
}
format = format.offset(1);
}
Ok(w.written as c_int)
}
pub unsafe fn printf<W: Write>(w: W, format: *const c_char, ap: VaList) -> c_int {
inner_printf(w, format, ap).unwrap_or(-1)
}
+7 -3
View File
@@ -39,7 +39,7 @@ unsafe fn inner_scanf<R: Read>(
macro_rules! read {
() => {{
let mut buf = &mut [byte];
let buf = &mut [byte];
match r.read(buf) {
Ok(0) => false,
Ok(_) => {
@@ -117,13 +117,17 @@ unsafe fn inner_scanf<R: Read>(
let mut kind = IntKind::Int;
loop {
kind = match c {
b'h' => if kind == IntKind::Short {
b'h' => if kind == IntKind::Short || kind == IntKind::Byte {
IntKind::Byte
} else {
IntKind::Short
},
b'j' => IntKind::IntMax,
b'l' => IntKind::Long,
b'l' => if kind == IntKind::Long || kind == IntKind::LongLong {
IntKind::LongLong
} else {
IntKind::Long
},
b'q' | b'L' => IntKind::LongLong,
b't' => IntKind::PtrDiff,
b'z' => IntKind::Size,
+78 -37
View File
@@ -1,6 +1,6 @@
use alloc::vec::Vec;
use core::{fmt, ptr};
use io::{self, Read};
use io::{self, Read, Write};
pub use self::allocator::*;
@@ -95,68 +95,88 @@ impl Read for FileReader {
}
pub struct StringWriter(pub *mut u8, pub usize);
impl StringWriter {
pub unsafe fn write(&mut self, buf: &[u8]) {
impl Write for StringWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.1 > 1 {
let copy_size = buf.len().min(self.1 - 1);
ptr::copy_nonoverlapping(
buf.as_ptr(),
self.0,
copy_size
);
self.1 -= copy_size;
unsafe {
ptr::copy_nonoverlapping(
buf.as_ptr(),
self.0,
copy_size
);
self.1 -= copy_size;
self.0 = self.0.offset(copy_size as isize);
*self.0 = 0;
self.0 = self.0.offset(copy_size as isize);
*self.0 = 0;
}
Ok(copy_size)
} else {
Ok(0)
}
}
}
impl fmt::Write for StringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe { self.write(s.as_bytes()) };
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl fmt::Write for StringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe {
// can't fail
self.write(s.as_bytes()).unwrap();
};
Ok(())
}
}
impl WriteByte for StringWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
unsafe { self.write(&[byte]) };
unsafe {
// can't fail
self.write(&[byte]).unwrap();
}
Ok(())
}
}
pub struct UnsafeStringWriter(pub *mut u8);
impl UnsafeStringWriter {
pub unsafe fn write(&mut self, buf: &[u8]) {
ptr::copy_nonoverlapping(
buf.as_ptr(),
self.0,
buf.len()
);
*self.0.offset(buf.len() as isize) = b'\0';
self.0 = self.0.offset(buf.len() as isize);
impl Write for UnsafeStringWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe {
ptr::copy_nonoverlapping(
buf.as_ptr(),
self.0,
buf.len()
);
*self.0.offset(buf.len() as isize) = b'\0';
self.0 = self.0.offset(buf.len() as isize);
}
Ok(buf.len())
}
}
impl fmt::Write for UnsafeStringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe { self.write(s.as_bytes()) };
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl fmt::Write for UnsafeStringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe {
// can't fail
self.write(s.as_bytes()).unwrap();
}
Ok(())
}
}
impl WriteByte for UnsafeStringWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
unsafe { self.write(&[byte]) };
unsafe {
// can't fail
self.write(&[byte]).unwrap();
}
Ok(())
}
}
pub struct UnsafeStringReader(pub *const u8);
impl Read for UnsafeStringReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
@@ -197,3 +217,24 @@ impl<T: WriteByte> WriteByte for CountingWriter<T> {
self.inner.write_u8(byte)
}
}
impl<T: Write> Write for CountingWriter<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let res = self.inner.write(buf);
if let Ok(written) = res {
self.written += written;
}
res
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self.inner.write_all(&buf) {
Ok(()) => (),
Err(ref err) if err.kind() == io::ErrorKind::WriteZero => (),
Err(err) => return Err(err)
}
self.written += buf.len();
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
+1 -1
View File
@@ -1 +1 @@
cos(3.14) = -0.9999987483024597
cos(3.140000) = -0.999999
+33 -1
View File
@@ -7,4 +7,36 @@ uint: 32
hex: beef
HEX: C0FFEE
string: end
len of previous write: 94
%n returned 51, total len of write: 94
Padding madness:
001 +2
c
0x00000ff
001
0 0x1
1
(00123) ( 123)
(-0123) ( -123)
( )
0xabcdef
(nil)
Positional madness:
4 3 2
00002
|Fiz |Buz | Fiz| Tot|
Float madness:
1.234568e+02
1.000000E-05
123.456789
0.000010
-1.234568e+02
-00000001.234568e+02
100000
1e+06
1.000000e+06
0.0001
1E-05
1.000000E-05
+9 -9
View File
@@ -1,9 +1,9 @@
2, { sa: 12, ia: 345, ib: 0, ic: 0, fa: 0, da: 0, ptr: 0x0, char: a, string: }
3, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0, da: 0, ptr: 0x0, char: a, string: }
2, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0x0, char: a, string: }
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: a, string: }
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: a, string: Hello }
1, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: a, string: Hello }
2, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: h, string: elllo }
1, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: h, string: elllo }
0, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.10000000149011612, da: 0.2, ptr: 0xabcdef, char: h, string: elllo }
2, { sa: 12, ia: 345, ib: 0, ic: 0, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string: }
3, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string: }
2, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: (nil), char: a, string: }
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string: }
1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string: Hello }
1, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string: Hello }
2, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string: elllo }
1, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string: elllo }
0, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string: elllo }
+1 -1
View File
@@ -1 +1 @@
-3.14
-3.140000
+9 -9
View File
@@ -1,9 +1,9 @@
d: 0 Endptr: "a 1 hello"
d: 1 Endptr: " hello"
d: 1 Endptr: " hello 2"
d: 10.123 Endptr: ""
d: 10.123 Endptr: ""
d: -5.3 Endptr: ""
d: 16.071044921875 Endptr: ""
d: 1.13671875 Endptr: ""
d: 3.12890625 Endptr: ""
d: 0.000000 Endptr: "a 1 hello"
d: 1.000000 Endptr: " hello"
d: 1.000000 Endptr: " hello 2"
d: 10.123000 Endptr: ""
d: 10.123000 Endptr: ""
d: -5.300000 Endptr: ""
d: 16.071045 Endptr: ""
d: 1.136719 Endptr: ""
d: 3.128906 Endptr: ""
+2 -2
View File
@@ -1,5 +1,5 @@
rust
libc we trust
NULL
NULL
(null)
(null)
RUST
+9 -9
View File
@@ -1,20 +1,20 @@
--- Running: test --test0 -a
getopt_long returned 1, argument test0=NULL
Option -a with value NULL
getopt_long returned 1, argument test0=(null)
Option -a with value (null)
--- Running: test --test1 -a
getopt_long returned 0, set flag to 2, argument test1=NULL
Option -a with value NULL
getopt_long returned 0, set flag to 2, argument test1=(null)
Option -a with value (null)
--- Running: test --test2 -a
getopt_long returned 3, argument test2=NULL
Option -a with value NULL
getopt_long returned 3, argument test2=(null)
Option -a with value (null)
--- Running: test --test2=arg -a
getopt_long returned 3, argument test2=arg
Option -a with value NULL
Option -a with value (null)
--- Running: test --test3 -a
getopt_long returned 4, argument test3=-a
--- Running: test --test3=arg -a
getopt_long returned 4, argument test3=arg
Option -a with value NULL
Option -a with value (null)
--- Running: test --test3 arg -a
getopt_long returned 4, argument test3=arg
Option -a with value NULL
Option -a with value (null)
+2 -2
View File
@@ -1,2 +1,2 @@
Processing 11 UTF-8 code units: [ 7a c3 9f e6 b0 b4 f0 9f 8d 8c 0 ]
into 5 wchar_t units: [ 7a df 6c34 1f34c 0 ]
Processing 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ]
into 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ]
+2 -2
View File
@@ -1,2 +1,2 @@
Processing 5 wchar_t units: [ 7a df 6c34 1f34c 0 ]
into 11 UTF-8 code units: [ 7a c3 9f e6 b0 b4 f0 9f 8d 8c 0 ]
Processing 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ]
into 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ]
+37 -2
View File
@@ -1,17 +1,52 @@
#include <stdio.h>
int main(int argc, char ** argv) {
int sofar = 0;
int len = printf(
"percent: %%\nstring: %s\nchar: %c\nchar: %c\nint: %d\nuint: %u\nhex: %x\nHEX: %X\nstring: %s\n",
"percent: %%\nstring: %s\nchar: %c\nchar: %c\nint: %d\n%nuint: %u\nhex: %x\nHEX: %X\nstring: %s\n",
"String",
'c',
254,
-16,
&sofar,
32,
0xbeef,
0xC0FFEE,
"end"
);
printf("len of previous write: %d\n", len);
printf("%%n returned %d, total len of write: %d\n", sofar, len);
puts("\nPadding madness:");
printf("% -5.3d %+3d\n", 1, 2);
printf("%4c\n", 'c');
printf("%#10.7x\n", 0xFF);
printf("%#4.3o\n", 01);
printf("%#x %#x\n", 0, 1);
printf("%.0d %.0d\n", 0, 1);
printf("(%05d) (%5d)\n", 123, 123);
printf("(%05d) (%5d)\n", -123, -123);
printf("(%13.0d)\n", 0);
printf("%p\n", (void*) 0xABCDEF);
printf("%p\n", (void*) 0);
puts("\nPositional madness:");
printf("%3$d %2$d %1$d\n", 2, 3, 4);
printf("%.*3$d\n", 2, 0, 5);
printf("|%-*6$.*5$s|%-*6$.*5$s|%*6$.*5$s|%*6$.*5$s|\n", "Fizz", "Buzz", "FizzBuzz", "TotalBuzz", 3, 8);
puts("\nFloat madness:");
printf("%20e\n", 123.456789123);
printf("%20E\n", 0.00001);
printf("%20f\n", 123.456789123);
printf("%20F\n", 0.00001);
printf("%20e\n", -123.456789123);
printf("%020e\n", -123.456789123);
printf("%g\n", 100000.0);
printf("%g\n", 1000000.0);
printf("%e\n", 1000000.0);
printf("%G\n", 0.0001);
printf("%G\n", 0.00001);
printf("%E\n", 0.00001);
return 0;
}