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