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}