1use core::ffi::*;
2
3use crate::{Argument, DoubleFormat, Flags, SignedInt, Specifier, UnsignedInt};
4use itertools::Itertools;
5
6fn next_char(sub: &[u8]) -> &[u8] {
7 sub.get(1..).unwrap_or(&[])
8}
9
10fn parse_flags(mut sub: &[u8]) -> (Flags, &[u8]) {
12 let mut flags: Flags = Flags::empty();
13 while let Some(&ch) = sub.first() {
14 flags.insert(match ch {
15 b'-' => Flags::LEFT_ALIGN,
16 b'+' => Flags::PREPEND_PLUS,
17 b' ' => Flags::PREPEND_SPACE,
18 b'0' => Flags::PREPEND_ZERO,
19 b'\'' => Flags::THOUSANDS_GROUPING,
20 b'#' => Flags::ALTERNATE_FORM,
21 _ => break,
22 });
23 sub = next_char(sub)
24 }
25 (flags, sub)
26}
27
28unsafe fn parse_width<'a>(mut sub: &'a [u8], args: &mut VaList) -> (c_int, &'a [u8]) {
30 let mut width: c_int = 0;
31 if sub.first() == Some(&b'*') {
32 return (unsafe { args.arg() }, next_char(sub));
33 }
34 while let Some(&ch) = sub.first() {
35 match ch {
36 b'0'..=b'9' => width = width * 10 + (ch & 0x0f) as c_int,
38 _ => break,
39 }
40 sub = next_char(sub);
41 }
42 (width, sub)
43}
44
45unsafe fn parse_precision<'a>(sub: &'a [u8], args: &mut VaList) -> (Option<c_int>, &'a [u8]) {
47 match sub.first() {
48 Some(&b'.') => {
49 let (prec, sub) = unsafe { parse_width(next_char(sub), args) };
50 (Some(prec), sub)
51 }
52 _ => (None, sub),
53 }
54}
55
56#[derive(Debug, Copy, Clone)]
57enum Length {
58 Int,
59 Char,
61 Short,
63 Long,
65 LongLong,
67 Usize,
69 Isize,
71}
72
73impl Length {
74 unsafe fn parse_signed(self, args: &mut VaList) -> SignedInt {
75 match self {
76 Length::Int => SignedInt::Int(unsafe { args.arg() }),
77 Length::Char => SignedInt::Char(unsafe { args.arg::<c_int>() } as c_schar),
78 Length::Short => SignedInt::Short(unsafe { args.arg::<c_int>() } as c_short),
79 Length::Long => SignedInt::Long(unsafe { args.arg() }),
80 Length::LongLong => SignedInt::LongLong(unsafe { args.arg() }),
81 Length::Usize | Length::Isize => SignedInt::Isize(unsafe { args.arg() }),
83 }
84 }
85 unsafe fn parse_unsigned(self, args: &mut VaList) -> UnsignedInt {
86 match self {
87 Length::Int => UnsignedInt::Int(unsafe { args.arg() }),
88 Length::Char => UnsignedInt::Char(unsafe { args.arg::<c_uint>() } as c_uchar),
89 Length::Short => UnsignedInt::Short(unsafe { args.arg::<c_uint>() } as c_ushort),
90 Length::Long => UnsignedInt::Long(unsafe { args.arg() }),
91 Length::LongLong => UnsignedInt::LongLong(unsafe { args.arg() }),
92 Length::Usize | Length::Isize => UnsignedInt::Isize(unsafe { args.arg() }),
94 }
95 }
96}
97
98fn parse_length(sub: &[u8]) -> (Length, &[u8]) {
100 match sub.first().copied() {
101 Some(b'h') => match sub.get(1).copied() {
102 Some(b'h') => (Length::Char, sub.get(2..).unwrap_or(&[])),
103 _ => (Length::Short, next_char(sub)),
104 },
105 Some(b'l') => match sub.get(1).copied() {
106 Some(b'l') => (Length::LongLong, sub.get(2..).unwrap_or(&[])),
107 _ => (Length::Long, next_char(sub)),
108 },
109 Some(b'z') => (Length::Usize, next_char(sub)),
110 Some(b't') => (Length::Isize, next_char(sub)),
111 _ => (Length::Int, sub),
112 }
113}
114
115pub unsafe fn format(
121 format: *const c_char,
122 mut args: VaList,
123 mut handler: impl FnMut(Argument) -> c_int,
124) -> c_int {
125 let str = unsafe { CStr::from_ptr(format).to_bytes() };
126 let mut iter = str.split(|&c| c == b'%');
127 let mut written = 0;
128
129 macro_rules! err {
130 ($ex: expr) => {{
131 let res = $ex;
132 if res < 0 {
133 return -1;
134 } else {
135 written += res;
136 }
137 }};
138 }
139 if let Some(begin) = iter.next() {
140 err!(handler(Specifier::Bytes(begin).into()));
141 }
142 let mut last_was_percent = false;
143 for (sub, next) in iter.map(Some).chain(core::iter::once(None)).tuple_windows() {
144 let sub = match sub {
145 Some(sub) => sub,
146 None => break,
147 };
148 if last_was_percent {
149 err!(handler(Specifier::Bytes(sub).into()));
150 last_was_percent = false;
151 continue;
152 }
153 let (flags, sub) = parse_flags(sub);
154 let (width, sub) = unsafe { parse_width(sub, &mut args) };
155 let (precision, sub) = unsafe { parse_precision(sub, &mut args) };
156 let (length, sub) = parse_length(sub);
157 let ch = sub
158 .first()
159 .unwrap_or(if next.is_some() { &b'%' } else { &0 });
160 err!(handler(Argument {
161 flags,
162 width,
163 precision,
164 specifier: match ch {
165 b'%' => {
166 last_was_percent = true;
167 Specifier::Percent
168 }
169 b'd' | b'i' => Specifier::Int(unsafe { length.parse_signed(&mut args) }),
170 b'x' => Specifier::Hex(unsafe { length.parse_unsigned(&mut args) }),
171 b'X' => Specifier::UpperHex(unsafe { length.parse_unsigned(&mut args) }),
172 b'u' => Specifier::Uint(unsafe { length.parse_unsigned(&mut args) }),
173 b'o' => Specifier::Octal(unsafe { length.parse_unsigned(&mut args) }),
174 b'f' | b'F' => Specifier::Double {
175 value: unsafe { args.arg() },
176 format: DoubleFormat::Normal.set_upper(ch.is_ascii_uppercase()),
177 },
178 b'e' | b'E' => Specifier::Double {
179 value: unsafe { args.arg() },
180 format: DoubleFormat::Scientific.set_upper(ch.is_ascii_uppercase()),
181 },
182 b'g' | b'G' => Specifier::Double {
183 value: unsafe { args.arg() },
184 format: DoubleFormat::Auto.set_upper(ch.is_ascii_uppercase()),
185 },
186 b'a' | b'A' => Specifier::Double {
187 value: unsafe { args.arg() },
188 format: DoubleFormat::Hex.set_upper(ch.is_ascii_uppercase()),
189 },
190 b's' => {
191 let arg: *mut c_char = unsafe { args.arg() };
192 if arg.is_null() {
195 Specifier::Bytes(b"(null)")
196 } else {
197 Specifier::String(unsafe { CStr::from_ptr(arg) })
198 }
199 }
200 b'c' => {
201 trait CharToInt {
202 type IntType;
203 }
204
205 impl CharToInt for c_schar {
206 type IntType = c_int;
207 }
208
209 impl CharToInt for c_uchar {
210 type IntType = c_uint;
211 }
212
213 Specifier::Char(
214 unsafe { args.arg::<<c_char as CharToInt>::IntType>() } as c_char
215 )
216 }
217 b'p' => Specifier::Pointer(unsafe { args.arg() }),
218 b'n' => Specifier::WriteBytesWritten(written, unsafe { args.arg() }),
219 _ => return -1,
220 },
221 }));
222 err!(handler(Specifier::Bytes(next_char(sub)).into()));
223 }
224 written
225}