1use alloc::string::String;
2use core::{ffi::c_char, fmt};
3
4use axio::Write;
5
6use crate::ctypes;
7
8pub trait WriteByte: fmt::Write {
9 fn write_u8(&mut self, byte: u8) -> fmt::Result;
10}
11
12struct StringWriter(pub *mut u8, pub usize);
13
14impl Write for StringWriter {
15 fn write(&mut self, buf: &[u8]) -> axerrno::AxResult<usize> {
16 if self.1 > 1 {
17 let copy_size = buf.len().min(self.1 - 1);
18 unsafe {
19 core::ptr::copy_nonoverlapping(buf.as_ptr(), self.0, copy_size);
20 self.1 -= copy_size;
21
22 self.0 = self.0.add(copy_size);
23 *self.0 = 0;
24 }
25 }
26 Ok(buf.len())
27 }
28 fn flush(&mut self) -> axerrno::AxResult {
29 Ok(())
30 }
31}
32
33impl fmt::Write for StringWriter {
34 fn write_str(&mut self, s: &str) -> fmt::Result {
35 self.write(s.as_bytes()).unwrap();
37 Ok(())
38 }
39}
40
41impl WriteByte for StringWriter {
42 fn write_u8(&mut self, byte: u8) -> fmt::Result {
43 self.write(&[byte]).unwrap();
45 Ok(())
46 }
47}
48
49struct CountingWriter<T> {
50 pub inner: T,
51 pub written: usize,
52}
53
54impl<T> CountingWriter<T> {
55 pub fn new(writer: T) -> Self {
56 Self {
57 inner: writer,
58 written: 0,
59 }
60 }
61}
62
63impl<T: fmt::Write> fmt::Write for CountingWriter<T> {
64 fn write_str(&mut self, s: &str) -> fmt::Result {
65 self.written += s.len();
66 self.inner.write_str(s)
67 }
68}
69
70impl<T: WriteByte> WriteByte for CountingWriter<T> {
71 fn write_u8(&mut self, byte: u8) -> fmt::Result {
72 self.written += 1;
73 self.inner.write_u8(byte)
74 }
75}
76
77impl<T: Write> Write for CountingWriter<T> {
78 fn write(&mut self, buf: &[u8]) -> axerrno::AxResult<usize> {
79 let res = self.inner.write(buf);
80 if let Ok(written) = res {
81 self.written += written;
82 }
83 res
84 }
85
86 fn write_all(&mut self, buf: &[u8]) -> axerrno::AxResult {
87 match self.inner.write_all(buf) {
88 Ok(()) => (),
89 Err(err) => return Err(err),
90 }
91 self.written += buf.len();
92 Ok(())
93 }
94
95 fn flush(&mut self) -> axerrno::AxResult {
96 self.inner.flush()
97 }
98}
99
100unsafe fn strftime_inner<W: WriteByte>(
101 w: W,
102 format: *const c_char,
103 t: *const ctypes::tm,
104) -> ctypes::size_t {
105 #[allow(clippy::unnecessary_cast)]
106 pub unsafe fn inner_strftime<W: WriteByte>(
107 w: &mut W,
108 mut format: *const c_char,
109 t: *const ctypes::tm,
110 ) -> bool {
111 macro_rules! w {
112 (byte $b:expr) => {{
113 if w.write_u8($b).is_err() {
114 return false;
115 }
116 }};
117 (char $chr:expr) => {{
118 if w.write_char($chr).is_err() {
119 return false;
120 }
121 }};
122 (recurse $fmt:expr) => {{
123 let mut fmt = String::with_capacity($fmt.len() + 1);
124 fmt.push_str($fmt);
125 fmt.push('\0');
126
127 if !inner_strftime(w, fmt.as_ptr() as *mut c_char, t) {
128 return false;
129 }
130 }};
131 ($str:expr) => {{
132 if w.write_str($str).is_err() {
133 return false;
134 }
135 }};
136 ($fmt:expr, $($args:expr),+) => {{
137 if write!(w, $fmt, $($args),+).is_err() {
139 return false;
140 }
141 }};
142 }
143 const WDAYS: [&str; 7] = [
144 "Sunday",
145 "Monday",
146 "Tuesday",
147 "Wednesday",
148 "Thursday",
149 "Friday",
150 "Saturday",
151 ];
152 const MONTHS: [&str; 12] = [
153 "January",
154 "Febuary",
155 "March",
156 "April",
157 "May",
158 "June",
159 "July",
160 "August",
161 "September",
162 "October",
163 "November",
164 "December",
165 ];
166
167 while *format != 0 {
168 if *format as u8 != b'%' {
169 w!(byte * format as u8);
170 format = format.offset(1);
171 continue;
172 }
173
174 format = format.offset(1);
175
176 if *format as u8 == b'E' || *format as u8 == b'O' {
177 format = format.offset(1);
179 }
180
181 match *format as u8 {
182 b'%' => w!(byte b'%'),
183 b'n' => w!(byte b'\n'),
184 b't' => w!(byte b'\t'),
185 b'a' => w!(&WDAYS[(*t).tm_wday as usize][..3]),
186 b'A' => w!(WDAYS[(*t).tm_wday as usize]),
187 b'b' | b'h' => w!(&MONTHS[(*t).tm_mon as usize][..3]),
188 b'B' => w!(MONTHS[(*t).tm_mon as usize]),
189 b'C' => {
190 let mut year = (*t).tm_year / 100;
191 if (*t).tm_year % 100 != 0 {
193 year += 1;
194 }
195 w!("{:02}", year + 19);
196 }
197 b'd' => w!("{:02}", (*t).tm_mday),
198 b'D' => w!(recurse "%m/%d/%y"),
199 b'e' => w!("{:2}", (*t).tm_mday),
200 b'F' => w!(recurse "%Y-%m-%d"),
201 b'H' => w!("{:02}", (*t).tm_hour),
202 b'I' => w!("{:02}", ((*t).tm_hour + 12 - 1) % 12 + 1),
203 b'j' => w!("{:03}", (*t).tm_yday),
204 b'k' => w!("{:2}", (*t).tm_hour),
205 b'l' => w!("{:2}", ((*t).tm_hour + 12 - 1) % 12 + 1),
206 b'm' => w!("{:02}", (*t).tm_mon + 1),
207 b'M' => w!("{:02}", (*t).tm_min),
208 b'p' => w!(if (*t).tm_hour < 12 { "AM" } else { "PM" }),
209 b'P' => w!(if (*t).tm_hour < 12 { "am" } else { "pm" }),
210 b'r' => w!(recurse "%I:%M:%S %p"),
211 b'R' => w!(recurse "%H:%M"),
212 b's' => w!("{}", super::mktime(t as *mut ctypes::tm)),
214 b'S' => w!("{:02}", (*t).tm_sec),
215 b'T' => w!(recurse "%H:%M:%S"),
216 b'u' => w!("{}", ((*t).tm_wday + 7 - 1) % 7 + 1),
217 b'U' => w!("{}", ((*t).tm_yday + 7 - (*t).tm_wday) / 7),
218 b'w' => w!("{}", (*t).tm_wday),
219 b'W' => w!("{}", ((*t).tm_yday + 7 - ((*t).tm_wday + 6) % 7) / 7),
220 b'y' => w!("{:02}", (*t).tm_year % 100),
221 b'Y' => w!("{}", (*t).tm_year + 1900),
222 b'z' => w!("+0000"), b'Z' => w!("UTC"), b'+' => w!(recurse "%a %b %d %T %Z %Y"),
225 _ => return false,
226 }
227
228 format = format.offset(1);
229 }
230 true
231 }
232
233 let mut w: CountingWriter<W> = CountingWriter::new(w);
234 if !inner_strftime(&mut w, format, t) {
235 return 0;
236 }
237
238 w.written
239}
240
241#[unsafe(no_mangle)]
243#[allow(clippy::unnecessary_cast)] pub unsafe extern "C" fn strftime(
245 buf: *mut c_char,
246 size: ctypes::size_t,
247 format: *const c_char,
248 timeptr: *const ctypes::tm,
249) -> ctypes::size_t {
250 let ret = strftime_inner(StringWriter(buf as *mut u8, size), format, timeptr);
251 if ret < size { ret } else { 0 }
252}