Skip to main content

relibc/header/stdio/
printf.rs

1// TODO: reuse more code with the wide printf impl
2use crate::{
3    c_str::{self, CStr, NulStr},
4    io::{self, Write},
5};
6use alloc::{
7    collections::BTreeMap,
8    string::{String, ToString},
9    vec::Vec,
10};
11use core::{cmp, ffi::VaList, fmt, num::FpCategory, ops::Range, slice};
12
13use crate::{
14    header::errno::{self, EILSEQ},
15    platform::{
16        self,
17        types::{
18            c_char, c_double, c_int, c_long, c_longdouble, c_longlong, c_short, c_uchar, c_uint,
19            c_ulong, c_ulonglong, c_ushort, c_void, intmax_t, ptrdiff_t, size_t, ssize_t,
20            uintmax_t, wchar_t, wint_t,
21        },
22    },
23};
24
25#[allow(unused_doc_comments)]
26/// cbindgen:ignore
27unsafe extern "C" {
28    pub unsafe fn relibc_ldtod(x: *const c_longdouble) -> c_double;
29    pub unsafe fn relibc_dtold(x: c_double, out: *mut c_longdouble);
30}
31
32//  ____        _ _                 _       _
33// | __ )  ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _
34// |  _ \ / _ \| | |/ _ \ '__| '_ \| |/ _` | __/ _ (_)
35// | |_) | (_) | | |  __/ |  | |_) | | (_| | ||  __/_
36// |____/ \___/|_|_|\___|_|  | .__/|_|\__,_|\__\___(_)
37//                           |_|
38
39#[derive(Clone, Copy, PartialEq, Eq, Debug)]
40pub(crate) enum IntKind {
41    Byte,
42    Short,
43    Int,
44    Long,
45    LongLong,
46    IntMax,
47    PtrDiff,
48    Size,
49}
50#[derive(Clone, Copy, PartialEq, Eq, Debug)]
51pub(crate) enum FmtKind {
52    Percent,
53
54    Signed,
55    Unsigned,
56
57    Scientific,
58    Decimal,
59    AnyNotation,
60
61    String,
62    Char,
63    Pointer,
64    GetWritten,
65}
66#[derive(Clone, Copy, Debug)]
67pub(crate) enum Number {
68    Static(usize),
69    Index(usize),
70    Next,
71}
72impl Number {
73    pub(crate) unsafe fn resolve(self, varargs: &mut VaListCache, ap: &mut VaList) -> usize {
74        let arg = match self {
75            Number::Static(num) => return num,
76            Number::Index(i) => unsafe { varargs.get(i - 1, ap, None) },
77            Number::Next => {
78                let i = varargs.i;
79                varargs.i += 1;
80                unsafe { varargs.get(i, ap, None) }
81            }
82        };
83        match arg {
84            VaArg::c_char(i) => i as usize,
85            VaArg::c_double(i) => i as usize,
86            #[cfg(target_pointer_width = "32")]
87            VaArg::c_longdouble(_) => 0 as usize,
88            #[cfg(target_pointer_width = "64")]
89            VaArg::c_longdouble(i) => i as usize,
90            VaArg::c_int(i) => i as usize,
91            VaArg::c_long(i) => i as usize,
92            VaArg::c_longlong(i) => i as usize,
93            VaArg::c_short(i) => i as usize,
94            VaArg::intmax_t(i) => i as usize,
95            VaArg::pointer(i) => i as usize,
96            VaArg::ptrdiff_t(i) => i as usize,
97            VaArg::ssize_t(i) => i as usize,
98            VaArg::wint_t(i) => i as usize,
99        }
100    }
101}
102#[derive(Clone, Copy, Debug)]
103pub(crate) enum VaArg {
104    c_char(c_char),
105    c_double(c_double),
106    c_longdouble(c_longdouble),
107    c_int(c_int),
108    c_long(c_long),
109    c_longlong(c_longlong),
110    c_short(c_short),
111    intmax_t(intmax_t),
112    pointer(*const c_void),
113    ptrdiff_t(ptrdiff_t),
114    ssize_t(ssize_t),
115    wint_t(wint_t),
116}
117impl VaArg {
118    pub(crate) unsafe fn arg_from(fmtkind: FmtKind, intkind: IntKind, ap: &mut VaList) -> VaArg {
119        // Per the C standard using va_arg with a type with a size
120        // less than that of an int for integers and double for floats
121        // is invalid. As a result any arguments smaller than an int or
122        // double passed to a function will be promoted to the smallest
123        // possible size. The VaList::arg function will handle this
124        // automagically.
125
126        match (fmtkind, intkind) {
127            (FmtKind::Percent, _) => panic!("Can't call arg_from on %"),
128
129            (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => {
130                VaArg::wint_t(unsafe { ap.next_arg::<wint_t>() })
131            }
132
133            (FmtKind::Char, _)
134            | (FmtKind::Unsigned, IntKind::Byte)
135            | (FmtKind::Signed, IntKind::Byte) => {
136                // c_int is passed but truncated to c_char
137                VaArg::c_char(unsafe { ap.next_arg::<c_int>() } as c_char)
138            }
139            (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => {
140                // c_int is passed but truncated to c_short
141                VaArg::c_short(unsafe { ap.next_arg::<c_int>() } as c_short)
142            }
143            (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => {
144                VaArg::c_int(unsafe { ap.next_arg::<c_int>() })
145            }
146            (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => {
147                VaArg::c_long(unsafe { ap.next_arg::<c_long>() })
148            }
149            (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => {
150                VaArg::c_longlong(unsafe { ap.next_arg::<c_longlong>() })
151            }
152            (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => {
153                VaArg::intmax_t(unsafe { ap.next_arg::<intmax_t>() })
154            }
155            (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => {
156                VaArg::ptrdiff_t(unsafe { ap.next_arg::<ptrdiff_t>() })
157            }
158            (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => {
159                VaArg::ssize_t(unsafe { ap.next_arg::<ssize_t>() })
160            }
161
162            (FmtKind::AnyNotation, IntKind::LongLong)
163            | (FmtKind::Decimal, IntKind::LongLong)
164            | (FmtKind::Scientific, IntKind::LongLong) => {
165                VaArg::c_longdouble(unsafe { VaArg::extract_longdouble(ap) })
166            }
167            (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
168                VaArg::c_double(unsafe { ap.next_arg::<c_double>() })
169            }
170
171            (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => {
172                VaArg::pointer(unsafe { ap.next_arg::<*const c_void>() })
173            }
174        }
175    }
176    #[cfg(target_arch = "x86")]
177    unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
178        todo_skip!(0, "long double in variadic printf is not supported");
179        [0, 0, 0]
180    }
181    #[cfg(target_arch = "x86_64")]
182    unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
183        // https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf (long double)
184
185        // exactly same as core::ffi::VaListImpl but all variables exposed
186        #[repr(C)]
187        struct VaListInner {
188            gp_offset: i32,
189            fp_offset: i32,
190            overflow_arg_area: *const c_void,
191            reg_save_area: *const c_void,
192        }
193
194        let ap_impl = unsafe {
195            let ptr_to_struct = core::ptr::from_mut::<core::ffi::VaList>(ap).cast::<VaListInner>();
196            &mut *ptr_to_struct
197        };
198
199        let ptr = ap_impl.overflow_arg_area.cast::<c_longdouble>();
200        let val = unsafe { ptr.read() };
201
202        ap_impl.overflow_arg_area = unsafe { ap_impl.overflow_arg_area.add(16) };
203
204        val
205    }
206    #[cfg(target_arch = "aarch64")]
207    unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
208        // https://c9x.me/compile/bib/abi-arm64.pdf (quad precision)
209
210        // exactly same as core::ffi::VaListImpl but all variables exposed
211        #[repr(C)]
212        struct VaListInner {
213            stack: *const c_void,
214            gr_top: *const c_void,
215            vr_top: *const c_void,
216            gr_offs: i32,
217            vr_offs: i32,
218        }
219
220        let ap_impl: &mut VaListInner = unsafe {
221            let ptr_to_struct = ap as *mut core::ffi::VaList as *mut VaListInner;
222            &mut *ptr_to_struct
223        };
224
225        let ptr = unsafe { ap_impl.vr_top.offset(ap_impl.vr_offs as isize) as *const c_longdouble };
226
227        ap_impl.vr_offs += 16;
228
229        unsafe { ptr.read() }
230    }
231
232    #[cfg(target_arch = "riscv64")]
233    unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
234        todo_skip!(0, "long double in variadic printf is not supported");
235        0u128
236    }
237    unsafe fn transmute(&self, fmtkind: FmtKind, intkind: IntKind) -> VaArg {
238        // At this point, there are conflicting printf arguments. An
239        // example of this is:
240        // ```c
241        // printf("%1$d %1$lf\n", 5, 0.1);
242        // ```
243        // We handle it just like glibc: We read it from the VaList
244        // using the *last* argument type, but we transmute it when we
245        // try to access the other ones.
246        union Untyped {
247            c_char: c_char,
248            c_double: c_double,
249            c_longdouble: c_longdouble,
250            c_int: c_int,
251            c_long: c_long,
252            c_longlong: c_longlong,
253            c_short: c_short,
254            intmax_t: intmax_t,
255            pointer: *const c_void,
256            ptrdiff_t: ptrdiff_t,
257            ssize_t: ssize_t,
258            wint_t: wint_t,
259        }
260        let untyped = match *self {
261            VaArg::c_char(i) => Untyped { c_char: i },
262            VaArg::c_double(i) => Untyped { c_double: i },
263            VaArg::c_longdouble(i) => Untyped { c_longdouble: i },
264            VaArg::c_int(i) => Untyped { c_int: i },
265            VaArg::c_long(i) => Untyped { c_long: i },
266            VaArg::c_longlong(i) => Untyped { c_longlong: i },
267            VaArg::c_short(i) => Untyped { c_short: i },
268            VaArg::intmax_t(i) => Untyped { intmax_t: i },
269            VaArg::pointer(i) => Untyped { pointer: i },
270            VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i },
271            VaArg::ssize_t(i) => Untyped { ssize_t: i },
272            VaArg::wint_t(i) => Untyped { wint_t: i },
273        };
274        match (fmtkind, intkind) {
275            (FmtKind::Percent, _) => panic!("Can't call transmute on %"),
276
277            (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => {
278                VaArg::wint_t(unsafe { untyped.wint_t })
279            }
280
281            (FmtKind::Char, _)
282            | (FmtKind::Unsigned, IntKind::Byte)
283            | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(unsafe { untyped.c_char }),
284            (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => {
285                VaArg::c_short(unsafe { untyped.c_short })
286            }
287            (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => {
288                VaArg::c_int(unsafe { untyped.c_int })
289            }
290            (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => {
291                VaArg::c_long(unsafe { untyped.c_long })
292            }
293            (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => {
294                VaArg::c_longlong(unsafe { untyped.c_longlong })
295            }
296            (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => {
297                VaArg::intmax_t(unsafe { untyped.intmax_t })
298            }
299            (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => {
300                VaArg::ptrdiff_t(unsafe { untyped.ptrdiff_t })
301            }
302            (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => {
303                VaArg::ssize_t(unsafe { untyped.ssize_t })
304            }
305
306            (FmtKind::AnyNotation, IntKind::LongLong)
307            | (FmtKind::Decimal, IntKind::LongLong)
308            | (FmtKind::Scientific, IntKind::LongLong) => {
309                VaArg::c_longdouble(unsafe { untyped.c_longdouble })
310            }
311            (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
312                VaArg::c_double(unsafe { untyped.c_double })
313            }
314
315            (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => {
316                VaArg::pointer(unsafe { untyped.pointer })
317            }
318        }
319    }
320}
321#[derive(Default)]
322pub(crate) struct VaListCache {
323    pub(crate) args: Vec<VaArg>,
324    pub(crate) i: usize,
325}
326impl VaListCache {
327    pub(crate) unsafe fn get(
328        &mut self,
329        i: usize,
330        ap: &mut VaList,
331        default: Option<(FmtKind, IntKind)>,
332    ) -> VaArg {
333        if let Some(&arg) = self.args.get(i) {
334            // This value is already cached
335            let mut arg = arg;
336            if let Some((fmtkind, intkind)) = default {
337                // ...but as a different type
338                arg = unsafe { arg.transmute(fmtkind, intkind) };
339            }
340            return arg;
341        }
342
343        // Get all values before this value
344        while self.args.len() < i {
345            // We can't POSSIBLY know the type if we reach this
346            // point. Reaching here means there are unused gaps in the
347            // arguments. Ultimately we'll have to settle down with
348            // defaulting to c_int.
349            self.args
350                .push(VaArg::c_int(unsafe { ap.next_arg::<c_int>() }))
351        }
352
353        // Add the value to the cache
354        self.args.push(match default {
355            Some((fmtkind, intkind)) => unsafe { VaArg::arg_from(fmtkind, intkind, ap) },
356            None => VaArg::c_int(unsafe { ap.next_arg::<c_int>() }),
357        });
358
359        // Return the value
360        self.args[i]
361    }
362}
363
364//  ___                 _                           _        _   _
365// |_ _|_ __ ___  _ __ | | ___ _ __ ___   ___ _ __ | |_ __ _| |_(_) ___  _ __  _
366//  | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \(_)
367//  | || | | | | | |_) | |  __/ | | | | |  __/ | | | || (_| | |_| | (_) | | | |_
368// |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_)
369//               |_|
370
371enum FmtCase {
372    Lower,
373    Upper,
374}
375
376// The spelled-out "infinity"/"INFINITY" is also permitted by the standard
377static INF_STR_LOWER: &str = "inf";
378static INF_STR_UPPER: &str = "INF";
379
380static NAN_STR_LOWER: &str = "nan";
381static NAN_STR_UPPER: &str = "NAN";
382
383fn pop_int_raw<T: c_str::Kind>(format: &mut NulStr<T>) -> Option<usize> {
384    let mut int = None;
385    while let Some((digit, rest)) = format
386        .split_first_char()
387        .and_then(|(d, r)| Some((d.to_digit(10)?, r)))
388    {
389        *format = rest;
390        if int.is_none() {
391            int = Some(0);
392        }
393        *int.as_mut().unwrap() *= 10;
394        *int.as_mut().unwrap() += digit as usize;
395    }
396    int
397}
398fn pop_index<T: c_str::Kind>(format: &mut NulStr<T>) -> Option<usize> {
399    // Peek ahead for a positional argument:
400    let mut format2 = *format;
401    if let Some(i) = pop_int_raw(&mut format2)
402        && let Some(('$', format2)) = format2.split_first_char()
403    {
404        *format = format2;
405        return Some(i);
406    }
407    None
408}
409fn pop_int<T: c_str::Kind>(format: &mut NulStr<T>) -> Option<Number> {
410    if let Some(('*', rest)) = format.split_first_char() {
411        *format = rest;
412        Some(pop_index(format).map(Number::Index).unwrap_or(Number::Next))
413    } else {
414        pop_int_raw(format).map(Number::Static)
415    }
416}
417
418fn fmt_int<I, T: c_str::Kind>(fmt: char, i: I) -> String
419where
420    I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex + fmt::Binary,
421{
422    match fmt {
423        'o' => format!("{:o}", i),
424        'u' => i.to_string(),
425        'x' => format!("{:x}", i),
426        'X' => format!("{:X}", i),
427        'b' | 'B' if T::IS_THIN_NOT_WIDE => format!("{:b}", i),
428        _ => panic!("fmt_int should never be called with the fmt {:?}", fmt,),
429    }
430}
431
432fn pad<W: Write>(
433    w: &mut W,
434    current_side: bool,
435    pad_char: u8,
436    range: Range<usize>,
437) -> io::Result<()> {
438    if current_side {
439        for _ in range {
440            w.write_all(&[pad_char])?;
441        }
442    }
443    Ok(())
444}
445
446fn float_string(float: c_double, precision: usize, trim: bool, alternate: bool) -> String {
447    // The Rust format! macro doesn't keep the dot on precision = 0 and alternate = true,
448    // so we have to perform a fix-up
449    //
450    // POSIX.1-2024 says "... if the precision is zero and no '#' flag is present,
451    // no radix character shall appear."
452    //
453    // This case is covered here.
454    let mut string = format!("{:.p$}", float, p = precision);
455    //
456    // Additionally, it says "For a, A, e, E, f, F, g, and G conversion specifiers,
457    // the result shall always contain a radix character, even if no digits follow
458    // the radix character."
459    //
460    if alternate && precision == 0 {
461        string.push('.');
462    } else if trim && string.contains('.') {
463        let truncate = {
464            let slice = string.trim_end_matches('0');
465            let mut truncate = slice.len();
466            if slice.ends_with('.') {
467                truncate -= 1;
468            }
469            truncate
470        };
471        string.truncate(truncate);
472    }
473    string
474}
475
476fn float_exp(mut float: c_double) -> (c_double, isize) {
477    let mut exp: isize = 0;
478    while float.abs() >= 10.0 {
479        float /= 10.0;
480        exp += 1;
481    }
482    while f64::EPSILON < float.abs() && float.abs() < 1.0 {
483        float *= 10.0;
484        exp -= 1;
485    }
486    (float, exp)
487}
488
489#[expect(clippy::too_many_arguments)]
490fn fmt_float_exp<W: Write>(
491    w: &mut W,
492    exp_fmt: char,
493    trim: bool,
494    alternate: bool,
495    precision: usize,
496    float: c_double,
497    exp: isize,
498    left: bool,
499    pad_space: usize,
500    pad_zero: usize,
501) -> io::Result<()> {
502    let mut exp2 = exp;
503    let mut exp_len = 1;
504    while exp2 >= 10 {
505        exp2 /= 10;
506        exp_len += 1;
507    }
508
509    let string = float_string(float, precision, trim, alternate);
510    let len = string.len() + 2 + 2.max(exp_len);
511
512    pad(w, !left, b' ', len..pad_space)?;
513    let bytes = if string.starts_with('-') {
514        w.write_all(b"-")?;
515        &string.as_bytes()[1..]
516    } else {
517        string.as_bytes()
518    };
519    pad(w, !left, b'0', len..pad_zero)?;
520    w.write_all(bytes)?;
521    write!(w, "{}{:+03}", exp_fmt, exp)?;
522    pad(w, left, b' ', len..pad_space)?;
523
524    Ok(())
525}
526
527#[expect(clippy::too_many_arguments)]
528fn fmt_float_normal<W: Write>(
529    w: &mut W,
530    trim: bool,
531    alternate: bool,
532    precision: usize,
533    float: c_double,
534    left: bool,
535    pad_space: usize,
536    pad_zero: usize,
537) -> io::Result<usize> {
538    let string = float_string(float, precision, trim, alternate);
539
540    pad(w, !left, b' ', string.len()..pad_space)?;
541    let bytes = if string.starts_with('-') {
542        w.write_all(b"-")?;
543        &string.as_bytes()[1..]
544    } else {
545        string.as_bytes()
546    };
547    pad(w, true, b'0', string.len()..pad_zero)?;
548    w.write_all(bytes)?;
549    pad(w, left, b' ', string.len()..pad_space)?;
550
551    Ok(string.len())
552}
553
554/// Write ±infinity or ±NaN representation for any floating-point style
555fn fmt_float_nonfinite<W: Write>(
556    w: &mut W,
557    float: c_double,
558    case: FmtCase,
559    left: bool,
560    pad_space: usize,
561    pad_zero: usize,
562) -> io::Result<()> {
563    let string = match float.classify() {
564        FpCategory::Infinite => match case {
565            FmtCase::Lower => INF_STR_LOWER,
566            FmtCase::Upper => INF_STR_UPPER,
567        },
568        FpCategory::Nan => match case {
569            FmtCase::Lower => NAN_STR_LOWER,
570            FmtCase::Upper => NAN_STR_UPPER,
571        },
572        _ => {
573            // This function should only be called with infinite or NaN value.
574            panic!("fmt_float_nonfinite called with finite float")
575        }
576    };
577
578    // Infinity is always padded with spaces, rather than zeroes
579    pad(w, !left, b' ', string.len()..pad_space + pad_zero)?;
580    if float.is_sign_negative() {
581        w.write_all(b"-")?;
582    }
583    w.write_all(string.as_bytes())?;
584    pad(w, left, b' ', string.len()..pad_space + pad_zero)?;
585
586    Ok(())
587}
588
589#[derive(Clone, Copy)]
590pub(crate) struct PrintfIter<'a, T: c_str::Kind> {
591    pub(crate) format: NulStr<'a, T>,
592}
593#[derive(Clone, Copy, Debug)]
594pub(crate) struct PrintfArg {
595    pub(crate) index: Option<usize>,
596    pub(crate) alternate: bool,
597    pub(crate) zero: bool,
598    pub(crate) left: bool,
599    pub(crate) sign_reserve: bool,
600    pub(crate) sign_always: bool,
601    pub(crate) min_width: Number,
602    pub(crate) precision: Option<Number>,
603    pub(crate) intkind: IntKind,
604    pub(crate) fmt: char,
605    pub(crate) fmtkind: FmtKind,
606}
607#[derive(Debug)]
608pub(crate) enum PrintfFmt<'a, U> {
609    Plain(&'a [U]),
610    Arg(PrintfArg),
611}
612impl<'a, T: c_str::Kind> Iterator for PrintfIter<'a, T> {
613    type Item = Result<PrintfFmt<'a, T::Char>, ()>;
614
615    fn next(&mut self) -> Option<Self::Item> {
616        // Send PrintfFmt::Plain until the next %
617        let first_percent = match self.format.find_get_subslice_or_all(b'%') {
618            Err(([], _)) => return None,
619            Ok((chunk @ [_, ..], rest)) | Err((chunk @ [_, ..], rest)) => {
620                self.format = rest;
621                return Some(Ok(PrintfFmt::Plain(chunk)));
622            }
623            Ok(([], rest)) => rest,
624        };
625
626        // at this point the next char must be %
627        self.format = first_percent.split_first().expect("must be %").1;
628
629        let mut peekahead = self.format;
630        let index = pop_index(&mut peekahead).inspect(|i| {
631            self.format = peekahead;
632        });
633
634        // Flags:
635        let mut alternate = false;
636        let mut zero = false;
637        let mut left = false;
638        let mut sign_reserve = false;
639        let mut sign_always = false;
640
641        while let Some((c, rest)) = self.format.split_first_char() {
642            match c {
643                '#' => alternate = true,
644                '0' => zero = true,
645                '-' => left = true,
646                ' ' => sign_reserve = true,
647                '+' => sign_always = true,
648                _ => break,
649            }
650            self.format = rest;
651        }
652
653        // Width and precision:
654        let min_width = pop_int(&mut self.format).unwrap_or(Number::Static(0));
655        let precision = if let Some(('.', rest)) = self.format.split_first_char() {
656            self.format = rest;
657            match pop_int(&mut self.format) {
658                int @ Some(_) => int,
659                None => return Some(Err(())),
660            }
661        } else {
662            None
663        };
664
665        // Integer size:
666        let mut intkind = IntKind::Int;
667        while let Some((byte, rest)) = self.format.split_first_char() {
668            intkind = match byte {
669                'h' => {
670                    if intkind == IntKind::Short || intkind == IntKind::Byte {
671                        IntKind::Byte
672                    } else {
673                        IntKind::Short
674                    }
675                }
676                'j' => IntKind::IntMax,
677                'l' => {
678                    if intkind == IntKind::Long || intkind == IntKind::LongLong {
679                        IntKind::LongLong
680                    } else {
681                        IntKind::Long
682                    }
683                }
684                'q' | 'L' => IntKind::LongLong,
685                't' => IntKind::PtrDiff,
686                'z' => IntKind::Size,
687                _ => break,
688            };
689
690            self.format = rest;
691        }
692        let Some((fmt, rest)) = self.format.split_first_char() else {
693            return Some(Err(()));
694        };
695        self.format = rest;
696        let fmtkind = match fmt {
697            '%' => FmtKind::Percent,
698            'd' | 'i' => FmtKind::Signed,
699            'o' | 'u' | 'x' | 'X' => FmtKind::Unsigned,
700            'b' | 'B' if T::IS_THIN_NOT_WIDE => FmtKind::Unsigned,
701            'e' | 'E' => FmtKind::Scientific,
702            'f' | 'F' | 'L' => FmtKind::Decimal,
703            'g' | 'G' => FmtKind::AnyNotation,
704            's' => FmtKind::String,
705            'c' => FmtKind::Char,
706            'p' => FmtKind::Pointer,
707            'n' => FmtKind::GetWritten,
708            'm' if T::IS_THIN_NOT_WIDE => {
709                // %m is technically for syslog only, but musl and glibc implement it for
710                // printf because it is difficult and error prone to implement a format
711                // specifier for just *one* function.
712                return Some(Ok(PrintfFmt::Plain(
713                    T::chars_from_bytes(
714                        errno::STR_ERROR
715                            .get(platform::ERRNO.get() as usize)
716                            .map(|e| e.as_bytes())
717                            .unwrap_or(b"unknown error"),
718                    )
719                    .expect("string must be thin"),
720                )));
721            }
722            _ => return Some(Err(())),
723        };
724        // "For b, B, d, i, o, u, x, and X conversions,
725        // if a precision is specified, the 0 flag is ignored."
726        match fmt {
727            'b' | 'B' | 'd' | 'i' | 'o' | 'u' | 'x' | 'X' if precision.is_some() => {
728                zero = false;
729            }
730            _ => (),
731        }
732
733        Some(Ok(PrintfFmt::Arg(PrintfArg {
734            index,
735            alternate,
736            zero,
737            left,
738            sign_reserve,
739            sign_always,
740            min_width,
741            precision,
742            intkind,
743            fmt,
744            fmtkind,
745        })))
746    }
747}
748
749pub(crate) unsafe fn inner_printf<T: c_str::Kind>(
750    w: impl Write,
751    format: NulStr<T>,
752    mut ap: VaList,
753) -> io::Result<c_int> {
754    let w = &mut platform::CountingWriter::new(w);
755
756    let iterator = PrintfIter { format };
757
758    // Pre-fetch vararg types
759    let mut varargs = VaListCache::default();
760    let mut positional = BTreeMap::new();
761    // ^ NOTE: This depends on the sorted order, do not change to HashMap or whatever
762
763    for section in iterator {
764        let arg = match section {
765            Ok(PrintfFmt::Plain(text)) => continue,
766            Ok(PrintfFmt::Arg(arg)) => arg,
767            Err(()) => return Ok(-1),
768        };
769        if arg.fmtkind == FmtKind::Percent {
770            continue;
771        }
772        for num in &[arg.min_width, arg.precision.unwrap_or(Number::Static(0))] {
773            match num {
774                Number::Next => varargs
775                    .args
776                    .push(VaArg::c_int(unsafe { ap.next_arg::<c_int>() })),
777                Number::Index(i) => {
778                    positional.insert(i - 1, (FmtKind::Signed, IntKind::Int));
779                }
780                Number::Static(_) => (),
781            }
782        }
783        match arg.index {
784            Some(i) => {
785                positional.insert(i - 1, (arg.fmtkind, arg.intkind));
786            }
787            None => varargs
788                .args
789                .push(unsafe { VaArg::arg_from(arg.fmtkind, arg.intkind, &mut ap) }),
790        }
791    }
792
793    // Make sure, in order, the positional arguments exist with the specified type
794    for (i, arg) in positional {
795        unsafe { varargs.get(i, &mut ap, Some(arg)) };
796    }
797
798    // Main loop
799    for section in iterator {
800        let arg = match section {
801            Ok(PrintfFmt::Plain(text)) => {
802                if T::IS_THIN_NOT_WIDE {
803                    let bytes = T::chars_to_bytes(text).expect("is thin");
804                    w.write_all(bytes)?;
805                } else {
806                    // TODO: wcsrtombs wrapper
807                    for c in text.iter().filter_map(|u| char::from_u32((*u).into())) {
808                        if let Ok(()) = write!(w, "{}", c) {}; // TODO handle error
809                    }
810                }
811                continue;
812            }
813            Ok(PrintfFmt::Arg(arg)) => arg,
814            Err(()) => return Ok(-1),
815        };
816        let alternate = arg.alternate;
817        let zero = arg.zero;
818        let mut left = arg.left;
819        let sign_reserve = arg.sign_reserve;
820        let sign_always = arg.sign_always;
821        let min_width = unsafe { arg.min_width.resolve(&mut varargs, &mut ap) };
822        let precision = arg
823            .precision
824            .map(|n| unsafe { n.resolve(&mut varargs, &mut ap) })
825            .filter(|&n| (n as c_int) >= 0);
826        let pad_zero = if zero { min_width } else { 0 };
827        let signed_space = match pad_zero {
828            0 => min_width as isize,
829            _ => 0,
830        };
831        let pad_space = if signed_space < 0 {
832            left = true;
833            -signed_space as usize
834        } else {
835            signed_space as usize
836        };
837        let intkind = arg.intkind;
838        let fmt = arg.fmt;
839        let fmtkind = arg.fmtkind;
840        let fmtcase = match fmt {
841            'b' if T::IS_THIN_NOT_WIDE => Some(FmtCase::Lower),
842            'B' if T::IS_THIN_NOT_WIDE => Some(FmtCase::Upper),
843            'x' | 'f' | 'e' | 'g' => Some(FmtCase::Lower),
844            'X' | 'F' | 'E' | 'G' => Some(FmtCase::Upper),
845            _ => None,
846        };
847
848        let index = arg.index.map(|i| i - 1).unwrap_or_else(|| {
849            if fmtkind == FmtKind::Percent {
850                0
851            } else {
852                let i = varargs.i;
853                varargs.i += 1;
854                i
855            }
856        });
857
858        match fmtkind {
859            FmtKind::Percent => w.write_all(b"%")?,
860            FmtKind::Signed => {
861                let string = match unsafe {
862                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
863                } {
864                    VaArg::c_char(i) => i.to_string(),
865                    VaArg::c_double(i) => panic!("this should not be possible"),
866                    VaArg::c_longdouble(i) => panic!("this should not be possible"),
867                    VaArg::c_int(i) => i.to_string(),
868                    VaArg::c_long(i) => i.to_string(),
869                    VaArg::c_longlong(i) => i.to_string(),
870                    VaArg::c_short(i) => i.to_string(),
871                    VaArg::intmax_t(i) => i.to_string(),
872                    VaArg::pointer(i) => (i as usize).to_string(),
873                    VaArg::ptrdiff_t(i) => i.to_string(),
874                    VaArg::ssize_t(i) => i.to_string(),
875                    VaArg::wint_t(_) => unreachable!("this should not be possible"),
876                };
877                let positive = !string.starts_with('-');
878                let zero = precision == Some(0) && string == "0";
879
880                let mut len = string.len();
881                let mut final_len = string.len().max(precision.unwrap_or(0));
882                if positive && (sign_reserve || sign_always) {
883                    final_len += 1;
884                }
885                if zero {
886                    len = 0;
887                    final_len = 0;
888                }
889
890                pad(w, !left, b' ', final_len..pad_space)?;
891
892                let bytes = if positive {
893                    if sign_reserve {
894                        w.write_all(b" ")?;
895                    } else if sign_always {
896                        w.write_all(b"+")?;
897                    }
898                    string.as_bytes()
899                } else {
900                    w.write_all(b"-")?;
901                    &string.as_bytes()[1..]
902                };
903                pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?;
904
905                if !zero {
906                    w.write_all(bytes)?;
907                }
908
909                pad(w, left, b' ', final_len..pad_space)?;
910            }
911            FmtKind::Unsigned => {
912                let string = match unsafe {
913                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
914                } {
915                    VaArg::c_char(i) => fmt_int::<_, T>(fmt, i as c_uchar),
916                    VaArg::c_double(i) => panic!("this should not be possible"),
917                    VaArg::c_longdouble(i) => panic!("this should not be possible"),
918                    VaArg::c_int(i) => fmt_int::<_, T>(fmt, i as c_uint),
919                    VaArg::c_long(i) => fmt_int::<_, T>(fmt, i as c_ulong),
920                    VaArg::c_longlong(i) => fmt_int::<_, T>(fmt, i as c_ulonglong),
921                    VaArg::c_short(i) => fmt_int::<_, T>(fmt, i as c_ushort),
922                    VaArg::intmax_t(i) => fmt_int::<_, T>(fmt, i as uintmax_t),
923                    VaArg::pointer(i) => fmt_int::<_, T>(fmt, i as usize),
924                    VaArg::ptrdiff_t(i) => fmt_int::<_, T>(fmt, i as size_t),
925                    VaArg::ssize_t(i) => fmt_int::<_, T>(fmt, i as size_t),
926                    VaArg::wint_t(_) => unreachable!("this should not be possible"),
927                };
928                let zero = precision == Some(0) && string == "0";
929
930                // If this int is padded out to be larger than it is, don't
931                // add an extra zero if octal.
932                let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true);
933
934                let len;
935                let final_len = if zero {
936                    len = 0;
937                    0
938                } else {
939                    len = string.len();
940                    len.max(precision.unwrap_or(0))
941                        + if alternate && string != "0" {
942                            match fmt {
943                                'o' if no_precision => 1,
944                                'x' | 'X' => 2,
945                                'b' | 'B' if T::IS_THIN_NOT_WIDE => 2,
946                                _ => 0,
947                            }
948                        } else {
949                            0
950                        }
951                };
952
953                pad(w, !left, b' ', final_len..pad_space)?;
954
955                if alternate && string != "0" {
956                    match fmt {
957                        'o' if no_precision => w.write_all(b"0")?,
958                        'x' => w.write_all(b"0x")?,
959                        'X' => w.write_all(b"0X")?,
960                        'b' if T::IS_THIN_NOT_WIDE => w.write_all(b"0b")?,
961                        'B' if T::IS_THIN_NOT_WIDE => w.write_all(b"0B")?,
962                        _ => (),
963                    }
964                }
965                pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?;
966
967                if !zero {
968                    w.write_all(string.as_bytes())?;
969                }
970
971                pad(w, left, b' ', final_len..pad_space)?;
972            }
973            FmtKind::Scientific => {
974                let float = match unsafe {
975                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
976                } {
977                    VaArg::c_double(i) => i,
978                    VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&raw const i) },
979                    _ => panic!("this should not be possible"),
980                };
981                if float.is_finite() {
982                    let (float, exp) = float_exp(float);
983                    let precision = precision.unwrap_or(6);
984
985                    fmt_float_exp(
986                        w, fmt, false, alternate, precision, float, exp, left, pad_space, pad_zero,
987                    )?;
988                } else {
989                    fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?;
990                }
991            }
992            FmtKind::Decimal => {
993                let float = match unsafe {
994                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
995                } {
996                    VaArg::c_double(i) => i,
997                    VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&raw const i) },
998                    _ => panic!("this should not be possible"),
999                };
1000                if float.is_finite() {
1001                    let precision = precision.unwrap_or(6);
1002
1003                    fmt_float_normal(
1004                        w, false, alternate, precision, float, left, pad_space, pad_zero,
1005                    )?;
1006                } else {
1007                    fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?;
1008                }
1009            }
1010            FmtKind::AnyNotation => {
1011                let float = match unsafe {
1012                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
1013                } {
1014                    VaArg::c_double(i) => i,
1015                    VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&raw const i) },
1016                    _ => panic!("this should not be possible"),
1017                };
1018                if float.is_finite() {
1019                    let (log, exp) = float_exp(float);
1020                    // TODO: .is_uppercase()?
1021                    let exp_fmt = if fmt as u32 & 32 == 32 { 'e' } else { 'E' };
1022                    let precision = precision.unwrap_or(6);
1023                    let use_exp_format = exp < -4 || exp >= precision as isize;
1024
1025                    if use_exp_format {
1026                        // Length of integral part will always be 1 here,
1027                        // because that's how x/floor(log10(x)) works
1028                        let precision = precision.saturating_sub(1);
1029                        fmt_float_exp(
1030                            w, exp_fmt, !alternate, alternate, precision, log, exp, left,
1031                            pad_space, pad_zero,
1032                        )?;
1033                    } else {
1034                        // Length of integral part will be the exponent of
1035                        // the unused logarithm, unless the exponent is
1036                        // negative which in case the integral part must
1037                        // of course be 0, 1 in length
1038                        let len = 1 + cmp::max(0, exp) as usize;
1039                        let precision = precision.saturating_sub(len);
1040                        fmt_float_normal(
1041                            w, !alternate, alternate, precision, float, left, pad_space, pad_zero,
1042                        )?;
1043                    }
1044                } else {
1045                    fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?;
1046                }
1047            }
1048            FmtKind::String => {
1049                let ptr = match unsafe {
1050                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
1051                } {
1052                    VaArg::pointer(p) => p,
1053                    _ => panic!("this should not be possible"),
1054                }
1055                .cast::<c_char>();
1056
1057                if ptr.is_null() {
1058                    w.write_all(b"(null)")?;
1059                } else {
1060                    let max = precision.unwrap_or(usize::MAX);
1061
1062                    if intkind == IntKind::Long || intkind == IntKind::LongLong {
1063                        // Handle wchar_t
1064                        let mut ptr = ptr.cast::<wchar_t>();
1065                        let mut string = String::new();
1066
1067                        while unsafe { *ptr } != 0 {
1068                            let c = match char::from_u32(unsafe { *ptr } as _) {
1069                                Some(c) => c,
1070                                None => {
1071                                    platform::ERRNO.set(EILSEQ);
1072                                    return Err(io::last_os_error());
1073                                }
1074                            };
1075                            if string.len() + c.len_utf8() >= max {
1076                                break;
1077                            }
1078                            string.push(c);
1079                            ptr = unsafe { ptr.add(1) };
1080                        }
1081
1082                        pad(w, !left, b' ', string.len()..pad_space)?;
1083                        w.write_all(string.as_bytes())?;
1084                        pad(w, left, b' ', string.len()..pad_space)?;
1085                    } else {
1086                        let mut len = 0;
1087                        while unsafe { *ptr.add(len) } != 0 && len < max {
1088                            len += 1;
1089                        }
1090
1091                        pad(w, !left, b' ', len..pad_space)?;
1092                        w.write_all(unsafe { slice::from_raw_parts(ptr.cast::<u8>(), len) })?;
1093                        pad(w, left, b' ', len..pad_space)?;
1094                    }
1095                }
1096            }
1097            FmtKind::Char => {
1098                match unsafe { varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) } {
1099                    VaArg::c_char(c) => {
1100                        pad(w, !left, b' ', 1..pad_space)?;
1101                        w.write_all(&[c as u8])?;
1102                        pad(w, left, b' ', 1..pad_space)?;
1103                    }
1104                    VaArg::wint_t(c) => {
1105                        let c = match char::from_u32(c as _) {
1106                            Some(c) => c,
1107                            None => {
1108                                platform::ERRNO.set(EILSEQ);
1109                                return Err(io::last_os_error());
1110                            }
1111                        };
1112                        let mut buf = [0; 4];
1113
1114                        pad(w, !left, b' ', 1..pad_space)?;
1115                        w.write_all(c.encode_utf8(&mut buf).as_bytes())?;
1116                        pad(w, left, b' ', 1..pad_space)?;
1117                    }
1118                    _ => unreachable!("this should not be possible"),
1119                }
1120            }
1121            FmtKind::Pointer => {
1122                let ptr = match unsafe {
1123                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
1124                } {
1125                    VaArg::pointer(p) => p,
1126                    _ => panic!("this should not be possible"),
1127                };
1128
1129                let mut len = 1;
1130                if ptr.is_null() {
1131                    len = "(nil)".len();
1132                } else {
1133                    let mut ptr = ptr as usize;
1134                    while ptr >= 10 {
1135                        ptr /= 10;
1136                        len += 1;
1137                    }
1138                }
1139
1140                pad(w, !left, b' ', len..pad_space)?;
1141                if ptr.is_null() {
1142                    write!(w, "(nil)")?;
1143                } else {
1144                    write!(w, "0x{:x}", ptr as usize)?;
1145                }
1146                pad(w, left, b' ', len..pad_space)?;
1147            }
1148            FmtKind::GetWritten => {
1149                let ptr = match unsafe {
1150                    varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
1151                } {
1152                    VaArg::pointer(p) => p,
1153                    _ => panic!("this should not be possible"),
1154                };
1155
1156                match intkind {
1157                    IntKind::Byte => unsafe { *(ptr as *mut c_char) = w.written as c_char },
1158                    IntKind::Short => unsafe { *(ptr as *mut c_short) = w.written as c_short },
1159                    IntKind::Int => unsafe { *(ptr as *mut c_int) = w.written as c_int },
1160                    IntKind::Long => unsafe { *(ptr as *mut c_long) = w.written as c_long },
1161                    IntKind::LongLong => unsafe {
1162                        *(ptr as *mut c_longlong) = w.written as c_longlong
1163                    },
1164                    IntKind::IntMax => unsafe { *(ptr as *mut intmax_t) = w.written as intmax_t },
1165                    IntKind::PtrDiff => unsafe {
1166                        *(ptr as *mut ptrdiff_t) = w.written as ptrdiff_t
1167                    },
1168                    IntKind::Size => unsafe { *(ptr as *mut size_t) = w.written as size_t },
1169                }
1170            }
1171        }
1172    }
1173    Ok(w.written as c_int)
1174}
1175
1176/// Implementation of `printf` formatting function, generic over a `writer`
1177///
1178/// This implementation in currently compliant over C17 specification (lacking a few one from C23)
1179/// and contains extensions as well.
1180///
1181/// # The Format Specification
1182/// ```text
1183/// %[conversion-flags][field-width][precision][length-modifier]<conversion-format>
1184/// ```
1185///
1186/// <div class="warning">
1187/// ※ : This symbol means it is not implemented yet, but it is defined in the C standard
1188/// </div>
1189///
1190/// ## Conversion Flags
1191/// Conversion flags are flags that modify the behavior of the [conversion
1192/// format]. Each one can happen only once per format specifier. They are:
1193///
1194/// - `-`: The result of the conversion is left-justified within the field (by default it is
1195///   right-justified).
1196/// - `+`: The sign of signed conversions is always prepended to the result of the conversion (by
1197///   default the result is preceded by minus **only** when it is negative).
1198/// - ` `(space): If the result of a signed conversion does not start with a sign character, or is
1199///   empty, space is prepended to the result.
1200///   - It is ignored if `+` flag is present.
1201/// - `#`: Alternative form of the conversion is performed. See the documentation for each
1202///   [conversion format] for details.
1203/// - `0`: For integer and floating-point number conversions, leading zeros are used to pad the
1204///   field instead of space characters.
1205///   - For integer numbers it is ignored if the precision is explicitly specified.
1206///   - For other conversions using this flag results in undefined behavior.
1207///   - It is ignored if `-` flag is present.
1208///
1209/// ## Field Width
1210/// Specifies minimum field width. This makes the result to be padded (with spaces by default, with
1211/// zeroes if `0` conversion flag is specified) if the converted value has fewer characters than the
1212/// specified width. It can take three forms:
1213///
1214/// - `N` where N is a positive integer: Specifies the field width value of `N`.
1215/// - `*`: The width is specified by an extra argument of type [`int`], which has to appear before
1216///   the argument to be converted and the [precision] (if specified with `.*`).
1217///   - If the value of e extra argument is negative, it is interpreted as with `-` [conversion
1218///     flag], i.e. left-justified result.
1219/// - `*P$` where P is a positive integer: The width is specified by an extra argument of type
1220///   [`int`], which has to appear exactly at the position specified by `P`.
1221///   - This is a popular extension of the C and POSIX standards.
1222///   - If the value of e extra argument is negative, it is interpreted as with `-` [conversion
1223///     flag], i.e. left-justified result.
1224///
1225/// ## Precision
1226/// Specifies the precision of the conversion.
1227///
1228/// For integer [conversion formats], this specifies the number of digits to appear in the result.
1229///
1230/// For float point [conversion formats], this specifies the number of digits to appear after the
1231/// decimal-point character.
1232///
1233/// It can take three forms:
1234///
1235/// - `.N` where N is a positive integer: Specifies the precision value of `N`.
1236/// - `.*`: The precision is specified by an extra argument of type [`int`], which  has to appear
1237///   before the argument to be converted and after the the [field width] (if specified with `*`).
1238///   - If the value of the extra argument is negative, it is interpreted as if the precision were
1239///     omitted.
1240/// - `.*P$` where P is a positive integer: The precision is specified by an extra argument of type
1241///   [`int`], which has to appear exactly at the position specified by `P`.
1242///   - This is an popular extension of the C and POSIX standards.
1243///   - If the value of e extra argument is negative, it is interpreted as if the precision were
1244///     omitted.
1245///
1246/// ## Length Modifier
1247/// Specifies the size of the argument. In combination with the [conversion format], it specifies
1248/// the type of the corresponding argument.
1249///
1250/// - `hh`: Byte size
1251///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1252///   - Works with written number conversion format (`n`)
1253/// - `h`: Short size
1254///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1255///   - Works with written number conversion format (`n`)
1256/// - `l`: Long size
1257///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1258///   - Works with character conversion format (`c`)
1259///   - Works with string conversion format (`s`)
1260///   - Works with written number conversion format (`n`)
1261///   - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) (C99)
1262/// - `ll`: Long long size
1263///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1264///   - Works with written number conversion format (`n`)
1265/// - `j`: Maximum width
1266///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1267///   - Works with written number conversion format (`n`)
1268/// - `z`: Pointer width size
1269///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1270///   - Works with written number conversion format (`n`)
1271/// - `t`: Pointer diff width
1272///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1273///   - Works with written number conversion format (`n`)
1274/// - `wN` (C23 ※): Specifies that the size should be N bits width version of the supported
1275///   conversion format.
1276///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1277///   - The supported values of `N` must be the same as the widths specified in `stdint.h`
1278/// - `wfN` (C23 ※): Specifies that the size should be the fast N bits width version of the
1279///   supported conversion format.
1280///   - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`)
1281///   - The supported values of `N` must be the same as the widths specified in `stdint.h`
1282/// - `L`: Long double size
1283///   - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`)
1284/// - `H` (C23 ※): _Decimal32 size
1285///   - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`)
1286/// - `D` (C23 ※): _Decimal64 size
1287///   - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`)
1288/// - `DD` (C23 ※): _Decimal128 size
1289///   - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`)
1290///
1291/// ## Conversion Format
1292/// Specifies the conversion format as one of the following:
1293///
1294/// - `%`: Writes a percent symbol. The full conversion format must be `%%`.
1295/// - `c`: Writes as single character
1296///   - Without length modifier:
1297///     - The argument is first converted to [`unsigned char`]
1298///   - With `l` length modifier:
1299///     - The argument is first converted to a character string as if by `%ls` with a array of 2
1300///       [`wchar_t`] argument.
1301/// - `s`: Writes a character string
1302///   - The argument is a pointer to the first character
1303///   - The [precision] specifies the maximum number of bytes to be written. If not specified,
1304///     writes up to the first null character found.
1305/// - `d` and `i`: Writes a decimal representation of a signed integer
1306///   - The [precision] specifies the minimal number to appear (defaults to `1`).
1307///   - If the precision is zero and the value to be written is also zero, the result is no
1308///     characters written.
1309/// - `u`: Writes the decimal representation of a unsigned integer
1310///   - The [precision] specifies the minimal number to appear (defaults to `1`).
1311///   - If the precision is zero and the value to be written is also zero, the result is no
1312///     characters written.
1313/// - `o`: Writes the octal representation of a unsigned integer.
1314///   - The [precision] specifies the minimal number to appear (defaults to `1`).
1315///   - If the precision is zero and the value to be written is also zero, the result is no
1316///     characters written.
1317///   - The alternative representation includes a leading `0`.
1318///   - The types are the same as `u`
1319/// - `x`: Writes the hexadecimal representation of a unsigned integer with lowercase characters.
1320///   - The [precision] specifies the minimal number to appear (defaults to `1`).
1321///   - If the precision is zero and the value to be written is also zero, the result is no
1322///     characters written.
1323///   - The alternative representation includes a leading `0x`.
1324///   - The types are the same as `u`
1325/// - `X`: Writes the hexadecimal representation of a unsigned integer with uppercase characters.
1326///   - The [precision] specifies the minimal number to appear (defaults to `1`).
1327///   - If the precision is zero and the value to be written is also zero, the result is no
1328///     characters written.
1329///   - The alternative representation includes a leading `0X`.
1330///   - The types are the same as `u`.
1331/// - `b` | `B` (C23): Writes the binary representation of a unsigned integer.
1332///   - The [precision] specifies the minimal number to appear (defaults to `1`).
1333///   - If the precision is zero and the value to be written is also zero, the result is no
1334///     characters written.
1335///   - The alternative representation includes a leading `0b` and `0B`, respectively.
1336///   - The types are the same as `u`.
1337/// - `f` | `F`: Writes the decimal representation of a float point number.
1338///   - The [precision] specifies the exact number of digits to appear after the decimal point
1339///     character (defaults to `6`).
1340///   - The alternative representation, the decimal point character is written even if no digits
1341///     follow it.
1342/// - `e` | `E`: Writes the float point number with the decimal exponential notation (\[-\]d.ddd
1343///   **e**±dd | \[-\]d.ddd **E**±dd)
1344///   - The [precision] specifies the exact number of digits to appear after the decimal point
1345///     character (defaults to `6`).
1346///   - The exponent contains at least two digits, more digits are used only if necessary.
1347///   - If the value is ​zero, the exponent is also ​zero​.
1348///   - The alternative representation: decimal point character is written even if no digits follow
1349///     it.
1350/// - `a` | `A`: Writes the float point number with the hexadecimal exponential notation (\[-\]
1351///   **0x**h.hhh **p**±d | \[-\] **0X**h.hhh **P**±d)
1352///   - The [precision] specifies the exact number of digits to appear after the hexadecimal point
1353///     character (defaults to `6`).
1354///   - If the value is ​zero, the exponent is also ​zero​.
1355///   - The alternative representation: decimal point character is written even if no digits follow
1356///     it.
1357/// - `g` | `G`: Writes the float point number to decimal or decimal exponent notation depending on
1358///   the value and the [precision].
1359///   - Let `P` equal the precision if nonzero, `6` if the precision is not specified, or `1` if the
1360///     precision is `​0`​. Then, if a conversion with style `E` would have an exponent of `X`:
1361///     - If `P > X ≥ −4`, the conversion is with the format `f` and precision `P − 1 − X`.
1362///     - Otherwise, the conversion is with the format `e` or `E` and precision `P − 1`.
1363///   - Unless alternative representation is requested, the trailing zeros are removed. Also the
1364///     decimal point character is removed if no fractional part is left.
1365/// - `n`: Writes the number of characters written in the call into the argument pointer
1366///   - It can not contain any [conversion flag], [field width], or [precision].
1367/// - `p`: Writes an implementation defined character sequence defining a pointer.
1368///
1369/// ### Types
1370/// The types expected by the format string can change with the [length modifier].
1371///
1372/// For the `c`:
1373/// - Without length modifier: [`int`]
1374/// - With `l` length modifier: [`wint_t`]
1375///
1376/// For the `s`:
1377/// - Without length modifier: pointer to [`char`] (`char*`, `const char*`)
1378/// - With `l` length modifier: pointer to [`wchar_t`] (`wchar_t*`, `const wchar_t*`)
1379///
1380/// For the `d` and `i`:
1381/// - Without length modifier: [`int`]
1382/// - With `hh` length modifier: [`signed char`]
1383/// - With `h` length modifier: [`short`]
1384/// - With `l` length modifier: [`long`]
1385/// - With `ll` length modifier: [`long long`]
1386/// - With `j` length modifier: [`intmax_t`]
1387/// - With `z` length modifier: [`ssize_t`]
1388/// - With `t` length modifier: [`ptrdiff_t`]
1389///
1390/// For the `u`, `o`, `x`, `X`, `b`, `B`:
1391/// - Without length modifier: [`unsigned int`]
1392/// - With `hh` length modifier: [`unsigned char`]
1393/// - With `h` length modifier: [`unsigned short`]
1394/// - With `l` length modifier: [`unsigned long`]
1395/// - With `ll` length modifier: [`unsigned long long`]
1396/// - With `j` length modifier: [`uintmax_t`]
1397/// - With `z` length modifier: [`size_t`]
1398/// - With `t` length modifier: [`unsigned ptrdiff_t`]
1399///
1400/// For the `f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`:
1401/// - Without length modifier: [`double`]
1402/// - With `l` length modifier: [`double`]
1403/// - With `L` length modifier: `long double`
1404/// - With `H` length modifier (C23 ※): `_Decimal32`
1405/// - With `D` length modifier (C23 ※): `_Decimal64`
1406/// - With `DD` length modifier (C23 ※): `_Decimal128`
1407///
1408/// For the `n`
1409/// - Without length modifier: pointer to [`int`] (`int*`)
1410/// - With `hh` length modifier: pointer to [`signed char`] (`signed char*`)
1411/// - With `h` length modifier: pointer to [`short`] (`short*`)
1412/// - With `l` length modifier: pointer to [`long`] (`long*`)
1413/// - With `ll` length modifier: pointer to [`long long`] (`long long*`)
1414/// - With `j` length modifier: pointer to [`intmax_t`] (`intmax_t*`)
1415/// - With `z` length modifier: pointer to [`ssize_t`] (`ssize_t*`)
1416/// - With `t` length modifier: pointer to [`ptrdiff_t`] (`ptrdiff_t*`)
1417///
1418/// For the `p`, it must always be a pointer to [`void`] (`void*` | `const void*`)
1419///
1420/// [precision]: #precision
1421/// [field width]: #field-width
1422/// [length modifier]: #length-modifier
1423/// [conversion format]: #conversion-format
1424/// [`int`]: c_int
1425/// [`unsigned char`]: c_uchar
1426/// [`unsigned short`]: c_ushort
1427/// [`unsigned int`]: c_uint
1428/// [`unsigned long`]: c_ulong
1429/// [`unsigned long long`]: c_ulonglong
1430/// [`unsigned ptrdiff_t`]: ptrdiff_t
1431/// [`wchar_t`]: wchar_t
1432/// [`char`]: c_char
1433/// [`signed char`]: c_schar
1434/// [`short`]: c_short
1435/// [`long`]: c_long
1436/// [`long long`]: c_longlong
1437/// [`double`]: c_double
1438/// [`long double`]: c_longdouble
1439/// [`void`]: c_void
1440///
1441/// # Safety
1442/// Behavior is undefined if any of the following conditions are violated:
1443/// - `ap` must follow the safety contract of variable arguments of C.
1444pub unsafe fn printf(w: impl Write, format: CStr, ap: VaList) -> c_int {
1445    unsafe { inner_printf::<c_str::Thin>(w, format, ap).unwrap_or(-1) }
1446}