xref: /relibc/src/header/stdio/printf.rs (revision a2f2484e4559206922b0fcc16217598a6867c8a2)
1 use crate::io::{self, Write};
2 use alloc::{
3     collections::BTreeMap,
4     string::{String, ToString},
5     vec::Vec,
6 };
7 use core::{char, cmp, f64, ffi::VaList, fmt, num::FpCategory, ops::Range, slice};
8 
9 use crate::{
10     header::errno::EILSEQ,
11     platform::{self, types::*},
12 };
13 
14 //  ____        _ _                 _       _
15 // | __ )  ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _
16 // |  _ \ / _ \| | |/ _ \ '__| '_ \| |/ _` | __/ _ (_)
17 // | |_) | (_) | | |  __/ |  | |_) | | (_| | ||  __/_
18 // |____/ \___/|_|_|\___|_|  | .__/|_|\__,_|\__\___(_)
19 //                           |_|
20 
21 #[derive(Clone, Copy, PartialEq, Eq)]
22 enum IntKind {
23     Byte,
24     Short,
25     Int,
26     Long,
27     LongLong,
28     IntMax,
29     PtrDiff,
30     Size,
31 }
32 #[derive(Clone, Copy, PartialEq, Eq)]
33 enum FmtKind {
34     Percent,
35 
36     Signed,
37     Unsigned,
38 
39     Scientific,
40     Decimal,
41     AnyNotation,
42 
43     String,
44     Char,
45     Pointer,
46     GetWritten,
47 }
48 #[derive(Clone, Copy, Debug)]
49 enum Number {
50     Static(usize),
51     Index(usize),
52     Next,
53 }
54 impl Number {
55     unsafe fn resolve(&self, varargs: &mut VaListCache, ap: &mut VaList) -> usize {
56         let arg = match *self {
57             Number::Static(num) => return num,
58             Number::Index(i) => varargs.get(i - 1, ap, None),
59             Number::Next => {
60                 let i = varargs.i;
61                 varargs.i += 1;
62                 varargs.get(i, ap, None)
63             }
64         };
65         match arg {
66             VaArg::c_char(i) => i as usize,
67             VaArg::c_double(i) => i as usize,
68             VaArg::c_int(i) => i as usize,
69             VaArg::c_long(i) => i as usize,
70             VaArg::c_longlong(i) => i as usize,
71             VaArg::c_short(i) => i as usize,
72             VaArg::intmax_t(i) => i as usize,
73             VaArg::pointer(i) => i as usize,
74             VaArg::ptrdiff_t(i) => i as usize,
75             VaArg::ssize_t(i) => i as usize,
76             VaArg::wint_t(i) => i as usize,
77         }
78     }
79 }
80 #[derive(Clone, Copy, Debug)]
81 enum VaArg {
82     c_char(c_char),
83     c_double(c_double),
84     c_int(c_int),
85     c_long(c_long),
86     c_longlong(c_longlong),
87     c_short(c_short),
88     intmax_t(intmax_t),
89     pointer(*const c_void),
90     ptrdiff_t(ptrdiff_t),
91     ssize_t(ssize_t),
92     wint_t(wint_t),
93 }
94 impl VaArg {
95     unsafe fn arg_from(fmtkind: FmtKind, intkind: IntKind, ap: &mut VaList) -> VaArg {
96         // Per the C standard using va_arg with a type with a size
97         // less than that of an int for integers and double for floats
98         // is invalid. As a result any arguments smaller than an int or
99         // double passed to a function will be promoted to the smallest
100         // possible size. The VaList::arg function will handle this
101         // automagically.
102 
103         match (fmtkind, intkind) {
104             (FmtKind::Percent, _) => panic!("Can't call arg_from on %"),
105 
106             (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => {
107                 VaArg::wint_t(ap.arg::<wint_t>())
108             }
109 
110             (FmtKind::Char, _)
111             | (FmtKind::Unsigned, IntKind::Byte)
112             | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(ap.arg::<c_char>()),
113             (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => {
114                 VaArg::c_short(ap.arg::<c_short>())
115             }
116             (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => {
117                 VaArg::c_int(ap.arg::<c_int>())
118             }
119             (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => {
120                 VaArg::c_long(ap.arg::<c_long>())
121             }
122             (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => {
123                 VaArg::c_longlong(ap.arg::<c_longlong>())
124             }
125             (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => {
126                 VaArg::intmax_t(ap.arg::<intmax_t>())
127             }
128             (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => {
129                 VaArg::ptrdiff_t(ap.arg::<ptrdiff_t>())
130             }
131             (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => {
132                 VaArg::ssize_t(ap.arg::<ssize_t>())
133             }
134 
135             (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
136                 VaArg::c_double(ap.arg::<c_double>())
137             }
138 
139             (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => {
140                 VaArg::pointer(ap.arg::<*const c_void>())
141             }
142         }
143     }
144     unsafe fn transmute(&self, fmtkind: FmtKind, intkind: IntKind) -> VaArg {
145         // At this point, there are conflicting printf arguments. An
146         // example of this is:
147         // ```c
148         // printf("%1$d %1$lf\n", 5, 0.1);
149         // ```
150         // We handle it just like glibc: We read it from the VaList
151         // using the *last* argument type, but we transmute it when we
152         // try to access the other ones.
153         union Untyped {
154             c_char: c_char,
155             c_double: c_double,
156             c_int: c_int,
157             c_long: c_long,
158             c_longlong: c_longlong,
159             c_short: c_short,
160             intmax_t: intmax_t,
161             pointer: *const c_void,
162             ptrdiff_t: ptrdiff_t,
163             ssize_t: ssize_t,
164             wint_t: wint_t,
165         }
166         let untyped = match *self {
167             VaArg::c_char(i) => Untyped { c_char: i },
168             VaArg::c_double(i) => Untyped { c_double: i },
169             VaArg::c_int(i) => Untyped { c_int: i },
170             VaArg::c_long(i) => Untyped { c_long: i },
171             VaArg::c_longlong(i) => Untyped { c_longlong: i },
172             VaArg::c_short(i) => Untyped { c_short: i },
173             VaArg::intmax_t(i) => Untyped { intmax_t: i },
174             VaArg::pointer(i) => Untyped { pointer: i },
175             VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i },
176             VaArg::ssize_t(i) => Untyped { ssize_t: i },
177             VaArg::wint_t(i) => Untyped { wint_t: i },
178         };
179         match (fmtkind, intkind) {
180             (FmtKind::Percent, _) => panic!("Can't call transmute on %"),
181 
182             (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => {
183                 VaArg::wint_t(untyped.wint_t)
184             }
185 
186             (FmtKind::Char, _)
187             | (FmtKind::Unsigned, IntKind::Byte)
188             | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(untyped.c_char),
189             (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => {
190                 VaArg::c_short(untyped.c_short)
191             }
192             (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => {
193                 VaArg::c_int(untyped.c_int)
194             }
195             (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => {
196                 VaArg::c_long(untyped.c_long)
197             }
198             (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => {
199                 VaArg::c_longlong(untyped.c_longlong)
200             }
201             (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => {
202                 VaArg::intmax_t(untyped.intmax_t)
203             }
204             (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => {
205                 VaArg::ptrdiff_t(untyped.ptrdiff_t)
206             }
207             (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => {
208                 VaArg::ssize_t(untyped.ssize_t)
209             }
210 
211             (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
212                 VaArg::c_double(untyped.c_double)
213             }
214 
215             (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => {
216                 VaArg::pointer(untyped.pointer)
217             }
218         }
219     }
220 }
221 #[derive(Default)]
222 struct VaListCache {
223     args: Vec<VaArg>,
224     i: usize,
225 }
226 impl VaListCache {
227     unsafe fn get(
228         &mut self,
229         i: usize,
230         ap: &mut VaList,
231         default: Option<(FmtKind, IntKind)>,
232     ) -> VaArg {
233         if let Some(&arg) = self.args.get(i) {
234             let mut arg = arg;
235             if let Some((fmtkind, intkind)) = default {
236                 arg = arg.transmute(fmtkind, intkind);
237             }
238             return arg;
239         }
240         while self.args.len() < i {
241             // We can't POSSIBLY know the type if we reach this
242             // point. Reaching here means there are unused gaps in the
243             // arguments. Ultimately we'll have to settle down with
244             // defaulting to c_int.
245             self.args.push(VaArg::c_int(ap.arg::<c_int>()))
246         }
247         self.args.push(match default {
248             Some((fmtkind, intkind)) => VaArg::arg_from(fmtkind, intkind, ap),
249             None => VaArg::c_int(ap.arg::<c_int>()),
250         });
251         self.args[i]
252     }
253 }
254 
255 //  ___                 _                           _        _   _
256 // |_ _|_ __ ___  _ __ | | ___ _ __ ___   ___ _ __ | |_ __ _| |_(_) ___  _ __  _
257 //  | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \(_)
258 //  | || | | | | | |_) | |  __/ | | | | |  __/ | | | || (_| | |_| | (_) | | | |_
259 // |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_)
260 //               |_|
261 
262 enum FmtCase {
263     Lower,
264     Upper,
265 }
266 
267 // The spelled-out "infinity"/"INFINITY" is also permitted by the standard
268 static INF_STR_LOWER: &str = "inf";
269 static INF_STR_UPPER: &str = "INF";
270 
271 static NAN_STR_LOWER: &str = "nan";
272 static NAN_STR_UPPER: &str = "NAN";
273 
274 unsafe fn pop_int_raw(format: &mut *const u8) -> Option<usize> {
275     let mut int = None;
276     while let Some(digit) = (**format as char).to_digit(10) {
277         *format = format.add(1);
278         if int.is_none() {
279             int = Some(0);
280         }
281         *int.as_mut().unwrap() *= 10;
282         *int.as_mut().unwrap() += digit as usize;
283     }
284     int
285 }
286 unsafe fn pop_index(format: &mut *const u8) -> Option<usize> {
287     // Peek ahead for a positional argument:
288     let mut format2 = *format;
289     if let Some(i) = pop_int_raw(&mut format2) {
290         if *format2 == b'$' {
291             *format = format2.add(1);
292             return Some(i);
293         }
294     }
295     None
296 }
297 unsafe fn pop_int(format: &mut *const u8) -> Option<Number> {
298     if **format == b'*' {
299         *format = format.add(1);
300         Some(pop_index(format).map(Number::Index).unwrap_or(Number::Next))
301     } else {
302         pop_int_raw(format).map(Number::Static)
303     }
304 }
305 
306 unsafe fn fmt_int<I>(fmt: u8, i: I) -> String
307 where
308     I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex,
309 {
310     match fmt {
311         b'o' => format!("{:o}", i),
312         b'u' => i.to_string(),
313         b'x' => format!("{:x}", i),
314         b'X' => format!("{:X}", i),
315         _ => panic!(
316             "fmt_int should never be called with the fmt {:?}",
317             fmt as char
318         ),
319     }
320 }
321 
322 fn pad<W: Write>(
323     w: &mut W,
324     current_side: bool,
325     pad_char: u8,
326     range: Range<usize>,
327 ) -> io::Result<()> {
328     if current_side {
329         for _ in range {
330             w.write_all(&[pad_char])?;
331         }
332     }
333     Ok(())
334 }
335 
336 fn abs(float: c_double) -> c_double {
337     // Don't ask me whe float.abs() seems absent...
338     if float.is_sign_negative() {
339         -float
340     } else {
341         float
342     }
343 }
344 
345 fn float_string(float: c_double, precision: usize, trim: bool) -> String {
346     let mut string = format!("{:.p$}", float, p = precision);
347     if trim && string.contains('.') {
348         let truncate = {
349             let slice = string.trim_end_matches('0');
350             let mut truncate = slice.len();
351             if slice.ends_with('.') {
352                 truncate -= 1;
353             }
354             truncate
355         };
356         string.truncate(truncate);
357     }
358     string
359 }
360 
361 fn float_exp(mut float: c_double) -> (c_double, isize) {
362     let mut exp: isize = 0;
363     while abs(float) >= 10.0 {
364         float /= 10.0;
365         exp += 1;
366     }
367     while f64::EPSILON < abs(float) && abs(float) < 1.0 {
368         float *= 10.0;
369         exp -= 1;
370     }
371     (float, exp)
372 }
373 
374 fn fmt_float_exp<W: Write>(
375     w: &mut W,
376     exp_fmt: u8,
377     trim: bool,
378     precision: usize,
379     float: c_double,
380     exp: isize,
381     left: bool,
382     pad_space: usize,
383     pad_zero: usize,
384 ) -> io::Result<()> {
385     let mut exp2 = exp;
386     let mut exp_len = 1;
387     while exp2 >= 10 {
388         exp2 /= 10;
389         exp_len += 1;
390     }
391 
392     let string = float_string(float, precision, trim);
393     let len = string.len() + 2 + 2.max(exp_len);
394 
395     pad(w, !left, b' ', len..pad_space)?;
396     let bytes = if string.starts_with('-') {
397         w.write_all(&[b'-'])?;
398         &string.as_bytes()[1..]
399     } else {
400         string.as_bytes()
401     };
402     pad(w, !left, b'0', len..pad_zero)?;
403     w.write_all(bytes)?;
404     write!(w, "{}{:+03}", exp_fmt as char, exp)?;
405     pad(w, left, b' ', len..pad_space)?;
406 
407     Ok(())
408 }
409 
410 fn fmt_float_normal<W: Write>(
411     w: &mut W,
412     trim: bool,
413     precision: usize,
414     float: c_double,
415     left: bool,
416     pad_space: usize,
417     pad_zero: usize,
418 ) -> io::Result<usize> {
419     let string = float_string(float, precision, trim);
420 
421     pad(w, !left, b' ', string.len()..pad_space)?;
422     let bytes = if string.starts_with('-') {
423         w.write_all(&[b'-'])?;
424         &string.as_bytes()[1..]
425     } else {
426         string.as_bytes()
427     };
428     pad(w, true, b'0', string.len()..pad_zero)?;
429     w.write_all(bytes)?;
430     pad(w, left, b' ', string.len()..pad_space)?;
431 
432     Ok(string.len())
433 }
434 
435 /// Write ±infinity or ±NaN representation for any floating-point style
436 fn fmt_float_nonfinite<W: Write>(w: &mut W, float: c_double, case: FmtCase) -> io::Result<()> {
437     if float.is_sign_negative() {
438         w.write_all(&[b'-'])?;
439     }
440 
441     let nonfinite_str = match float.classify() {
442         FpCategory::Infinite => match case {
443             FmtCase::Lower => INF_STR_LOWER,
444             FmtCase::Upper => INF_STR_UPPER,
445         },
446         FpCategory::Nan => match case {
447             FmtCase::Lower => NAN_STR_LOWER,
448             FmtCase::Upper => NAN_STR_UPPER,
449         },
450         _ => {
451             // This function should only be called with infinite or NaN value.
452             panic!("this should not be possible")
453         }
454     };
455 
456     w.write_all(nonfinite_str.as_bytes())?;
457 
458     Ok(())
459 }
460 
461 #[derive(Clone, Copy)]
462 struct PrintfIter {
463     format: *const u8,
464 }
465 #[derive(Clone, Copy)]
466 struct PrintfArg {
467     index: Option<usize>,
468     alternate: bool,
469     zero: bool,
470     left: bool,
471     sign_reserve: bool,
472     sign_always: bool,
473     min_width: Number,
474     precision: Option<Number>,
475     pad_space: Number,
476     pad_zero: Number,
477     intkind: IntKind,
478     fmt: u8,
479     fmtkind: FmtKind,
480 }
481 enum PrintfFmt {
482     Plain(&'static [u8]),
483     Arg(PrintfArg),
484 }
485 impl Iterator for PrintfIter {
486     type Item = Result<PrintfFmt, ()>;
487     fn next(&mut self) -> Option<Self::Item> {
488         unsafe {
489             // Send PrintfFmt::Plain until the next %
490             let mut len = 0;
491             while *self.format.add(len) != 0 && *self.format.add(len) != b'%' {
492                 len += 1;
493             }
494             if len > 0 {
495                 let slice = slice::from_raw_parts(self.format as *const u8, len);
496                 self.format = self.format.add(len);
497                 return Some(Ok(PrintfFmt::Plain(slice)));
498             }
499             self.format = self.format.add(len);
500             if *self.format == 0 {
501                 return None;
502             }
503 
504             // *self.format is guaranteed to be '%' at this point
505             self.format = self.format.add(1);
506 
507             let mut peekahead = self.format;
508             let index = pop_index(&mut peekahead).map(|i| {
509                 self.format = peekahead;
510                 i
511             });
512 
513             // Flags:
514             let mut alternate = false;
515             let mut zero = false;
516             let mut left = false;
517             let mut sign_reserve = false;
518             let mut sign_always = false;
519 
520             loop {
521                 match *self.format {
522                     b'#' => alternate = true,
523                     b'0' => zero = true,
524                     b'-' => left = true,
525                     b' ' => sign_reserve = true,
526                     b'+' => sign_always = true,
527                     _ => break,
528                 }
529                 self.format = self.format.add(1);
530             }
531 
532             // Width and precision:
533             let min_width = pop_int(&mut self.format).unwrap_or(Number::Static(0));
534             let precision = if *self.format == b'.' {
535                 self.format = self.format.add(1);
536                 match pop_int(&mut self.format) {
537                     int @ Some(_) => int,
538                     None => return Some(Err(())),
539                 }
540             } else {
541                 None
542             };
543 
544             let pad_space = if zero { Number::Static(0) } else { min_width };
545             let pad_zero = if zero { min_width } else { Number::Static(0) };
546 
547             // Integer size:
548             let mut intkind = IntKind::Int;
549             loop {
550                 intkind = match *self.format {
551                     b'h' => {
552                         if intkind == IntKind::Short || intkind == IntKind::Byte {
553                             IntKind::Byte
554                         } else {
555                             IntKind::Short
556                         }
557                     }
558                     b'j' => IntKind::IntMax,
559                     b'l' => {
560                         if intkind == IntKind::Long || intkind == IntKind::LongLong {
561                             IntKind::LongLong
562                         } else {
563                             IntKind::Long
564                         }
565                     }
566                     b'q' | b'L' => IntKind::LongLong,
567                     b't' => IntKind::PtrDiff,
568                     b'z' => IntKind::Size,
569                     _ => break,
570                 };
571 
572                 self.format = self.format.add(1);
573             }
574             let fmt = *self.format;
575             let fmtkind = match fmt {
576                 b'%' => FmtKind::Percent,
577                 b'd' | b'i' => FmtKind::Signed,
578                 b'o' | b'u' | b'x' | b'X' => FmtKind::Unsigned,
579                 b'e' | b'E' => FmtKind::Scientific,
580                 b'f' | b'F' => FmtKind::Decimal,
581                 b'g' | b'G' => FmtKind::AnyNotation,
582                 b's' => FmtKind::String,
583                 b'c' => FmtKind::Char,
584                 b'p' => FmtKind::Pointer,
585                 b'n' => FmtKind::GetWritten,
586                 _ => return Some(Err(())),
587             };
588             self.format = self.format.add(1);
589 
590             Some(Ok(PrintfFmt::Arg(PrintfArg {
591                 index,
592                 alternate,
593                 zero,
594                 left,
595                 sign_reserve,
596                 sign_always,
597                 min_width,
598                 precision,
599                 pad_space,
600                 pad_zero,
601                 intkind,
602                 fmt,
603                 fmtkind,
604             })))
605         }
606     }
607 }
608 
609 unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) -> io::Result<c_int> {
610     let w = &mut platform::CountingWriter::new(w);
611 
612     let iterator = PrintfIter {
613         format: format as *const u8,
614     };
615 
616     // Pre-fetch vararg types
617     let mut varargs = VaListCache::default();
618     let mut positional = BTreeMap::new();
619     // ^ NOTE: This depends on the sorted order, do not change to HashMap or whatever
620 
621     for section in iterator {
622         let arg = match section {
623             Ok(PrintfFmt::Plain(text)) => continue,
624             Ok(PrintfFmt::Arg(arg)) => arg,
625             Err(()) => return Ok(-1),
626         };
627         if arg.fmtkind == FmtKind::Percent {
628             continue;
629         }
630         for num in &[arg.min_width, arg.precision.unwrap_or(Number::Static(0))] {
631             match num {
632                 Number::Next => varargs.args.push(VaArg::c_int(ap.arg::<c_int>())),
633                 Number::Index(i) => {
634                     positional.insert(i - 1, (FmtKind::Signed, IntKind::Int));
635                 }
636                 Number::Static(_) => (),
637             }
638         }
639         match arg.index {
640             Some(i) => {
641                 positional.insert(i - 1, (arg.fmtkind, arg.intkind));
642             }
643             None => varargs
644                 .args
645                 .push(VaArg::arg_from(arg.fmtkind, arg.intkind, &mut ap)),
646         }
647     }
648     // Make sure, in order, the positional arguments exist with the specified type
649     for (i, arg) in positional {
650         varargs.get(i, &mut ap, Some(arg));
651     }
652 
653     // Main loop
654     for section in iterator {
655         let arg = match section {
656             Ok(PrintfFmt::Plain(text)) => {
657                 w.write_all(text)?;
658                 continue;
659             }
660             Ok(PrintfFmt::Arg(arg)) => arg,
661             Err(()) => return Ok(-1),
662         };
663         let alternate = arg.alternate;
664         let zero = arg.zero;
665         let left = arg.left;
666         let sign_reserve = arg.sign_reserve;
667         let sign_always = arg.sign_always;
668         let min_width = arg.min_width.resolve(&mut varargs, &mut ap);
669         let precision = arg.precision.map(|n| n.resolve(&mut varargs, &mut ap));
670         let pad_space = arg.pad_space.resolve(&mut varargs, &mut ap);
671         let pad_zero = arg.pad_zero.resolve(&mut varargs, &mut ap);
672         let intkind = arg.intkind;
673         let fmt = arg.fmt;
674         let fmtkind = arg.fmtkind;
675         let fmtcase = match fmt {
676             b'x' | b'f' | b'e' | b'g' => Some(FmtCase::Lower),
677             b'X' | b'F' | b'E' | b'G' => Some(FmtCase::Upper),
678             _ => None,
679         };
680 
681         let index = arg.index.map(|i| i - 1).unwrap_or_else(|| {
682             if fmtkind == FmtKind::Percent {
683                 0
684             } else {
685                 let i = varargs.i;
686                 varargs.i += 1;
687                 i
688             }
689         });
690 
691         match fmtkind {
692             FmtKind::Percent => w.write_all(&[b'%'])?,
693             FmtKind::Signed => {
694                 let string = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
695                     VaArg::c_char(i) => i.to_string(),
696                     VaArg::c_double(i) => panic!("this should not be possible"),
697                     VaArg::c_int(i) => i.to_string(),
698                     VaArg::c_long(i) => i.to_string(),
699                     VaArg::c_longlong(i) => i.to_string(),
700                     VaArg::c_short(i) => i.to_string(),
701                     VaArg::intmax_t(i) => i.to_string(),
702                     VaArg::pointer(i) => (i as usize).to_string(),
703                     VaArg::ptrdiff_t(i) => i.to_string(),
704                     VaArg::ssize_t(i) => i.to_string(),
705                     VaArg::wint_t(_) => unreachable!("this should not be possible"),
706                 };
707                 let positive = !string.starts_with('-');
708                 let zero = precision == Some(0) && string == "0";
709 
710                 let mut len = string.len();
711                 let mut final_len = string.len().max(precision.unwrap_or(0));
712                 if positive && (sign_reserve || sign_always) {
713                     final_len += 1;
714                 }
715                 if zero {
716                     len = 0;
717                     final_len = 0;
718                 }
719 
720                 pad(w, !left, b' ', final_len..pad_space)?;
721 
722                 let bytes = if positive {
723                     if sign_reserve {
724                         w.write_all(&[b' '])?;
725                     } else if sign_always {
726                         w.write_all(&[b'+'])?;
727                     }
728                     string.as_bytes()
729                 } else {
730                     w.write_all(&[b'-'])?;
731                     &string.as_bytes()[1..]
732                 };
733                 pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?;
734 
735                 if !zero {
736                     w.write_all(bytes)?;
737                 }
738 
739                 pad(w, left, b' ', final_len..pad_space)?;
740             }
741             FmtKind::Unsigned => {
742                 let string = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
743                     VaArg::c_char(i) => fmt_int(fmt, i as c_uchar),
744                     VaArg::c_double(i) => panic!("this should not be possible"),
745                     VaArg::c_int(i) => fmt_int(fmt, i as c_uint),
746                     VaArg::c_long(i) => fmt_int(fmt, i as c_ulong),
747                     VaArg::c_longlong(i) => fmt_int(fmt, i as c_ulonglong),
748                     VaArg::c_short(i) => fmt_int(fmt, i as c_ushort),
749                     VaArg::intmax_t(i) => fmt_int(fmt, i as uintmax_t),
750                     VaArg::pointer(i) => fmt_int(fmt, i as usize),
751                     VaArg::ptrdiff_t(i) => fmt_int(fmt, i as size_t),
752                     VaArg::ssize_t(i) => fmt_int(fmt, i as size_t),
753                     VaArg::wint_t(_) => unreachable!("this should not be possible"),
754                 };
755                 let zero = precision == Some(0) && string == "0";
756 
757                 // If this int is padded out to be larger than it is, don't
758                 // add an extra zero if octal.
759                 let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true);
760 
761                 let len;
762                 let final_len = if zero {
763                     len = 0;
764                     0
765                 } else {
766                     len = string.len();
767                     len.max(precision.unwrap_or(0))
768                         + if alternate && string != "0" {
769                             match fmt {
770                                 b'o' if no_precision => 1,
771                                 b'x' | b'X' => 2,
772                                 _ => 0,
773                             }
774                         } else {
775                             0
776                         }
777                 };
778 
779                 pad(w, !left, b' ', final_len..pad_space)?;
780 
781                 if alternate && string != "0" {
782                     match fmt {
783                         b'o' if no_precision => w.write_all(&[b'0'])?,
784                         b'x' => w.write_all(&[b'0', b'x'])?,
785                         b'X' => w.write_all(&[b'0', b'X'])?,
786                         _ => (),
787                     }
788                 }
789                 pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?;
790 
791                 if !zero {
792                     w.write_all(string.as_bytes())?;
793                 }
794 
795                 pad(w, left, b' ', final_len..pad_space)?;
796             }
797             FmtKind::Scientific => {
798                 let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
799                     VaArg::c_double(i) => i,
800                     _ => panic!("this should not be possible"),
801                 };
802                 if float.is_finite() {
803                     let (float, exp) = float_exp(float);
804                     let precision = precision.unwrap_or(6);
805 
806                     fmt_float_exp(
807                         w, fmt, false, precision, float, exp, left, pad_space, pad_zero,
808                     )?;
809                 } else {
810                     fmt_float_nonfinite(w, float, fmtcase.unwrap())?;
811                 }
812             }
813             FmtKind::Decimal => {
814                 let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
815                     VaArg::c_double(i) => i,
816                     _ => panic!("this should not be possible"),
817                 };
818                 if float.is_finite() {
819                     let precision = precision.unwrap_or(6);
820 
821                     fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?;
822                 } else {
823                     fmt_float_nonfinite(w, float, fmtcase.unwrap())?;
824                 }
825             }
826             FmtKind::AnyNotation => {
827                 let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
828                     VaArg::c_double(i) => i,
829                     _ => panic!("this should not be possible"),
830                 };
831                 if float.is_finite() {
832                     let (log, exp) = float_exp(float);
833                     let exp_fmt = b'E' | (fmt & 32);
834                     let precision = precision.unwrap_or(6);
835                     let use_exp_format = exp < -4 || exp >= precision as isize;
836 
837                     if use_exp_format {
838                         // Length of integral part will always be 1 here,
839                         // because that's how x/floor(log10(x)) works
840                         let precision = precision.saturating_sub(1);
841                         fmt_float_exp(
842                             w, exp_fmt, true, precision, log, exp, left, pad_space, pad_zero,
843                         )?;
844                     } else {
845                         // Length of integral part will be the exponent of
846                         // the unused logarithm, unless the exponent is
847                         // negative which in case the integral part must
848                         // of course be 0, 1 in length
849                         let len = 1 + cmp::max(0, exp) as usize;
850                         let precision = precision.saturating_sub(len);
851                         fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?;
852                     }
853                 } else {
854                     fmt_float_nonfinite(w, float, fmtcase.unwrap())?;
855                 }
856             }
857             FmtKind::String => {
858                 let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
859                     VaArg::pointer(p) => p,
860                     _ => panic!("this should not be possible"),
861                 } as *const c_char;
862 
863                 if ptr.is_null() {
864                     w.write_all(b"(null)")?;
865                 } else {
866                     let max = precision.unwrap_or(::core::usize::MAX);
867 
868                     if intkind == IntKind::Long || intkind == IntKind::LongLong {
869                         // Handle wchar_t
870                         let mut ptr = ptr as *const wchar_t;
871                         let mut string = String::new();
872 
873                         while *ptr != 0 {
874                             let c = match char::from_u32(*ptr as _) {
875                                 Some(c) => c,
876                                 None => {
877                                     platform::errno = EILSEQ;
878                                     return Err(io::last_os_error());
879                                 }
880                             };
881                             if string.len() + c.len_utf8() >= max {
882                                 break;
883                             }
884                             string.push(c);
885                             ptr = ptr.add(1);
886                         }
887 
888                         pad(w, !left, b' ', string.len()..pad_space)?;
889                         w.write_all(string.as_bytes())?;
890                         pad(w, left, b' ', string.len()..pad_space)?;
891                     } else {
892                         let mut len = 0;
893                         while *ptr.add(len) != 0 && len < max {
894                             len += 1;
895                         }
896 
897                         pad(w, !left, b' ', len..pad_space)?;
898                         w.write_all(slice::from_raw_parts(ptr as *const u8, len))?;
899                         pad(w, left, b' ', len..pad_space)?;
900                     }
901                 }
902             }
903             FmtKind::Char => match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
904                 VaArg::c_char(c) => {
905                     pad(w, !left, b' ', 1..pad_space)?;
906                     w.write_all(&[c as u8])?;
907                     pad(w, left, b' ', 1..pad_space)?;
908                 }
909                 VaArg::wint_t(c) => {
910                     let c = match char::from_u32(c as _) {
911                         Some(c) => c,
912                         None => {
913                             platform::errno = EILSEQ;
914                             return Err(io::last_os_error());
915                         }
916                     };
917                     let mut buf = [0; 4];
918 
919                     pad(w, !left, b' ', 1..pad_space)?;
920                     w.write_all(c.encode_utf8(&mut buf).as_bytes())?;
921                     pad(w, left, b' ', 1..pad_space)?;
922                 }
923                 _ => unreachable!("this should not be possible"),
924             },
925             FmtKind::Pointer => {
926                 let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
927                     VaArg::pointer(p) => p,
928                     _ => panic!("this should not be possible"),
929                 };
930 
931                 let mut len = 1;
932                 if ptr.is_null() {
933                     len = "(nil)".len();
934                 } else {
935                     let mut ptr = ptr as usize;
936                     while ptr >= 10 {
937                         ptr /= 10;
938                         len += 1;
939                     }
940                 }
941 
942                 pad(w, !left, b' ', len..pad_space)?;
943                 if ptr.is_null() {
944                     write!(w, "(nil)")?;
945                 } else {
946                     write!(w, "0x{:x}", ptr as usize)?;
947                 }
948                 pad(w, left, b' ', len..pad_space)?;
949             }
950             FmtKind::GetWritten => {
951                 let ptr = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) {
952                     VaArg::pointer(p) => p,
953                     _ => panic!("this should not be possible"),
954                 };
955 
956                 match intkind {
957                     IntKind::Byte => *(ptr as *mut c_char) = w.written as c_char,
958                     IntKind::Short => *(ptr as *mut c_short) = w.written as c_short,
959                     IntKind::Int => *(ptr as *mut c_int) = w.written as c_int,
960                     IntKind::Long => *(ptr as *mut c_long) = w.written as c_long,
961                     IntKind::LongLong => *(ptr as *mut c_longlong) = w.written as c_longlong,
962                     IntKind::IntMax => *(ptr as *mut intmax_t) = w.written as intmax_t,
963                     IntKind::PtrDiff => *(ptr as *mut ptrdiff_t) = w.written as ptrdiff_t,
964                     IntKind::Size => *(ptr as *mut size_t) = w.written as size_t,
965                 }
966             }
967         }
968     }
969     Ok(w.written as c_int)
970 }
971 
972 pub unsafe fn printf<W: Write>(w: W, format: *const c_char, ap: VaList) -> c_int {
973     inner_printf(w, format, ap).unwrap_or(-1)
974 }
975