2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
5 Function to format a string like printf().
9 * This function is used by debug functions during early startup.
10 * Please keep it self-contained, at least when compiled with -DSTDC_STATIC.
13 /* Original source from libnix */
20 #ifndef AROS_NO_LIMITS_H
23 # define ULONG_MAX 4294967295UL
30 #define FULL_SPECIFIERS
34 # define BITSPERBYTE 8
37 #if (__WORDSIZE == 64)
38 /* On 64-bit machines long and long long are the same, so we don't need separate processing for long long */
39 #undef AROS_HAVE_LONG_LONG
42 /* a little macro to make life easier */
44 { if((*outc)((unsigned char)(c),data)==EOF) \
49 #define MINFLOATSIZE (DBL_DIG+1) /* Why not 1 more - it's 97% reliable */
50 #define MININTSIZE (sizeof(unsigned long)*BITSPERBYTE/3+1)
51 #define MINPOINTSIZE (sizeof(void *)*BITSPERBYTE/4+1)
52 #define REQUIREDBUFFER (MININTSIZE>MINPOINTSIZE? \
53 (MININTSIZE>MINFLOATSIZE?MININTSIZE:MINFLOATSIZE): \
54 (MINPOINTSIZE>MINFLOATSIZE?MINPOINTSIZE:MINFLOATSIZE))
56 #define ALTERNATEFLAG 1 /* '#' is set */
57 #define ZEROPADFLAG 2 /* '0' is set */
58 #define LALIGNFLAG 4 /* '-' is set */
59 #define BLANKFLAG 8 /* ' ' is set */
60 #define SIGNFLAG 16 /* '+' is set */
62 const unsigned char *const __decimalpoint
= ".";
64 static size_t format_long(char *buffer
, char type
, unsigned long v
)
68 unsigned char mask
= 0;
69 unsigned char shift
= 0;
86 default: /* 'd' and 'u' */
87 /* Use slow divide operations for decimal numbers */
100 /* Divisor is a power of 2, so use fast shifts for division */
105 *--buffer
= (c
< 10) ? c
+ '0' : c
+ hex
;
113 #ifdef AROS_HAVE_LONG_LONG
116 * This is the same as format_long(), but takes long long argument.
117 * This is used to process long long values on 32-bit machines. 64-bit
118 * operations are performed slower there, and may need to call libgcc routines.
120 static size_t format_longlong(char *buffer
, char type
, unsigned long long v
)
124 unsigned char mask
= 0;
125 unsigned char shift
= 0;
144 * FIXME: this is not compiled for $(GENDIR)/lib32/librom.a because this requires
145 * __umoddi3() and __udivdi3() from 32-bit version of libgcc which is not supplied
146 * with 64-bit AROS gcc.
147 * Perhaps these routines needs to be implemented explicitly for the bootstrap. Or
148 * this code needs to be rewritten without these division operations, implemenging
149 * decimal division explicitly.
150 * As a consequence, %llu and %lld do not work in x86-64 bootstrap. Use hexadecimal
151 * output or fix this.
171 *--buffer
= (c
< 10) ? c
+ '0' : c
+ hex
;
181 /*****************************************************************************
189 int (* outc
)(int, void *),
194 Format a list of arguments and call a function for each char
198 data - This is passed to the usercallback outc
199 outc - Call this function for every character that should be
200 emitted. The function should return EOF on error and
202 format - A printf() format string.
203 args - A list of arguments for the format string.
206 The number of characters written.
218 ******************************************************************************/
226 static const char flagc
[] = { '#','0','-',' ','+' };
227 size_t width
=0,preci
=ULONG_MAX
,flags
=0; /* Specifications */
228 char type
,subtype
='i';
229 #ifdef AROS_HAVE_LONG_LONG
232 char buffer1
[2]; /* Signs and that like */
233 char buffer
[REQUIREDBUFFER
]; /* The body */
234 char *buffer2
=buffer
; /* So we can set this to any other strings */
235 size_t size1
= 0, size2
= 0;/* How many chars in buffer? */
236 const char *ptr
=format
+1; /* pointer to format string */
237 size_t i
,pad
; /* Some temporary variables */
240 for(i
=0;i
<sizeof(flagc
);i
++)
245 while(i
<sizeof(flagc
));
247 if(*ptr
=='*') /* read width from arguments */
250 a
=va_arg(args
,signed int);
258 width
=width
*10+(*ptr
++-'0');
262 if(*ptr
=='*') /* read precision from arguments */
265 a
=va_arg(args
,signed int);
271 preci
=preci
*10+(*ptr
++-'0');
275 if (*ptr
== 'h' || *ptr
== 'l' || *ptr
== 'L' || *ptr
== 'z')
278 if (*ptr
== 'l' || *ptr
== 'q')
280 #ifdef AROS_HAVE_LONG_LONG
299 #ifdef AROS_HAVE_LONG_LONG
300 unsigned long long llv
= 0;
304 if (type
=='p') /* This is written as 0x08lx (or 0x016lx on 64 bits) */
309 width
= sizeof(void *) * 2;
310 flags
|= ZEROPADFLAG
;
313 if (type
=='d' || type
=='i') /* These are signed */
319 #ifdef AROS_HAVE_LONG_LONG
322 signed long long llv2
;
324 llv2
= va_arg(args
, signed long long);
328 v2
= -1; /* Assign a dummy value to v2 in order to process sign below */
338 v2
=va_arg(args
, signed long);
340 else if (subtype
=='z')
341 v2
= va_arg(args
,size_t);
343 v2
= va_arg(args
,signed int);
347 buffer1
[size1
++]='-';
352 if (flags
& SIGNFLAG
)
353 buffer1
[size1
++] = '+';
354 else if (flags
& BLANKFLAG
)
355 buffer1
[size1
++] = ' ';
359 else /* These are unsigned */
363 #ifdef AROS_HAVE_LONG_LONG
365 llv
= va_arg(args
, unsigned long long);
368 v
= va_arg(args
,unsigned long);
370 else if (subtype
== 'z')
371 v
= va_arg(args
, size_t);
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
397 size2
= format_longlong(buffer2
, type
, llv
);
400 size2
= format_long(buffer2
, type
, v
);
401 /* Position to the beginning of the string */
404 if (preci
== ULONG_MAX
) /* default */
407 flags
&= ~ZEROPADFLAG
;
414 #ifdef AROS_HAVE_LONG_LONG
416 *buffer2
= va_arg(args
, long long);
419 *buffer2
= va_arg(args
, long);
422 *buffer2
= va_arg(args
, int);
429 buffer2
= va_arg(args
, char *);
432 size2
= strlen(buffer2
);
433 size2
= size2
<= preci
? size2
: preci
;
438 buffer2
= BADDR(va_arg(args
, BPTR
));
441 size2
= strlen(buffer2
);
443 size2
= *(unsigned char *)buffer2
++;
451 size2
= size2
<= preci
? size2
: preci
;
455 #ifdef FULL_SPECIFIERS
463 char killzeros
=0,sign
=0; /* some flags */
464 int ex1
,ex2
; /* Some temporary variables */
465 size_t size
,dnum
,dreq
;
468 v
=va_arg(args
,double);
479 { size2
=strlen(udstr
);
484 if(preci
==ULONG_MAX
) /* old default */
485 preci
=6; /* new default */
493 else if(flags
&BLANKFLAG
)
501 v
=v
*pow(10,- --ex1
); /* Caution: (int)log10(.5)!=-1 */
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 */
512 if(tolower(type
)=='g')
514 v
+=.5/pow(10,ex2
<MINFLOATSIZE
?ex2
:MINFLOATSIZE
); /* Round up */
516 if(v
>=10.0) /* Adjusts log10(10.)=.999999999 too */
520 if(tolower(type
)=='g') /* This changes to one of the other types */
521 { if(ex1
<(signed long)preci
&&ex1
>=-4)
525 type
=type
=='g'?'e':'E';
527 if(!(flags
&ALTERNATEFLAG
))
528 killzeros
=1; /* set flag to kill trailing zeros */
531 dreq
=preci
+1; /* Calculate number of decimal places required */
533 dreq
+=ex1
; /* even more before the decimal point */
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'))
544 if(type
=='f')/* Calculate actual size of string (without sign) */
545 { size
=preci
+1; /* numbers after decimal point + 1 before */
547 size
+=ex1
; /* numbers >= 10 */
548 if(preci
||flags
&ALTERNATEFLAG
)
549 size
++; /* 1 for decimal point */
551 { size
=preci
+5; /* 1 for the number before the decimal point, and 4 for the exponent */
552 if(preci
||flags
&ALTERNATEFLAG
)
555 size
++; /* exponent needs an extra decimal place */
559 pad
=pad
>=width
?0:width
-pad
;
561 if(sign
&&flags
&ZEROPADFLAG
)
564 if(!(flags
&LALIGNFLAG
))
566 OUT(flags
&ZEROPADFLAG
?'0':' ');
568 if(sign
&&!(flags
&ZEROPADFLAG
))
577 { OUT(dreq
<dnum
?buffer
[dreq
++]:'0');
579 if(preci
||flags
&ALTERNATEFLAG
)
580 { OUT(__decimalpoint
[0]);
585 OUT(dreq
<dnum
?buffer
[dreq
++]:'0');
588 { OUT(buffer
[dreq
++]);
589 if(preci
||flags
&ALTERNATEFLAG
)
590 { OUT(__decimalpoint
[0]);
592 OUT(dreq
<dnum
?buffer
[dreq
++]:'0');
610 width
=preci
=0; /* Everything already done */
620 *va_arg(args
,int *)=outcount
;
625 ptr
--; /* We've gone too far - step one back */
626 buffer2
=(char *)format
;
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 */
638 if(!(flags
&LALIGNFLAG
)) /* Pad left */
640 OUT(flags
&ZEROPADFLAG
?'0':' ');
642 if(!(flags
&ZEROPADFLAG
)) /* print sign if not zero padded */
646 for(i
=size2
;i
<preci
;i
++) /* extend to precision */
649 for(i
=0;i
<size2
;i
++) /* print body */
652 if(flags
&LALIGNFLAG
) /* Pad right */