1 /*
2 * Copyright (c) 2008-2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lk/debug.h>
9 #include <limits.h>
10 #include <printf.h>
11 #include <stdarg.h>
12 #include <sys/types.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <platform/debug.h>
16
17 // The main printf engine and all of the printf wrapper routines.
18 // It's important these are all in the same file, or at least all
19 // compiled with the same flags concerning floating point support.
20
21 #if WITH_NO_FP
22 #define FLOAT_PRINTF 0
23 #else
24 #define FLOAT_PRINTF 1
25 #endif
26
sprintf(char * str,const char * fmt,...)27 int sprintf(char *str, const char *fmt, ...) {
28 int err;
29
30 va_list ap;
31 va_start(ap, fmt);
32 err = vsprintf(str, fmt, ap);
33 va_end(ap);
34
35 return err;
36 }
37
snprintf(char * str,size_t len,const char * fmt,...)38 int snprintf(char *str, size_t len, const char *fmt, ...) {
39 int err;
40
41 va_list ap;
42 va_start(ap, fmt);
43 err = vsnprintf(str, len, fmt, ap);
44 va_end(ap);
45
46 return err;
47 }
48
vsprintf(char * str,const char * fmt,va_list ap)49 int vsprintf(char *str, const char *fmt, va_list ap) {
50 return vsnprintf(str, INT_MAX, fmt, ap);
51 }
52
53 struct _output_args {
54 char *outstr;
55 size_t len;
56 size_t pos;
57 };
58
_vsnprintf_output(const char * str,size_t len,void * state)59 static int _vsnprintf_output(const char *str, size_t len, void *state) {
60 struct _output_args *args = state;
61
62 size_t count = 0;
63 while (count < len) {
64 if (args->pos < args->len) {
65 args->outstr[args->pos++] = *str;
66 }
67
68 str++;
69 count++;
70 }
71
72 // Return the count of the number of bytes that would be written even if the buffer
73 // wasn't large enough.
74 return (int)count;
75 }
76
vsnprintf(char * str,size_t len,const char * fmt,va_list ap)77 int vsnprintf(char *str, size_t len, const char *fmt, va_list ap) {
78 struct _output_args args;
79 int wlen;
80
81 args.outstr = str;
82 args.len = len;
83 args.pos = 0;
84
85 wlen = _printf_engine(&_vsnprintf_output, (void *)&args, fmt, ap);
86 if (len == 0) {
87 // do nothing, we can't null terminate the output
88 } else if (args.pos >= len) {
89 str[len-1] = '\0';
90 } else {
91 str[wlen] = '\0';
92 }
93 return wlen;
94 }
95
vfprintf(FILE * fp,const char * fmt,va_list ap)96 int vfprintf(FILE *fp, const char *fmt, va_list ap) {
97 return _printf_engine(&_fprintf_output_func, (void *)fp, fmt, ap);
98 }
99
fprintf(FILE * fp,const char * fmt,...)100 int fprintf(FILE *fp, const char *fmt, ...) {
101 va_list ap;
102 int err;
103
104 va_start(ap, fmt);
105 err = vfprintf(fp, fmt, ap);
106 va_end(ap);
107 return err;
108 }
109
110 #if !DISABLE_DEBUG_OUTPUT
printf(const char * fmt,...)111 int printf(const char *fmt, ...) {
112 va_list ap;
113 int err;
114
115 va_start(ap, fmt);
116 err = vfprintf(stdout, fmt, ap);
117 va_end(ap);
118
119 return err;
120 }
121
vprintf(const char * fmt,va_list ap)122 int vprintf(const char *fmt, va_list ap) {
123 return vfprintf(stdout, fmt, ap);
124 }
125 #endif // !DISABLE_DEBUG_OUTPUT
126
127 #define LONGFLAG 0x00000001
128 #define LONGLONGFLAG 0x00000002
129 #define HALFFLAG 0x00000004
130 #define HALFHALFFLAG 0x00000008
131 #define SIZETFLAG 0x00000010
132 #define INTMAXFLAG 0x00000020
133 #define PTRDIFFFLAG 0x00000040
134 #define ALTFLAG 0x00000080
135 #define CAPSFLAG 0x00000100
136 #define SHOWSIGNFLAG 0x00000200
137 #define SIGNEDFLAG 0x00000400
138 #define LEFTFORMATFLAG 0x00000800
139 #define LEADZEROFLAG 0x00001000
140 #define BLANKPOSFLAG 0x00002000
141
longlong_to_string(char * buf,unsigned long long n,size_t len,uint flag,char * signchar)142 __NO_INLINE static char *longlong_to_string(char *buf, unsigned long long n, size_t len, uint flag, char *signchar) {
143 size_t pos = len;
144 bool negative = false;
145
146 if ((flag & SIGNEDFLAG) && (long long)n < 0) {
147 negative = true;
148 n = -n;
149 }
150
151 buf[--pos] = 0;
152
153 /* only do the math if the number is >= 10 */
154 while (n >= 10) {
155 unsigned int digit = n % 10;
156
157 n /= 10;
158
159 buf[--pos] = (char)digit + '0';
160 }
161 buf[--pos] = (char)n + '0';
162
163 if (negative) {
164 *signchar = '-';
165 } else if ((flag & SHOWSIGNFLAG)) {
166 *signchar = '+';
167 } else if ((flag & BLANKPOSFLAG)) {
168 *signchar = ' ';
169 } else {
170 *signchar = '\0';
171 }
172
173 return &buf[pos];
174 }
175
176 static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
177 static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
178
longlong_to_hexstring(char * buf,unsigned long long u,size_t len,uint flag)179 __NO_INLINE static char *longlong_to_hexstring(char *buf, unsigned long long u, size_t len, uint flag) {
180 size_t pos = len;
181 const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
182
183 buf[--pos] = 0;
184 do {
185 unsigned int digit = u % 16;
186 u /= 16;
187
188 buf[--pos] = table[digit];
189 } while (u != 0);
190
191 return &buf[pos];
192 }
193
194 #if FLOAT_PRINTF
195 union double_int {
196 double d;
197 uint64_t i;
198 };
199
200 #define OUT(c) buf[pos++] = (c)
201 #define OUTSTR(str) do { for (size_t i = 0; (str)[i] != 0; i++) OUT((str)[i]); } while (0)
202
203 /* print up to a 4 digit exponent as string, with sign */
exponent_to_string(char * buf,int32_t exponent)204 __NO_INLINE static size_t exponent_to_string(char *buf, int32_t exponent) {
205 size_t pos = 0;
206
207 /* handle sign */
208 if (exponent < 0) {
209 OUT('-');
210 exponent = -exponent;
211 } else {
212 OUT('+');
213 }
214
215 /* see how far we need to bump into the string to print from the right */
216 if (exponent >= 1000) pos += 4;
217 else if (exponent >= 100) pos += 3;
218 else if (exponent >= 10) pos += 2;
219 else pos++;
220
221 /* print decimal string, from the right */
222 uint i = pos;
223 do {
224 uint digit = (uint32_t)exponent % 10;
225
226 buf[--i] = (char)digit + '0';
227
228 exponent /= 10;
229 } while (exponent != 0);
230
231 /* return number of characters printed */
232 return pos;
233 }
234
double_to_string(char * buf,size_t len,double d,uint flag)235 __NO_INLINE static char *double_to_string(char *buf, size_t len, double d, uint flag) {
236 size_t pos = 0;
237 union double_int du = { d };
238
239 uint32_t exponent = (du.i >> 52) & 0x7ff;
240 uint64_t fraction = (du.i & ((1ULL << 52) - 1));
241 bool neg = !!(du.i & (1ULL << 63));
242
243 /* start constructing the string */
244 if (neg) {
245 OUT('-');
246 d = -d;
247 }
248
249 /* longest:
250 * 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000o
251 */
252
253 /* look for special cases */
254 if (exponent == 0x7ff) {
255 if (fraction == 0) {
256 /* infinity */
257 if (flag & CAPSFLAG) OUTSTR("INF");
258 else OUTSTR("inf");
259 } else {
260 /* NaN */
261 if (flag & CAPSFLAG) OUTSTR("NAN");
262 else OUTSTR("nan");
263 }
264 } else if (exponent == 0) {
265 if (fraction == 0) {
266 /* zero */
267 OUTSTR("0.000000");
268 } else {
269 /* denormalized */
270 /* XXX does not handle */
271 if (flag & CAPSFLAG) OUTSTR("DEN");
272 else OUTSTR("den");
273 }
274 } else {
275 /* see if it's in the range of floats we can easily print */
276 int exponent_signed = exponent - 1023;
277 if (exponent_signed < -52 || exponent_signed > 52) {
278 OUTSTR("<range>");
279 } else {
280 /* start by walking backwards through the string */
281 #define OUTREV(c) do { if (&buf[pos] == buf) goto done; else buf[--pos] = (c); } while (0)
282 pos = len;
283 OUTREV(0);
284
285 /* reserve space for the fractional component first */
286 for (int i = 0; i <= 6; i++)
287 OUTREV('0');
288 size_t decimal_spot = pos;
289
290 /* print the integer portion */
291 uint64_t u;
292 if (exponent_signed >= 0) {
293 u = fraction;
294 u |= (1ULL<<52);
295 u >>= (52 - exponent_signed);
296
297 char *s = longlong_to_string(buf, u, pos + 1, flag, &(char) {0});
298
299 pos = s - buf;
300 } else {
301 /* exponent is negative */
302 u = 0;
303 OUTREV('0');
304 }
305
306 buf[decimal_spot] = '.';
307
308 /* handle the fractional part */
309 uint32_t frac = ((d - u) * 1000000) + .5;
310
311 uint i = decimal_spot + 6 + 1;
312 while (frac != 0) {
313 uint digit = frac % 10;
314
315 buf[--i] = digit + '0';
316
317 frac /= 10;
318 }
319
320 if (neg)
321 OUTREV('-');
322
323 done:
324 /* separate return path, since we've been walking backwards through the string */
325 return &buf[pos];
326 }
327 #undef OUTREV
328 }
329
330 buf[pos] = 0;
331 return buf;
332 }
333
double_to_hexstring(char * buf,size_t len,double d,uint flag)334 __NO_INLINE static char *double_to_hexstring(char *buf, size_t len, double d, uint flag) {
335 size_t pos = 0;
336 union double_int u = { d };
337
338 uint32_t exponent = (u.i >> 52) & 0x7ff;
339 uint64_t fraction = (u.i & ((1ULL << 52) - 1));
340 bool neg = !!(u.i & (1ULL << 63));
341
342 /* start constructing the string */
343 if (neg) {
344 OUT('-');
345 }
346
347 /* look for special cases */
348 if (exponent == 0x7ff) {
349 if (fraction == 0) {
350 /* infinity */
351 if (flag & CAPSFLAG) OUTSTR("INF");
352 else OUTSTR("inf");
353 } else {
354 /* NaN */
355 if (flag & CAPSFLAG) OUTSTR("NAN");
356 else OUTSTR("nan");
357 }
358 } else if (exponent == 0) {
359 if (fraction == 0) {
360 /* zero */
361 if (flag & CAPSFLAG) OUTSTR("0X0P+0");
362 else OUTSTR("0x0p+0");
363 } else {
364 /* denormalized */
365 /* XXX does not handle */
366 if (flag & CAPSFLAG) OUTSTR("DEN");
367 else OUTSTR("den");
368 }
369 } else {
370 /* regular normalized numbers:
371 * 0x1p+1
372 * 0x1.0000000000001p+1
373 * 0X1.FFFFFFFFFFFFFP+1023
374 * 0x1.FFFFFFFFFFFFFP+1023
375 */
376 int32_t exponent_signed = exponent - 1023;
377
378 /* implicit 1. */
379 if (flag & CAPSFLAG) OUTSTR("0X1");
380 else OUTSTR("0x1");
381
382 /* select the appropriate hex case table */
383 const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
384
385 int zero_count = 0;
386 bool output_dot = false;
387 for (int i = 52 - 4; i >= 0; i -= 4) {
388 uint digit = (fraction >> i) & 0xf;
389
390 if (digit == 0) {
391 zero_count++;
392 } else {
393 /* output a . the first time we output a char */
394 if (!output_dot) {
395 OUT('.');
396 output_dot = true;
397 }
398 /* if we have a non zero digit, see if we need to output a string of zeros */
399 while (zero_count > 0) {
400 OUT('0');
401 zero_count--;
402 }
403 buf[pos++] = table[digit];
404 }
405 }
406
407 /* handle the exponent */
408 OUT((flag & CAPSFLAG) ? 'P' : 'p');
409 pos += exponent_to_string(&buf[pos], exponent_signed);
410 }
411
412 buf[pos] = 0;
413 return buf;
414 }
415
416 #undef OUT
417 #undef OUTSTR
418
419 #endif // FLOAT_PRINTF
420
_printf_engine(_printf_engine_output_func out,void * state,const char * fmt,va_list ap)421 int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap) {
422 int err = 0;
423 char c;
424 unsigned char uc;
425 const char *s;
426 size_t string_len;
427 unsigned long long n;
428 void *ptr;
429 int flags;
430 unsigned int format_num;
431 char signchar;
432 size_t chars_written = 0;
433 char num_buffer[32];
434
435 #define OUTPUT_STRING(str, len) do { err = out(str, len, state); if (err < 0) { goto exit; } else { chars_written += err; } } while(0)
436 #define OUTPUT_CHAR(c) do { char __temp[1] = { c }; OUTPUT_STRING(__temp, 1); } while (0)
437
438 for (;;) {
439 /* reset the format state */
440 flags = 0;
441 format_num = 0;
442 signchar = '\0';
443
444 /* handle regular chars that aren't format related */
445 s = fmt;
446 string_len = 0;
447 while ((c = *fmt++) != 0) {
448 if (c == '%')
449 break; /* we saw a '%', break and start parsing format */
450 string_len++;
451 }
452
453 /* output the string we've accumulated */
454 OUTPUT_STRING(s, string_len);
455
456 /* make sure we haven't just hit the end of the string */
457 if (c == 0)
458 break;
459
460 next_format:
461 /* grab the next format character */
462 c = *fmt++;
463 if (c == 0)
464 break;
465
466 switch (c) {
467 case '0'...'9':
468 if (c == '0' && format_num == 0)
469 flags |= LEADZEROFLAG;
470 format_num *= 10;
471 format_num += c - '0';
472 goto next_format;
473 case '.':
474 /* XXX for now eat numeric formatting */
475 goto next_format;
476 case '%':
477 OUTPUT_CHAR('%');
478 break;
479 case 'c':
480 uc = va_arg(ap, unsigned int);
481 OUTPUT_CHAR(uc);
482 break;
483 case 's':
484 s = va_arg(ap, const char *);
485 if (s == 0)
486 s = "<null>";
487 flags &= ~LEADZEROFLAG; /* doesn't make sense for strings */
488 goto _output_string;
489 case '-':
490 flags |= LEFTFORMATFLAG;
491 goto next_format;
492 case '+':
493 flags |= SHOWSIGNFLAG;
494 goto next_format;
495 case ' ':
496 flags |= BLANKPOSFLAG;
497 goto next_format;
498 case '#':
499 flags |= ALTFLAG;
500 goto next_format;
501 case 'l':
502 if (flags & LONGFLAG)
503 flags |= LONGLONGFLAG;
504 flags |= LONGFLAG;
505 goto next_format;
506 case 'h':
507 if (flags & HALFFLAG)
508 flags |= HALFHALFFLAG;
509 flags |= HALFFLAG;
510 goto next_format;
511 case 'z':
512 flags |= SIZETFLAG;
513 goto next_format;
514 case 'j':
515 flags |= INTMAXFLAG;
516 goto next_format;
517 case 't':
518 flags |= PTRDIFFFLAG;
519 goto next_format;
520 case 'i':
521 case 'd':
522 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
523 (flags & LONGFLAG) ? va_arg(ap, long) :
524 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
525 (flags & HALFFLAG) ? (short)va_arg(ap, int) :
526 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) :
527 (flags & INTMAXFLAG) ? va_arg(ap, intmax_t) :
528 (flags & PTRDIFFFLAG) ? va_arg(ap, ptrdiff_t) :
529 va_arg(ap, int);
530 flags |= SIGNEDFLAG;
531 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
532 goto _output_string;
533 case 'u':
534 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
535 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
536 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
537 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
538 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
539 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
540 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
541 va_arg(ap, unsigned int);
542 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
543 goto _output_string;
544 case 'p':
545 flags |= SIZETFLAG | ALTFLAG;
546 goto hex;
547 case 'X':
548 flags |= CAPSFLAG;
549 /* fallthrough */
550 hex:
551 case 'x':
552 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
553 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
554 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
555 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
556 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
557 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
558 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
559 va_arg(ap, unsigned int);
560 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
561
562 /* Normalize c, since code in _output_string needs to know that this is printing hex */
563 c = 'x';
564
565 /* Altflag processing should be bypassed when n == 0 so that 0x is not prepended to it */
566 if (n == 0) {
567 flags &= ~ALTFLAG;
568 }
569
570 goto _output_string;
571 case 'n':
572 ptr = va_arg(ap, void *);
573 if (flags & LONGLONGFLAG)
574 *(long long *)ptr = chars_written;
575 else if (flags & LONGFLAG)
576 *(long *)ptr = chars_written;
577 else if (flags & HALFHALFFLAG)
578 *(signed char *)ptr = chars_written;
579 else if (flags & HALFFLAG)
580 *(short *)ptr = chars_written;
581 else if (flags & SIZETFLAG)
582 *(size_t *)ptr = chars_written;
583 else
584 *(int *)ptr = chars_written;
585 break;
586 #if FLOAT_PRINTF
587 case 'F':
588 flags |= CAPSFLAG;
589 /* fallthrough */
590 case 'f': {
591 double d = va_arg(ap, double);
592 s = double_to_string(num_buffer, sizeof(num_buffer), d, flags);
593 goto _output_string;
594 }
595 case 'A':
596 flags |= CAPSFLAG;
597 /* fallthrough */
598 case 'a': {
599 double d = va_arg(ap, double);
600 s = double_to_hexstring(num_buffer, sizeof(num_buffer), d, flags);
601 goto _output_string;
602 }
603 #endif
604 default:
605 OUTPUT_CHAR('%');
606 OUTPUT_CHAR(c);
607 break;
608 }
609
610 /* move on to the next field */
611 continue;
612
613 /* shared output code */
614 _output_string:
615 string_len = strlen(s);
616
617 if (flags & LEFTFORMATFLAG) {
618 /* left justify the text */
619 OUTPUT_STRING(s, string_len);
620 uint written = err;
621
622 /* pad to the right (if necessary) */
623 for (; format_num > written; format_num--)
624 OUTPUT_CHAR(' ');
625 } else {
626 /* right justify the text (digits) */
627
628 /* if we're going to print a sign digit,
629 it'll chew up one byte of the format size */
630 if (signchar != '\0' && format_num > 0)
631 format_num--;
632
633 /* output the sign char before the leading zeros */
634 if (flags & LEADZEROFLAG && signchar != '\0')
635 OUTPUT_CHAR(signchar);
636
637 /* Handle (altflag) printing 0x before the number */
638 /* Note that this needs to be done before padding the number */
639 if (c == 'x' && (flags & ALTFLAG)) {
640 OUTPUT_CHAR('0');
641 OUTPUT_CHAR(flags & CAPSFLAG ? 'X' : 'x');
642 /* Width is adjusted so i.e printf("%#04x", 0x02) -> 0x02 instead of 0x0002 */
643 if (format_num >= 2) {
644 format_num -= 2;
645 }
646 }
647
648 /* pad according to the format string */
649 for (; format_num > string_len; format_num--)
650 OUTPUT_CHAR(flags & LEADZEROFLAG ? '0' : ' ');
651
652 /* if not leading zeros, output the sign char just before the number */
653 if (!(flags & LEADZEROFLAG) && signchar != '\0')
654 OUTPUT_CHAR(signchar);
655
656 /* output the string */
657 OUTPUT_STRING(s, string_len);
658 }
659 }
660
661 #undef OUTPUT_STRING
662 #undef OUTPUT_CHAR
663
664 exit:
665 return (err < 0) ? err : (int)chars_written;
666 }
667