application.mui: implemented MUIM_Application_UnpushMethod
[AROS.git] / compiler / stdc / __vcformat.c
blob27b820e4d0408ccf768c09b0f71214be412364a7
1 /*
2 Copyright © 1995-2013, 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 -DSTDC_STATIC.
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 <float.h>
29 #ifndef STDC_STATIC
30 #define FULL_SPECIFIERS
31 #endif
33 #ifndef BITSPERBYTE
34 # define BITSPERBYTE 8
35 #endif
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
40 #endif
42 /* a little macro to make life easier */
43 #define OUT(c) do \
44 { if((*outc)((c),data)==EOF) \
45 return outcount; \
46 outcount++; \
47 }while(0)
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)
66 size_t size = 0;
67 char hex = 'a' - 10;
68 unsigned char mask = 0;
69 unsigned char shift = 0;
71 switch (type)
73 case 'X':
74 hex = 'A' - 10;
76 case 'x':
77 shift = 4;
78 mask = 0x0F;
79 break;
81 case 'o':
82 shift = 3;
83 mask = 0x07;
84 break;
86 default: /* 'd' and 'u' */
87 /* Use slow divide operations for decimal numbers */
90 char c = v % 10;
92 *--buffer = c + '0';
93 v /= 10;
94 size++;
95 } while (v);
97 return size;
100 /* Divisor is a power of 2, so use fast shifts for division */
103 char c = v & mask;
105 *--buffer = (c < 10) ? c + '0' : c + hex;
106 v >>= shift;
107 size++;
108 } while (v);
110 return size;
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)
122 size_t size = 0;
123 char hex = 'a' - 10;
124 unsigned char mask = 0;
125 unsigned char shift = 0;
127 switch (type)
129 case 'X':
130 hex = 'A' - 10;
132 case 'x':
133 shift = 4;
134 mask = 0x0F;
135 break;
137 case 'o':
138 shift = 3;
139 mask = 0x07;
140 break;
142 default:
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.
153 #ifndef STDC_LIB32
156 char c = v % 10;
158 *--buffer = c + '0';
159 v /= 10;
160 size++;
161 } while (v);
162 #endif
164 return size;
169 char c = v & mask;
171 *--buffer = (c < 10) ? c + '0' : c + hex;
172 v >>= shift;
173 size++;
174 } while (v);
176 return size;
179 #endif
181 /*****************************************************************************
183 NAME */
185 int __vcformat (
187 /* SYNOPSIS */
188 void * data,
189 int (* outc)(int, void *),
190 const char * format,
191 va_list args)
193 /* FUNCTION
194 Format a list of arguments and call a function for each char
195 to print.
197 INPUTS
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
201 > 0 otherwise.
202 format - A printf() format string.
203 args - A list of arguments for the format string.
205 RESULT
206 The number of characters written.
208 NOTES
210 EXAMPLE
212 BUGS
214 SEE ALSO
216 INTERNALS
218 ******************************************************************************/
220 size_t outcount=0;
222 while(*format)
224 if(*format=='%')
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
230 char lltype=0;
231 #endif
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 */
239 do /* read flags */
240 for(i=0;i<sizeof(flagc);i++)
241 if(flagc[i]==*ptr)
242 { flags|=1<<i;
243 ptr++;
244 break; }
245 while(i<sizeof(flagc));
247 if(*ptr=='*') /* read width from arguments */
248 { signed int a;
249 ptr++;
250 a=va_arg(args,signed int);
251 if(a<0)
252 { flags|=LALIGNFLAG;
253 width=-a; }
254 else
255 width=a;
256 }else
257 while(isdigit(*ptr))
258 width=width*10+(*ptr++-'0');
260 if(*ptr=='.')
261 { ptr++;
262 if(*ptr=='*') /* read precision from arguments */
263 { signed int a;
264 ptr++;
265 a=va_arg(args,signed int);
266 if(a>=0)
267 preci=a;
268 }else
269 { preci=0;
270 while(isdigit(*ptr))
271 preci=preci*10+(*ptr++-'0');
275 if (*ptr == 'h' || *ptr == 'l' || *ptr == 'L' || *ptr == 'z')
276 subtype=*ptr++;
278 if (*ptr == 'l' || *ptr == 'q')
280 #ifdef AROS_HAVE_LONG_LONG
281 lltype = 1;
282 subtype = 'l';
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;