blob: f5ec1faacdb6fdde7b859a092822a68009b934e3 [file] [log] [blame]
xiaohu.huangbf62d7b2023-07-18 18:02:43 +08001/*
2 * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7#include <stdio.h>
8#include <stdarg.h>
9#include "aml_vsnprintf.h"
10
11#include "aml_strnlen.h"
12#include "aml_isdigit.h"
13#include <stdarg.h>
14#include <stddef.h>
15
16#if CONFIG_LIBC_AML_FLOAT_PRINT
17#define HAS_FLOAT 1
18#else
19#define HAS_FLOAT 0
20#endif
21
22#define ZEROPAD (1 << 0) /* Pad with zero */
23#define SIGN (1 << 1) /* Unsigned/signed long */
24#define PLUS (1 << 2) /* Show plus */
25#define SPACE (1 << 3) /* Spacer */
26#define LEFT (1 << 4) /* Left justified */
27#define HEX_PREP (1 << 5) /* 0x */
28#define UPPERCASE (1 << 6) /* 'ABCDEF' */
29
30static char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
31static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
32
33static int skip_atoi(const char **s)
34{
35 int i = 0;
36
37 while (isdigit(**s))
38 i = i * 10 + *((*s)++) - '0';
39
40 return i;
41}
42
43static char *number(char *str, long num, int base, int size, int precision, int type)
44{
45 char c, sign, tmp[66];
46 char *dig = digits;
47 int i;
48
49 if (type & UPPERCASE)
50 dig = upper_digits;
51 if (type & LEFT)
52 type &= ~ZEROPAD;
53 if (base < 2 || base > 36)
54 return 0;
55
56 c = (type & ZEROPAD) ? '0' : ' ';
57 sign = 0;
58 if (type & SIGN) {
59 if (num < 0) {
60 sign = '-';
61 num = -num;
62 size--;
63 } else if (type & PLUS) {
64 sign = '+';
65 size--;
66 } else if (type & SPACE) {
67 sign = ' ';
68 size--;
69 }
70 }
71
72 if (type & HEX_PREP) {
73 if (base == 16)
74 size -= 2;
75 else if (base == 8)
76 size--;
77 }
78
79 i = 0;
80
81 if (num == 0)
82 tmp[i++] = '0';
83 else {
84 while (num != 0) {
85 tmp[i++] = dig[((unsigned long)num) % (unsigned int)base];
86 num = ((unsigned long)num) / (unsigned int)base;
87 }
88 }
89
90 if (i > precision)
91 precision = i;
92 size -= precision;
93 if (!(type & (ZEROPAD | LEFT)))
94 while (size-- > 0)
95 *str++ = ' ';
96 if (sign)
97 *str++ = sign;
98
99 if (type & HEX_PREP) {
100 if (base == 8)
101 *str++ = '0';
102 else if (base == 16) {
103 *str++ = '0';
104 *str++ = digits[33];
105 }
106 }
107
108 if (!(type & LEFT))
109 while (size-- > 0)
110 *str++ = c;
111 while (i < precision--)
112 *str++ = '0';
113 while (i-- > 0)
114 *str++ = tmp[i];
115 while (size-- > 0)
116 *str++ = ' ';
117
118 return str;
119}
120
121static char *eaddr(char *str, unsigned char *addr, int size, int precision, int type)
122{
123 char tmp[24];
124 char *dig = digits;
125 int i, len;
126
127 if (type & UPPERCASE)
128 dig = upper_digits;
129 len = 0;
130 for (i = 0; i < 6; i++) {
131 if (i != 0)
132 tmp[len++] = ':';
133 tmp[len++] = dig[addr[i] >> 4];
134 tmp[len++] = dig[addr[i] & 0x0F];
135 }
136
137 if (!(type & LEFT))
138 while (len < size--)
139 *str++ = ' ';
140 for (i = 0; i < len; ++i)
141 *str++ = tmp[i];
142 while (len < size--)
143 *str++ = ' ';
144
145 return str;
146}
147
148static char *iaddr(char *str, unsigned char *addr, int size, int precision, int type)
149{
150 char tmp[24];
151 int i, n, len;
152
153 len = 0;
154 for (i = 0; i < 4; i++) {
155 if (i != 0)
156 tmp[len++] = '.';
157 n = addr[i];
158
159 if (n == 0)
160 tmp[len++] = digits[0];
161 else {
162 if (n >= 100) {
163 tmp[len++] = digits[n / 100];
164 n = n % 100;
165 tmp[len++] = digits[n / 10];
166 n = n % 10;
167 } else if (n >= 10) {
168 tmp[len++] = digits[n / 10];
169 n = n % 10;
170 }
171
172 tmp[len++] = digits[n];
173 }
174 }
175
176 if (!(type & LEFT))
177 while (len < size--)
178 *str++ = ' ';
179 for (i = 0; i < len; ++i)
180 *str++ = tmp[i];
181 while (len < size--)
182 *str++ = ' ';
183
184 return str;
185}
186
187char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf);
188char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf);
189static void ee_bufcpy(char *d, char *s, int count);
190
191void ee_bufcpy(char *pd, char *ps, int count)
192{
193 char *pe = ps + count;
194
195 while (ps != pe)
196 *pd++ = *ps++;
197}
198
199static void parse_float(double value, char *buffer, char fmt, int precision)
200{
201 int decpt, sign, exp, pos;
202 char *digits = NULL;
203 char cvtbuf[80];
204 int capexp = 0;
205 int magnitude;
206
207 if (fmt == 'G' || fmt == 'E') {
208 capexp = 1;
209 fmt += 'a' - 'A';
210 }
211
212 if (fmt == 'g') {
213 digits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf);
214 magnitude = decpt - 1;
215 if (magnitude < -4 || magnitude > precision - 1) {
216 fmt = 'e';
217 precision -= 1;
218 } else {
219 fmt = 'f';
220 precision -= decpt;
221 }
222 }
223
224 if (fmt == 'e') {
225 digits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf);
226
227 if (sign)
228 *buffer++ = '-';
229 *buffer++ = *digits;
230 if (precision > 0)
231 *buffer++ = '.';
232 ee_bufcpy(buffer, digits + 1, precision);
233 buffer += precision;
234 *buffer++ = capexp ? 'E' : 'e';
235
236 if (decpt == 0) {
237 if (value == 0.0)
238 exp = 0;
239 else
240 exp = -1;
241 } else
242 exp = decpt - 1;
243
244 if (exp < 0) {
245 *buffer++ = '-';
246 exp = -exp;
247 } else
248 *buffer++ = '+';
249
250 buffer[2] = (exp % 10) + '0';
251 exp = exp / 10;
252 buffer[1] = (exp % 10) + '0';
253 exp = exp / 10;
254 buffer[0] = (exp % 10) + '0';
255 buffer += 3;
256 } else if (fmt == 'f') {
257 digits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf);
258 if (sign)
259 *buffer++ = '-';
260 if (*digits) {
261 if (decpt <= 0) {
262 *buffer++ = '0';
263 *buffer++ = '.';
264 for (pos = 0; pos < -decpt; pos++)
265 *buffer++ = '0';
266 while (*digits)
267 *buffer++ = *digits++;
268 } else {
269 pos = 0;
270 while (*digits) {
271 if (pos++ == decpt)
272 *buffer++ = '.';
273 *buffer++ = *digits++;
274 }
275 }
276 } else {
277 *buffer++ = '0';
278 if (precision > 0) {
279 *buffer++ = '.';
280 for (pos = 0; pos < precision; pos++)
281 *buffer++ = '0';
282 }
283 }
284 }
285
286 *buffer = '\0';
287}
288
289static void decimal_point(char *buffer)
290{
291 while (*buffer) {
292 if (*buffer == '.')
293 return;
294 if (*buffer == 'e' || *buffer == 'E')
295 break;
296 buffer++;
297 }
298
299 if (*buffer) {
300 int n = strnlen(buffer, 256);
301
302 while (n > 0) {
303 buffer[n + 1] = buffer[n];
304 n--;
305 }
306
307 *buffer = '.';
308 } else {
309 *buffer++ = '.';
310 *buffer = '\0';
311 }
312}
313
314static void cropzeros(char *buffer)
315{
316 char *stop;
317
318 while (*buffer && *buffer != '.')
319 buffer++;
320 if (*buffer++) {
321 while (*buffer && *buffer != 'e' && *buffer != 'E')
322 buffer++;
323 stop = buffer--;
324 while (*buffer == '0')
325 buffer--;
326 if (*buffer == '.')
327 buffer--;
328 while (buffer != stop)
329 *++buffer = 0;
330 }
331}
332
333static char *flt(char *str, double num, int size, int precision, char fmt, int flags)
334{
335 char tmp[80];
336 char c, sign;
337 int n, i;
338
339 // Left align means no zero padding
340 if (flags & LEFT)
341 flags &= ~ZEROPAD;
342
343 // Determine padding and sign char
344 c = (flags & ZEROPAD) ? '0' : ' ';
345 sign = 0;
346 if (flags & SIGN) {
347 if (num < 0.0) {
348 sign = '-';
349 num = -num;
350 size--;
351 } else if (flags & PLUS) {
352 sign = '+';
353 size--;
354 } else if (flags & SPACE) {
355 sign = ' ';
356 size--;
357 }
358 }
359
360 // Compute the precision value
361 if (precision < 0)
362 precision = 6; // Default precision: 6
363
364 // Convert floating point number to text
365 parse_float(num, tmp, fmt, precision);
366
367 if ((flags & HEX_PREP) && precision == 0)
368 decimal_point(tmp);
369 if (fmt == 'g' && !(flags & HEX_PREP))
370 cropzeros(tmp);
371
372 n = strnlen(tmp, 256);
373
374 // Output number with alignment and padding
375 size -= n;
376 if (!(flags & (ZEROPAD | LEFT)))
377 while (size-- > 0)
378 *str++ = ' ';
379 if (sign)
380 *str++ = sign;
381 if (!(flags & LEFT))
382 while (size-- > 0)
383 *str++ = c;
384 for (i = 0; i < n; i++)
385 *str++ = tmp[i];
386 while (size-- > 0)
387 *str++ = ' ';
388
389 return str;
390}
391
392int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
393{
394 int len;
395 unsigned long num;
396 int i, base;
397 char *str;
398 char *s;
399
400 int flags; // Flags to number()
401
402 int field_width; // Width of output field
403 int precision; // Min. # of digits for integers; max number of chars for from string
404 int qualifier; // 'h', 'l', or 'L' for integer fields
405
406 for (str = buf; *fmt; fmt++) {
407 if (*fmt != '%') {
408 *str++ = *fmt;
409 continue;
410 }
411
412 // Process flags
413 flags = 0;
414repeat:
415 fmt++; // This also skips first '%'
416 switch (*fmt) {
417 case '-':
418 flags |= LEFT;
419 goto repeat;
420 case '+':
421 flags |= PLUS;
422 goto repeat;
423 case ' ':
424 flags |= SPACE;
425 goto repeat;
426 case '#':
427 flags |= HEX_PREP;
428 goto repeat;
429 case '0':
430 flags |= ZEROPAD;
431 goto repeat;
432 }
433
434 // Get field width
435 field_width = -1;
436 if (isdigit(*fmt))
437 field_width = skip_atoi(&fmt);
438 else if (*fmt == '*') {
439 fmt++;
440 field_width = va_arg(args, int);
441 if (field_width < 0) {
442 field_width = -field_width;
443 flags |= LEFT;
444 }
445 }
446
447 // Get the precision
448 precision = -1;
449 if (*fmt == '.') {
450 ++fmt;
451 if (isdigit(*fmt))
452 precision = skip_atoi(&fmt);
453 else if (*fmt == '*') {
454 ++fmt;
455 precision = va_arg(args, int);
456 }
457 if (precision < 0)
458 precision = 0;
459 }
460
461 // Get the conversion qualifier
462 qualifier = -1;
463 if (*fmt == 'l' || *fmt == 'L') {
464 qualifier = *fmt;
465 fmt++;
466 }
xiaohu.huangee0b3352024-11-26 10:32:42 +0800467#if CONFIG_LIBC_AML_LONG_LONG_PRINT
468 if (((*(fmt-1) == 'l') && (*fmt == 'l'))
469 || ((*(fmt - 1) == 'L') && (*fmt == 'L'))) {
470 qualifier = 'j';
471 fmt++;
472 }
473#endif
xiaohu.huangbf62d7b2023-07-18 18:02:43 +0800474
475 // Default base
476 base = 10;
477
478 switch (*fmt) {
479 case 'c':
480 if (!(flags & LEFT))
481 while (--field_width > 0)
482 *str++ = ' ';
483 *str++ = (unsigned char)va_arg(args, int);
484 while (--field_width > 0)
485 *str++ = ' ';
486 continue;
487
488 case 's':
489 s = va_arg(args, char *);
490 if (!s)
491 s = "<NULL>";
492 len = strnlen(s, size - (str-buf) - 1);
493 if (!(flags & LEFT))
494 while (len < field_width--)
495 *str++ = ' ';
496 for (i = 0; i < len; ++i)
497 *str++ = *s++;
498 while (len < field_width--)
499 *str++ = ' ';
500 continue;
501
502 case 'p':
503 if (field_width == -1) {
504 field_width = 2 * sizeof(void *);
505 flags |= ZEROPAD;
506 }
507 str = number(str, (unsigned long)va_arg(args, void *), 16, field_width,
508 precision, flags);
509 continue;
510
511 case 'A':
512 flags |= UPPERCASE;
513
514 case 'a':
515 if (qualifier == 'l')
516 str = eaddr(str, va_arg(args, unsigned char *), field_width,
517 precision, flags);
518 else
519 str = iaddr(str, va_arg(args, unsigned char *), field_width,
520 precision, flags);
521 continue;
522
523 // Integer number formats - set up the flags and "break"
524 case 'o':
525 base = 8;
526 break;
527
528 case 'X':
529 flags |= UPPERCASE;
530
531 case 'x':
532 base = 16;
533 break;
534
535 case 'd':
536 case 'i':
537 flags |= SIGN;
538
539 case 'u':
540 break;
541
542#if HAS_FLOAT
543 case 'f':
544 str = flt(str, va_arg(args, double), field_width, precision, *fmt,
545 flags | SIGN);
546 continue;
547
548#endif
549
550 default:
551 if (*fmt != '%')
552 *str++ = '%';
553 if (*fmt)
554 *str++ = *fmt;
555 else
556 --fmt;
557 continue;
558 }
559
560 if (qualifier == 'l')
561 num = va_arg(args, unsigned long);
xiaohu.huangee0b3352024-11-26 10:32:42 +0800562#if CONFIG_LIBC_AML_LONG_LONG_PRINT
563 else if (qualifier == 'j')
564 if (flags & SIGN)
565 num = va_arg(args, signed long long);
566 else
567 num = va_arg(args, unsigned long long);
568#endif
xiaohu.huangbf62d7b2023-07-18 18:02:43 +0800569 else if (flags & SIGN)
570 num = va_arg(args, int);
571 else
572 num = va_arg(args, unsigned int);
573
574 str = number(str, num, base, field_width, precision, flags);
575 }
576
577 *str = '\0';
578 return str - buf;
579}