Added missing version tags, clean-up
[AROS.git] / compiler / clib / __vcformat.c
blob975c6db241224ad51c1c938fdddb48d2f465c76f
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Function to format a string like printf().
6 */
8 /*
9 * This function is used by debug functions during early startup.
10 * Please keep it self-contained, at least when compiled with -DAROSC_ROM.
13 /* Original source from libnix */
14 #include <dos/bptr.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #ifndef AROS_NO_LIMITS_H
21 # include <limits.h>
22 #else
23 # define ULONG_MAX 4294967295UL
24 #endif
25 #include <ctype.h>
26 #include "__math.h"
27 #include <math.h>
28 #include <float.h>
30 #ifndef AROSC_ROM
31 #define FULL_SPECIFIERS
32 #endif
34 #ifndef BITSPERBYTE
35 # define BITSPERBYTE 8
36 #endif
38 #if (__WORDSIZE == 64)
39 /* On 64-bit machines long and long long are the same, so we don't need separate processing for long long */
40 #undef AROS_HAVE_LONG_LONG
41 #endif
43 /* a little macro to make life easier */
44 #define OUT(c) do \
45 { if((*outc)((c),data)==EOF) \
46 return outcount; \
47 outcount++; \
48 }while(0)
50 #define MINFLOATSIZE (DBL_DIG+1) /* Why not 1 more - it's 97% reliable */
51 #define MININTSIZE (sizeof(unsigned long)*BITSPERBYTE/3+1)
52 #define MINPOINTSIZE (sizeof(void *)*BITSPERBYTE/4+1)
53 #define REQUIREDBUFFER (MININTSIZE>MINPOINTSIZE? \
54 (MININTSIZE>MINFLOATSIZE?MININTSIZE:MINFLOATSIZE): \
55 (MINPOINTSIZE>MINFLOATSIZE?MINPOINTSIZE:MINFLOATSIZE))
57 #define ALTERNATEFLAG 1 /* '#' is set */
58 #define ZEROPADFLAG 2 /* '0' is set */
59 #define LALIGNFLAG 4 /* '-' is set */
60 #define BLANKFLAG 8 /* ' ' is set */
61 #define SIGNFLAG 16 /* '+' is set */
63 const unsigned char *const __decimalpoint = ".";
65 static size_t format_long(char *buffer, char type, unsigned long v)
67 size_t size = 0;
68 char hex = 'a' - 10;
69 unsigned char mask = 0;
70 unsigned char shift = 0;
72 switch (type)
74 case 'X':
75 hex = 'A' - 10;
77 case 'x':
78 shift = 4;
79 mask = 0x0F;
80 break;
82 case 'o':
83 shift = 3;
84 mask = 0x07;
85 break;
87 default: /* 'd' and 'u' */
88 /* Use slow divide operations for decimal numbers */
91 char c = v % 10;
93 *--buffer = c + '0';
94 v /= 10;
95 size++;
96 } while (v);
98 return size;
101 /* Divisor is a power of 2, so use fast shifts for division */
104 char c = v & mask;
106 *--buffer = (c < 10) ? c + '0' : c + hex;
107 v >>= shift;
108 size++;
109 } while (v);
111 return size;
114 #ifdef AROS_HAVE_LONG_LONG
117 * This is the same as format_long(), but takes long long argument.
118 * This is used to process long long values on 32-bit machines. 64-bit
119 * operations are performed slower there, and may need to call libgcc routines.
121 static size_t format_longlong(char *buffer, char type, unsigned long long v)
123 size_t size = 0;
124 char hex = 'a' - 10;
125 unsigned char mask = 0;
126 unsigned char shift = 0;
128 switch (type)
130 case 'X':
131 hex = 'A' - 10;
133 case 'x':
134 shift = 4;
135 mask = 0x0F;
136 break;
138 case 'o':
139 shift = 3;
140 mask = 0x07;
141 break;
143 default:
145 * FIXME: this is not compiled for $(GENDIR)/lib32/librom.a because this requires
146 * __umoddi3() and __udivdi3() from 32-bit version of libgcc which is not supplied
147 * with 64-bit AROS gcc.
148 * Perhaps these routines needs to be implemented explicitly for the bootstrap. Or
149 * this code needs to be rewritten without these division operations, implemenging
150 * decimal division explicitly.
151 * As a consequence, %llu and %lld do not work in x86-64 bootstrap. Use hexadecimal
152 * output or fix this.
154 #ifndef AROSC_LIB32
157 char c = v % 10;
159 *--buffer = c + '0';
160 v /= 10;
161 size++;
162 } while (v);
163 #endif
165 return size;
170 char c = v & mask;
172 *--buffer = (c < 10) ? c + '0' : c + hex;
173 v >>= shift;
174 size++;
175 } while (v);
177 return size;
180 #endif
182 /*****************************************************************************
184 NAME */
186 int __vcformat (
188 /* SYNOPSIS */
189 void * data,
190 int (* outc)(int, void *),
191 const char * format,
192 va_list args)
194 /* FUNCTION
195 Format a list of arguments and call a function for each char
196 to print.
198 INPUTS
199 data - This is passed to the usercallback outc
200 outc - Call this function for every character that should be
201 emitted. The function should return EOF on error and
202 > 0 otherwise.
203 format - A printf() format string.
204 args - A list of arguments for the format string.
206 RESULT
207 The number of characters written.
209 NOTES
211 EXAMPLE
213 BUGS
215 SEE ALSO
217 INTERNALS
219 ******************************************************************************/
221 size_t outcount=0;
223 while(*format)
225 if(*format=='%')
227 static const char flagc[] = { '#','0','-',' ','+' };
228 size_t width=0,preci=ULONG_MAX,flags=0; /* Specifications */
229 char type,subtype='i';
230 #ifdef AROS_HAVE_LONG_LONG
231 char lltype=0;
232 #endif
233 char buffer1[2]; /* Signs and that like */
234 char buffer[REQUIREDBUFFER]; /* The body */
235 char *buffer2=buffer; /* So we can set this to any other strings */
236 size_t size1 = 0, size2 = 0;/* How many chars in buffer? */
237 const char *ptr=format+1; /* pointer to format string */
238 size_t i,pad; /* Some temporary variables */
240 do /* read flags */
241 for(i=0;i<sizeof(flagc);i++)
242 if(flagc[i]==*ptr)
243 { flags|=1<<i;
244 ptr++;
245 break; }
246 while(i<sizeof(flagc));
248 if(*ptr=='*') /* read width from arguments */
249 { signed int a;
250 ptr++;
251 a=va_arg(args,signed int);
252 if(a<0)
253 { flags|=LALIGNFLAG;
254 width=-a; }
255 else
256 width=a;
257 }else
258 while(isdigit(*ptr))
259 width=width*10+(*ptr++-'0');
261 if(*ptr=='.')
262 { ptr++;
263 if(*ptr=='*') /* read precision from arguments */
264 { signed int a;
265 ptr++;
266 a=va_arg(args,signed int);
267 if(a>=0)
268 preci=a;
269 }else
270 { preci=0;
271 while(isdigit(*ptr))
272 preci=preci*10+(*ptr++-'0');
276 if (*ptr == 'h' || *ptr == 'l' || *ptr == 'L' || *ptr == 'z')
277 subtype=*ptr++;
279 if (*ptr == 'l' || *ptr == 'q')
281 #ifdef AROS_HAVE_LONG_LONG
282 lltype = 1;
283 #endif
284 ptr++;
287 type = *ptr++;
289 switch(type)
291 case 'd':
292 case 'i':
293 case 'o':
294 case 'p':
295 case 'u':
296 case 'x':
297 case 'X':
299 #ifdef AROS_HAVE_LONG_LONG
300 unsigned long long llv = 0;
301 #endif
302 unsigned long v = 0;
304 if (type=='p') /* This is written as 0x08lx (or 0x016lx on 64 bits) */
306 subtype = 'l';
307 type = 'x';
308 if (!width)
309 width = sizeof(void *) * 2;
310 flags |= ZEROPADFLAG;
313 if (type=='d' || type=='i') /* These are signed */
315 signed long v2;
317 if (subtype=='l')
319 #ifdef AROS_HAVE_LONG_LONG
320 if (lltype)
322 signed long long llv2;
324 llv2 = va_arg(args, signed long long);
325 if (llv2 < 0)
327 llv = - llv2;
328 v2 = -1; /* Assign a dummy value to v2 in order to process sign below */
330 else
332 llv = llv2;
333 v2 = llv2 ? 1 : 0;
336 else
337 #endif
338 v2=va_arg(args, signed long);
340 else if (subtype=='z')
341 v2 = va_arg(args,size_t);
342 else
343 v2 = va_arg(args,signed int);
345 if (v2 < 0)
347 buffer1[size1++]='-';
348 v = -v2;
350 else
352 if (flags & SIGNFLAG)
353 buffer1[size1++] = '+';
354 else if (flags & BLANKFLAG)
355 buffer1[size1++] = ' ';
356 v = v2;
359 else /* These are unsigned */
361 if (subtype=='l')
363 #ifdef AROS_HAVE_LONG_LONG
364 if (lltype)
365 llv = va_arg(args, unsigned long long);
366 else
367 #endif
368 v = va_arg(args,unsigned long);
370 else if (subtype == 'z')
371 v = va_arg(args, size_t);
372 else
373 v = va_arg(args, unsigned int);
375 if (flags & ALTERNATEFLAG)
377 if (type == 'o' && preci && v)
378 buffer1[size1++] = '0';
379 if ((type == 'x' || type == 'X') && v)
381 buffer1[size1++] = '0';
382 buffer1[size1++] = type;
387 buffer2 = &buffer[sizeof(buffer)]; /* Calculate body string */
389 #ifdef AROS_HAVE_LONG_LONG
391 * For long long type we have actual value in llv.
392 * For long we have actual value in v.
393 * This avoids slow 64-bit operations on 32-bit processors
394 * when not needed.
396 if (lltype)
397 size2 = format_longlong(buffer2, type, llv);
398 else
399 #endif
400 size2 = format_long(buffer2, type, v);
401 /* Position to the beginning of the string */
402 buffer2 -= size2;
404 if (preci == ULONG_MAX) /* default */
405 preci = 0;
406 else
407 flags &= ~ZEROPADFLAG;
408 break;
411 case 'c':
412 if (subtype=='l')
414 #ifdef AROS_HAVE_LONG_LONG
415 if (lltype)
416 *buffer2 = va_arg(args, long long);
417 else
418 #endif
419 *buffer2 = va_arg(args, long);
421 else
422 *buffer2 = va_arg(args, int);
424 size2 = 1;
425 preci = 0;
426 break;
428 case 's':
429 buffer2 = va_arg(args, char *);
430 if (!buffer2)
431 buffer2 = "(null)";
432 size2 = strlen(buffer2);
433 size2 = size2 <= preci ? size2 : preci;
434 preci = 0;
435 break;
437 case 'b':
438 buffer2 = BADDR(va_arg(args, BPTR));
439 if (buffer2)
440 #if AROS_FAST_BSTR
441 size2 = strlen(buffer2);
442 #else
443 size2 = *(unsigned char *)buffer2++;
444 #endif
445 else
447 buffer2 = "(null)";
448 size2 = 6;
451 size2 = size2 <= preci ? size2 : preci;
452 preci = 0;
453 break;
455 #ifdef FULL_SPECIFIERS
456 case 'f':
457 case 'e':
458 case 'E':
459 case 'g':
460 case 'G':
462 double v;
463 char killzeros=0,sign=0; /* some flags */
464 int ex1,ex2; /* Some temporary variables */
465 size_t size,dnum,dreq;
466 char *udstr=NULL;
468 v=va_arg(args,double);
470 if(isinf(v))
471 { if(v>0)
472 udstr="+inf";
473 else
474 udstr="-inf";
475 }else if(isnan(v))
476 udstr="NaN";
478 if(udstr!=NULL)
479 { size2=strlen(udstr);
480 preci=0;
481 buffer2=udstr;
482 break; }
484 if(preci==ULONG_MAX) /* old default */
485 preci=6; /* new default */
487 if(v<0.0)
488 { sign='-';
489 v=-v;
490 }else
491 { if(flags&SIGNFLAG)
492 sign='+';
493 else if(flags&BLANKFLAG)
494 sign=' ';
497 ex1=0;
498 if(v!=0.0)
499 { ex1=log10(v);
500 if(v<1.0)
501 v=v*pow(10,- --ex1); /* Caution: (int)log10(.5)!=-1 */
502 else
503 v=v/pow(10,ex1);
504 if(v<1.0) /* adjust if we are too low (log10(.1)=-.999999999) */
505 { v*=10.0; /* luckily this cannot happen with FLT_MAX and FLT_MIN */
506 ex1--; } /* The case too high (log(10.)=.999999999) is done later */
509 ex2=preci;
510 if(type=='f')
511 ex2+=ex1;
512 if(tolower(type)=='g')
513 ex2--;
514 v+=.5/pow(10,ex2<MINFLOATSIZE?ex2:MINFLOATSIZE); /* Round up */
516 if(v>=10.0) /* Adjusts log10(10.)=.999999999 too */
517 { v/=10.0;
518 ex1++; }
520 if(tolower(type)=='g') /* This changes to one of the other types */
521 { if(ex1<(signed long)preci&&ex1>=-4)
522 { type='f';
523 preci-=ex1;
524 }else
525 type=type=='g'?'e':'E';
526 preci--;
527 if(!(flags&ALTERNATEFLAG))
528 killzeros=1; /* set flag to kill trailing zeros */
531 dreq=preci+1; /* Calculate number of decimal places required */
532 if(type=='f')
533 dreq+=ex1; /* even more before the decimal point */
535 dnum=0;
536 while(dnum<dreq&&dnum<MINFLOATSIZE) /* Calculate all decimal places needed */
537 { buffer[dnum++]=(char)v+'0';
538 v=(v-(double)(char)v)*10.0; }
540 if(killzeros) /* Kill trailing zeros if possible */
541 while(preci&&(dreq-->dnum||buffer[dreq]=='0'))
542 preci--;
544 if(type=='f')/* Calculate actual size of string (without sign) */
545 { size=preci+1; /* numbers after decimal point + 1 before */
546 if(ex1>0)
547 size+=ex1; /* numbers >= 10 */
548 if(preci||flags&ALTERNATEFLAG)
549 size++; /* 1 for decimal point */
550 }else
551 { size=preci+5; /* 1 for the number before the decimal point, and 4 for the exponent */
552 if(preci||flags&ALTERNATEFLAG)
553 size++;
554 if(ex1>99||ex1<-99)
555 size++; /* exponent needs an extra decimal place */
558 pad=size+(sign!=0);
559 pad=pad>=width?0:width-pad;
561 if(sign&&flags&ZEROPADFLAG)
562 OUT(sign);
564 if(!(flags&LALIGNFLAG))
565 for(i=0;i<pad;i++)
566 OUT(flags&ZEROPADFLAG?'0':' ');
568 if(sign&&!(flags&ZEROPADFLAG))
569 OUT(sign);
571 dreq=0;
572 if(type=='f')
573 { if(ex1<0)
574 OUT('0');
575 else
576 while(ex1>=0)
577 { OUT(dreq<dnum?buffer[dreq++]:'0');
578 ex1--; }
579 if(preci||flags&ALTERNATEFLAG)
580 { OUT(__decimalpoint[0]);
581 while(preci--)
582 if(++ex1<0)
583 OUT('0');
584 else
585 OUT(dreq<dnum?buffer[dreq++]:'0');
587 }else
588 { OUT(buffer[dreq++]);
589 if(preci||flags&ALTERNATEFLAG)
590 { OUT(__decimalpoint[0]);
591 while(preci--)
592 OUT(dreq<dnum?buffer[dreq++]:'0');
594 OUT(type);
595 if(ex1<0)
596 { OUT('-');
597 ex1=-ex1; }
598 else
599 OUT('+');
600 if(ex1>99)
601 OUT(ex1/100+'0');
602 OUT(ex1/10%10+'0');
603 OUT(ex1%10+'0');
606 if(flags&LALIGNFLAG)
607 for(i=0;i<pad;i++)
608 OUT(' ');
610 width=preci=0; /* Everything already done */
611 break;
613 #endif
614 case '%':
615 buffer2="%";
616 size2=1;
617 preci=0;
618 break;
619 case 'n':
620 *va_arg(args,int *)=outcount;
621 width=preci=0;
622 break;
623 default:
624 if(!type)
625 ptr--; /* We've gone too far - step one back */
626 buffer2=(char *)format;
627 size2=ptr-format;
628 width=preci=0;
629 break;
631 pad=size1+(size2>=preci?size2:preci); /* Calculate the number of characters */
632 pad=pad>=width?0:width-pad; /* and the number of resulting pad bytes */
634 if(flags&ZEROPADFLAG) /* print sign and that like */
635 for(i=0;i<size1;i++)
636 OUT(buffer1[i]);
638 if(!(flags&LALIGNFLAG)) /* Pad left */
639 for(i=0;i<pad;i++)
640 OUT(flags&ZEROPADFLAG?'0':' ');
642 if(!(flags&ZEROPADFLAG)) /* print sign if not zero padded */
643 for(i=0;i<size1;i++)
644 OUT(buffer1[i]);
646 for(i=size2;i<preci;i++) /* extend to precision */
647 OUT('0');
649 for(i=0;i<size2;i++) /* print body */
650 OUT(buffer2[i]);
652 if(flags&LALIGNFLAG) /* Pad right */
653 for(i=0;i<pad;i++)
654 OUT(' ');
656 format=ptr;
658 else
659 OUT(*format++);
661 return outcount;