1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Copyright (c) 2021-2022 The Pennsylvania State University and the project contributors.
// SPDX-License-Identifier: MIT OR Apache-2.0

//! An example of how to use the types and traits of this crate to safely
//! wrap functions with `printf(3)`-style format strings and varargs.

use crate::{PrintfArgument, PrintfFmt};
use libc::{c_char, c_int};

macro_rules! tuple_impl {
    ($num:tt, $p_name:ident, $snp_name:ident, ( $($t:ident),* ), ( $($var:ident),* )) => {
        #[doc = concat!("A safe wrapper around [`printf`](libc::printf) for ", stringify!($num), " argument(s).")]
        #[inline]
        pub fn $p_name<$($t),*>(fmt: PrintfFmt<($($t,)*)>, $($var: $t),*) -> c_int
            where $($t: PrintfArgument),* {

            unsafe {
                libc::printf(fmt.as_ptr() $(, $var.as_c_val())* )
            }
        }

        #[doc = concat!("A safe wrapper around [`snprintf`](libc::snprintf) for ", stringify!($num), " argument(s).")]
        #[inline]
        pub fn $snp_name<$($t),*>(dst: &mut [u8], fmt: PrintfFmt<($($t,)*)>, $($var: $t),*) -> c_int
            where $($t: PrintfArgument),* {

            unsafe {
                libc::snprintf(
                    dst.as_mut_ptr() as *mut c_char,
                    dst.len(),
                    fmt.as_ptr()
                    $(, $var.as_c_val())*
                )
            }
        }
    };
}

tuple_impl!(0, printf0, snprintf0, (), ());
tuple_impl!(1, printf1, snprintf1, (A), (a));
tuple_impl!(2, printf2, snprintf2, (A, B), (a, b));
tuple_impl!(3, printf3, snprintf3, (A, B, C), (a, b, c));
tuple_impl!(4, printf4, snprintf4, (A, B, C, D), (a, b, c, d));
tuple_impl!(5, printf5, snprintf5, (A, B, C, D, E), (a, b, c, d, e));
tuple_impl!(6, printf6, snprintf6, (A, B, C, D, E, F), (a, b, c, d, e, f));
tuple_impl!(7, printf7, snprintf7, (A, B, C, D, E, F, G), (a, b, c, d, e, f, g));
tuple_impl!(8, printf8, snprintf8, (A, B, C, D, E, F, G, H), (a, b, c, d, e, f, g, h));

#[cfg(test)]
mod tests {
    use super::*;
    use crate::PrintfFmt;

    #[test]
    fn snprintf_test_invocation() {
        let mut x: [u8; 12] = [5u8; 12];

        assert_eq!(
            snprintf2(&mut x[..], PrintfFmt::new("X %u Y %c\0").unwrap(), 15u32, b'Z'),
            8,
            "snprintf2 return value should be 8"
        );
        assert_eq!(&x, b"X 15 Y Z\0\x05\x05\x05", "contents of x");
    }

    #[test]
    fn snprintf_no_buffer_overflow() {
        let mut x: [u8; 8] = [5u8; 8];
        assert_eq!(
            snprintf1(&mut x[..4], PrintfFmt::new("a%d \0").unwrap(), -100),
            6,
            "snprintf1 return value should be 6"
        );
        assert_eq!(&x[4..], [5u8; 4], "only 4 bytes should have been written by snprintf1");
    }
}