1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 * + Contributed by bellrise <bellrise.dev@gmail.com>
5 *
6 * Use of this source code is governed by a BSD-style license that can be
7 * found in the LICENSE file.
8 */
9#include "_internal.h"
10#include <errno.h>
11#include <stdarg.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/_structs.h>
16#include <sys/types.h>
17#include <unistd.h>
18
19static const char* HEX_alphabet = "0123456789ABCDEF";
20static const char* hex_alphabet = "0123456789abcdef";
21
22static int _printf_hex32_impl(unsigned int value, const char* alph, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
23{
24 int nxt = 0;
25 char tmp_buf[16];
26
27 while (value > 0) {
28 tmp_buf[nxt++] = alph[(value % 16)];
29 value /= 16;
30 }
31
32 callback('0', base_buf, written, callback_params);
33 callback('x', base_buf, written, callback_params);
34 if (nxt == 0) {
35 callback('0', base_buf, written, callback_params);
36 }
37
38 while (nxt) {
39 callback(tmp_buf[--nxt], base_buf, written, callback_params);
40 }
41 return 0;
42}
43
44static int _printf_hex64_impl(unsigned long value, const char* alph, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
45{
46 int nxt = 0;
47 char tmp_buf[32];
48
49 while (value > 0) {
50 tmp_buf[nxt++] = alph[(value % 16)];
51 value /= 16;
52 }
53
54 callback('0', base_buf, written, callback_params);
55 callback('x', base_buf, written, callback_params);
56 if (nxt == 0) {
57 callback('0', base_buf, written, callback_params);
58 }
59
60 while (nxt) {
61 callback(tmp_buf[--nxt], base_buf, written, callback_params);
62 }
63 return 0;
64}
65
66static int _printf_hex32(unsigned int value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
67{
68 return _printf_hex32_impl(value, hex_alphabet, base_buf, written, callback, callback_params);
69}
70
71static int _printf_HEX32(unsigned int value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
72{
73 return _printf_hex32_impl(value, HEX_alphabet, base_buf, written, callback, callback_params);
74}
75
76static int _printf_hex64(unsigned long value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
77{
78 return _printf_hex64_impl(value, hex_alphabet, base_buf, written, callback, callback_params);
79}
80
81static int _printf_HEX64(unsigned long value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
82{
83 return _printf_hex64_impl(value, HEX_alphabet, base_buf, written, callback, callback_params);
84}
85
86static int _printf_u32(unsigned int value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
87{
88 int nxt = 0;
89 char tmp_buf[16];
90
91 while (value > 0) {
92 tmp_buf[nxt++] = (value % 10) + '0';
93 value /= 10;
94 }
95
96 if (nxt == 0) {
97 callback('0', base_buf, written, callback_params);
98 }
99
100 while (nxt) {
101 callback(tmp_buf[--nxt], base_buf, written, callback_params);
102 }
103 return 0;
104}
105
106static int _printf_u64(unsigned long value, char* base_buf, size_t* written, _putch_callback callback, void* callback_params)
107{
108 int nxt = 0;
109 char tmp_buf[32];
110
111 while (value > 0) {
112 tmp_buf[nxt++] = (value % 10) + '0';
113 value /= 10;
114 }
115
116 if (nxt == 0) {
117 callback('0', base_buf, written, callback_params);
118 }
119
120 while (nxt) {
121 callback(tmp_buf[--nxt], base_buf, written, callback_params);
122 }
123 return 0;
124}
125
126static int _printf_i32(int value, char* buf, size_t* written, _putch_callback callback, void* callback_params)
127{
128 if (value < 0) {
129 callback('-', buf, written, callback_params);
130 value = -value;
131 }
132 return _printf_u32(value, buf, written, callback, callback_params);
133}
134
135static int _printf_i64(long value, char* buf, size_t* written, _putch_callback callback, void* callback_params)
136{
137 if (value < 0) {
138 callback('-', buf, written, callback_params);
139 value = -value;
140 }
141 return _printf_u64(value, buf, written, callback, callback_params);
142}
143
144static int _printf_string(const char* value, char* buf, size_t* written, _putch_callback callback, void* callback_params)
145{
146 size_t len = strlen(value);
147 for (size_t i = 0; i < len; i++) {
148 callback(value[i], buf, written, callback_params);
149 }
150 return 0;
151}
152
153static ssize_t _printf_internal(char* buf, const char* format, _putch_callback callback, void* callback_params, va_list arg)
154{
155 const char* p = format;
156 size_t written = 0;
157 while (*p) {
158 int l_arg = 0;
159 int h_arg = 0;
160 if (*p == '%' && *(p + 1)) {
161 // Reading arguments
162 parse_args:
163 p++;
164 switch (*p) {
165 case 'l':
166 l_arg++;
167 if (*(p + 1)) {
168 goto parse_args;
169 }
170 break;
171 case 'h':
172 h_arg++;
173 if (*(p + 1)) {
174 goto parse_args;
175 }
176 break;
177 default:
178 break;
179 }
180
181 // Reading conversion specifiers
182 switch (*p) {
183 case 'i':
184 case 'd':
185 if (l_arg) {
186 long value = va_arg(arg, long);
187 _printf_i64(value, buf, &written, callback, callback_params);
188 } else {
189 int value = va_arg(arg, int);
190 _printf_i32(value, buf, &written, callback, callback_params);
191 }
192 break;
193 case 'u':
194 if (l_arg) {
195 uint64_t value = va_arg(arg, uint64_t);
196 _printf_u64(value, buf, &written, callback, callback_params);
197 } else {
198 uint32_t value = va_arg(arg, uint32_t);
199 _printf_u32(value, buf, &written, callback, callback_params);
200 }
201 break;
202 case 'x':
203 if (l_arg) {
204 uint64_t value = va_arg(arg, uint64_t);
205 _printf_hex64(value, buf, &written, callback, callback_params);
206 } else {
207 uint32_t value = va_arg(arg, uint32_t);
208 _printf_hex32(value, buf, &written, callback, callback_params);
209 }
210 break;
211 case 'X':
212 if (l_arg) {
213 uint64_t value = va_arg(arg, uint64_t);
214 _printf_HEX64(value, buf, &written, callback, callback_params);
215 } else {
216 uint32_t value = va_arg(arg, uint32_t);
217 _printf_HEX32(value, buf, &written, callback, callback_params);
218 }
219 break;
220 case 'c': {
221 char value = (char)va_arg(arg, int);
222 callback(value, buf, &written, callback_params);
223 } break;
224 case 's': {
225 const char* value = va_arg(arg, const char*);
226 _printf_string(value, buf, &written, callback, callback_params);
227 } break;
228 default:
229 break;
230 }
231 } else {
232 callback(*p, buf, &written, callback_params);
233 }
234 p++;
235 }
236 return written;
237}
238
239static int putch_callback_sized_buf(char ch, char* buf_base, size_t* written, void* callback_params)
240{
241 if (!callback_params) {
242 return -1;
243 }
244
245 if (!written) {
246 return -1;
247 }
248
249 size_t n = *(size_t*)callback_params;
250 size_t vw = *written;
251 if (vw >= n) {
252 return -1;
253 }
254 buf_base[vw++] = ch;
255 *written = vw;
256 return 0;
257}
258
259int vsnprintf(char* s, size_t n, const char* format, va_list arg)
260{
261 if (!s) {
262 return 0;
263 }
264
265 if (!n) {
266 return 0;
267 }
268
269 ssize_t wr = _printf_internal(s, format, putch_callback_sized_buf, &n, arg);
270 if (wr == n) {
271 s[n - 1] = '\0';
272 } else {
273 s[wr] = '\0';
274 }
275 return (int)wr;
276}
277
278int snprintf(char* s, size_t n, const char* format, ...)
279{
280 va_list arg;
281 va_start(arg, format);
282 int res = vsnprintf(s, n, format, arg);
283 va_end(arg);
284 return res;
285}
286
287static int putch_callback_buf(char ch, char* buf_base, size_t* written, void* callback_params)
288{
289 if (!written) {
290 return -1;
291 }
292
293 size_t vw = *written;
294 buf_base[vw++] = ch;
295 *written = vw;
296 return 0;
297}
298
299int vsprintf(char* s, const char* format, va_list arg)
300{
301 if (!s) {
302 return 0;
303 }
304 ssize_t wr = _printf_internal(s, format, putch_callback_buf, NULL, arg);
305 s[wr] = '\0';
306 return (int)wr;
307}
308
309int sprintf(char* s, const char* format, ...)
310{
311 va_list arg;
312 va_start(arg, format);
313 int res = vsprintf(s, format, arg);
314 va_end(arg);
315 return res;
316}
317
318static int putch_callback_stream(char c, char* buf_base, size_t* written, void* callback_params)
319{
320 FILE* stream = (FILE*)callback_params;
321 if (!stream) {
322 return -1;
323 }
324 return fputc(c, stream);
325}
326
327static int vfprintf(FILE* stream, const char* format, va_list arg)
328{
329 return _printf_internal(NULL, format, putch_callback_stream, stream, arg);
330}
331
332int fprintf(FILE* stream, const char* format, ...)
333{
334 va_list arg;
335 va_start(arg, format);
336 int res = vfprintf(stream, format, arg);
337 va_end(arg);
338 return res;
339}
340
341static int vprintf(const char* format, va_list arg)
342{
343 return vfprintf(stdout, format, arg);
344}
345
346int printf(const char* format, ...)
347{
348 va_list arg;
349 va_start(arg, format);
350 int ret = vprintf(format, arg);
351 va_end(arg);
352 return ret;
353}