blob: 0b14c7d1252607661bc7af6f360cb6a5e8ca3ae5 [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 }
467
468 // Default base
469 base = 10;
470
471 switch (*fmt) {
472 case 'c':
473 if (!(flags & LEFT))
474 while (--field_width > 0)
475 *str++ = ' ';
476 *str++ = (unsigned char)va_arg(args, int);
477 while (--field_width > 0)
478 *str++ = ' ';
479 continue;
480
481 case 's':
482 s = va_arg(args, char *);
483 if (!s)
484 s = "<NULL>";
485 len = strnlen(s, size - (str-buf) - 1);
486 if (!(flags & LEFT))
487 while (len < field_width--)
488 *str++ = ' ';
489 for (i = 0; i < len; ++i)
490 *str++ = *s++;
491 while (len < field_width--)
492 *str++ = ' ';
493 continue;
494
495 case 'p':
496 if (field_width == -1) {
497 field_width = 2 * sizeof(void *);
498 flags |= ZEROPAD;
499 }
500 str = number(str, (unsigned long)va_arg(args, void *), 16, field_width,
501 precision, flags);
502 continue;
503
504 case 'A':
505 flags |= UPPERCASE;
506
507 case 'a':
508 if (qualifier == 'l')
509 str = eaddr(str, va_arg(args, unsigned char *), field_width,
510 precision, flags);
511 else
512 str = iaddr(str, va_arg(args, unsigned char *), field_width,
513 precision, flags);
514 continue;
515
516 // Integer number formats - set up the flags and "break"
517 case 'o':
518 base = 8;
519 break;
520
521 case 'X':
522 flags |= UPPERCASE;
523
524 case 'x':
525 base = 16;
526 break;
527
528 case 'd':
529 case 'i':
530 flags |= SIGN;
531
532 case 'u':
533 break;
534
535#if HAS_FLOAT
536 case 'f':
537 str = flt(str, va_arg(args, double), field_width, precision, *fmt,
538 flags | SIGN);
539 continue;
540
541#endif
542
543 default:
544 if (*fmt != '%')
545 *str++ = '%';
546 if (*fmt)
547 *str++ = *fmt;
548 else
549 --fmt;
550 continue;
551 }
552
553 if (qualifier == 'l')
554 num = va_arg(args, unsigned long);
555 else if (flags & SIGN)
556 num = va_arg(args, int);
557 else
558 num = va_arg(args, unsigned int);
559
560 str = number(str, num, base, field_width, precision, flags);
561 }
562
563 *str = '\0';
564 return str - buf;
565}