1 //! Convert C format strings to Rust. 2 3 extern crate libc; 4 5 use libc::size_t; 6 use std::iter::repeat; 7 use std::io::Error; 8 use std::os::raw::*; 9 10 const INITIAL_BUFFER_SIZE: usize = 512; 11 12 /// The result of a vsprintf call. 13 pub type Result<T> = ::std::result::Result<T, Error>; 14 15 /// Prints a format string into a Rust string. vsprintf<V>(format: *const c_char, va_list: *mut V) -> Result<String>16 pub unsafe fn vsprintf<V>(format: *const c_char, 17 va_list: *mut V) -> Result<String> { 18 vsprintf_raw(format, va_list).map(|bytes| { 19 String::from_utf8(bytes).expect("vsprintf result is not valid utf-8") 20 }) 21 } 22 23 /// Prints a format string into a list of raw bytes that form 24 /// a null-terminated C string. vsprintf_raw<V>(format: *const c_char, va_list: *mut V) -> Result<Vec<u8>>25 pub unsafe fn vsprintf_raw<V>(format: *const c_char, 26 va_list: *mut V) -> Result<Vec<u8>> { 27 let list_ptr = va_list as *mut c_void; 28 29 let mut buffer = Vec::new(); 30 buffer.extend([0u8; INITIAL_BUFFER_SIZE].iter().cloned()); 31 32 loop { 33 let character_count = vsnprintf_wrapper( 34 buffer.as_mut_ptr(), buffer.len(), format, list_ptr 35 ); 36 37 // Check for errors. 38 if character_count == -1 { 39 // C does not require vsprintf to set errno, but POSIX does. 40 // 41 // Default handling will just generate an 'unknown' IO error 42 // if no errno is set. 43 return Err(Error::last_os_error()); 44 } else { 45 assert!(character_count >= 0); 46 let character_count = character_count as usize; 47 48 let current_max = buffer.len() - 1; 49 50 // Check if we had enough room in the buffer to fit everything. 51 if character_count > current_max { 52 let extra_space_required = character_count - current_max; 53 54 // Reserve enough space and try again. 55 buffer.extend(repeat(0).take(extra_space_required as usize)); 56 continue; 57 } else { // We fit everything into the buffer. 58 // Truncate the buffer up until the null terminator. 59 buffer = buffer.into_iter() 60 .take_while(|&b| b != 0) 61 .collect(); 62 break; 63 } 64 } 65 } 66 67 Ok(buffer) 68 } 69 70 extern { vsnprintf_wrapper(buffer: *mut u8, size: size_t, format: *const c_char, va_list: *mut c_void) -> libc::c_int71 fn vsnprintf_wrapper(buffer: *mut u8, 72 size: size_t, 73 format: *const c_char, 74 va_list: *mut c_void) -> libc::c_int; 75 } 76