Usermode/libc - Fixed DIV0 in fread/fopen
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / printf.c
1 /*
2  * Acess2 C Library
3  * - By John Hodge (thePowersGang)
4  *
5  * scanf.c
6  * - *scanf family of functions
7  */
8 #include "lib.h"
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <ctype.h>      // toupper
13 #include <assert.h>     // assert() 
14
15 // === TYPES ===
16 typedef void    (*printf_putch_t)(void *h, char ch);
17 enum eFPN {
18         FPN_STD,
19         FPN_SCI,
20         FPN_SHORTEST,
21 };
22
23 // === PROTOTYPES ===
24 void    itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned);
25 size_t  _printf_itoa(printf_putch_t putch_cb, void *putch_h, uint64_t num, size_t base, int minLength, char pad, int bSigned);
26 size_t  _printf_ftoa(printf_putch_t putch_cb, void *putch_h, long double num, size_t Base, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals);
27
28 // === CODE ===
29 /**
30  * \fn EXPORT void vsnprintf(char *buf, const char *format, va_list args)
31  * \brief Prints a formatted string to a buffer
32  * \param buf   Pointer - Destination Buffer
33  * \param format        String - Format String
34  * \param args  VarArgs List - Arguments
35  */
36 EXPORT int _vcprintf_int(printf_putch_t putch_cb, void *putch_h, const char *format, va_list args)
37 {
38         char    tmp[65];
39          int    c, minSize, precision, len;
40         size_t  pos = 0;
41         char    *p;
42         char    pad;
43         uint64_t        arg;
44         long double     arg_f;
45 //      char    cPositiveChar;
46          int    bLongLong, bPadLeft, bLong;
47
48         #define _addchar(ch) do { \
49                 putch_cb(putch_h, ch); \
50                 pos ++; \
51         } while(0)
52
53         tmp[32] = '\0';
54         
55         while((c = *format++) != 0)
56         {
57                 // Non-control character
58                 if (c != '%') {
59                         _addchar(c);
60                         continue;
61                 }
62                 
63                 // Control Character
64                 c = *format++;
65                 if(c == '%') {  // Literal %
66                         _addchar('%');
67                         continue;
68                 }
69                 
70                 bPadLeft = 0;
71                 bLong = 0;
72                 bLongLong = 0;
73                 minSize = 0;
74                 precision = -1;
75 //              cPositiveChar = '\0';
76                 pad = ' ';
77                 
78                 // - Flags
79                 // Alternate form (0<oct>, 0x<hex>, 123.)
80                 if(c == '#') {
81                         // TODO:
82                         c = *format++;
83                 }
84                 // Padding with '0'
85                 if(c == '0') {
86                         pad = '0';
87                         c = *format++;
88                 }
89                 // Pad on left
90                 if(c == '-') {
91                         bPadLeft = 1;
92                         c = *format++;
93                 }
94                 // Include space for positive sign
95                 if(c == ' ') {
96                         // TODO:
97 //                      cPositiveChar = ' ';
98                         c = *format++;
99                 }
100                 // Always include sign
101                 if(c == '+') {
102 //                      cPositiveChar = '+';
103                         c = *format++;
104                 }
105                 
106                 // Padding length
107                 if( c == '*' ) {
108                         // Variable length
109                         minSize = va_arg(args, size_t);
110                         c = *format++;
111                 }
112                 else if('1' <= c && c <= '9')
113                 {
114                         minSize = 0;
115                         while('0' <= c && c <= '9')
116                         {
117                                 minSize *= 10;
118                                 minSize += c - '0';
119                                 c = *format++;
120                         }
121                 }
122
123                 // Precision
124                 if(c == '.') {
125                         c = *format++;
126                         if(c == '*') {
127                                 precision = va_arg(args, size_t);
128                                 c = *format++;
129                         }
130                         else if('1' <= c && c <= '9')
131                         {
132                                 precision = 0;
133                                 while('0' <= c && c <= '9')
134                                 {
135                                         precision *= 10;
136                                         precision += c - '0';
137                                         c = *format++;
138                                 }
139                         }
140                 }
141         
142                 // Check for long long
143                 if(c == 'l')
144                 {
145                         bLong = 1;
146                         c = *format++;
147                         if(c == 'l') {
148                                 bLongLong = 1;
149                                 c = *format++;
150                         }
151                 }
152                 
153                 // Just help things along later
154                 p = tmp;
155                 
156                 // Get Type
157                 switch( c )
158                 {
159                 // Signed Integer
160                 case 'd':       case 'i':
161                         // Get Argument
162                         if(bLongLong)   arg = va_arg(args, int64_t);
163                         else                    arg = va_arg(args, int32_t);
164                         itoa(tmp, arg, 10, minSize, pad, 1);
165                         precision = -1;
166                         goto sprintf_puts;
167                 
168                 // Unsigned Integer
169                 case 'u':
170                         // Get Argument
171                         if(bLongLong)   arg = va_arg(args, uint64_t);
172                         else                    arg = va_arg(args, uint32_t);
173                         itoa(tmp, arg, 10, minSize, pad, 0);
174                         precision = -1;
175                         goto sprintf_puts;
176                 
177                 // Pointer
178                 case 'p':
179                         _addchar('*');
180                         _addchar('0');
181                         _addchar('x');
182                         arg = va_arg(args, intptr_t);
183                         itoa(tmp, arg, 16, minSize, pad, 0);
184                         precision = -1;
185                         goto sprintf_puts;
186                 // Unsigned Hexadecimal
187                 case 'x':
188                         if(bLongLong)   arg = va_arg(args, uint64_t);
189                         else                    arg = va_arg(args, uint32_t);
190                         itoa(tmp, arg, 16, minSize, pad, 0);
191                         precision = -1;
192                         goto sprintf_puts;
193                 
194                 // Unsigned Octal
195                 case 'o':
196                         if(bLongLong)   arg = va_arg(args, uint64_t);
197                         else                    arg = va_arg(args, uint32_t);
198                         itoa(tmp, arg, 8, minSize, pad, 0);
199                         precision = -1;
200                         goto sprintf_puts;
201                 
202                 // Unsigned binary
203                 case 'b':
204                         if(bLongLong)   arg = va_arg(args, uint64_t);
205                         else                    arg = va_arg(args, uint32_t);
206                         itoa(tmp, arg, 2, minSize, pad, 0);
207                         precision = -1;
208                         goto sprintf_puts;
209
210                 // Standard float
211                 case 'f':
212                         if(bLong)       arg_f = va_arg(args, long double);
213                         else    arg_f = va_arg(args, double);
214                         pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_STD, precision, 0, bPadLeft, 0);
215                         break;
216                 case 'F':
217                         if(bLong)       arg_f = va_arg(args, long double);
218                         else    arg_f = va_arg(args, double);
219                         pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_STD, precision, 0, bPadLeft, 1);
220                         break;
221                 // Scientific Float
222                 case 'e':
223                         if(bLong)       arg_f = va_arg(args, long double);
224                         else    arg_f = va_arg(args, double);
225                         pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_SCI, precision, 0, bPadLeft, 0);
226                         break;
227                 case 'E':
228                         if(bLong)       arg_f = va_arg(args, long double);
229                         else    arg_f = va_arg(args, double);
230                         pos += _printf_ftoa(putch_cb, putch_h, arg_f, 10, FPN_SCI, precision, 0, bPadLeft, 1);
231                         break;
232
233                 // String
234                 case 's':
235                         p = va_arg(args, char*);
236                 sprintf_puts:
237                         if(!p)  p = "(null)";
238                         //_SysDebug("vsnprintf: p = '%s'", p);
239                         if(precision >= 0)
240                                 len = strnlen(p, precision);
241                         else
242                                 len = strlen(p);
243                         if(bPadLeft)    while(minSize > len++)  _addchar(pad);
244                         while( *p ) {
245                                 if(precision >= 0 && precision -- == 0)
246                                         break;
247                                 _addchar(*p++);
248                         }
249                         if(!bPadLeft)   while(minSize > len++)  _addchar(pad);
250                         break;
251
252                 // Unknown, just treat it as a character
253                 default:
254                         arg = va_arg(args, uint32_t);
255                         _addchar(arg);
256                         break;
257                 }
258         }
259         #undef _addchar
260         
261         return pos;
262 }
263
264 struct s_sprintf_info {
265         char    *dest;
266         size_t  ofs;
267         size_t  maxlen;
268 };
269
270 void _vsnprintf_putch(void *h, char ch)
271 {
272         struct s_sprintf_info   *info = h;
273         if(info->ofs < info->maxlen)
274                 info->dest[info->ofs++] = ch;
275 }
276
277 EXPORT int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list __args)
278 {
279         struct s_sprintf_info   info = {__s, 0, __maxlen};
280         int ret;
281         ret = _vcprintf_int(_vsnprintf_putch, &info, __format, __args);
282         _vsnprintf_putch(&info, '\0');
283         return ret;
284 }
285
286 EXPORT int snprintf(char *buf, size_t maxlen, const char *format, ...)
287 {
288          int    ret;
289         va_list args;
290         va_start(args, format);
291         ret = vsnprintf((char*)buf, maxlen, (char*)format, args);
292         va_end(args);
293         return ret;
294 }
295
296
297 EXPORT int vsprintf(char * __s, const char *__format, va_list __args)
298 {
299         return vsnprintf(__s, 0x7FFFFFFF, __format, __args);
300 }
301 EXPORT int sprintf(char *buf, const char *format, ...)
302 {
303          int    ret;
304         va_list args;
305         va_start(args, format);
306         ret = vsprintf((char*)buf, (char*)format, args);
307         va_end(args);
308         return ret;
309 }
310
311 void _vfprintf_putch(void *h, char ch)
312 {
313         fputc(ch, h);
314 }
315
316 EXPORT int vfprintf(FILE *__fp, const char *__format, va_list __args)
317 {
318         return _vcprintf_int(_vfprintf_putch, __fp, __format, __args);
319 }
320
321 EXPORT int fprintf(FILE *fp, const char *format, ...)
322 {
323         va_list args;
324          int    ret;
325         
326         // Get Size
327         va_start(args, format);
328         ret = vfprintf(fp, (char*)format, args);
329         va_end(args);
330         
331         return ret;
332 }
333
334 EXPORT int vprintf(const char *__format, va_list __args)
335 {
336         return vfprintf(stdout, __format, __args);
337 }
338
339 EXPORT int printf(const char *format, ...)
340 {
341         va_list args;
342          int    ret;
343         
344         // Get final size
345         va_start(args, format);
346         ret = vprintf(format, args);
347         va_end(args);
348         
349         // Return
350         return ret;
351 }
352
353 void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned)
354 {
355         struct s_sprintf_info   info = {buf, 0, 1024};
356         if(!buf)        return;
357         _printf_itoa(_vsnprintf_putch, &info, num, base, minLength, pad, bSigned);
358         buf[info.ofs] = '\0';
359 }
360
361 const char cDIGITS[] = "0123456789abcdef";
362 /**
363  * \brief Convert an integer into a character string
364  * \param buf   Destination Buffer
365  * \param num   Number to convert
366  * \param base  Base-n number output
367  * \param minLength     Minimum length of output
368  * \param pad   Padding used to ensure minLength
369  * \param bSigned       Signed number output?
370  */
371 size_t _printf_itoa(printf_putch_t putch_cb, void *putch_h, uint64_t num, size_t base, int minLength, char pad, int bSigned)
372 {
373         char    tmpBuf[64];
374          int    pos=0;
375         size_t ret = 0;
376
377         if(base > 16 || base < 2) {
378                 return 0;
379         }
380         
381         if(bSigned && (int64_t)num < 0)
382         {
383                 num = -num;
384                 bSigned = 1;
385         } else
386                 bSigned = 0;
387         
388         // Encode into reversed string
389         while(num > base-1) {
390                 tmpBuf[pos++] = cDIGITS[ num % base ];
391                 num = (uint64_t) num / (uint64_t)base;          // Shift {number} right 1 digit
392         }
393
394         tmpBuf[pos++] = cDIGITS[ num % base ];          // Last digit of {number}
395         if(bSigned)     tmpBuf[pos++] = '-';    // Append sign symbol if needed
396         
397         minLength -= pos;
398         while(minLength-- > 0) {
399                 putch_cb(putch_h, pad);
400                 ret ++;
401         }
402         while(pos-- > 0) {
403                 putch_cb(putch_h, tmpBuf[pos]); // Reverse the order of characters
404                 ret ++;
405         }
406         
407         return ret;
408 }
409
410 int expand_double(double num, uint64_t *Significand, int16_t *Exponent, int *SignIsNeg)
411 {
412         // IEEE 754 binary64
413         #if 0
414         {
415                 uint64_t test_exp = 0xC000000000000000;
416                 double test_value = -2.0f;
417                 assert( *((uint64_t*)&test_value) == test_exp );
418         }
419         #endif
420         
421         const uint64_t  *bit_rep = (void*)&num;
422         
423         *SignIsNeg = *bit_rep >> 63;
424         *Exponent = ((*bit_rep >> 52) & 0x7FF) - 1023;
425         *Significand = (*bit_rep & ((1ULL << 52)-1)) << (64-52);
426
427 //      printf("%llx %i %i %llx\n", *bit_rep, (int)*SignIsNeg, (int)*Exponent, *Significand);
428
429         // Subnormals
430         if( *Exponent == -1023 && *Significand != 0 )
431                 return 1;
432         // Infinity
433         if( *Exponent == 0x800 && *Significand == 0)
434                 return 2;
435         // NaNs
436         if( *Exponent == 0x800 && *Significand != 0)
437                 return 3;
438
439         return 0;
440 }
441
442 /**
443  * Internal function
444  * \return Remainder
445  */
446 double _longdiv(double num, double den, int *quot)
447 {
448         assert(num >= 0);
449         assert(den > 0);
450 //      printf("%llu / %llu\n", (long long int)num, (long long int)den);
451         
452         *quot = 0;
453         assert(num < den*10);
454         while(num >= den)
455         {
456                 num -= den;
457                 (*quot) ++;
458                 assert( *quot < 10 );
459         }
460 //      printf(" %i\n", *quot);
461         return num;
462 }
463
464 size_t _printf_ftoa(printf_putch_t putch_cb, void *putch_h, long double num, size_t Base, enum eFPN Notation, int Precision, int bForcePoint, int bForceSign, int bCapitals)
465 {
466         uint64_t        significand;
467         int16_t exponent;
468          int    signisneg;
469          int    rv;
470         size_t  ret = 0;
471
472         #define _putch(_ch) do{\
473                 if(bCapitals)\
474                         putch_cb(putch_h, toupper(_ch));\
475                 else\
476                         putch_cb(putch_h, _ch);\
477                 ret ++;\
478         }while(0)
479
480         if( Base <= 1 || Base > 16 )
481                 return 0;
482
483         rv = expand_double(num, &significand, &exponent, &signisneg);
484         switch(rv)
485         {
486         // 0: No errors, nothing special
487         case 0:
488                 break;
489         // 1: Subnormals
490         case 1:
491                 // TODO: Subnormal = 0?
492                 break;
493         // 2: Infinity
494         case 2:
495                 _putch('i');
496                 _putch('n');
497                 _putch('f');
498                 return 3;
499         case 3:
500                 _putch('N');
501                 _putch('a');
502                 _putch('N');
503                 return 3;
504         }
505
506         // - Used as 0/1 bools in arithmatic later on
507         bForcePoint = !!bForcePoint;
508         bForceSign = !!bForceSign;
509
510         // Apply default to precision
511         if( Precision == -1 )
512                 Precision = 6;
513
514         if( num < 0 )
515                 num = -num;
516
517         // Select optimum type
518         if( Notation == FPN_SHORTEST )
519         {
520                 //TODO:
521                 //int   first_set_sig = BSL(significand);
522         //      TODO: if( num > pos(Base, 2+Precision+2+log_base(exponent) )
523                 Notation = FPN_SCI;
524         }
525
526         double precision_max = 1;
527         while(Precision--)
528                 precision_max /= Base;
529
530         // Determine scientific's exponent and starting denominator
531         double den = 1;
532          int    sci_exponent = 0;
533         if( Notation == FPN_SCI )
534         {
535                 if( num < 1 )
536                 {
537                         while(num < 1)
538                         {
539                                 num *= Base;
540                                 sci_exponent ++;
541                         }
542                 }
543                 else if( num >= Base )
544                 {
545                         while(num >= Base)
546                         {
547                                 num /= Base;
548                                 sci_exponent ++;
549                         }
550                 }
551                 else
552                 {
553                         // no exponent
554                 }
555                 den = 1;
556         }
557         else
558         {
559                 while( den < num )
560                         den *= Base;
561                 den /= Base;
562         }
563
564         // Leading sign
565         if( signisneg )
566                 _putch('-');
567         else if( bForceSign )
568                 _putch('+');
569         else {
570         }
571
572          int    value;
573         // Whole section
574         do
575         {
576                 num = _longdiv(num, den, &value);
577                 _putch(cDIGITS[value]);
578                 den /= Base;
579         } while( den >= 1 );
580
581         // Decimal point (if needed/forced)     
582         if( den >= precision_max || bForcePoint )
583                 _putch('.');
584         // Decimal section
585         while( den >= precision_max )
586         {
587                 num = _longdiv(num, den, &value);
588                 _putch(cDIGITS[value]);
589                 den /= Base;
590         }
591
592         if( Notation == FPN_SCI )
593         {
594                 if( Base == 16 )
595                         _putch('p');
596                 else
597                         _putch('e');
598                 if(sci_exponent < 0)
599                 {
600                         _putch('-');
601                         sci_exponent = -sci_exponent;
602                 }
603                 else
604                         _putch('+');
605                 _printf_itoa(putch_cb, putch_h, sci_exponent, Base, 0, 0, 0);
606         }       
607
608         #undef _putch
609
610         return ret;
611 }

UCC git Repository :: git.ucc.asn.au