3 * - By John Hodge (thePowersGang)
6 * - *scanf family of functions
12 #include <ctype.h> // toupper
13 #include <assert.h> // assert()
21 typedef void (*printf_puts_t)(void *h, const char *s, size_t len);
29 void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned);
30 size_t _printf_itoa(printf_puts_t puts_cb, void *puts_h, uint64_t num,
31 size_t base, int bUpper,
32 int bSigned, char SignChar, int Precision,
33 int PadLength, char PadChar, int bPadRight);
34 size_t _printf_ftoa_hex(printf_puts_t puts_cb, void *puts_h, long double num, int Precision, int bForcePoint, int bForceSign, int bCapitals);
35 size_t _printf_ftoa(printf_puts_t puts_cb, void *puts_h, long double num, size_t Base, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals);
39 * \fn EXPORT void vsnprintf(char *buf, const char *format, va_list args)
40 * \brief Prints a formatted string to a buffer
41 * \param buf Pointer - Destination Buffer
42 * \param format String - Format String
43 * \param args VarArgs List - Arguments
45 EXPORT int _vcprintf_int(printf_puts_t puts_cb, void *puts_h, const char *format, va_list args)
48 int c, minSize, precision, len;
53 // char cPositiveChar;
54 BOOL bLongLong, bLong, bJustifyLeft, bAltForm;
57 #define _addchar(ch) do { \
59 puts_cb(puts_h, &_ch, 1); \
65 while((c = *format++) != 0)
67 // Non-control character
69 const char *start = format-1;
70 while( (c = *format) != 0 && c != '%' )
72 puts_cb(puts_h, start, format - start);
73 pos += format - start;
79 if(c == '%') { // Literal %
96 // Alternate form (0<oct>, 0x<hex>, 123.)
108 // Include space for positive sign
112 // Always include sign
119 while( (c = *format++) );
124 minSize = va_arg(args, size_t);
127 else if('1' <= c && c <= '9')
130 while('0' <= c && c <= '9')
142 precision = va_arg(args, size_t);
145 else if('1' <= c && c <= '9')
148 while('0' <= c && c <= '9')
151 precision += c - '0';
157 // Check for long long
168 // Just help things along later
178 arg = bLongLong ? va_arg(args, int64_t) : va_arg(args, int32_t);
179 if( arg == 0 && precision == 0 )
181 pos += _printf_itoa(puts_cb, puts_h, arg, 10, FALSE,
182 TRUE, cPlus, precision, minSize, cNumPad, bJustifyLeft);
187 arg = bLongLong ? va_arg(args, int64_t) : va_arg(args, int32_t);
188 pos += _printf_itoa(puts_cb, puts_h, arg, 10, FALSE,
189 FALSE, '\0', precision, minSize, cNumPad, bJustifyLeft);
197 arg = va_arg(args, intptr_t);
198 pos += _printf_itoa(puts_cb, puts_h, arg, 16, FALSE,
199 FALSE, '\0', sizeof(intptr_t)*2, 0,'\0',FALSE);
201 // Unsigned Hexadecimal
208 arg = bLongLong ? va_arg(args, uint64_t) : va_arg(args, uint32_t);
209 pos += _printf_itoa(puts_cb, puts_h, arg, 16, c=='X',
210 FALSE, '\0', precision, minSize,cNumPad,bJustifyLeft);
218 arg = bLongLong ? va_arg(args, int64_t) : va_arg(args, int32_t);
219 pos += _printf_itoa(puts_cb, puts_h, arg, 8, FALSE,
220 FALSE, '\0', precision, minSize,cNumPad,bJustifyLeft);
229 arg = bLongLong ? va_arg(args, int64_t) : va_arg(args, int32_t);
230 pos += _printf_itoa(puts_cb, puts_h, arg, 2, FALSE,
231 FALSE, '\0', precision, minSize,cNumPad,bJustifyLeft);
237 arg_f = bLong ? va_arg(args, long double) : va_arg(args, double);
238 pos += _printf_ftoa(puts_cb, puts_h, arg_f, 10, FPN_STD,
239 precision, 0, bJustifyLeft, c == 'F');
244 arg_f = bLong ? va_arg(args, long double) : va_arg(args, double);
245 pos += _printf_ftoa(puts_cb, puts_h, arg_f, 10, FPN_SCI,
246 precision, 0, bJustifyLeft, c == 'E');
251 arg_f = bLong ? va_arg(args, long double) : va_arg(args, double);
252 pos += _printf_ftoa(puts_cb, puts_h, arg_f, 10, FPN_SHORTEST,
253 precision, 0, bJustifyLeft, c == 'G');
255 // Hexadecimal Scientific
258 arg_f = bLong ? va_arg(args, long double) : va_arg(args, double);
259 pos += _printf_ftoa_hex(puts_cb, puts_h, arg_f, precision, 0, bJustifyLeft, c == 'A');
264 p = va_arg(args, char*);
266 //_SysDebug("vsnprintf: p = '%s'", p);
268 len = strnlen(p, precision);
272 while(minSize-- > len) _addchar(' ');
273 puts_cb(puts_h, p, len); pos += len;
275 while(minSize-- > len) _addchar(' ');
278 // Unknown, just treat it as a character
280 arg = va_arg(args, uint32_t);
290 struct s_sprintf_info {
296 void _vsnprintf_puts(void *h, const char *str, size_t len)
298 struct s_sprintf_info *info = h;
299 while( info->ofs < info->maxlen && len -- )
300 info->dest[info->ofs++] = *str++;
303 EXPORT int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list __args)
305 struct s_sprintf_info info = {__s, 0, __maxlen};
307 ret = _vcprintf_int(_vsnprintf_puts, &info, __format, __args);
308 _vsnprintf_puts(&info, "", 1);
312 EXPORT int snprintf(char *buf, size_t maxlen, const char *format, ...)
316 va_start(args, format);
317 ret = vsnprintf((char*)buf, maxlen, (char*)format, args);
323 EXPORT int vsprintf(char * __s, const char *__format, va_list __args)
325 return vsnprintf(__s, 0x7FFFFFFF, __format, __args);
327 EXPORT int sprintf(char *buf, const char *format, ...)
331 va_start(args, format);
332 ret = vsprintf((char*)buf, (char*)format, args);
337 void _vfprintf_puts(void *h, const char *str, size_t len)
339 fwrite(str, len, 1, h);
342 EXPORT int vfprintf(FILE *__fp, const char *__format, va_list __args)
344 return _vcprintf_int(_vfprintf_puts, __fp, __format, __args);
347 EXPORT int fprintf(FILE *fp, const char *format, ...)
353 va_start(args, format);
354 ret = vfprintf(fp, (char*)format, args);
360 EXPORT int vprintf(const char *__format, va_list __args)
362 return vfprintf(stdout, __format, __args);
365 EXPORT int printf(const char *format, ...)
371 va_start(args, format);
372 ret = vprintf(format, args);
379 void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned)
381 struct s_sprintf_info info = {buf, 0, 1024};
383 _printf_itoa(_vsnprintf_puts, &info, num, base, FALSE, bSigned, '\0', 0, minLength, pad, FALSE);
384 buf[info.ofs] = '\0';
387 const char cDIGITS[] = "0123456789abcdef";
388 const char cUDIGITS[] = "0123456789ABCDEF";
390 * \brief Convert an integer into a character string
391 * \param buf Destination Buffer
392 * \param num Number to convert
393 * \param base Base-n number output
394 * \param minLength Minimum length of output
395 * \param pad Padding used to ensure minLength
396 * \param bSigned Signed number output?
398 size_t _printf_itoa(printf_puts_t puts_cb, void *puts_h, uint64_t num,
399 size_t base, int bUpper,
400 int bSigned, char SignChar, int Precision,
401 int PadLength, char PadChar, int bPadRight)
404 int pos = sizeof(tmpBuf);
407 const char *map = bUpper ? cUDIGITS : cDIGITS;
409 if(base > 16 || base < 2) {
413 if(bSigned && (int64_t)num < 0)
419 // Encode into string
420 while(num > base-1) {
421 tmpBuf[--pos] = map[ num % base ];
422 num = (uint64_t) num / (uint64_t)base; // Shift {number} right 1 digit
424 tmpBuf[--pos] = map[ num % base ]; // Most significant digit of {number}
426 // TODO: This assertion may not be valid
427 assert(Precision <= (int)sizeof(tmpBuf));
428 Precision -= sizeof(tmpBuf)-pos + (sign_is_neg || SignChar != '\0');
429 while( Precision-- > 0 )
434 tmpBuf[--pos] = '-'; // Negative sign character
436 tmpBuf[--pos] = SignChar; // positive sign character
440 // length of number, minus the sign character
441 size_t len = sizeof(tmpBuf)-pos;
445 while(PadLength-- > 0)
446 puts_cb(puts_h, &PadChar, 1), ret ++;
449 puts_cb(puts_h, tmpBuf+pos, len);
454 while(PadLength-- > 0)
455 puts_cb(puts_h, &PadChar, 1), ret ++;
461 int expand_double(double num, uint64_t *Significand, int16_t *Exponent, int *SignIsNeg)
466 uint64_t test_exp = 0xC000000000000000;
467 double test_value = -2.0f;
468 assert( *((uint64_t*)&test_value) == test_exp );
472 const uint64_t *bit_rep = (void*)#
474 *SignIsNeg = *bit_rep >> 63;
475 *Exponent = ((*bit_rep >> 52) & 0x7FF) - 1023;
476 *Significand = (*bit_rep & ((1ULL << 52)-1)) << (64-52);
478 // printf("%llx %i %i %llx\n", *bit_rep, (int)*SignIsNeg, (int)*Exponent, *Significand);
481 if( *Exponent == -0x3FF && *Significand != 0 )
484 if( *Exponent == 0x400 && *Significand == 0)
487 if( *Exponent == 0x400 && *Significand != 0)
497 double _longdiv(double num, double den, int *quot)
501 // printf("%llu / %llu\n", (long long int)num, (long long int)den);
504 assert(num < den*10);
509 assert( *quot < 10 );
511 // printf(" %i\n", *quot);
515 size_t _printf_ftoa_hex(printf_puts_t puts_cb, void *puts_h, long double num, int Precision, int bForcePoint, int bForceSign, int bCapitals)
517 uint64_t significand;
523 #define _putch(_ch) do{\
524 char __ch = (bCapitals ? toupper(_ch) : _ch);\
525 puts_cb(puts_h, &__ch, 1); \
529 if( Precision == -1 )
530 Precision = (64-4)/4;
532 rv = expand_double(num, &significand, &exponent, &signisneg);
535 // 0: No errors, nothing special
540 // TODO: Subnormal = 0?
544 puts_cb(puts_h, "inf", 3);
547 puts_cb(puts_h, "NaN", 3);
551 // int exp_ofs = exponent % 4;
552 // int exp_16 = exponent - exp_ofs;
553 //uint8_t whole = (1 << exp_ofs) | ((significand >> (64-3)) >> exp_ofs);
554 //significand <<= 3 - exp_ofs;
555 uint8_t whole = (rv != 1) ? 1 : 0;
559 else if( bForceSign )
565 _putch(cDIGITS[whole]);
566 if( significand || bForcePoint )
569 while( significand && Precision -- )
571 uint8_t val = significand >> (64-4);
572 _putch(cDIGITS[val]);
576 //ret += _printf_itoa(puts_cb, puts_h, exp_16, 16, bCapitals, TRUE, '+', 0, 0, '\0', 0);
577 ret += _printf_itoa(puts_cb, puts_h, exponent, 10, bCapitals, TRUE, '+', 0, 0, '\0', 0);
584 size_t _printf_itoa_fixed(printf_putch_t putch_cb, void *putch_h, uint64_t num, size_t Base)
589 den = 1ULL << (64-1);
593 putch_cb(putch_h, cDIGITS[num / den]);
602 size_t _printf_ftoa_dec(printf_putch_t putch_cb, void *putch_h, long double num, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals)
607 #define _putch(_ch) do{\
608 putch_cb(putch_h, bCapitals ? toupper(_ch) : _ch), ret++;\
611 uint64_t significand;
614 int rv = expand_double(num, &significand, &exponent, &signisneg);
617 // 0: No errors, nothing special
622 // TODO: Subnormal = 0?
637 uint64_t whole, part;
638 int pre_zeros, post_zeros;
641 if( Notation == FPN_SCI )
644 else if( exponent < 0 )
651 else if( exponent >= 64 )
660 // Split into fixed point
661 whole = (1 << exponent) | (significand >> (64-exponent));
662 part = significand << exponent;
669 ret += _printf_itoa(putch_cb, putch_h, whole, 10, FALSE, FALSE, '\0', 0, 0, '\0', FALSE);
670 for(i = pre_zeros; i --; ) _putch('0');
671 // TODO: Conditional point
673 for(i = post_zeros; i--; ) _putch('0');
674 ret += _printf_itoa_fixed(putch_cb, putch_h, part, 10);
682 size_t _printf_ftoa(printf_puts_t puts_cb, void *puts_h, long double num, size_t Base, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals)
684 uint64_t significand;
690 #define _putch(_ch) do{\
691 char __ch = (bCapitals ? toupper(_ch) : _ch);\
692 puts_cb(puts_h, &__ch, 1); \
696 if( Base <= 1 || Base > 16 )
699 rv = expand_double(num, &significand, &exponent, &signisneg);
702 // 0: No errors, nothing special
707 // TODO: Subnormal = 0?
722 // - Used as 0/1 bools in arithmatic later on
723 bForcePoint = !!bForcePoint;
724 bForceSign = !!bForceSign;
726 // Apply default to precision
727 if( Precision == -1 )
733 // Select optimum type
734 if( Notation == FPN_SHORTEST )
737 //int first_set_sig = BSL(significand);
738 // bSign+log10(num)+1+precision vs. bSign+1+1+precision+1+1+log10(exponent)
739 int log10_num = exponent * 301 / 1000; // log_10(2) = 0.30102999566...
741 int sci_len = (signisneg || bForceSign) + 2 + (Precision-1) + 2 + log10_exp10;
742 int std_whole_len = (log10_num > 0 ? log10_num : 1);
743 int std_len = (signisneg || bForceSign) + std_whole_len + 1 + (Precision-std_whole_len);
744 if( sci_len > std_len ) {
745 Precision -= std_whole_len;
754 double precision_max = 1;
755 for(i = Precision; i--; )
756 precision_max /= Base;
758 // Determine scientific's exponent and starting denominator
760 int sci_exponent = 0;
761 if( Notation == FPN_SCI )
771 else if( num >= Base )
785 else if( num != 0.0 )
795 else if( bForceSign )
800 num += precision_max/10 * 4.999;
806 num = _longdiv(num, den, &value);
807 _putch(cDIGITS[value]);
811 // Decimal point (if needed/forced)
812 if( Precision > 0 || bForcePoint )
815 for(i = Precision; i--; )
817 num = _longdiv(num, den, &value);
818 _putch(cDIGITS[value]);
822 if( Notation == FPN_SCI )
828 ret += _printf_itoa(puts_cb, puts_h, sci_exponent, Base, FALSE, TRUE, '+', 3, 0, '\0', FALSE);