vsprintf/
lib.rs

1//! Convert C format strings to Rust.
2
3extern crate libc;
4
5use libc::size_t;
6use std::io::Error;
7use std::os::raw::*;
8
9/// The result of a vsprintf call.
10pub type Result<T> = ::std::result::Result<T, Error>;
11
12/// Prints a format string into a Rust string.
13pub unsafe fn vsprintf<V>(format: *const c_char, va_list: *mut V) -> Result<String> {
14    vsprintf_raw(format, va_list)
15        .map(|bytes| String::from_utf8(bytes).expect("vsprintf result is not valid utf-8"))
16}
17
18/// Prints a format string into a list of raw bytes that form
19/// a null-terminated C string.
20pub unsafe fn vsprintf_raw<V>(format: *const c_char, va_list: *mut V) -> Result<Vec<u8>> {
21    let list_ptr = va_list as *mut c_void;
22
23    let mut buffer: Vec<u8> = Vec::new();
24    loop {
25        let rv = vsnprintf_wrapper(buffer.as_mut_ptr(), buffer.len(), format, list_ptr);
26
27        // Check for errors.
28        let character_count = if rv < 0 {
29            // C does not require vsprintf to set errno, but POSIX does.
30            //
31            // Default handling will just generate an 'unknown' IO error
32            // if no errno is set.
33            return Err(Error::last_os_error());
34        } else {
35            rv as usize
36        };
37
38        if character_count >= buffer.len() {
39            let new_len = character_count + 1;
40            buffer.reserve_exact(new_len - buffer.len());
41            // SAFETY: Any bit pattern is a valid u8, and we reserved the space.
42            buffer.set_len(new_len);
43            continue;
44        }
45
46        // Drop NULL byte and any excess capacity.
47        buffer.truncate(character_count);
48        break;
49    }
50
51    Ok(buffer)
52}
53
54extern "C" {
55    fn vsnprintf_wrapper(
56        buffer: *mut u8,
57        size: size_t,
58        format: *const c_char,
59        va_list: *mut c_void,
60    ) -> libc::c_int;
61}