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