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